blob: 44d7b587969cc119ef3bae991c50e771cf8d1ff8 [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
//
class TestTimeZone: XCTestCase {
var initialDefaultTimeZone: TimeZone?
override func setUp() {
initialDefaultTimeZone = NSTimeZone.default
super.setUp()
}
override func tearDown() {
super.tearDown()
if let tz = initialDefaultTimeZone {
NSTimeZone.default = tz
}
}
func test_abbreviation() {
let tz = NSTimeZone.system
let abbreviation1 = tz.abbreviation()
let abbreviation2 = tz.abbreviation(for: Date())
XCTAssertEqual(abbreviation1, abbreviation2, "\(abbreviation1 as Optional) should be equal to \(abbreviation2 as Optional)")
}
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 as Optional) should be equal to \(abbreviation2 as Optional)")
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 as Optional) should be equal to \(nextDaylightSavingTimeTransition2 as Optional), or in the rare circumstance where a daylight saving time transition has just occurred, \(nextDaylightSavingTimeTransition2 as Optional) should be equal to \(nextDaylightSavingTimeTransition3 as Optional)")
}
func test_knownTimeZoneNames() {
let known = NSTimeZone.knownTimeZoneNames
XCTAssertNotEqual([], known, "known time zone names not expected to be empty")
}
func test_localizedName() {
let initialTimeZone = NSTimeZone.default
NSTimeZone.default = TimeZone(identifier: "America/New_York")!
let defaultTimeZone = NSTimeZone.default
let locale = Locale(identifier: "en_US")
XCTAssertEqual(defaultTimeZone.localizedName(for: .standard, locale: locale), "Eastern Standard Time")
XCTAssertEqual(defaultTimeZone.localizedName(for: .shortStandard, locale: locale), "EST")
XCTAssertEqual(defaultTimeZone.localizedName(for: .generic, locale: locale), "Eastern Time")
XCTAssertEqual(defaultTimeZone.localizedName(for: .daylightSaving, locale: locale), "Eastern Daylight Time")
XCTAssertEqual(defaultTimeZone.localizedName(for: .shortDaylightSaving, locale: locale), "EDT")
XCTAssertEqual(defaultTimeZone.localizedName(for: .shortGeneric, locale: locale), "ET")
NSTimeZone.default = initialTimeZone //reset the TimeZone
}
func test_initializingTimeZoneWithOffset() {
let tz = TimeZone(identifier: "GMT-0400")
XCTAssertNotNil(tz)
let seconds = tz?.secondsFromGMT(for: Date()) ?? 0
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 as Optional)\"")
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 as Optional)\"")
let seconds2 = tz2?.secondsFromGMT() ?? 0
XCTAssertEqual(seconds2, -14400, "GMT-0400 should be -14400 seconds but got \(seconds2) instead")
let tz3 = TimeZone(identifier: "GMT-9999")
XCTAssertNil(tz3)
XCTAssertNotNil(TimeZone(secondsFromGMT: -18 * 3600))
XCTAssertNotNil(TimeZone(secondsFromGMT: 18 * 3600))
XCTAssertNil(TimeZone(secondsFromGMT: -18 * 3600 - 1))
XCTAssertNil(TimeZone(secondsFromGMT: 18 * 3600 + 1))
}
func test_initializingTimeZoneWithAbbreviation() {
// Test invalid timezone abbreviation
var tz = TimeZone(abbreviation: "XXX")
XCTAssertNil(tz)
// Test valid timezone abbreviation of "AST" for "America/Halifax"
tz = TimeZone(abbreviation: "AST")
let expectedIdentifier = "America/Halifax"
let actualIdentifier = tz?.identifier
XCTAssertEqual(actualIdentifier, expectedIdentifier, "expected identifier \"\(expectedIdentifier)\" is not equal to \"\(actualIdentifier as Optional)\"")
}
#if !os(Windows)
func test_systemTimeZoneUsesSystemTime() {
tzset()
var t = time(nil)
var lt = tm()
localtime_r(&t, &lt)
let zoneName = NSTimeZone.system.abbreviation() ?? "Invalid Abbreviation"
let expectedName = String(cString: lt.tm_zone, encoding: .ascii) ?? "Invalid Zone"
XCTAssertEqual(zoneName, expectedName, "expected name \"\(expectedName)\" is not equal to \"\(zoneName)\"")
}
#endif
func test_tz_customMirror() {
let tz = TimeZone.current
let mirror = Mirror(reflecting: tz as TimeZone)
var children = [String : Any](minimumCapacity: Int(mirror.children.count))
mirror.children.forEach {
if let label = $0.label {
children[label] = $0.value
}
}
XCTAssertNotNil(children["identifier"])
XCTAssertNotNil(children["kind"])
XCTAssertNotNil(children["secondsFromGMT"])
XCTAssertNotNil(children["isDaylightSavingTime"])
}
func test_knownTimeZones() {
let timeZones = TimeZone.knownTimeZoneIdentifiers.sorted()
XCTAssertTrue(timeZones.count > 0, "No known timezones")
for tz in timeZones {
XCTAssertNotNil(TimeZone(identifier: tz), "Cant instantiate valid timeZone: \(tz)")
}
}
func test_systemTimeZoneName() {
// Ensure that the system time zone creates names the same way as creating them with an identifier.
// If it isn't the same, bugs in DateFormat can result, but in this specific case, the bad length
// is only visible to CoreFoundation APIs, and the Swift versions hide it, making it hard to detect.
let timeZoneName = NSTimeZone.system.identifier as NSString
let createdTimeZone = TimeZone(identifier: TimeZone.current.identifier)!
XCTAssertEqual(timeZoneName.length, TimeZone.current.identifier.count)
XCTAssertEqual(timeZoneName.length, createdTimeZone.identifier.count)
}
func test_autoupdatingTimeZone() {
let system = NSTimeZone.system
let date = Date()
for zone in [NSTimeZone.local, TimeZone.autoupdatingCurrent] {
XCTAssertEqual(zone.identifier, system.identifier)
XCTAssertEqual(zone.secondsFromGMT(for: date), system.secondsFromGMT(for: date))
XCTAssertEqual(zone.abbreviation(for: date), system.abbreviation(for: date))
XCTAssertEqual(zone.isDaylightSavingTime(for: date), system.isDaylightSavingTime(for: date))
XCTAssertEqual(zone.daylightSavingTimeOffset(for: date), system.daylightSavingTimeOffset(for: date))
XCTAssertEqual(zone.nextDaylightSavingTimeTransition(after: date), system.nextDaylightSavingTimeTransition(after: date))
for style in [NSTimeZone.NameStyle.standard,
NSTimeZone.NameStyle.shortStandard,
NSTimeZone.NameStyle.daylightSaving,
NSTimeZone.NameStyle.shortDaylightSaving,
NSTimeZone.NameStyle.generic,
NSTimeZone.NameStyle.shortGeneric,] {
XCTAssertEqual(zone.localizedName(for: style, locale: NSLocale.system), system.localizedName(for: style, locale: NSLocale.system), "For style: \(style)")
}
}
}
func test_nextDaylightSavingTimeTransition() throws {
// Timezones without DST
let gmt = try XCTUnwrap(TimeZone(secondsFromGMT: 0))
let msk = try XCTUnwrap(TimeZone(identifier: "Europe/Moscow"))
// Timezones with DST
let bst = try XCTUnwrap(TimeZone(abbreviation: "BST"))
let aest = try XCTUnwrap(TimeZone(identifier: "Australia/Sydney"))
XCTAssertNil(gmt.nextDaylightSavingTimeTransition)
XCTAssertNil(msk.nextDaylightSavingTimeTransition)
XCTAssertNotNil(bst.nextDaylightSavingTimeTransition)
XCTAssertNotNil(aest.nextDaylightSavingTimeTransition)
let formatter = DateFormatter()
formatter.timeZone = TimeZone(identifier: "UTC")
formatter.dateFormat = "yyyy-MM-dd"
let dt1 = try XCTUnwrap(formatter.date(from: "2018-01-01"))
XCTAssertNil(gmt.nextDaylightSavingTimeTransition(after: dt1))
XCTAssertNil(msk.nextDaylightSavingTimeTransition(after: dt1))
XCTAssertEqual(bst.nextDaylightSavingTimeTransition(after: dt1)?.description, "2018-03-25 01:00:00 +0000")
XCTAssertEqual(aest.nextDaylightSavingTimeTransition(after: dt1)?.description, "2018-03-31 16:00:00 +0000")
formatter.timeZone = aest
let dt2 = try XCTUnwrap(formatter.date(from: "2018-06-06"))
XCTAssertNil(gmt.nextDaylightSavingTimeTransition(after: dt2))
XCTAssertNil(msk.nextDaylightSavingTimeTransition(after: dt2))
XCTAssertEqual(bst.nextDaylightSavingTimeTransition(after: dt2)?.description, "2018-10-28 01:00:00 +0000")
XCTAssertEqual(aest.nextDaylightSavingTimeTransition(after: dt2)?.description, "2018-10-06 16:00:00 +0000")
}
static var allTests: [(String, (TestTimeZone) -> () throws -> Void)] {
var tests: [(String, (TestTimeZone) -> () throws -> Void)] = [
("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),
("test_localizedName", test_localizedName),
("test_customMirror", test_tz_customMirror),
("test_knownTimeZones", test_knownTimeZones),
("test_systemTimeZoneName", test_systemTimeZoneName),
("test_autoupdatingTimeZone", test_autoupdatingTimeZone),
("test_nextDaylightSavingTimeTransition", test_nextDaylightSavingTimeTransition),
]
#if !os(Windows)
tests.append(contentsOf: [
("test_systemTimeZoneUsesSystemTime", test_systemTimeZoneUsesSystemTime),
])
#endif
return tests
}
}