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, &lt)
-        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)\"")
     }