// // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import XCTest import SignalServiceKit class DateUtilTest: XCTestCase { func buildDate(year: Int = 0, month: Int = 0, day: Int = 0, hour: Int = 0, minute: Int = 0, second: Int = 0) -> Date { let calendar = Calendar.current let timeZone = TimeZone.current let dateComponents = DateComponents(calendar: calendar, timeZone: timeZone, year: year, month: month, day: day, hour: hour, minute: minute, second: second) return calendar.date(from: dateComponents)! } func testDateComparison() { let firstDate = Date() let sameDate = Date(timeIntervalSinceReferenceDate: firstDate.timeIntervalSinceReferenceDate) let laterDate = Date(timeIntervalSinceReferenceDate: firstDate.timeIntervalSinceReferenceDate + 1) XCTAssertEqual(firstDate.timeIntervalSinceReferenceDate, sameDate.timeIntervalSinceReferenceDate) XCTAssertNotEqual(firstDate.timeIntervalSinceReferenceDate, laterDate.timeIntervalSinceReferenceDate) XCTAssertEqual(firstDate, sameDate) XCTAssertNotEqual(firstDate, laterDate) XCTAssertTrue(firstDate.timeIntervalSinceReferenceDate < laterDate.timeIntervalSinceReferenceDate) XCTAssertFalse(firstDate.isBefore(sameDate)) XCTAssertTrue(firstDate.isBefore(laterDate)) XCTAssertFalse(laterDate.isBefore(firstDate)) XCTAssertFalse(firstDate.isAfter(sameDate)) XCTAssertFalse(firstDate.isAfter(laterDate)) XCTAssertTrue(laterDate.isAfter(firstDate)) } func testDateComparators() { // Use a specific reference date to make this test deterministic, // and to avoid failing around midnight, new year's, etc. var nowDateComponents = DateComponents() nowDateComponents.year = 2015 nowDateComponents.month = 8 nowDateComponents.day = 31 nowDateComponents.hour = 8 let now = Calendar.current.date(from: nowDateComponents)! let formatter = DateFormatter() formatter.dateStyle = .long formatter.timeStyle = .long Logger.info("now: \(formatter.string(from: now))") let oneSecondAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kSecondInterval) let oneMinuteAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kMinuteInterval) let oneDayAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kDayInterval) let threeDaysAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kDayInterval * 3) let tenDaysAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kDayInterval * 10) let oneYearAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kYearInterval) let twoYearsAgo = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate - kYearInterval * 2) let oneSecondAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kSecondInterval) let oneMinuteAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kMinuteInterval) let oneDayAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kDayInterval) let threeDaysAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kDayInterval * 3) let tenDaysAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kDayInterval * 10) let oneYearAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kYearInterval) let twoYearsAhead = Date(timeIntervalSinceReferenceDate: now.timeIntervalSinceReferenceDate + kYearInterval * 2) Logger.info("oneSecondAgo: \(formatter.string(from: oneSecondAgo))") XCTAssertTrue(DateUtil.dateIsToday(oneSecondAgo, now: now)) XCTAssertTrue(DateUtil.dateIsToday(oneMinuteAgo, now: now)) XCTAssertFalse(DateUtil.dateIsToday(oneDayAgo, now: now)) XCTAssertFalse(DateUtil.dateIsToday(threeDaysAgo, now: now)) XCTAssertFalse(DateUtil.dateIsToday(tenDaysAgo, now: now)) XCTAssertFalse(DateUtil.dateIsToday(oneYearAgo, now: now)) XCTAssertFalse(DateUtil.dateIsToday(twoYearsAgo, now: now)) XCTAssertTrue(DateUtil.dateIsToday(oneSecondAhead, now: now)) XCTAssertTrue(DateUtil.dateIsToday(oneMinuteAhead, now: now)) XCTAssertFalse(DateUtil.dateIsToday(oneDayAhead, now: now)) XCTAssertFalse(DateUtil.dateIsToday(threeDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsToday(tenDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsToday(oneYearAhead, now: now)) XCTAssertFalse(DateUtil.dateIsToday(twoYearsAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneSecondAgo, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneMinuteAgo, now: now)) XCTAssertTrue(DateUtil.dateIsYesterday(oneDayAgo, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(threeDaysAgo, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(tenDaysAgo, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneYearAgo, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(twoYearsAgo, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneSecondAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneMinuteAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneDayAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(threeDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(tenDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(oneYearAhead, now: now)) XCTAssertFalse(DateUtil.dateIsYesterday(twoYearsAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(oneSecondAgo, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(oneMinuteAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(oneDayAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(threeDaysAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(tenDaysAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(oneYearAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(twoYearsAgo, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(oneSecondAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(oneMinuteAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(oneDayAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(threeDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(tenDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(oneYearAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(twoYearsAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneSecondAgo, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneMinuteAgo, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneDayAgo, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(threeDaysAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanOneWeek(tenDaysAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanOneWeek(oneYearAgo, now: now)) XCTAssertTrue(DateUtil.dateIsOlderThanOneWeek(twoYearsAgo, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneSecondAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneMinuteAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneDayAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(threeDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(tenDaysAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(oneYearAhead, now: now)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(twoYearsAhead, now: now)) XCTAssertTrue(DateUtil.dateIsThisYear(oneSecondAgo, now: now)) XCTAssertTrue(DateUtil.dateIsThisYear(oneMinuteAgo, now: now)) XCTAssertTrue(DateUtil.dateIsThisYear(oneDayAgo, now: now)) XCTAssertFalse(DateUtil.dateIsThisYear(oneYearAgo, now: now)) XCTAssertFalse(DateUtil.dateIsThisYear(twoYearsAgo, now: now)) XCTAssertTrue(DateUtil.dateIsThisYear(oneSecondAhead, now: now)) XCTAssertTrue(DateUtil.dateIsThisYear(oneMinuteAhead, now: now)) XCTAssertTrue(DateUtil.dateIsThisYear(oneDayAhead, now: now)) XCTAssertFalse(DateUtil.dateIsThisYear(oneYearAhead, now: now)) XCTAssertFalse(DateUtil.dateIsThisYear(twoYearsAhead, now: now)) } func testDateComparators_timezoneVMidnight () { let formatter = DateFormatter() formatter.dateStyle = .long formatter.timeStyle = .long let yesterdayBeforeMidnight = buildDate(year: 2015, month: 8, day: 10, hour: 23, minute: 55) Logger.info("yesterdayBeforeMidnight: \(formatter.string(from: yesterdayBeforeMidnight))") let todayAfterMidnight = buildDate(year: 2015, month: 8, day: 11, hour: 0, minute: 5) Logger.info("todayAfterMidnight: \(formatter.string(from: todayAfterMidnight))") let todayNoon = buildDate(year: 2015, month: 8, day: 11, hour: 12, minute: 0) Logger.info("todayNoon: \(formatter.string(from: todayNoon))") // Before Midnight, after Midnight. XCTAssertFalse(DateUtil.dateIsToday(yesterdayBeforeMidnight, now: todayAfterMidnight)) XCTAssertTrue(DateUtil.dateIsYesterday(yesterdayBeforeMidnight, now: todayAfterMidnight)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(yesterdayBeforeMidnight, now: todayAfterMidnight)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(yesterdayBeforeMidnight, now: todayAfterMidnight)) XCTAssertTrue(DateUtil.dateIsThisYear(yesterdayBeforeMidnight, now: todayAfterMidnight)) // Before Midnight, noon. XCTAssertFalse(DateUtil.dateIsToday(yesterdayBeforeMidnight, now: todayNoon)) XCTAssertTrue(DateUtil.dateIsYesterday(yesterdayBeforeMidnight, now: todayNoon)) XCTAssertTrue(DateUtil.dateIsOlderThanToday(yesterdayBeforeMidnight, now: todayNoon)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(yesterdayBeforeMidnight, now: todayNoon)) XCTAssertTrue(DateUtil.dateIsThisYear(yesterdayBeforeMidnight, now: todayNoon)) // After Midnight, noon. XCTAssertTrue(DateUtil.dateIsToday(todayAfterMidnight, now: todayNoon)) XCTAssertFalse(DateUtil.dateIsYesterday(todayAfterMidnight, now: todayNoon)) XCTAssertFalse(DateUtil.dateIsOlderThanToday(todayAfterMidnight, now: todayNoon)) XCTAssertFalse(DateUtil.dateIsOlderThanOneWeek(todayAfterMidnight, now: todayNoon)) XCTAssertTrue(DateUtil.dateIsThisYear(todayAfterMidnight, now: todayNoon)) } func testDaysFrom() { let date1 = buildDate(year: 2017, month: 5, day: 17, hour: 19, minute: 37, second: 23) let date2 = buildDate(year: 2017, month: 5, day: 17, hour: 0, minute: 1, second: 2) let date3 = buildDate(year: 2017, month: 5, day: 16, hour: 0, minute: 1, second: 2) let date4 = buildDate(year: 2017, month: 5, day: 18, hour: 0, minute: 1, second: 2) XCTAssertEqual(0, DateUtil.daysFrom(firstDate: date1, toSecondDate: date2)) XCTAssertEqual(-1, DateUtil.daysFrom(firstDate: date1, toSecondDate: date3)) XCTAssertEqual(+1, DateUtil.daysFrom(firstDate: date3, toSecondDate: date1)) XCTAssertEqual(+1, DateUtil.daysFrom(firstDate: date1, toSecondDate: date4)) XCTAssertEqual(-1, DateUtil.daysFrom(firstDate: date4, toSecondDate: date1)) } func testFastDaysFrom() { let referenceDate = buildDate(year: 2021, month: 9, day: 6, hour: 13, minute: 08, second: 5) let date1 = buildDate(year: 2017, month: 5, day: 17, hour: 19, minute: 37, second: 23) let date2 = buildDate(year: 2017, month: 5, day: 17, hour: 0, minute: 1, second: 2) let date3 = buildDate(year: 2017, month: 5, day: 16, hour: 5, minute: 31, second: 13) let date4 = buildDate(year: 2017, month: 5, day: 18, hour: 23, minute: 51, second: 34) XCTAssertEqual(1573, DateUtil.daysFrom(firstDate: date1, toSecondDate: referenceDate)) XCTAssertEqual(1573, DateUtil.daysFrom(firstDate: date2, toSecondDate: referenceDate)) XCTAssertEqual(1574, DateUtil.daysFrom(firstDate: date3, toSecondDate: referenceDate)) XCTAssertEqual(1572, DateUtil.daysFrom(firstDate: date4, toSecondDate: referenceDate)) } }