Как форматировать временные интервалы для пользовательского отображения (например, в социальной сети)?

У меня есть временной интервал, скажем, 12600, что эквивалентно 3 часам 30 минут. Как я мог отформатировать любой такой временной интервал, чтобы сохранить только самую большую часть интервала (например, в этом случае цифра, часы) и добавить к этому правилу аббревиатуру правильной локали. Например, 10 м (10 минут), 3d (3 дня), 1 год (1 год).


EDIT: Вот несколько примеров:

Time interval in: 90000    Whole string: 1d      String out: 1d
Time interval in: 900      Whole string: 15m     String out: 15m
Time interval in: 13500    Whole String: 3h 45m  String out: 4h

Как правило, применяйте обычные правила округления (3,4 раунда вниз, 3,6 раунда).

Ответ 1

Используйте DateComponentFormatter, доступно с iOS 8:

func format(duration: TimeInterval) -> String {
    let formatter = DateComponentsFormatter()
    formatter.allowedUnits = [.day, .hour, .minute, .second]
    formatter.unitsStyle = .abbreviated
    formatter.maximumUnitCount = 1

    return formatter.string(from: duration)!
}

for d in [12600.0, 90000.0, 900.0, 13500.0] {
    let str = format(duration: d)
    print("\(d): \(str)")
}

Это печатает:

12600.0: 4h
90000.0: 1d
900.0: 15m
13500.0: 4h

Ответ 2

Расширение Swift 3:

extension TimeInterval {

  func format() -> String? {
    let formatter = DateComponentsFormatter()
    formatter.allowedUnits = [.day, .hour, .minute, .second, .nanosecond]
    formatter.unitsStyle = .abbreviated
    formatter.maximumUnitCount = 1
    return formatter.string(from: self)
  }
}

Ответ 3

На всякий случай, если кто-то захочет. Swift 4

extension TimeInterval {
    func format(using units: NSCalendar.Unit) -> String? {
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = units
        formatter.unitsStyle = .abbreviated
        formatter.zeroFormattingBehavior = .pad

        return formatter.string(from: self)
    }
}

Пример использования:

let value:TimeInterval =  12600.0
print("\(value.format(using: [.hour, .minute, .second])!)")

и результат будет:

3h 30m 0s

Ответ 4

Вы можете использовать NSDate и NSCalendar. Вы можете сказать что-то вроде:

let timeInterval:Double = 12600
let calendar = NSCalendar.currentCalendar()
let date = NSDate(timeInterval: -timeInterval, sinceDate: NSDate())
let components = calendar.components([.Year,.Day,.Hour, .Minute, .Second, .Nanosecond], fromDate: date, toDate: NSDate(), options: [])

let hour = components.hour //3
let minute = components.minute //30

В предложениях для duncan и rmaddy используется NSDateComponentsFormatter

Ответ 5

Взгляните на класс NSDateComponentsFormatter. Он позволяет вам рассчитать любые единицы, которые вы хотите либо с использованием двух дат, либо с использованием NSTimeInterval, и автоматически поддерживать разные языки и локали. Там было несколько сообщений здесь в SO по этому вопросу.

Ответ 6

Я создал для вас функцию! Я надеюсь тебе понравится. И это очень легко реализовать и очень настраиваемый.

func totime(time: Double) -> (String) {

var timex = time

var fancytime: String = "a while"

if time < 61 {
    fancytime = "\(timex)s"
} else if time < 3601 {
    timex = timex/60
    timex = round(timex)
    fancytime = "\(timex)m"
} else if time < 86401 {
    timex = timex/3600
    timex = round(timex)
    fancytime = "\(timex)h"
} else if Double(time) < 3.15576E+07 {
    timex = timex/86400
    timex = round(timex)
    fancytime = "\(timex)d"
} else {
    fancytime = "more than one year"
}

fancytime = fancytime.stringByReplacingOccurrencesOfString(".0", withString: "")

return fancytime
}

Протестировано, и оно работает безупречно:

print(totime(90000)) // prints "1d"
print(totime(900)) // prints "15m"
print(totime(13500)) // prints "4h"

Просто назовите его totime(Double).