Files

576 lines
21 KiB
Swift

//
// SwiftDate
// Parse, validate, manipulate, and display dates, time and timezones in Swift
//
// Created by Daniele Margutti
// - Web: https://www.danielemargutti.com
// - Twitter: https://twitter.com/danielemargutti
// - Mail: hello@danielemargutti.com
//
// Copyright © 2019 Daniele Margutti. Licensed under MIT License.
//
import SwiftDate
import XCTest
class TestFormatters: XCTestCase {
public func datesList() -> [String: [String: String]] {
return [
"2017-07-22T18:27:02+02:00": [
"dotnet": "/Date(1500740822000+0200)/",
"rss": "Sat, 22 Jul 2017 18:27:02 +0200",
"rss_alt": "22 Jul 2017 18:27:02 +0200",
"sql": "2017-07-22T18:27:02.000+02",
"iso": "2017-07-22T18:27:02+02:00"
],
"2017-02-03T06:30:12+01:00": [
"dotnet": "/Date(1486099812000+0100)/",
"rss": "Fri, 3 Feb 2017 06:30:12 +0100",
"rss_alt": "3 Feb 2017 06:30:12 +0100",
"sql": "2017-02-03T06:30:12.000+01",
"iso": "2017-02-03T06:30:12+01:00"
],
"2016-03-31T10:57:44+02:00": [
"dotnet": "/Date(1459414664000+0200)/",
"rss": "Thu, 31 Mar 2016 10:57:44 +0200",
"rss_alt": "31 Mar 2016 10:57:44 +0200",
"sql": "2016-03-31T10:57:44.000+02",
"iso": "2016-03-31T10:57:44+02:00"
],
"2016-07-23T17:05:31+02:00": [
"dotnet": "/Date(1469286331000+0200)/",
"rss": "Sat, 23 Jul 2016 17:05:31 +0200",
"rss_alt": "23 Jul 2016 17:05:31 +0200",
"sql": "2016-07-23T17:05:31.000+02",
"iso": "2016-07-23T17:05:31+02:00"
],
"2017-05-15T11:26:36+02:00": [
"dotnet": "/Date(1494840396000+0200)/",
"rss": "Mon, 15 May 2017 11:26:36 +0200",
"rss_alt": "15 May 2017 11:26:36 +0200",
"sql": "2017-05-15T11:26:36.000+02",
"iso": "2017-05-15T11:26:36+02:00"
],
"2018-03-27T20:50:37+02:00": [
"dotnet": "/Date(1522176637000+0200)/",
"rss": "Tue, 27 Mar 2018 20:50:37 +0200",
"rss_alt": "27 Mar 2018 20:50:37 +0200",
"sql": "2018-03-27T20:50:37.000+02",
"iso": "2018-03-27T20:50:37+02:00"
],
"2018-02-16T14:52:52+01:00": [
"dotnet": "/Date(1518789172000+0100)/",
"rss": "Fri, 16 Feb 2018 14:52:52 +0100",
"rss_alt": "16 Feb 2018 14:52:52 +0100",
"sql": "2018-02-16T14:52:52.000+01",
"iso": "2018-02-16T14:52:52+01:00"
],
"2016-01-10T16:55:35+01:00": [
"dotnet": "/Date(1452441335000+0100)/",
"rss": "Sun, 10 Jan 2016 16:55:35 +0100",
"rss_alt": "10 Jan 2016 16:55:35 +0100",
"sql": "2016-01-10T16:55:35.000+01",
"iso": "2016-01-10T16:55:35+01:00"
],
"2017-05-02T23:53:44+02:00": [
"dotnet": "/Date(1493762024000+0200)/",
"rss": "Tue, 2 May 2017 23:53:44 +0200",
"rss_alt": "2 May 2017 23:53:44 +0200",
"sql": "2017-05-02T23:53:44.000+02",
"iso": "2017-05-02T23:53:44+02:00"
],
"2017-05-13T20:05:38+02:00": [
"dotnet": "/Date(1494698738000+0200)/",
"rss": "Sat, 13 May 2017 20:05:38 +0200",
"rss_alt": "13 May 2017 20:05:38 +0200",
"sql": "2017-05-13T20:05:38.000+02",
"iso": "2017-05-13T20:05:38+02:00"
],
"2017-10-04T17:25:36+02:00": [
"dotnet": "/Date(1507130736000+0200)/",
"rss": "Wed, 4 Oct 2017 17:25:36 +0200",
"rss_alt": "4 Oct 2017 17:25:36 +0200",
"sql": "2017-10-04T17:25:36.000+02",
"iso": "2017-10-04T17:25:36+02:00"
],
"2016-04-14T11:58:58+02:00": [
"dotnet": "/Date(1460627938000+0200)/",
"rss": "Thu, 14 Apr 2016 11:58:58 +0200",
"rss_alt": "14 Apr 2016 11:58:58 +0200",
"sql": "2016-04-14T11:58:58.000+02",
"iso": "2016-04-14T11:58:58+02:00"
],
"2016-10-03T13:15:37+02:00": [
"dotnet": "/Date(1475493337000+0200)/",
"rss": "Mon, 3 Oct 2016 13:15:37 +0200",
"rss_alt": "3 Oct 2016 13:15:37 +0200",
"sql": "2016-10-03T13:15:37.000+02",
"iso": "2016-10-03T13:15:37+02:00"
],
"2015-08-23T16:28:34+02:00": [
"dotnet": "/Date(1440340114000+0200)/",
"rss": "Sun, 23 Aug 2015 16:28:34 +0200",
"rss_alt": "23 Aug 2015 16:28:34 +0200",
"sql": "2015-08-23T16:28:34.000+02",
"iso": "2015-08-23T16:28:34+02:00"
],
"2016-09-04T19:52:29+02:00": [
"dotnet": "/Date(1473011549000+0200)/",
"rss": "Sun, 4 Sep 2016 19:52:29 +0200",
"rss_alt": "4 Sep 2016 19:52:29 +0200",
"sql": "2016-09-04T19:52:29.000+02",
"iso": "2016-09-04T19:52:29+02:00"
],
"2016-05-09T14:09:55+02:00": [
"dotnet": "/Date(1462795795000+0200)/",
"rss": "Mon, 9 May 2016 14:09:55 +0200",
"rss_alt": "9 May 2016 14:09:55 +0200",
"sql": "2016-05-09T14:09:55.000+02",
"iso": "2016-05-09T14:09:55+02:00"
],
"2016-05-11T02:58:47+02:00": [
"dotnet": "/Date(1462928327000+0200)/",
"rss": "Wed, 11 May 2016 02:58:47 +0200",
"rss_alt": "11 May 2016 02:58:47 +0200",
"sql": "2016-05-11T02:58:47.000+02",
"iso": "2016-05-11T02:58:47+02:00"
],
"2017-04-08T01:49:29+02:00": [
"dotnet": "/Date(1491608969000+0200)/",
"rss": "Sat, 8 Apr 2017 01:49:29 +0200",
"rss_alt": "8 Apr 2017 01:49:29 +0200",
"sql": "2017-04-08T01:49:29.000+02",
"iso": "2017-04-08T01:49:29+02:00"
],
"2017-03-14T04:06:47+01:00": [
"dotnet": "/Date(1489460807000+0100)/",
"rss": "Tue, 14 Mar 2017 04:06:47 +0100",
"rss_alt": "14 Mar 2017 04:06:47 +0100",
"sql": "2017-03-14T04:06:47.000+01",
"iso": "2017-03-14T04:06:47+01:00"
],
"2016-05-31T14:31:50+02:00": [
"dotnet": "/Date(1464697910000+0200)/",
"rss": "Tue, 31 May 2016 14:31:50 +0200",
"rss_alt": "31 May 2016 14:31:50 +0200",
"sql": "2016-05-31T14:31:50.000+02",
"iso": "2016-05-31T14:31:50+02:00"
],
"2015-09-22T08:28:16+02:00": [
"dotnet": "/Date(1442903296000+0200)/",
"rss": "Tue, 22 Sep 2015 08:28:16 +0200",
"rss_alt": "22 Sep 2015 08:28:16 +0200",
"sql": "2015-09-22T08:28:16.000+02",
"iso": "2015-09-22T08:28:16+02:00"
],
"2016-08-11T06:53:56+02:00": [
"dotnet": "/Date(1470891236000+0200)/",
"rss": "Thu, 11 Aug 2016 06:53:56 +0200",
"rss_alt": "11 Aug 2016 06:53:56 +0200",
"sql": "2016-08-11T06:53:56.000+02",
"iso": "2016-08-11T06:53:56+02:00"
],
"2018-03-27T19:49:32+02:00": [
"dotnet": "/Date(1522172972000+0200)/",
"rss": "Tue, 27 Mar 2018 19:49:32 +0200",
"rss_alt": "27 Mar 2018 19:49:32 +0200",
"sql": "2018-03-27T19:49:32.000+02",
"iso": "2018-03-27T19:49:32+02:00"
],
"2017-03-04T21:04:51+01:00": [
"dotnet": "/Date(1488657891000+0100)/",
"rss": "Sat, 4 Mar 2017 21:04:51 +0100",
"rss_alt": "4 Mar 2017 21:04:51 +0100",
"sql": "2017-03-04T21:04:51.000+01",
"iso": "2017-03-04T21:04:51+01:00"
],
"2016-09-19T10:22:17+02:00": [
"dotnet": "/Date(1474273337000+0200)/",
"rss": "Mon, 19 Sep 2016 10:22:17 +0200",
"rss_alt": "19 Sep 2016 10:22:17 +0200",
"sql": "2016-09-19T10:22:17.000+02",
"iso": "2016-09-19T10:22:17+02:00"
],
"2016-01-09T08:24:32+01:00": [
"dotnet": "/Date(1452324272000+0100)/",
"rss": "Sat, 9 Jan 2016 08:24:32 +0100",
"rss_alt": "9 Jan 2016 08:24:32 +0100",
"sql": "2016-01-09T08:24:32.000+01",
"iso": "2016-01-09T08:24:32+01:00"
],
"2018-05-16T02:33:15+02:00": [
"dotnet": "/Date(1526430795000+0200)/",
"rss": "Wed, 16 May 2018 02:33:15 +0200",
"rss_alt": "16 May 2018 02:33:15 +0200",
"sql": "2018-05-16T02:33:15.000+02",
"iso": "2018-05-16T02:33:15+02:00"
],
"2016-11-09T14:42:29+01:00": [
"dotnet": "/Date(1478698949000+0100)/",
"rss": "Wed, 9 Nov 2016 14:42:29 +0100",
"rss_alt": "9 Nov 2016 14:42:29 +0100",
"sql": "2016-11-09T14:42:29.000+01",
"iso": "2016-11-09T14:42:29+01:00"
],
"2017-09-18T05:07:13+02:00": [
"dotnet": "/Date(1505704033000+0200)/",
"rss": "Mon, 18 Sep 2017 05:07:13 +0200",
"rss_alt": "18 Sep 2017 05:07:13 +0200",
"sql": "2017-09-18T05:07:13.000+02",
"iso": "2017-09-18T05:07:13+02:00"
],
"2016-11-08T19:03:38+01:00": [
"dotnet": "/Date(1478628218000+0100)/",
"rss": "Tue, 8 Nov 2016 19:03:38 +0100",
"rss_alt": "8 Nov 2016 19:03:38 +0100",
"sql": "2016-11-08T19:03:38.000+01",
"iso": "2016-11-08T19:03:38+01:00"
],
"2017-08-25T04:33:32+02:00": [
"dotnet": "/Date(1503628412000+0200)/",
"rss": "Fri, 25 Aug 2017 04:33:32 +0200",
"rss_alt": "25 Aug 2017 04:33:32 +0200",
"sql": "2017-08-25T04:33:32.000+02",
"iso": "2017-08-25T04:33:32+02:00"
],
"2016-06-07T15:10:47+02:00": [
"dotnet": "/Date(1465305047000+0200)/",
"rss": "Tue, 7 Jun 2016 15:10:47 +0200",
"rss_alt": "7 Jun 2016 15:10:47 +0200",
"sql": "2016-06-07T15:10:47.000+02",
"iso": "2016-06-07T15:10:47+02:00"
],
"2016-12-02T14:54:10+01:00": [
"dotnet": "/Date(1480686850000+0100)/",
"rss": "Fri, 2 Dec 2016 14:54:10 +0100",
"rss_alt": "2 Dec 2016 14:54:10 +0100",
"sql": "2016-12-02T14:54:10.000+01",
"iso": "2016-12-02T14:54:10+01:00"
],
"2016-02-11T11:48:21+01:00": [
"dotnet": "/Date(1455187701000+0100)/",
"rss": "Thu, 11 Feb 2016 11:48:21 +0100",
"rss_alt": "11 Feb 2016 11:48:21 +0100",
"sql": "2016-02-11T11:48:21.000+01",
"iso": "2016-02-11T11:48:21+01:00"
],
"2018-01-23T17:44:12+01:00": [
"dotnet": "/Date(1516725852000+0100)/",
"rss": "Tue, 23 Jan 2018 17:44:12 +0100",
"rss_alt": "23 Jan 2018 17:44:12 +0100",
"sql": "2018-01-23T17:44:12.000+01",
"iso": "2018-01-23T17:44:12+01:00"
],
"2016-05-09T22:11:28+02:00": [
"dotnet": "/Date(1462824688000+0200)/",
"rss": "Mon, 9 May 2016 22:11:28 +0200",
"rss_alt": "9 May 2016 22:11:28 +0200",
"sql": "2016-05-09T22:11:28.000+02",
"iso": "2016-05-09T22:11:28+02:00"
],
"2015-06-27T04:15:55+02:00": [
"dotnet": "/Date(1435371355000+0200)/",
"rss": "Sat, 27 Jun 2015 04:15:55 +0200",
"rss_alt": "27 Jun 2015 04:15:55 +0200",
"sql": "2015-06-27T04:15:55.000+02",
"iso": "2015-06-27T04:15:55+02:00"
],
"2017-08-05T16:04:03+02:00": [
"dotnet": "/Date(1501941843000+0200)/",
"rss": "Sat, 5 Aug 2017 16:04:03 +0200",
"rss_alt": "5 Aug 2017 16:04:03 +0200",
"sql": "2017-08-05T16:04:03.000+02",
"iso": "2017-08-05T16:04:03+02:00"
],
"2015-11-19T22:20:40+01:00": [
"dotnet": "/Date(1447968040000+0100)/",
"rss": "Thu, 19 Nov 2015 22:20:40 +0100",
"rss_alt": "19 Nov 2015 22:20:40 +0100",
"sql": "2015-11-19T22:20:40.000+01",
"iso": "2015-11-19T22:20:40+01:00"
],
"2017-06-20T14:49:19+02:00": [
"dotnet": "/Date(1497962959000+0200)/",
"rss": "Tue, 20 Jun 2017 14:49:19 +0200",
"rss_alt": "20 Jun 2017 14:49:19 +0200",
"sql": "2017-06-20T14:49:19.000+02",
"iso": "2017-06-20T14:49:19+02:00"
]
]
}
/*public func testGenerateSomeRandomDates() {
var total = ""
let upperBound = DateInRegion()
let lowerBound = (upperBound - 3.years)
let randomDates = DateInRegion.randomDates(count: 40, between: lowerBound, and: upperBound)
randomDates.forEach {
let adjustedDateNoMS = $0.toISO().toISODate()!
total += "\"\($0.toISO())\" : [\n\t"
total += "\n\t" + "\"dotnet\": \"\(adjustedDateNoMS.toDotNET())\","
total += "\n\t" + "\"rss\": \"\(adjustedDateNoMS.toRSS(alt: false))\","
total += "\n\t" + "\"rss_alt\": \"\(adjustedDateNoMS.toRSS(alt: true))\","
total += "\n\t" + "\"sql\": \"\(adjustedDateNoMS.toSQL())\","
total += "\n\t" + "\"iso\": \"\(adjustedDateNoMS.toISO())\""
total += "\n],\n"
}
print("\(total)")
}*/
public func testDotNETFormatter() {
SwiftDate.defaultRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
datesList().forEach {
XCTTestFormatterParser(dateStr: $0.key, expected: $0.value["dotnet"]!, type: "dotnet", region: SwiftDate.defaultRegion)
}
}
public func testRSSFormatter() {
SwiftDate.defaultRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
datesList().forEach {
XCTTestFormatterParser(dateStr: $0.key, expected: $0.value["rss"]!, type: "rss", region: SwiftDate.defaultRegion)
}
}
public func testRSSAltFormatter() {
SwiftDate.defaultRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
datesList().forEach {
XCTTestFormatterParser(dateStr: $0.key, expected: $0.value["rss_alt"]!, type: "rss_alt", region: SwiftDate.defaultRegion)
}
}
public func testSQLFormatter() {
SwiftDate.defaultRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
datesList().forEach {
XCTTestFormatterParser(dateStr: $0.key, expected: $0.value["sql"]!, type: "sql", region: SwiftDate.defaultRegion)
}
}
public func testISOFormatter() {
SwiftDate.defaultRegion = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.english)
datesList().forEach {
XCTTestFormatterParser(dateStr: $0.key, expected: $0.value["iso"]!, type: "iso", region: SwiftDate.defaultRegion)
}
}
func XCTTestFormatterParser(dateStr: String, expected: String, type: String, region: Region = SwiftDate.defaultRegion) {
guard let srcDate = dateStr.toDate("yyyy-MM-dd'T'HH:mm:ssZZZZZ", region: SwiftDate.defaultRegion) else {
XCTFail("Failed to correctly parse date: '\(dateStr)'")
return
}
let dateAsStr: String = dateToString(date: srcDate, format: type)
XCTAssert( (dateAsStr == expected),
"Failed to convert date '\(srcDate.description)' to \(type) format. Expected '\(expected)', got '\(dateAsStr)'")
guard let decodedSrcDate = stringToDate(string: dateAsStr, format: type, region: region) else {
XCTFail("Failed to convert date string to date of type \(type): \(dateAsStr)")
return
}
XCTAssert( (srcDate == decodedSrcDate),
"Failed to validate formatter. Got '\(decodedSrcDate)' expecting '\(srcDate)'")
}
func dateToString(date srcDate: DateInRegion, format: String) -> String {
switch format {
case "dotnet": return srcDate.toDotNET()
case "rss": return srcDate.toRSS(alt: false)
case "rss_alt": return srcDate.toRSS(alt: true)
case "sql": return srcDate.toSQL()
case "iso": return srcDate.toISO()
default:
XCTFail("Unsupported type: \(format)")
return ""
}
}
func stringToDate(string: String, format: String, region: Region) -> DateInRegion? {
switch format {
case "dotnet": return string.toDotNETDate(region: region)
case "rss": return string.toRSSDate(alt: false, region: region)
case "rss_alt": return string.toRSSDate(alt: true, region: region)
case "sql": return string.toSQLDate(region: region)
case "iso": return string.toISODate(nil, region: region)
default:
XCTFail("Unsupported type: \(format)")
return nil
}
}
func testTZInISOParser() {
let gmtTimezone = "2017-08-05T16:04:03".toISODate(region: Region.ISO)! // force timezone
let timezoneInDate = "2017-08-05T16:04:03+02:00".toISODate()! // parse from iso
XCTAssert(gmtTimezone.region.timeZone.secondsFromGMT() == 0, "ISO Date does not contains timezone (is gmt)")
XCTAssert(timezoneInDate.region.timeZone.secondsFromGMT() == 7200, "ISO Date does not contains timezone (is gmt)")
}
func testRSSAltLocale() {
let regionAny = Region(calendar: Calendars.buddhist, zone: Zones.indianMayotte, locale: Locales.italian)
// region must not use passed locale to perform parsing but only locale as final output
let date1 = "Tue, 20 Jun 2017 14:49:19 +0200".toRSSDate(alt: false, region: regionAny)
let date2 = "20 Jun 2017 14:49:19 +0200".toRSSDate(alt: true, region: regionAny)
XCTAssertNotNil(date1, "Wrong RSS Date region")
XCTAssertNotNil(date2, "Wrong RSS Alt Date region")
}
func testTimeInterval_Clock() {
let value = (2.hours + 5.minutes).timeInterval.toClock()
XCTAssert(value == "02:05:00", "Failed to format clock")
#if os(Linux)
let zeroBehavior = DateComponentsFormatter.ZeroFormattingBehavior(rawValue: 14)
let value2 = (4.minutes + 50.minutes).timeInterval.toClock(zero: zeroBehavior)
XCTAssert(value2 == "54:00", "Failed to format clock")
#else
let value2 = (4.minutes + 50.minutes).timeInterval.toClock(zero: DateComponentsFormatter.ZeroFormattingBehavior.dropAll)
XCTAssert(value2 == "54", "Failed to format clock")
#endif
}
func testFormatterCustom() {
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = DateInRegion(year: 2015, month: 1, day: 15, hour: 20, minute: 00, second: 5, nanosecond: 0, region: rome)
let fixedFormat = date.toFormat("MMM dd yyyy", locale: Locales.english)
let regionFormat = date.toFormat("MMM dd yyyy")
XCTAssert( fixedFormat == "Jan 15 2015", "Failed to format with fixed locale")
XCTAssert( regionFormat == "gen 15 2015", "Failed to format with standard locale")
}
func testTimeInterval_FormatterUnits() {
// for TimeInterval
let values = (36.hours + 2.days + 1.weeks).timeInterval.toUnits([.day, .hour])
XCTAssert(values[.hour] == 12 && values[.day] == 10, "Failed to extract day components")
let singleValue = (1.days).timeInterval.toUnit(.minute)
XCTAssert(singleValue == 1440, "Failed to extract single date component")
}
func testTimeInterval_Formatter() {
let value1 = (2.hours + 5.minutes + 32.seconds).timeInterval.toString(options: {
$0.unitsStyle = .full
$0.collapsesLargestUnit = false
$0.allowsFractionalUnits = true
$0.locale = Locales.english
})
let value2 = (5.hours + 2.days).timeInterval.toString(options: {
$0.unitsStyle = .abbreviated
$0.locale = Locales.english
})
XCTAssert(value1 == "2 hours, 5 minutes, 32 seconds", "Failed to format interval to string")
XCTAssert(value2 == "2d 5h", "Failed to format interval to string")
}
func testColloquialFormatter() {
let ago5Mins = DateInRegion() - 5.minutes
let r1 = ago5Mins.toRelative(style: RelativeFormatter.defaultStyle(), locale: Locales.italian)
let r2 = ago5Mins.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian)
XCTAssert(r1 == "5 minuti fa", "Failed to use colloquial formatter")
XCTAssert(r2 == "5 min fa", "Failed to use colloquial formatter")
let justNow = DateInRegion() - 10.seconds
let r3 = justNow.toRelative(style: RelativeFormatter.defaultStyle(), locale: Locales.italian)
XCTAssert(r3 == "ora", "Failed to use colloquial formatter")
let justNow2 = DateInRegion() - 2.hours
let r4 = justNow2.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.italian)
XCTAssert(r4 == "2h fa", "Failed to use colloquial formatter")
let justNow3 = DateInRegion() - 1.minutes
let r5 = justNow3.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.english)
XCTAssert(r5 == "1 min. ago", "Failed to use colloquial formatter")
let justNow4 = DateInRegion() - 51.seconds
let r6 = justNow4.toRelative(style: RelativeFormatter.twitterStyle(), locale: Locales.english)
XCTAssert(r6 == "1 min. ago", "Failed to use colloquial formatter")
}
func testISOParser() {
func testISO(_ src: String, _ exp: String) {
let output = src.toISODate()?.toISO()
XCTAssert(output == exp, "Failed to parse ISO '\(src)'")
}
testISO("20060506", "2006-05-06T00:00:00Z") // YYYYMMDD
testISO("2001-12-14", "2001-12-14T00:00:00Z") // YYYY-MM-DD
testISO("2001-06", "2001-06-01T00:00:00Z") // YYYY-MM
testISO("2015", "2015-01-01T00:00:00Z") // YYYY
//testISO("15", "1518-01-01T00:00:00Z") // YY
// Implied century: YY is 00-99
testISO("150603", "2015-06-03T00:00:00Z") // YYMMDD
testISO("161201", "2016-12-01T00:00:00Z") // YY-MM-DD
// Implied year
testISO("--1215", "\(Date().year)-12-15T00:00:00Z") // --MMDD
testISO("--12-15", "\(Date().year)-12-15T00:00:00Z") // --MM-DD
testISO("--12", "\(Date().year)-12-01T00:00:00Z") // --MM
// Implied year and month
testISO("---15", "\(Date().year)-\(String(format: "%02d", Date().month))-15T00:00:00Z") // ---DD
// Ordinal dates: DDD is the number of the day in the year (1-366)
testISO("2015010", "2015-01-10T00:00:00Z") // YYYYDDD (DDD is the number of the day of the year)
testISO("2015032", "2015-02-01T00:00:00Z") // YYYY-DDD
testISO("15-032", "2015-02-01T00:00:00Z") // YY-DDD
testISO("15032", "2015-02-01T00:00:00Z") // YYDDD
testISO("-032", "\(Date().year)-02-01T00:00:00Z") // -DDD
// Week-based dates: ww is the number of the week, and d is the number (1-7) of the day in the week
testISO("2018W012", "2018-01-02T00:00:00Z") // yyyyWwwd
testISO("2018-W01-2", "2018-01-02T00:00:00Z") // yyyy-Www-d
testISO("2018-W01", "2017-12-31T00:00:00Z") // yyyyWww
testISO("2018-W01", "2017-12-31T00:00:00Z") // yyyy-Www
testISO("18-W01", "2017-12-31T00:00:00Z") // yyWwwd
testISO("18-W01-2", "2018-01-02T00:00:00Z") // yy-Www-d
testISO("18W01", "2017-12-31T00:00:00Z") // yyWww
testISO("18-W01", "2017-12-31T00:00:00Z") // yy-Www
// Date
testISO("1970-01-01", "1970-01-01T00:00:00Z")
testISO("2001", "2001-01-01T00:00:00Z")
testISO("2001-02-03", "2001-02-03T00:00:00Z")
testISO("2001-02-03T04:05", "2001-02-03T04:05:00Z")
testISO("2001-02-03T04:05:06.007-06:30", "2001-02-03T04:05:06-06:30")
}
}