iOS DateComponentsFormatter - использовать единицы измерения

Я хотел бы преобразовать время из секунд в позиционный формат, например 2:05 мин или 1:23 ч или 19 с. У меня проблема с получением локализованной сокращенной единицы времени. Вот мой код.

let secs: Double = 3801
let stopWatchFormatter = DateComponentsFormatter()
stopWatchFormatter.unitsStyle = .positional
stopWatchFormatter.maximumUnitCount = 2
stopWatchFormatter.allowedUnits = [.hour, .minute, .second]
print(stopWatchFormatter.string(from: secs)) // 1:03
stopWatchFormatter.unitsStyle = .short
print(stopWatchFormatter.string(from: secs)) // 1 hr, 3 min

Как вы можете видеть, 3801 секунда отформатирована в 1:03, что нормально, но я не знаю, использовались ли DateComponentsFormatter часы или минуты и т. д. Я могу использовать простую логику MOD, чтобы проверить это, но затем наступает сложная часть локализации. Также обратите внимание, что решение MOD бесполезно, если я устанавливаю collapsesLargestUnit в false.


person jendan    schedule 12.01.2018    source источник
comment
Если вы выберете .positional и используете maximumUnitCount, вы просто не узнаете. Вам нужно будет либо использовать .short (1 час 3 минуты), либо .abbreviated (1 час 3 минуты). Если вы хотите 1:03 мин (я бы не рекомендовал это, поскольку мне не совсем понятно, что это значит), вам придется получить DateComponents и самостоятельно создать локализованную строку (с вашей собственной локализованной версией любых сокращений). ты хочешь). Но DateComponentsFormatter не может сделать для вас этот пользовательский формат...   -  person Rob    schedule 12.01.2018


Ответы (2)


DateComponentsFormatter не поддерживает напрямую нужный вам формат, который по сути является позиционным форматом, но с первой единицей короткого формата, показанной в конце.

Следующая вспомогательная функция объединяет эти два отдельных результата в то, что вы хотите. Это должно работать с любой локалью, но для подтверждения этого необходимо провести тщательное тестирование.

func formatStopWatchTime(seconds: Double) -> String {
    let stopWatchFormatter = DateComponentsFormatter()
    stopWatchFormatter.unitsStyle = .positional
    stopWatchFormatter.maximumUnitCount = 2
    stopWatchFormatter.allowedUnits = [.hour, .minute, .second]
    var pos = stopWatchFormatter.string(from: seconds)!

    // Work around a bug where some values return 3 units despite only requesting 2 units
    let parts = pos.components(separatedBy: CharacterSet.decimalDigits.inverted)
    if parts.count > 2 {
        let seps = pos.components(separatedBy: .decimalDigits).filter { !$0.isEmpty }
        pos = parts[0..<2].joined(separator: seps[0])
    }

    stopWatchFormatter.maximumUnitCount = 1
    stopWatchFormatter.unitsStyle = .short
    let unit = stopWatchFormatter.string(from: seconds)!

    // Replace the digits in the unit result with the pos result
    let res = unit.replacingOccurrences(of: "[\\d]+", with: pos, options: [.regularExpression])

    return res
}

print(formatStopWatchTime(seconds: 3801))

Выход:

1:03 hr

person rmaddy    schedule 12.01.2018
comment
Я заметил, что он не работает в течение 3601 секунды. Я ожидал вывода 1:00 час, но я получил 1:00:01 час. Другими словами, он показывает не 2 единицы, а 3. - person jendan; 10.07.2018
comment
Странный. В DateComponentsFormatter есть ошибка, из-за которой любое значение seconds, представляющее любое количество часов и минут с интервалом от 0 до 59 секунд или любое количество часов и ровно 1 минуту, приводит к тому, что в строке возвращается 3 единицы вместо запрошенных 2. Пример - любое seconds в диапазоне 3600-3660 или 7200-7260 и т. д. Я обновил ответ с помощью хака, который имеет дело с этим. - person rmaddy; 10.07.2018

Насколько я понимаю, вам нужен не String, а DateComponent:

let dateA = Date(timeIntervalSince1970: 0)
let dateB = Date(timeIntervalSince1970: timeInterval)
let components = Calendar.current.dateComponents([.hour, .minute, .second], from: dateA, to: dateB)
person MichaelV    schedule 12.01.2018
comment
Нет. Мне нужна локализованная строка в формате xx:yy unit, где xx — самое большое ненулевое значение, а yy следует за ним, unit — это локализованная строка единиц измерения, используемая для xx. Это для секундомера цели. Другими словами, мне нужно что-то вроде это, но по времени, а не по длине . - person jendan; 12.01.2018
comment
DateComponentsFormatter отсутствует индивидуальный стиль. Имея DateComponents, вы можете распечатать их в любом удобном для вас формате. - person MichaelV; 15.01.2018