// // 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 TestDateInRegion_Components: XCTestCase { func testDateInRegion_Components() { let regionLondon = Region(calendar: Calendars.gregorian, zone: Zones.europeLondon, locale: Locales.italian) let dateFormat = "yyyy-MM-dd HH:mm:ss" // TEST #1: Date In Italian let dateA = DateInRegion("2018-02-05 23:14:45", format: dateFormat, region: regionLondon)! XCTValidateDateComponents(date: dateA, expec: ExpectedDateComponents { $0.year = 2018 $0.month = 2 $0.day = 5 $0.hour = 23 $0.minute = 14 $0.second = 45 $0.monthNameDefault = "febbraio" $0.monthNameDefaultStd = "febbraio" $0.monthNameShort = "feb" $0.monthNameStandaloneShort = "feb" $0.msInDay = 83_686_000 $0.weekday = 2 $0.weekdayNameShort = "lun" $0.weekdayNameVeryShort = "L" $0.weekOfMonth = 2 $0.eraNameShort = "a.C." $0.weekdayOrdinal = 1 $0.nearestHour = 23 $0.yearForWeekOfYear = 2018 $0.quarter = 1 }) // TEST #1: Date In French let regionParis = Region(calendar: Calendars.gregorian, zone: Zones.europeParis, locale: Locales.french) let dateB = DateInRegion("2018-02-05 23:14:45", format: dateFormat, region: regionParis)! XCTValidateDateComponents(date: dateB, expec: ExpectedDateComponents { $0.monthNameDefault = "février" $0.monthNameShort = "févr." $0.eraNameShort = "av. J.-C." $0.weekdayNameDefault = "lundi" }) // TEST #3: Other components XCTAssert( (dateB.region == regionParis), "Failed to assign correct region to date") XCTAssert( (dateB.calendar.identifier == regionParis.calendar.identifier), "Failed to assign correct region's calendar to date") XCTAssert( (dateB.quarterName(.default) == "1er trimestre"), "Failed to get quarterName in default") XCTAssert( (dateB.quarterName(.short) == "T1"), "Failed to get quarterName in short") XCTAssert( (dateB.quarterName(.default, locale: Locales.italian) == "1º trimestre"), "Failed to get quarterName with overwrite of locale") } func testDateInRegion_isLeapMonth() { let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian) let dateFormat = "yyyy-MM-dd HH:mm:ss" let dateA = DateInRegion("2018-02-05 00:00:00", format: dateFormat, region: regionRome)! let dateB = DateInRegion("2016-02-22 00:00:00", format: dateFormat, region: regionRome)! let dateC = DateInRegion("2017-12-10 00:00:00", format: dateFormat, region: regionRome)! XCTAssert( dateA.isLeapMonth == false, "Failed to evaluate is date isLeapMonth == false") XCTAssert( dateC.isLeapMonth == false, "Failed to evaluate is date isLeapMonth == false") XCTAssert( dateB.isLeapMonth, "Failed to evaluate is date isLeapMonth") } func testDateInRegion_dateBySet() { let originalDate = "2018-10-10T12:02:16.024".toISODate() let newDate = originalDate?.dateBySet(hour: nil, min: nil, secs: nil, ms: 7) XCTAssert( newDate?.toISO([.withInternetDateTimeExtended]) == "2018-10-10T12:02:16.007Z", "Failed to set milliseconds") } func testDateInRegion_isLeapYear() { let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian) let dateFormat = "yyyy-MM-dd HH:mm:ss" let dateA = DateInRegion("2018-04-05 00:00:00", format: dateFormat, region: regionRome)! let dateB = DateInRegion("2016-07-22 00:00:00", format: dateFormat, region: regionRome)! let dateC = DateInRegion("2017-12-10 00:00:00", format: dateFormat, region: regionRome)! XCTAssert( dateA.isLeapYear == false, "Failed to evaluate is date isLeapYear == false") XCTAssert( dateC.isLeapYear == false, "Failed to evaluate is date isLeapYear == false") XCTAssert( dateB.isLeapYear, "Failed to evaluate is date isLeapYear") } func testDateInRegion_julianDayAndModifiedJulianDay() { // swiftlint:disable nesting struct ExpectedJulian { var dateISO: String var julianDay: Double var modifiedJulianDay: Double } let dates = [ ExpectedJulian(dateISO: "2017-12-22T00:06:18+01:00", julianDay: 2_458_109.462_708_333_5, modifiedJulianDay: 58108.962_708_333_51), ExpectedJulian(dateISO: "2018-06-02T02:14:45+02:00", julianDay: 2_458_271.510_243_055_4, modifiedJulianDay: 58271.010_243_055_41), ExpectedJulian(dateISO: "2018-04-04T13:31:12+02:00", julianDay: 2_458_212.98, modifiedJulianDay: 58212.479_999_999_98), ExpectedJulian(dateISO: "2018-03-18T10:11:10+01:00", julianDay: 2_458_195.882_754_629_5, modifiedJulianDay: 58195.382_754_629_48), ExpectedJulian(dateISO: "2018-03-10T18:03:22+01:00", julianDay: 2_458_188.210_671_296_3, modifiedJulianDay: 58187.710_671_296_34), ExpectedJulian(dateISO: "2017-07-14T06:33:47+02:00", julianDay: 2_457_948.690_127_315, modifiedJulianDay: 57948.190_127_315), ExpectedJulian(dateISO: "2018-02-14T16:51:14+01:00", julianDay: 2_458_164.160_578_703_5, modifiedJulianDay: 58163.660_578_703_51), ExpectedJulian(dateISO: "2017-08-15T17:41:44+02:00", julianDay: 2_457_981.153_981_481_7, modifiedJulianDay: 57980.653_981_481_68), ExpectedJulian(dateISO: "2018-03-04T09:54:54+01:00", julianDay: 2_458_181.871_458_333, modifiedJulianDay: 58181.371_458_332_986), ExpectedJulian(dateISO: "2017-09-23T08:18:15+02:00", julianDay: 2_458_019.762_673_611, modifiedJulianDay: 58019.262_673_610_82), ExpectedJulian(dateISO: "2017-12-10T10:29:42+01:00", julianDay: 2_458_097.895625, modifiedJulianDay: 58097.395_624_999_89), ExpectedJulian(dateISO: "2017-11-11T02:49:41+01:00", julianDay: 2_458_068.576_168_981_4, modifiedJulianDay: 58068.076_168_981_38), ExpectedJulian(dateISO: "2017-07-06T04:05:39+02:00", julianDay: 2_457_940.587_256_944_7, modifiedJulianDay: 57940.087_256_944_74), ExpectedJulian(dateISO: "2017-12-02T00:23:52+01:00", julianDay: 2_458_089.474_907_407_5, modifiedJulianDay: 58088.974_907_407_54), ExpectedJulian(dateISO: "2017-11-14T17:59:46+01:00", julianDay: 2_458_072.208_171_296_4, modifiedJulianDay: 58071.708_171_296_4), ExpectedJulian(dateISO: "2018-03-02T10:53:52+01:00", julianDay: 2_458_179.912_407_407_5, modifiedJulianDay: 58179.412_407_407_54), ExpectedJulian(dateISO: "2018-04-14T23:46:35+02:00", julianDay: 2_458_223.407_349_537, modifiedJulianDay: 58222.907_349_537_13), ExpectedJulian(dateISO: "2018-04-28T07:25:22+02:00", julianDay: 2_458_236.725_949_074, modifiedJulianDay: 58236.225_949_074_14), ExpectedJulian(dateISO: "2018-01-06T14:36:53+01:00", julianDay: 2_458_125.067_280_092_3, modifiedJulianDay: 58124.567_280_092_28), ExpectedJulian(dateISO: "2017-09-24T19:58:19+02:00", julianDay: 2_458_021.248_831_019, modifiedJulianDay: 58020.748_831_018_806), ExpectedJulian(dateISO: "2017-12-17T21:12:31+01:00", julianDay: 2_458_105.342_025_463, modifiedJulianDay: 58104.842_025_463_004), ExpectedJulian(dateISO: "2018-05-04T02:28:42+02:00", julianDay: 2_458_242.519_930_555_5, modifiedJulianDay: 58242.019_930_555_485), ExpectedJulian(dateISO: "2018-01-21T18:41:34+01:00", julianDay: 2_458_140.237_199_074, modifiedJulianDay: 58139.737_199_074_12), ExpectedJulian(dateISO: "2018-04-05T02:36:54+02:00", julianDay: 2_458_213.525625, modifiedJulianDay: 58213.025_624_999_78), ExpectedJulian(dateISO: "2018-02-07T13:35:16+01:00", julianDay: 2_458_157.024_490_741, modifiedJulianDay: 58156.524_490_741_08), ExpectedJulian(dateISO: "2017-11-30T00:58:20+01:00", julianDay: 2_458_087.498_842_592_4, modifiedJulianDay: 58086.998_842_592_35), ExpectedJulian(dateISO: "2018-04-10T07:10:34+02:00", julianDay: 2_458_218.715_671_296, modifiedJulianDay: 58218.215_671_296_23), ExpectedJulian(dateISO: "2017-08-11T09:36:56+02:00", julianDay: 2_457_976.817_314_815, modifiedJulianDay: 57976.317_314_814_776), ExpectedJulian(dateISO: "2018-04-28T12:30:18+02:00", julianDay: 2_458_236.937_708_333, modifiedJulianDay: 58236.437_708_333_135), ExpectedJulian(dateISO: "2017-09-17T11:59:29+02:00", julianDay: 2_458_013.916_307_870_3, modifiedJulianDay: 58013.416_307_870_3) ] dates.forEach { let date = $0.dateISO.toISODate()! guard date.julianDay == $0.julianDay else { XCTFail("Failed to evaluate julianDay of '\($0.dateISO)'. Got '\(date.julianDay)', expected '\($0.julianDay)'") return } guard date.modifiedJulianDay == $0.modifiedJulianDay else { XCTFail("Failed to evaluate modifiedJulianDay of '\($0.dateISO)'. Got '\(date.modifiedJulianDay)', expected '\($0.modifiedJulianDay)'") return } } } @available(iOS 9.0, macOS 10.11, *) func test_ordinalDay() { let newYork = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.englishUnitedStates) let localDate = DateInRegion(components: { $0.year = 2002 $0.month = 3 $0.day = 1 $0.hour = 5 $0.minute = 30 }, region: newYork)! XCTAssert(localDate.ordinalDay == "1st", "Failed to get the correct value of the ordinalDay for a date") let localDate2 = DateInRegion(components: { $0.year = 2002 $0.month = 3 $0.day = 2 $0.hour = 5 $0.minute = 30 }, region: newYork)! XCTAssert(localDate2.ordinalDay == "2nd", "Failed to get the correct value of the ordinalDay for a date") let localDate3 = DateInRegion(components: { $0.year = 2002 $0.month = 3 $0.day = 3 $0.hour = 5 $0.minute = 30 }, region: newYork)! XCTAssert(localDate3.ordinalDay == "3rd", "Failed to get the correct value of the ordinalDay for a date") let localDate4 = DateInRegion(components: { $0.year = 2002 $0.month = 3 $0.day = 4 $0.hour = 5 $0.minute = 30 }, region: newYork)! XCTAssert(localDate4.ordinalDay == "4th", "Failed to get the correct value of the ordinalDay for a date") let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian) let localDate5 = DateInRegion(components: { $0.year = 2002 $0.month = 3 $0.day = 2 $0.hour = 5 $0.minute = 30 }, region: regionRome)! XCTAssert(localDate5.ordinalDay == "2º", "Failed to get the correct value of the ordinalDay for a date") } func testDateInRegion_ISOFormatterAlt() { let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian) let dateFormat = "yyyy-MM-dd HH:mm:ss" let date = DateInRegion("2017-07-22 00:00:00", format: dateFormat, region: regionRome)! XCTAssert( date.toISO() == "2017-07-22T00:00:00+02:00", "Failed to format ISO") XCTAssert( date.toISO([.withFullDate]) == "2017-07-22", "Failed to format ISO") XCTAssert( date.toISO([.withFullDate, .withFullTime, .withDashSeparatorInDate, .withSpaceBetweenDateAndTime]) == "2017-07-22 00:00:00+02:00", "Failed to format ISO") } func testDateInRegion_getIntervalForComponentBetweenDates() { let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian) let dateFormat = "yyyy-MM-dd HH:mm:ss" let dateA = DateInRegion("2017-07-22 00:00:00", format: dateFormat, region: regionRome)! let dateB = DateInRegion("2017-07-23 12:00:00", format: dateFormat, region: regionRome)! let dateC = DateInRegion("2017-09-23 12:00:00", format: dateFormat, region: regionRome)! let dateD = DateInRegion("2017-09-23 12:54:00", format: dateFormat, region: regionRome)! XCTAssert( (dateA.getInterval(toDate: dateB, component: .hour) == 36), "Failed to evaluate is hours interval") XCTAssert( (dateA.getInterval(toDate: dateB, component: .day) == 2), "Failed to evaluate is days interval") // 1 days, 12 hours XCTAssert( (dateB.getInterval(toDate: dateC, component: .month) == 2), "Failed to evaluate is months interval") XCTAssert( (dateB.getInterval(toDate: dateC, component: .day) == 62), "Failed to evaluate is days interval") XCTAssert( (dateB.getInterval(toDate: dateC, component: .year) == 0), "Failed to evaluate is years interval") XCTAssert( (dateB.getInterval(toDate: dateC, component: .weekOfYear) == 9), "Failed to evaluate is weeksOfYear interval") XCTAssert( (dateC.getInterval(toDate: dateD, component: .minute) == 54), "Failed to evaluate is minutes interval") XCTAssert( (dateC.getInterval(toDate: dateD, component: .hour) == 0), "Failed to evaluate is hours interval") XCTAssert( (dateA.getInterval(toDate: dateD, component: .minute) == 91494), "Failed to evaluate is minutes interval") } func testDateInRegion_timeIntervalSince() { let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian) let regionLondon = Region(calendar: Calendars.gregorian, zone: Zones.europeLondon, locale: Locales.english) let dateFormat = "yyyy-MM-dd HH:mm:ss" let dateA = DateInRegion("2017-07-22 00:00:00", format: dateFormat, region: regionRome)! let dateB = DateInRegion("2017-07-22 00:00:00", format: dateFormat, region: regionRome)! let dateC = DateInRegion("2017-07-23 13:20:15", format: dateFormat, region: regionLondon)! let dateD = DateInRegion("2017-07-23 13:20:20", format: dateFormat, region: regionLondon)! XCTAssert( dateA.timeIntervalSince(dateC) == -138015.0, "Failed to evaluate is minutes interval") XCTAssert( dateA.timeIntervalSince(dateB) == 0, "Failed to evaluate is minutes interval") XCTAssert( dateC.timeIntervalSince(dateD) == -5, "Failed to evaluate is minutes interval") } func testQuarter() { let regionLondon = Region(calendar: Calendars.gregorian, zone: Zones.europeLondon, locale: Locales.english) let dateFormat = "yyyy-MM-dd HH:mm:ss" let dateA = DateInRegion("2018-02-05 23:14:45", format: dateFormat, region: regionLondon)! let dateB = DateInRegion("2018-09-05 23:14:45", format: dateFormat, region: regionLondon)! let dateC = DateInRegion("2018-12-05 23:14:45", format: dateFormat, region: regionLondon)! XCTAssert( dateA.quarter == 1, "Failed to evaluate quarter property") XCTAssert( dateB.quarter == 3, "Failed to evaluate quarter property") XCTAssert( dateC.quarter == 4, "Failed to evaluate quarter property") } func testAbsoluteDateISOFormatting() { let now = DateInRegion() let iso8601_string = now.toISO([.withInternetDateTime]) let absoluteDate = now.date let absoluteDate_iso8601_string = absoluteDate.toISO([.withInternetDateTime]) XCTAssert( absoluteDate_iso8601_string == iso8601_string, "Failed respect the absolute ISO date") } func testComparingTimeUnitsWithDateComponents() { SwiftDate.defaultRegion = .local let now = Date() XCTAssert((now.addingTimeInterval(3600) - now).in(.hour) == 1, "Failed to compare date") XCTAssert((now.addingTimeInterval(3600) - now) == 1.hours, "Failed to compare date") } }