blob: ffe0ae07f56fc601b6ad59ec5c4b6bbe0c1aa57a [file] [log] [blame]
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
import CoreFoundation
#if _runtime(_ObjC)
internal let kCFDateIntervalFormatterNoStyle = CFDateIntervalFormatterStyle.noStyle
internal let kCFDateIntervalFormatterShortStyle = CFDateIntervalFormatterStyle.shortStyle
internal let kCFDateIntervalFormatterMediumStyle = CFDateIntervalFormatterStyle.mediumStyle
internal let kCFDateIntervalFormatterLongStyle = CFDateIntervalFormatterStyle.longStyle
internal let kCFDateIntervalFormatterFullStyle = CFDateIntervalFormatterStyle.fullStyle
internal let kCFDateIntervalFormatterBoundaryStyleDefault = _CFDateIntervalFormatterBoundaryStyle.cfDateIntervalFormatterBoundaryStyleDefault
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
internal let kCFDateIntervalFormatterBoundaryStyleMinimizeAdjacentMonths = _CFDateIntervalFormatterBoundaryStyle.cfDateIntervalFormatterBoundaryStyleMinimizeAdjacentMonths
#endif
#endif
extension DateIntervalFormatter {
// Keep these in sync with CFDateIntervalFormatterStyle.
public enum Style: UInt {
case none = 0
case short = 1
case medium = 2
case long = 3
case full = 4
}
}
internal extension DateIntervalFormatter.Style {
init(_ cfStyle: CFDateIntervalFormatterStyle) {
switch cfStyle {
case kCFDateIntervalFormatterNoStyle: self = .none
case kCFDateIntervalFormatterShortStyle: self = .short
case kCFDateIntervalFormatterMediumStyle: self = .medium
case kCFDateIntervalFormatterLongStyle: self = .long
case kCFDateIntervalFormatterFullStyle: self = .full
default: fatalError()
}
}
}
internal extension CFDateIntervalFormatterStyle {
init(_ style: DateIntervalFormatter.Style) {
switch style {
case .none: self = kCFDateIntervalFormatterNoStyle
case .short: self = kCFDateIntervalFormatterShortStyle
case .medium: self = kCFDateIntervalFormatterMediumStyle
case .long: self = kCFDateIntervalFormatterLongStyle
case .full: self = kCFDateIntervalFormatterFullStyle
}
}
}
internal extension DateIntervalFormatter.BoundaryStyle {
init(_ cfStyle: _CFDateIntervalFormatterBoundaryStyle) {
switch cfStyle {
case kCFDateIntervalFormatterBoundaryStyleDefault: self = .default
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
case kCFDateIntervalFormatterBoundaryStyleMinimizeAdjacentMonths: self = .minimizeAdjacentMonths
#endif
default: fatalError()
}
}
}
internal extension _CFDateIntervalFormatterBoundaryStyle {
init(_ style: DateIntervalFormatter.BoundaryStyle) {
switch style {
case .default: self = kCFDateIntervalFormatterBoundaryStyleDefault
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
case .minimizeAdjacentMonths: self = kCFDateIntervalFormatterBoundaryStyleMinimizeAdjacentMonths
#endif
}
}
}
// DateIntervalFormatter is used to format the range between two NSDates in a locale-sensitive way.
// DateIntervalFormatter returns nil and NO for all methods in Formatter.
open class DateIntervalFormatter: Formatter {
let core: CFDateIntervalFormatter
public override init() {
core = CFDateIntervalFormatterCreate(nil, nil, kCFDateIntervalFormatterShortStyle, kCFDateIntervalFormatterShortStyle)
super.init()
}
private init(cfFormatter: CFDateIntervalFormatter) {
self.core = cfFormatter
super.init()
}
public required init?(coder: NSCoder) {
guard coder.allowsKeyedCoding else { fatalError("Requires a keyed coding-capable archiver.") }
func cfObject<T: NSObject & _CFBridgeable>(of aClass: T.Type, from coder: NSCoder, forKey key: String) -> T.CFType? {
if coder.containsValue(forKey: key) {
let object = coder.decodeObject(forKey: key) as? T
return object?._cfObject
} else {
return nil
}
}
let core = CFDateIntervalFormatterCreate(nil, nil, kCFDateIntervalFormatterMediumStyle, kCFDateIntervalFormatterMediumStyle)
_CFDateIntervalFormatterInitializeFromCoderValues(core,
coder.decodeInt64(forKey: "NS.dateStyle"),
coder.decodeInt64(forKey: "NS.timeStyle"),
cfObject(of: NSString.self, from: coder, forKey: "NS.dateTemplate"),
cfObject(of: NSString.self, from: coder, forKey: "NS.dateTemplateFromStyle"),
coder.decodeBool(forKey: "NS.modified"),
coder.decodeBool(forKey: "NS.useTemplate"),
cfObject(of: NSLocale.self, from: coder, forKey: "NS.locale"),
cfObject(of: NSCalendar.self, from: coder, forKey: "NS.calendar"),
cfObject(of: NSTimeZone.self, from: coder, forKey: "NS.timeZone"))
self.core = core
super.init(coder: coder)
}
open override func encode(with aCoder: NSCoder) {
guard aCoder.allowsKeyedCoding else { fatalError("Requires a keyed coding-capable archiver.") }
super.encode(with: aCoder)
var dateStyle: Int64 = 0
var timeStyle: Int64 = 0
var dateTemplate: Unmanaged<CFString>?
var dateTemplateFromStyles: Unmanaged<CFString>?
var modified: Bool = false
var useTemplate: Bool = false
var locale: Unmanaged<CFLocale>?
var calendar: Unmanaged<CFCalendar>?
var timeZone: Unmanaged<CFTimeZone>?
_CFDateIntervalFormatterCopyCoderValues(core,
&dateStyle,
&timeStyle,
&dateTemplate,
&dateTemplateFromStyles,
&modified,
&useTemplate,
&locale,
&calendar,
&timeZone);
aCoder.encode(dateStyle, forKey: "NS.dateStyle")
aCoder.encode(timeStyle, forKey: "NS.timeStyle")
let dateTemplateNS = dateTemplate?.takeRetainedValue()._nsObject
aCoder.encode(dateTemplateNS, forKey: "NS.dateTemplate")
let dateTemplateFromStylesNS = dateTemplateFromStyles?.takeRetainedValue()._nsObject
aCoder.encode(dateTemplateFromStylesNS, forKey: "NS.dateTemplateFromStyles")
aCoder.encode(modified, forKey: "NS.modified");
aCoder.encode(useTemplate, forKey: "NS.useTemplate")
let localeNS = locale?.takeRetainedValue()._nsObject
aCoder.encode(localeNS, forKey: "NS.locale")
let calendarNS = calendar?.takeRetainedValue()._nsObject
aCoder.encode(calendarNS, forKey: "NS.calendar")
let timeZoneNS = timeZone?.takeRetainedValue()._nsObject
aCoder.encode(timeZoneNS, forKey: "NS.timeZone")
}
/*@NSCopying*/ open var locale: Locale! {
get { return CFDateIntervalFormatterCopyLocale(core)._swiftObject }
set { CFDateIntervalFormatterSetLocale(core, newValue?._cfObject) }
}
/*@NSCopying*/ open var calendar: Calendar! {
get { return CFDateIntervalFormatterCopyCalendar(core)._swiftObject }
set { CFDateIntervalFormatterSetCalendar(core, newValue?._cfObject) }
}
/*@NSCopying*/ open var timeZone: TimeZone! {
get { return CFDateIntervalFormatterCopyTimeZone(core)._swiftObject }
set { CFDateIntervalFormatterSetTimeZone(core, newValue?._cfObject) }
}
open var dateTemplate: String! {
get { return CFDateIntervalFormatterCopyDateTemplate(core)._swiftObject }
set { CFDateIntervalFormatterSetDateTemplate(core, newValue?._cfObject) }
}
open var dateStyle: Style {
get { return Style(CFDateIntervalFormatterGetDateStyle(core)) }
set { CFDateIntervalFormatterSetDateStyle(core, CFDateIntervalFormatterStyle(newValue)) }
}
open var timeStyle: Style {
get { return Style(CFDateIntervalFormatterGetTimeStyle(core)) }
set { CFDateIntervalFormatterSetTimeStyle(core, CFDateIntervalFormatterStyle(newValue)) }
}
internal enum BoundaryStyle: UInt {
case `default` = 0
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
case minimizeAdjacentMonths = 1
#endif
}
internal var boundaryStyle: BoundaryStyle {
get { return BoundaryStyle(_CFDateIntervalFormatterGetBoundaryStyle(core)) }
set { _CFDateIntervalFormatterSetBoundaryStyle(core, _CFDateIntervalFormatterBoundaryStyle(newValue) )}
}
/*
If the range smaller than the resolution specified by the dateTemplate, a single date format will be produced. If the range is larger than the format specified by the dateTemplate, a locale-specific fallback will be used to format the items missing from the pattern.
For example, if the range is 2010-03-04 07:56 - 2010-03-04 19:56 (12 hours)
- The pattern jm will produce
for en_US, "7:56 AM - 7:56 PM"
for en_GB, "7:56 - 19:56"
- The pattern MMMd will produce
for en_US, "Mar 4"
for en_GB, "4 Mar"
If the range is 2010-03-04 07:56 - 2010-03-08 16:11 (4 days, 8 hours, 15 minutes)
- The pattern jm will produce
for en_US, "3/4/2010 7:56 AM - 3/8/2010 4:11 PM"
for en_GB, "4/3/2010 7:56 - 8/3/2010 16:11"
- The pattern MMMd will produce
for en_US, "Mar 4-8"
for en_GB, "4-8 Mar"
*/
open func string(from fromDate: Date, to toDate: Date) -> String {
return CFDateIntervalFormatterCreateStringFromDateToDate(core, fromDate._cfObject, toDate._cfObject)._swiftObject
}
open func string(from dateInterval: DateInterval) -> String? {
let result = CFDateIntervalFormatterCreateStringFromDateToDate(core, dateInterval.start._cfObject, dateInterval.end._cfObject)._swiftObject
return result.isEmpty ? nil : result
}
open override func string(for obj: Any) -> String? {
guard let interval = obj as? DateInterval else {
return nil
}
return string(from: interval)
}
open override func editingString(for obj: Any) -> String? {
return nil
}
open override func copy(with zone: NSZone? = nil) -> Any {
return DateIntervalFormatter(cfFormatter: CFDateIntervalFormatterCreateCopy(nil, core))
}
}