Merge pull request #609 from itaiferber/pr-datetime-fixes
Swift 3 API parity for [NS]TimeZone & [NS]Calendar
diff --git a/Foundation/Calendar.swift b/Foundation/Calendar.swift
index be2bfc0..b3a59c3 100644
--- a/Foundation/Calendar.swift
+++ b/Foundation/Calendar.swift
@@ -20,14 +20,14 @@
return CFCalendarCopyCurrent()._nsObject
}
-internal func __NSCalendarInit(_ identifier : String) -> NSCalendar? {
+internal func __NSCalendarInit(_ identifier: String) -> NSCalendar? {
return NSCalendar(identifier: NSCalendar.Identifier(rawValue: identifier))
}
/**
`Calendar` encapsulates information about systems of reckoning time in which the beginning, length, and divisions of a year are defined. It provides information about the calendar and support for calendrical computations such as determining the range of a given calendrical unit and adding units to a given absolute time.
*/
-public struct Calendar : CustomStringConvertible, CustomDebugStringConvertible, Hashable, Equatable, ReferenceConvertible, _MutableBoxing {
+public struct Calendar : Hashable, Equatable, ReferenceConvertible, _MutableBoxing {
public typealias ReferenceType = NSCalendar
internal var _autoupdating: Bool
@@ -84,9 +84,13 @@
}
/// Returns the user's current calendar. This calendar does not track changes that the user makes to their preferences.
- public static var current : Calendar {
+ public static var current: Calendar {
return Calendar(adoptingReference: __NSCalendarCurrent(), autoupdating: false)
}
+
+ public static var autoupdatingCurrent: Calendar {
+ return Calendar(adoptingReference: __NSCalendarCurrent(), autoupdating: true)
+ }
// MARK: -
@@ -104,7 +108,7 @@
// MARK: -
// MARK: Bridging
- internal init(reference : NSCalendar) {
+ internal init(reference: NSCalendar) {
_handle = _MutableHandle(reference: reference)
if __NSCalendarIsAutoupdating(reference) {
_autoupdating = true
@@ -122,12 +126,12 @@
//
/// The identifier of the calendar.
- public var identifier : Identifier {
+ public var identifier: Identifier {
return Calendar._fromNSCalendarIdentifier(_handle.map({ $0.calendarIdentifier }))
}
/// The locale of the calendar.
- public var locale : Locale? {
+ public var locale: Locale? {
get {
return _handle.map { $0.locale }
}
@@ -137,7 +141,7 @@
}
/// The time zone of the calendar.
- public var timeZone : TimeZone {
+ public var timeZone: TimeZone {
get {
return _handle.map { $0.timeZone }
}
@@ -147,7 +151,7 @@
}
/// The first weekday of the calendar.
- public var firstWeekday : Int {
+ public var firstWeekday: Int {
get {
return _handle.map { $0.firstWeekday }
}
@@ -157,7 +161,7 @@
}
/// The number of minimum days in the first week.
- public var minimumDaysInFirstWeek : Int {
+ public var minimumDaysInFirstWeek: Int {
get {
return _handle.map { $0.minimumDaysInFirstWeek }
}
@@ -409,8 +413,8 @@
/// - parameter date: The specified date.
/// - returns: A new `DateInterval` if the starting time and duration of a component could be calculated, otherwise `nil`.
public func dateInterval(of component: Component, for date: Date) -> DateInterval? {
- var start : Date = Date(timeIntervalSinceReferenceDate: 0)
- var interval : TimeInterval = 0
+ var start: Date = Date(timeIntervalSinceReferenceDate: 0)
+ var interval: TimeInterval = 0
if self.dateInterval(of: component, start: &start, interval: &interval, for: date) {
return DateInterval(start: start, duration: interval)
} else {
@@ -847,15 +851,7 @@
// MARK: -
- public var description: String {
- return _handle.map { $0.description }
- }
-
- public var debugDescription : String {
- return _handle.map { $0.debugDescription }
- }
-
- public var hashValue : Int {
+ public var hashValue: Int {
// We implement hash ourselves, because we need to make sure autoupdating calendars have the same hash
if _autoupdating {
return 1
@@ -864,12 +860,25 @@
}
}
+ public static func ==(lhs: Calendar, rhs: Calendar) -> Bool {
+ if lhs._autoupdating || rhs._autoupdating {
+ return lhs._autoupdating == rhs._autoupdating
+ } else {
+ // NSCalendar's isEqual is broken (27019864) so we must implement this ourselves
+ return lhs.identifier == rhs.identifier &&
+ lhs.locale == rhs.locale &&
+ lhs.timeZone == rhs.timeZone &&
+ lhs.firstWeekday == rhs.firstWeekday &&
+ lhs.minimumDaysInFirstWeek == rhs.minimumDaysInFirstWeek
+ }
+ }
+
// MARK: -
// MARK: Conversion Functions
/// Turn our more-specific options into the big bucket option set of NSCalendar
internal static func _toCalendarOptions(matchingPolicy: MatchingPolicy, repeatedTimePolicy: RepeatedTimePolicy, direction: SearchDirection) -> NSCalendar.Options {
- var result : NSCalendar.Options = []
+ var result: NSCalendar.Options = []
switch matchingPolicy {
case .nextTime:
@@ -899,24 +908,24 @@
return result
}
- internal static func _toCalendarUnit(_ units : Set<Component>) -> NSCalendar.Unit {
- let unitMap : [Component : NSCalendar.Unit] =
- [.era : .era,
- .year : .year,
- .month : .month,
- .day : .day,
- .hour : .hour,
- .minute : .minute,
- .second : .second,
- .weekday : .weekday,
- .weekdayOrdinal : .weekdayOrdinal,
- .quarter : .quarter,
- .weekOfMonth : .weekOfMonth,
- .weekOfYear : .weekOfYear,
- .yearForWeekOfYear : .yearForWeekOfYear,
- .nanosecond : .nanosecond,
- .calendar : .calendar,
- .timeZone : .timeZone]
+ internal static func _toCalendarUnit(_ units: Set<Component>) -> NSCalendar.Unit {
+ let unitMap: [Component : NSCalendar.Unit] =
+ [.era: .era,
+ .year: .year,
+ .month: .month,
+ .day: .day,
+ .hour: .hour,
+ .minute: .minute,
+ .second: .second,
+ .weekday: .weekday,
+ .weekdayOrdinal: .weekdayOrdinal,
+ .quarter: .quarter,
+ .weekOfMonth: .weekOfMonth,
+ .weekOfYear: .weekOfYear,
+ .yearForWeekOfYear: .yearForWeekOfYear,
+ .nanosecond: .nanosecond,
+ .calendar: .calendar,
+ .timeZone: .timeZone]
var result = NSCalendar.Unit()
for u in units {
@@ -925,7 +934,7 @@
return result
}
- internal static func _fromCalendarUnit(_ unit : NSCalendar.Unit) -> Component {
+ internal static func _fromCalendarUnit(_ unit: NSCalendar.Unit) -> Component {
switch unit {
case NSCalendar.Unit.era:
return Component.era
@@ -964,7 +973,7 @@
}
}
- internal static func _fromCalendarUnits(_ units : NSCalendar.Unit) -> Set<Component> {
+ internal static func _fromCalendarUnits(_ units: NSCalendar.Unit) -> Set<Component> {
var result = Set<Component>()
if units.contains(.era) {
result.insert(.era)
@@ -1033,104 +1042,101 @@
return result
}
- internal static func _toNSCalendarIdentifier(_ identifier : Identifier) -> NSCalendar.Identifier {
+ internal static func _toNSCalendarIdentifier(_ identifier: Identifier) -> NSCalendar.Identifier {
if #available(OSX 10.10, iOS 8.0, *) {
- let identifierMap : [Identifier : NSCalendar.Identifier] =
- [.gregorian : .gregorian,
- .buddhist : .buddhist,
- .chinese : .chinese,
- .coptic : .coptic,
- .ethiopicAmeteMihret : .ethiopicAmeteMihret,
- .ethiopicAmeteAlem : .ethiopicAmeteAlem,
- .hebrew : .hebrew,
- .iso8601 : .ISO8601,
- .indian : .indian,
- .islamic : .islamic,
- .islamicCivil : .islamicCivil,
- .japanese : .japanese,
- .persian : .persian,
- .republicOfChina : .republicOfChina,
- .islamicTabular : .islamicTabular,
- .islamicUmmAlQura : .islamicUmmAlQura]
+ let identifierMap: [Identifier : NSCalendar.Identifier] =
+ [.gregorian: .gregorian,
+ .buddhist: .buddhist,
+ .chinese: .chinese,
+ .coptic: .coptic,
+ .ethiopicAmeteMihret: .ethiopicAmeteMihret,
+ .ethiopicAmeteAlem: .ethiopicAmeteAlem,
+ .hebrew: .hebrew,
+ .iso8601: .ISO8601,
+ .indian: .indian,
+ .islamic: .islamic,
+ .islamicCivil: .islamicCivil,
+ .japanese: .japanese,
+ .persian: .persian,
+ .republicOfChina: .republicOfChina,
+ .islamicTabular: .islamicTabular,
+ .islamicUmmAlQura: .islamicUmmAlQura]
return identifierMap[identifier]!
} else {
- let identifierMap : [Identifier : NSCalendar.Identifier] =
- [.gregorian : .gregorian,
- .buddhist : .buddhist,
- .chinese : .chinese,
- .coptic : .coptic,
- .ethiopicAmeteMihret : .ethiopicAmeteMihret,
- .ethiopicAmeteAlem : .ethiopicAmeteAlem,
- .hebrew : .hebrew,
- .iso8601 : .ISO8601,
- .indian : .indian,
- .islamic : .islamic,
- .islamicCivil : .islamicCivil,
- .japanese : .japanese,
- .persian : .persian,
- .republicOfChina : .republicOfChina]
+ let identifierMap: [Identifier : NSCalendar.Identifier] =
+ [.gregorian: .gregorian,
+ .buddhist: .buddhist,
+ .chinese: .chinese,
+ .coptic: .coptic,
+ .ethiopicAmeteMihret: .ethiopicAmeteMihret,
+ .ethiopicAmeteAlem: .ethiopicAmeteAlem,
+ .hebrew: .hebrew,
+ .iso8601: .ISO8601,
+ .indian: .indian,
+ .islamic: .islamic,
+ .islamicCivil: .islamicCivil,
+ .japanese: .japanese,
+ .persian: .persian,
+ .republicOfChina: .republicOfChina]
return identifierMap[identifier]!
}
}
- internal static func _fromNSCalendarIdentifier(_ identifier : NSCalendar.Identifier) -> Identifier {
+ internal static func _fromNSCalendarIdentifier(_ identifier: NSCalendar.Identifier) -> Identifier {
if #available(OSX 10.10, iOS 8.0, *) {
- let identifierMap : [NSCalendar.Identifier : Identifier] =
- [.gregorian : .gregorian,
- .buddhist : .buddhist,
- .chinese : .chinese,
- .coptic : .coptic,
- .ethiopicAmeteMihret : .ethiopicAmeteMihret,
- .ethiopicAmeteAlem : .ethiopicAmeteAlem,
- .hebrew : .hebrew,
- .ISO8601 : .iso8601,
- .indian : .indian,
- .islamic : .islamic,
- .islamicCivil : .islamicCivil,
- .japanese : .japanese,
- .persian : .persian,
- .republicOfChina : .republicOfChina,
- .islamicTabular : .islamicTabular,
- .islamicUmmAlQura : .islamicUmmAlQura]
+ let identifierMap: [NSCalendar.Identifier : Identifier] =
+ [.gregorian: .gregorian,
+ .buddhist: .buddhist,
+ .chinese: .chinese,
+ .coptic: .coptic,
+ .ethiopicAmeteMihret: .ethiopicAmeteMihret,
+ .ethiopicAmeteAlem: .ethiopicAmeteAlem,
+ .hebrew: .hebrew,
+ .ISO8601: .iso8601,
+ .indian: .indian,
+ .islamic: .islamic,
+ .islamicCivil: .islamicCivil,
+ .japanese: .japanese,
+ .persian: .persian,
+ .republicOfChina: .republicOfChina,
+ .islamicTabular: .islamicTabular,
+ .islamicUmmAlQura: .islamicUmmAlQura]
return identifierMap[identifier]!
} else {
- let identifierMap : [NSCalendar.Identifier : Identifier] =
- [.gregorian : .gregorian,
- .buddhist : .buddhist,
- .chinese : .chinese,
- .coptic : .coptic,
- .ethiopicAmeteMihret : .ethiopicAmeteMihret,
- .ethiopicAmeteAlem : .ethiopicAmeteAlem,
- .hebrew : .hebrew,
- .ISO8601 : .iso8601,
- .indian : .indian,
- .islamic : .islamic,
- .islamicCivil : .islamicCivil,
- .japanese : .japanese,
- .persian : .persian,
- .republicOfChina : .republicOfChina]
+ let identifierMap: [NSCalendar.Identifier : Identifier] =
+ [.gregorian: .gregorian,
+ .buddhist: .buddhist,
+ .chinese: .chinese,
+ .coptic: .coptic,
+ .ethiopicAmeteMihret: .ethiopicAmeteMihret,
+ .ethiopicAmeteAlem: .ethiopicAmeteAlem,
+ .hebrew: .hebrew,
+ .ISO8601: .iso8601,
+ .indian: .indian,
+ .islamic: .islamic,
+ .islamicCivil: .islamicCivil,
+ .japanese: .japanese,
+ .persian: .persian,
+ .republicOfChina: .republicOfChina]
return identifierMap[identifier]!
}
}
}
-public func ==(lhs: Calendar, rhs: Calendar) -> Bool {
- if lhs._autoupdating || rhs._autoupdating {
- return lhs._autoupdating == rhs._autoupdating
- } else {
- // NSCalendar's isEqual is broken (27019864) so we must implement this ourselves
- return lhs.identifier == rhs.identifier &&
- lhs.locale == rhs.locale &&
- lhs.timeZone == rhs.timeZone &&
- lhs.firstWeekday == rhs.firstWeekday &&
- lhs.minimumDaysInFirstWeek == rhs.minimumDaysInFirstWeek
+extension Calendar : CustomDebugStringConvertible, CustomStringConvertible, CustomReflectable {
+ public var description: String {
+ return _handle.map { $0.description }
}
+
+ public var debugDescription: String {
+ return _handle.map { $0.debugDescription }
+ }
+
+ public var customMirror: Mirror { NSUnimplemented() }
}
-extension Calendar : _ObjectTypeBridgeable {
- public static func _isBridgedToObjectiveC() -> Bool {
- return true
- }
+extension Calendar: _ObjectTypeBridgeable {
+ public typealias _ObjectType = NSCalendar
@_semantics("convertToObjectiveC")
public func _bridgeToObjectiveC() -> NSCalendar {
@@ -1143,6 +1149,7 @@
}
}
+ @discardableResult
public static func _conditionallyBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) -> Bool {
result = Calendar(reference: input)
return true
@@ -1154,4 +1161,3 @@
return result!
}
}
-
diff --git a/Foundation/NSCalendar.swift b/Foundation/NSCalendar.swift
index 9a75d61..2fd8f55 100644
--- a/Foundation/NSCalendar.swift
+++ b/Foundation/NSCalendar.swift
@@ -119,7 +119,7 @@
public func <(_ lhs: NSCalendar.Identifier, _ rhs: NSCalendar.Identifier) -> Bool {
return lhs.rawValue < rhs.rawValue
}
-
+
open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
typealias CFType = CFCalendar
private var _base = _CFInfo(typeID: CFCalendarGetTypeID())
@@ -213,7 +213,7 @@
}
}
- public init?(calendar ident: Identifier) {
+ public init?(calendarIdentifier ident: Identifier) {
super.init()
if !_CFCalendarInitWithIdentifier(_cfObject, ident.rawValue._cfObject) {
return nil
@@ -1243,7 +1243,9 @@
// notification is received by observers in a "timely" manner, same as
// with distributed notifications.
-public let NSCalendarDayChangedNotification: String = "" // NSUnimplemented
+extension NSNotification.Name {
+ public static let NSCalendarDayChanged = NSNotification.Name(rawValue: "") // NSUnimplemented
+}
// This is a just used as an extensible struct, basically;
// note that there are two uses: one for specifying a date
@@ -1446,8 +1448,6 @@
}
/*@NSCopying*/ open var timeZone: TimeZone?
- // these all should probably be optionals
-
open var era: Int {
get {
return _values[0]
diff --git a/Foundation/NSDateFormatter.swift b/Foundation/NSDateFormatter.swift
index 9747cdc..637f24f 100644
--- a/Foundation/NSDateFormatter.swift
+++ b/Foundation/NSDateFormatter.swift
@@ -148,7 +148,7 @@
open var generatesCalendarDates = false { willSet { _reset() } }
- /*@NSCopying*/ open var timeZone: TimeZone! = NSTimeZone.systemTimeZone() { willSet { _reset() } }
+ /*@NSCopying*/ open var timeZone: TimeZone! = NSTimeZone.system { willSet { _reset() } }
/*@NSCopying*/ internal var _calendar: Calendar! { willSet { _reset() } }
open var calendar: Calendar! {
diff --git a/Foundation/NSTimeZone.swift b/Foundation/NSTimeZone.swift
index 15845fc..7259005 100644
--- a/Foundation/NSTimeZone.swift
+++ b/Foundation/NSTimeZone.swift
@@ -79,10 +79,27 @@
_CFDeinit(self)
}
+ // `init(forSecondsFromGMT:)` is not a failable initializer, so we need a designated initializer that isn't failable.
+ internal init(_name tzName: String) {
+ super.init()
+ _CFTimeZoneInit(_cfObject, tzName._cfObject, nil)
+ }
+
// Time zones created with this never have daylight savings and the
// offset is constant no matter the date; the name and abbreviation
// do NOT follow the POSIX convention (of minutes-west).
- public convenience init(forSecondsFromGMT seconds: Int) { NSUnimplemented() }
+ public convenience init(forSecondsFromGMT seconds: Int) {
+ let sign = seconds < 0 ? "-" : "+"
+ let absoluteValue = abs(seconds)
+ var minutes = absoluteValue / 60
+ if (absoluteValue % 60) >= 30 { minutes += 1 }
+ var hours = minutes / 60
+ minutes %= 60
+ hours = min(hours, 99) // Two digits only; leave CF to enforce actual max offset.
+ let mm = minutes < 10 ? "0\(minutes)" : "\(minutes)"
+ let hh = hours < 10 ? "0\(hours)" : "\(hours)"
+ self.init(_name: "GMT" + sign + hh + mm)
+ }
public convenience init?(abbreviation: String) {
let abbr = abbreviation._cfObject
@@ -95,7 +112,7 @@
open func encode(with aCoder: NSCoder) {
if aCoder.allowsKeyedCoding {
aCoder.encode(self.name._bridgeToObjectiveC(), forKey:"NS.name")
- // darwin versions of this method can and will encode mutable data, however it is not required for compatability
+ // Darwin versions of this method can and will encode mutable data, however it is not required for compatibility
aCoder.encode(self.data._bridgeToObjectiveC(), forKey:"NS.data")
} else {
}
@@ -165,7 +182,7 @@
extension NSTimeZone {
- open class func systemTimeZone() -> TimeZone {
+ open class var system: TimeZone {
return CFTimeZoneCopySystem()._swiftObject
}
@@ -173,13 +190,76 @@
CFTimeZoneResetSystem()
}
- open class func defaultTimeZone() -> TimeZone {
- return CFTimeZoneCopyDefault()._swiftObject
+ open class var `default`: TimeZone {
+ get {
+ return CFTimeZoneCopyDefault()._swiftObject
+ }
+ set {
+ CFTimeZoneSetDefault(newValue._cfObject)
+ }
}
- open class func setDefaultTimeZone(_ aTimeZone: TimeZone) {
- CFTimeZoneSetDefault(aTimeZone._cfObject)
+ open class var local: TimeZone { NSUnimplemented() }
+
+ open class var knownTimeZoneNames: [String] {
+ guard let knownNames = CFTimeZoneCopyKnownNames() else { return [] }
+ return knownNames._nsObject._bridgeToSwift() as! [String]
}
+
+ open class var abbreviationDictionary: [String : String] {
+ get {
+ guard let dictionary = CFTimeZoneCopyAbbreviationDictionary() else { return [:] }
+ return dictionary._nsObject._bridgeToSwift() as! [String : String]
+ }
+ set {
+ // CFTimeZoneSetAbbreviationDictionary(newValue._cfObject)
+ NSUnimplemented()
+ }
+ }
+
+ open class var timeZoneDataVersion: String { NSUnimplemented() }
+
+ open var secondsFromGMT: Int {
+ let currentDate = Date()
+ return secondsFromGMT(for: currentDate)
+ }
+
+ /// The abbreviation for the receiver, such as "EDT" (Eastern Daylight Time). (read-only)
+ ///
+ /// This invokes `abbreviationForDate:` with the current date as the argument.
+ open var abbreviation: String? {
+ let currentDate = Date()
+ return abbreviation(for: currentDate)
+ }
+
+ open var isDaylightSavingTime: Bool {
+ let currentDate = Date()
+ return isDaylightSavingTime(for: currentDate)
+ }
+
+ open var daylightSavingTimeOffset: TimeInterval {
+ let currentDate = Date()
+ return daylightSavingTimeOffset(for: currentDate)
+ }
+
+ /*@NSCopying*/ open var nextDaylightSavingTimeTransition: Date? {
+ let currentDate = Date()
+ return nextDaylightSavingTimeTransition(after: currentDate)
+ }
+
+ open func isEqual(to aTimeZone: TimeZone) -> Bool {
+ return CFEqual(self._cfObject, aTimeZone._cfObject)
+ }
+
+ open func localizedName(_ style: NameStyle, locale: Locale?) -> String? {
+ #if os(OSX) || os(iOS)
+ let cfStyle = CFTimeZoneNameStyle(rawValue: style.rawValue)!
+ #else
+ let cfStyle = CFTimeZoneNameStyle(style.rawValue)
+ #endif
+ return CFTimeZoneCopyLocalizedName(self._cfObject, cfStyle, locale?._cfObject ?? CFLocaleCopyCurrent())._swiftObject
+ }
+
}
extension NSTimeZone: _SwiftBridgable, _CFBridgable {
@@ -201,43 +281,6 @@
}
extension NSTimeZone {
- open class func localTimeZone() -> TimeZone { NSUnimplemented() }
-
- open class var knownTimeZoneNames: [String] { NSUnimplemented() }
-
- open class var abbreviationDictionary: [String : String] {
- get {
- NSUnimplemented()
- }
- set {
- NSUnimplemented()
- }
- }
-
- open class var timeZoneDataVersion: String { NSUnimplemented() }
-
- open var secondsFromGMT: Int { NSUnimplemented() }
-
- /// The abbreviation for the receiver, such as "EDT" (Eastern Daylight Time). (read-only)
- ///
- /// This invokes `abbreviationForDate:` with the current date as the argument.
- open var abbreviation: String? {
- let currentDate = Date()
- return abbreviation(for: currentDate)
- }
-
- open var daylightSavingTime: Bool { NSUnimplemented() }
- open var daylightSavingTimeOffset: TimeInterval { NSUnimplemented() }
- /*@NSCopying*/ open var nextDaylightSavingTimeTransition: Date? { NSUnimplemented() }
-
- open func isEqual(to aTimeZone: TimeZone) -> Bool {
- return CFEqual(self._cfObject, aTimeZone._cfObject)
- }
-
- open func localizedName(_ style: NameStyle, locale: Locale?) -> String? { NSUnimplemented() }
-}
-
-extension NSTimeZone {
public enum NameStyle : Int {
case standard // Central Standard Time
@@ -249,3 +292,7 @@
}
}
+
+extension NSNotification.Name {
+ public static let NSSystemTimeZoneDidChange = NSNotification.Name(rawValue: "") // NSUnimplemented
+}
diff --git a/Foundation/TimeZone.swift b/Foundation/TimeZone.swift
index 87b379b..fd998df 100644
--- a/Foundation/TimeZone.swift
+++ b/Foundation/TimeZone.swift
@@ -11,13 +11,16 @@
//===----------------------------------------------------------------------===//
-
internal func __NSTimeZoneIsAutoupdating(_ timezone: NSTimeZone) -> Bool {
return false
}
+internal func __NSTimeZoneAutoupdating() -> NSTimeZone {
+ return NSTimeZone.local._nsObject
+}
+
internal func __NSTimeZoneCurrent() -> NSTimeZone {
- fatalError()
+ return NSTimeZone.system._nsObject
}
/**
@@ -29,7 +32,7 @@
Cocoa does not provide any API to change the time zone of the computer, or of other applications.
*/
-public struct TimeZone : CustomStringConvertible, CustomDebugStringConvertible, Hashable, Equatable, ReferenceConvertible {
+public struct TimeZone : Hashable, Equatable, ReferenceConvertible {
public typealias ReferenceType = NSTimeZone
internal var _wrapped : NSTimeZone
@@ -39,6 +42,15 @@
public static var current : TimeZone {
return TimeZone(adoptingReference: __NSTimeZoneCurrent(), autoupdating: false)
}
+
+ /// The time zone currently used by the system, automatically updating to the user's current preference.
+ ///
+ /// If this time zone is mutated, then it no longer tracks the application time zone.
+ ///
+ /// The autoupdating time zone only compares equal to itself.
+ public static var autoupdatingCurrent : TimeZone {
+ return TimeZone(adoptingReference: __NSTimeZoneAutoupdating(), autoupdating: true)
+ }
// MARK: -
//
@@ -194,14 +206,6 @@
// MARK: -
- public var description: String {
- return _wrapped.description
- }
-
- public var debugDescription : String {
- return _wrapped.debugDescription
- }
-
public var hashValue : Int {
if _autoupdating {
return 1
@@ -209,13 +213,41 @@
return _wrapped.hash
}
}
+
+ public static func ==(lhs: TimeZone, rhs: TimeZone) -> Bool {
+ if lhs._autoupdating || rhs._autoupdating {
+ return lhs._autoupdating == rhs._autoupdating
+ } else {
+ return lhs._wrapped.isEqual(rhs._wrapped)
+ }
+ }
}
-public func ==(_ lhs: TimeZone, _ rhs: TimeZone) -> Bool {
- if lhs._autoupdating || rhs._autoupdating {
- return lhs._autoupdating == rhs._autoupdating
- } else {
- return lhs._wrapped.isEqual(rhs._wrapped)
+extension TimeZone : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable {
+ private var _kindDescription : String {
+ if (self == TimeZone.current) {
+ return "current"
+ } else {
+ return "fixed"
+ }
+ }
+
+ public var customMirror : Mirror {
+ var c: [(label: String?, value: Any)] = []
+ c.append((label: "identifier", value: identifier))
+ c.append((label: "kind", value: _kindDescription))
+ c.append((label: "abbreviation", value: abbreviation()))
+ c.append((label: "secondsFromGMT", value: secondsFromGMT()))
+ c.append((label: "isDaylightSavingTime", value: isDaylightSavingTime()))
+ return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct)
+ }
+
+ public var description: String {
+ return "\(identifier) (\(_kindDescription))"
+ }
+
+ public var debugDescription : String {
+ return "\(identifier) (\(_kindDescription))"
}
}
@@ -247,4 +279,3 @@
return result!
}
}
-
diff --git a/TestFoundation/TestNSTimeZone.swift b/TestFoundation/TestNSTimeZone.swift
index ae33247..e915303 100644
--- a/TestFoundation/TestNSTimeZone.swift
+++ b/TestFoundation/TestNSTimeZone.swift
@@ -25,25 +25,118 @@
return [
// Disabled see https://bugs.swift.org/browse/SR-300
// ("test_abbreviation", test_abbreviation),
+
+ // Disabled because `CFTimeZoneSetAbbreviationDictionary()` attempts
+ // to release non-CF objects while removing values from
+ // `__CFTimeZoneCache`
+ // ("test_abbreviationDictionary", test_abbreviationDictionary),
+
+ ("test_changingDefaultTimeZone", test_changingDefaultTimeZone),
+ ("test_computedPropertiesMatchMethodReturnValues", test_computedPropertiesMatchMethodReturnValues),
("test_initializingTimeZoneWithOffset", test_initializingTimeZoneWithOffset),
("test_initializingTimeZoneWithAbbreviation", test_initializingTimeZoneWithAbbreviation),
+
// Also disabled due to https://bugs.swift.org/browse/SR-300
// ("test_systemTimeZoneUsesSystemTime", test_systemTimeZoneUsesSystemTime),
]
}
func test_abbreviation() {
- let tz = NSTimeZone.systemTimeZone()
+ let tz = NSTimeZone.system
let abbreviation1 = tz.abbreviation()
let abbreviation2 = tz.abbreviation(for: Date())
XCTAssertEqual(abbreviation1, abbreviation2, "\(abbreviation1) should be equal to \(abbreviation2)")
}
-
+
+ func test_abbreviationDictionary() {
+ let oldDictionary = TimeZone.abbreviationDictionary
+ let newDictionary = [
+ "UTC": "UTC",
+ "JST": "Asia/Tokyo",
+ "GMT": "GMT",
+ "ICT": "Asia/Bangkok",
+ "TEST": "Foundation/TestNSTimeZone"
+ ]
+ TimeZone.abbreviationDictionary = newDictionary
+ XCTAssertEqual(TimeZone.abbreviationDictionary, newDictionary)
+ TimeZone.abbreviationDictionary = oldDictionary
+ XCTAssertEqual(TimeZone.abbreviationDictionary, oldDictionary)
+ }
+
+ func test_changingDefaultTimeZone() {
+ let oldDefault = NSTimeZone.default
+ let oldSystem = NSTimeZone.system
+
+ let expectedDefault = TimeZone(identifier: "GMT-0400")!
+ NSTimeZone.default = expectedDefault
+ let newDefault = NSTimeZone.default
+ let newSystem = NSTimeZone.system
+ XCTAssertEqual(oldSystem, newSystem)
+ XCTAssertEqual(expectedDefault, newDefault)
+
+ let expectedDefault2 = TimeZone(identifier: "GMT+0400")!
+ NSTimeZone.default = expectedDefault2
+ let newDefault2 = NSTimeZone.default
+ XCTAssertEqual(expectedDefault2, newDefault2)
+ XCTAssertNotEqual(newDefault, newDefault2)
+
+ NSTimeZone.default = oldDefault
+ let revertedDefault = NSTimeZone.default
+ XCTAssertEqual(oldDefault, revertedDefault)
+ }
+
+ func test_computedPropertiesMatchMethodReturnValues() {
+ let tz = NSTimeZone.default
+ let obj = tz._bridgeToObjectiveC()
+
+ let secondsFromGMT1 = tz.secondsFromGMT()
+ let secondsFromGMT2 = obj.secondsFromGMT
+ let secondsFromGMT3 = tz.secondsFromGMT()
+ XCTAssert(secondsFromGMT1 == secondsFromGMT2 || secondsFromGMT2 == secondsFromGMT3, "\(secondsFromGMT1) should be equal to \(secondsFromGMT2), or in the rare circumstance where a daylight saving time transition has just occurred, \(secondsFromGMT2) should be equal to \(secondsFromGMT3)")
+
+ let abbreviation1 = tz.abbreviation()
+ let abbreviation2 = obj.abbreviation
+ XCTAssertEqual(abbreviation1, abbreviation2, "\(abbreviation1) should be equal to \(abbreviation2)")
+
+ let isDaylightSavingTime1 = tz.isDaylightSavingTime()
+ let isDaylightSavingTime2 = obj.isDaylightSavingTime
+ let isDaylightSavingTime3 = tz.isDaylightSavingTime()
+ XCTAssert(isDaylightSavingTime1 == isDaylightSavingTime2 || isDaylightSavingTime2 == isDaylightSavingTime3, "\(isDaylightSavingTime1) should be equal to \(isDaylightSavingTime2), or in the rare circumstance where a daylight saving time transition has just occurred, \(isDaylightSavingTime2) should be equal to \(isDaylightSavingTime3)")
+
+ let daylightSavingTimeOffset1 = tz.daylightSavingTimeOffset()
+ let daylightSavingTimeOffset2 = obj.daylightSavingTimeOffset
+ XCTAssertEqual(daylightSavingTimeOffset1, daylightSavingTimeOffset2, "\(daylightSavingTimeOffset1) should be equal to \(daylightSavingTimeOffset2)")
+
+ let nextDaylightSavingTimeTransition1 = tz.nextDaylightSavingTimeTransition
+ let nextDaylightSavingTimeTransition2 = obj.nextDaylightSavingTimeTransition
+ let nextDaylightSavingTimeTransition3 = tz.nextDaylightSavingTimeTransition(after: Date())
+ XCTAssert(nextDaylightSavingTimeTransition1 == nextDaylightSavingTimeTransition2 || nextDaylightSavingTimeTransition2 == nextDaylightSavingTimeTransition3, "\(nextDaylightSavingTimeTransition1) should be equal to \(nextDaylightSavingTimeTransition2), or in the rare circumstance where a daylight saving time transition has just occurred, \(nextDaylightSavingTimeTransition2) should be equal to \(nextDaylightSavingTimeTransition3)")
+ }
+
+ func test_knownTimeZoneNames() {
+ let known = NSTimeZone.knownTimeZoneNames
+ XCTAssertNotEqual([], known, "known time zone names not expected to be empty")
+ }
+
func test_initializingTimeZoneWithOffset() {
let tz = TimeZone(identifier: "GMT-0400")
XCTAssertNotNil(tz)
let seconds = tz?.secondsFromGMT(for: Date())
XCTAssertEqual(seconds, -14400, "GMT-0400 should be -14400 seconds but got \(seconds) instead")
+
+ let tz2 = TimeZone(secondsFromGMT: -14400)
+ XCTAssertNotNil(tz2)
+ let expectedName = "GMT-0400"
+ let actualName = tz2?.identifier
+ XCTAssertEqual(actualName, expectedName, "expected name \"\(expectedName)\" is not equal to \"\(actualName)\"")
+ let expectedLocalizedName = "GMT-04:00"
+ let actualLocalizedName = tz2?.localizedName(for: .generic, locale: Locale(identifier: "en_US"))
+ XCTAssertEqual(actualLocalizedName, expectedLocalizedName, "expected name \"\(expectedLocalizedName)\" is not equal to \"\(actualLocalizedName)\"")
+ let seconds2 = tz2?.secondsFromGMT()
+ XCTAssertEqual(seconds2, -14400, "GMT-0400 should be -14400 seconds but got \(seconds2) instead")
+
+ let tz3 = TimeZone(identifier: "GMT-9999")
+ XCTAssertNil(tz3)
}
func test_initializingTimeZoneWithAbbreviation() {
@@ -61,7 +154,7 @@
var t = time(nil)
var lt = tm()
localtime_r(&t, <)
- let zoneName = NSTimeZone.systemTimeZone().abbreviation() ?? "Invalid Abbreviation"
+ let zoneName = NSTimeZone.system.abbreviation() ?? "Invalid Abbreviation"
let expectedName = String(cString: lt.tm_zone, encoding: String.Encoding.ascii) ?? "Invalid Zone"
XCTAssertEqual(zoneName, expectedName, "expected name \"\(expectedName)\" is not equal to \"\(zoneName)\"")
}