1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if canImport(FoundationEssentials)
import FoundationEssentials
#endif
internal import _FoundationICU
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
internal final class ICURelativeDateFormatter : @unchecked Sendable {
struct Signature : Hashable {
let localeIdentifier: String
let numberFormatStyle: UNumberFormatStyle.RawValue?
let relativeDateStyle: UDateRelativeDateTimeFormatterStyle.RawValue
let context: UDisplayContext.RawValue
}
static let sortedAllowedComponents : [Calendar.Component] = [ .year, .month, .weekOfMonth, .day, .hour, .minute, .second ]
static let componentsToURelativeDateUnit : [Calendar.Component: URelativeDateTimeUnit] = [
.year: .year,
.month: .month,
.weekOfMonth: .week,
.day: .day,
.hour: .hour,
.minute: .minute,
.second: .second
]
/// `Sendable` notes: `ureldatefmt_format` is thread safe after initialization.
let uformatter: OpaquePointer
internal static let cache = FormatterCache<Signature, ICURelativeDateFormatter?>()
private init?(signature: Signature) {
var status = U_ZERO_ERROR
let numberFormat: UnsafeMutablePointer<UNumberFormat?>?
if let numberFormatStyle = signature.numberFormatStyle {
// The uformatter takes ownership of this after we pass it to the open call below
numberFormat = unum_open(UNumberFormatStyle(rawValue: numberFormatStyle), nil, 0, signature.localeIdentifier, nil, &status)
// If status is not a success, simply use nil
} else {
numberFormat = nil
}
let result = ureldatefmt_open(signature.localeIdentifier, numberFormat, UDateRelativeDateTimeFormatterStyle(rawValue: signature.relativeDateStyle), UDisplayContext(rawValue: signature.context), &status)
guard let result, status.isSuccess else { return nil }
uformatter = result
}
deinit {
ureldatefmt_close(uformatter)
}
func format(value: Int, component: Calendar.Component, presentation: Date.RelativeFormatStyle.Presentation) -> String? {
guard let urelUnit = Self.componentsToURelativeDateUnit[component] else { return nil }
switch presentation.option {
case .named:
return _withResizingUCharBuffer { buffer, size, status in
ureldatefmt_format(uformatter, Double(value), urelUnit, buffer, size, &status)
}
case .numeric:
return _withResizingUCharBuffer { buffer, size, status in
ureldatefmt_formatNumeric(uformatter, Double(value), urelUnit, buffer, size, &status)
}
}
}
internal static func formatter(for style: Date.RelativeFormatStyle) -> ICURelativeDateFormatter {
let signature = Signature(localeIdentifier: style.locale.identifier, numberFormatStyle: style.unitsStyle.icuNumberFormatStyle?.rawValue, relativeDateStyle: style.unitsStyle.icuRelativeDateStyle.rawValue, context: style.capitalizationContext.icuContext.rawValue)
let formatter = Self.cache.formatter(for: signature) {
ICURelativeDateFormatter(signature: signature)
}
return formatter!
}
}
|