325 lines
12 KiB
Swift
325 lines
12 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
|
|
|
|
public func randomNumber<T: SignedInteger>(inRange range: ClosedRange<T> = 1...6) -> T {
|
|
let length = Int64(range.upperBound - range.lowerBound + 1)
|
|
let value = Int64.random(in: 0..<Int64.max) % length + Int64(range.lowerBound)
|
|
return T(value)
|
|
}
|
|
|
|
class TestDateInRegion_Create: XCTestCase {
|
|
|
|
func testDateInRegion_DateBySetTime() {
|
|
let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
|
|
let date1 = DateInRegion("2010-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
guard let date1a = date1.dateBySet(hour: 20, min: 13, secs: 15) else {
|
|
XCTFail("Failed to alter date's time")
|
|
return
|
|
}
|
|
XCTAssert( (date1a.hour == 20 && date1a.minute == 13 && date1a.second == 15 ), "Failed to alter date's time")
|
|
|
|
// altering only one component
|
|
guard let date2a = date1.dateBySet(hour: 20, min: nil, secs: nil) else {
|
|
XCTFail("Failed to alter date's time")
|
|
return
|
|
}
|
|
XCTAssert( (date2a.hour == 20 && date2a.minute == 00 && date2a.second == 00 ), "Failed to alter date's time")
|
|
|
|
}
|
|
|
|
func testDateInRegion_DateBySet() {
|
|
// swiftlint:disable nesting
|
|
struct ExpectDateBySet {
|
|
var components: [Calendar.Component: Int?] = [:]
|
|
|
|
init() {
|
|
let allCmps: [Calendar.Component] = [.second, .minute, .hour, .day, .month, .year]
|
|
for i in 0..<allCmps.count {
|
|
let componentToAlter = allCmps[i]
|
|
let range: ClosedRange<Int>?
|
|
switch componentToAlter {
|
|
case .second, .minute: range = 0...50
|
|
case .hour: range = 0...20
|
|
case .day: range = 1...28
|
|
case .month: range = 1...12
|
|
case .year: range = 2000...2050
|
|
default: range = nil
|
|
}
|
|
if let range = range {
|
|
let value = randomNumber(inRange: range)
|
|
components[componentToAlter] = value
|
|
}
|
|
}
|
|
}
|
|
|
|
func verify(date: DateInRegion) {
|
|
components.keys.forEach {
|
|
if let value = date.dateComponents.value(for: $0), let expected = components[$0] as? Int {
|
|
if value != expected {
|
|
XCTFail("Failed to set value of component \($0). Got \(value), expected \(expected)")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
for _ in 0..<50 {
|
|
let randomDate = "2041-05-18T18:00:25Z".toISODate()! //DateInRegion.randomDate(region: regionRome)
|
|
let alterComponents = ExpectDateBySet()
|
|
if let alteredDate = randomDate.dateBySet(alterComponents.components) {
|
|
alterComponents.verify(date: alteredDate)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDateInRegion_RandomDatesInRange() {
|
|
// Random dates
|
|
let regionNorw = Region(calendar: Calendars.gregorian, zone: Zones.europeOslo, locale: Locales.norwegianBokmlSvalbardJanMayen)
|
|
let randomDateAny = DateInRegion.randomDate(region: regionNorw)
|
|
guard randomDateAny.region == regionNorw else {
|
|
XCTFail("Failed to generate a random date in region")
|
|
return
|
|
}
|
|
|
|
// Random dates in range
|
|
let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
|
|
let upperLimit = DateInRegion("2015-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
let lowerLimit = DateInRegion("2010-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
|
|
let randomDates = DateInRegion.randomDates(count: 50, between: lowerLimit, and: upperLimit, region: regionRome)
|
|
randomDates.forEach {
|
|
guard $0.isInRange(date: lowerLimit, and: upperLimit) else {
|
|
XCTFail("Random date '\($0.description)' is not in given range")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDateInRegion_RandomDatesBackToDays() {
|
|
for _ in 0..<50 {
|
|
let daysBack = (Int.random(in: 0..<365) + 1)
|
|
let randomDate = DateInRegion.randomDate(withinDaysBeforeToday: daysBack)
|
|
guard randomDate.getInterval(toDate: DateInRegion(), component: .day) <= daysBack else {
|
|
XCTFail("Failed to generate a random back date back to max \(daysBack) days")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDateInRegion_EnumerateDates() {
|
|
let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
|
|
|
|
// TEST DATE #1
|
|
// +1 days
|
|
let fromDate1 = DateInRegion("2015-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
let toDate1 = DateInRegion("2015-01-02 03:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
|
|
let increment1 = DateComponents.create {
|
|
$0.hour = 1
|
|
}
|
|
let enumeratedDates1 = DateInRegion.enumerateDates(from: fromDate1, to: toDate1, increment: increment1)
|
|
let expectedDates1 = [
|
|
"2015-01-01 00:00:00",
|
|
"2015-01-01 01:00:00",
|
|
"2015-01-01 02:00:00",
|
|
"2015-01-01 03:00:00",
|
|
"2015-01-01 04:00:00",
|
|
"2015-01-01 05:00:00",
|
|
"2015-01-01 06:00:00",
|
|
"2015-01-01 07:00:00",
|
|
"2015-01-01 08:00:00",
|
|
"2015-01-01 09:00:00",
|
|
"2015-01-01 10:00:00",
|
|
"2015-01-01 11:00:00",
|
|
"2015-01-01 12:00:00",
|
|
"2015-01-01 13:00:00",
|
|
"2015-01-01 14:00:00",
|
|
"2015-01-01 15:00:00",
|
|
"2015-01-01 16:00:00",
|
|
"2015-01-01 17:00:00",
|
|
"2015-01-01 18:00:00",
|
|
"2015-01-01 19:00:00",
|
|
"2015-01-01 20:00:00",
|
|
"2015-01-01 21:00:00",
|
|
"2015-01-01 22:00:00",
|
|
"2015-01-01 23:00:00",
|
|
"2015-01-02 00:00:00",
|
|
"2015-01-02 01:00:00",
|
|
"2015-01-02 02:00:00",
|
|
"2015-01-02 03:00:00"]
|
|
XCTExpectedFormattedDates(enumeratedDates1, expected: expectedDates1)
|
|
|
|
// TEST DATE #2
|
|
// +1 hour, +30 minutes, +10 seconds
|
|
let increment2 = DateComponents.create {
|
|
$0.hour = 1
|
|
$0.minute = 30
|
|
$0.second = 10
|
|
}
|
|
let fromDate2 = DateInRegion("2015-01-01 10:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
let toDate2 = DateInRegion("2015-01-02 03:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
let enumeratedDates2 = DateInRegion.enumerateDates(from: fromDate2, to: toDate2, increment: increment2)
|
|
let expectedDates2 = [
|
|
"2015-01-01 10:00:00",
|
|
"2015-01-01 11:30:10",
|
|
"2015-01-01 13:00:20",
|
|
"2015-01-01 14:30:30",
|
|
"2015-01-01 16:00:40",
|
|
"2015-01-01 17:30:50",
|
|
"2015-01-01 19:01:00",
|
|
"2015-01-01 20:31:10",
|
|
"2015-01-01 22:01:20",
|
|
"2015-01-01 23:31:30",
|
|
"2015-01-02 01:01:40",
|
|
"2015-01-02 02:31:50"
|
|
]
|
|
XCTExpectedFormattedDates(enumeratedDates2, expected: expectedDates2)
|
|
|
|
// TEST DATE #3
|
|
// +1 month
|
|
let increment3 = DateComponents.create {
|
|
$0.month = 1
|
|
}
|
|
let fromDate3 = DateInRegion("2015-01-01 01:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
let toDate3 = DateInRegion("2016-02-05 02:00:00", format: "yyyy-MM-dd HH:mm:ss", region: regionRome)!
|
|
let enumeratedDates3 = DateInRegion.enumerateDates(from: fromDate3, to: toDate3, increment: increment3)
|
|
let expectedDates3 = [
|
|
"2015-01-01 01:00:00",
|
|
"2015-02-01 01:00:00",
|
|
"2015-03-01 01:00:00",
|
|
"2015-04-01 01:00:00",
|
|
"2015-05-01 01:00:00",
|
|
"2015-06-01 01:00:00",
|
|
"2015-07-01 01:00:00",
|
|
"2015-08-01 01:00:00",
|
|
"2015-09-01 01:00:00",
|
|
"2015-10-01 01:00:00",
|
|
"2015-11-01 01:00:00",
|
|
"2015-12-01 01:00:00",
|
|
"2016-01-01 01:00:00",
|
|
"2016-02-01 01:00:00"
|
|
]
|
|
XCTExpectedFormattedDates(enumeratedDates3, expected: expectedDates3)
|
|
}
|
|
|
|
func XCTExpectedFormattedDates(_ list: [DateInRegion], expected expectedDates: [String]) {
|
|
list.enumerated().forEach {
|
|
let formatted = $0.element.toFormat("yyyy-MM-dd HH:mm:ss")
|
|
let expected = expectedDates[$0.offset]
|
|
guard expected == formatted else {
|
|
XCTFail("Failed to enumerate dates. Got '\(formatted)' expected '\(expected)")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDateInRegion_oldestAndNewestAndSortsIn() {
|
|
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 regionNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.english)
|
|
|
|
let datesArrayFormat = "yyyy-MM-dd HH:mm:ss"
|
|
|
|
let date1 = DateInRegion("2015-09-24 13:20:55", format: datesArrayFormat, region: regionRome)!
|
|
let date2 = DateInRegion("2015-11-14 14:44:00", format: datesArrayFormat, region: regionRome)!
|
|
let date3 = DateInRegion("2017-01-01 00:00:00", format: datesArrayFormat, region: regionRome)!
|
|
let date4 = DateInRegion("2015-09-24 13:00:55", format: datesArrayFormat, region: regionNY)!
|
|
let date5 = DateInRegion("2017-11-30 20:00:40", format: datesArrayFormat, region: regionNY)!
|
|
let date6 = DateInRegion("2017-11-29 23:59:59", format: datesArrayFormat, region: regionLondon)!
|
|
let datesArray = [date1, date2, date3, date4, date5, date6]
|
|
|
|
// Oldest and Newest
|
|
XCTAssert( (DateInRegion.oldestIn(list: datesArray) == date1), "Failed to get the oldest date in list")
|
|
XCTAssert( (DateInRegion.newestIn(list: datesArray) == date5), "Failed to get the newest date in list")
|
|
|
|
// Order by newest
|
|
let orderedByNewest = DateInRegion.sortedByNewest(list: datesArray)
|
|
XCTSameDateArray([date5, date6, date3, date2, date4, date1], orderedByNewest)
|
|
|
|
// Order by oldest
|
|
let orderedByOldest = DateInRegion.sortedByOldest(list: datesArray)
|
|
XCTSameDateArray([date1, date4, date2, date3, date6, date5], orderedByOldest)
|
|
}
|
|
|
|
@discardableResult
|
|
func XCTSameDateArray(_ left: [DateInRegion], _ right: [DateInRegion]) -> Bool {
|
|
guard left.count == right.count else { return false }
|
|
// swiftlint:disable for_where
|
|
for idx in 0..<left.count {
|
|
if left[idx] != right[idx] {
|
|
XCTFail("At index \(idx). Expected date = {\(left[idx].date)}, got = {\(right[idx].date)}")
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func testDateInRegion_DateForWeekdays() {
|
|
func validateArrayOfISODates(_ found: [String], _ expected: [String]) {
|
|
guard found.count == expected.count else {
|
|
XCTFail("Failed to validate array of dates. Different in number.")
|
|
return
|
|
}
|
|
for i in 0..<expected.count {
|
|
guard found[i] == expected[i] else {
|
|
XCTFail("Failed to validate item '\(i)'. Found '\(found[i])', expecting '\(expected[i])'")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
let mondaysInJan2019 = DateInRegion.datesForWeekday(.monday, inMonth: 1, ofYear: 2019, region: Region.UTC).map { $0.toISO() }
|
|
validateArrayOfISODates(mondaysInJan2019, [
|
|
"2019-01-07T00:00:00Z",
|
|
"2019-01-14T00:00:00Z",
|
|
"2019-01-21T00:00:00Z",
|
|
"2019-01-28T00:00:00Z"
|
|
])
|
|
|
|
let fromDate = DateInRegion(year: 2019, month: 5, day: 27, hour: 0, minute: 0)
|
|
let toDate = DateInRegion(year: 2019, month: 6, day: 8, hour: 0, minute: 0)
|
|
|
|
let fridaysInJunePartial = DateInRegion.datesForWeekday(.friday, from: fromDate, to: toDate, region: Region.UTC).map {
|
|
$0.toISO()
|
|
}
|
|
validateArrayOfISODates(fridaysInJunePartial, [
|
|
"2019-05-31T00:00:00Z",
|
|
"2019-06-07T00:00:00Z"
|
|
])
|
|
}
|
|
|
|
func testDateInRegion_DateAtStartOf() {
|
|
let regionRome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
|
|
let currentDate = DateInRegion(Date(), region: regionRome)
|
|
|
|
let startOfDay1 = currentDate.dateAt(.startOfDay).date
|
|
let startOfDay2 = currentDate.dateAtStartOf(.day).date
|
|
|
|
XCTAssert( startOfDay1 == startOfDay2, "dateAt(.startOfDay), dateAtStartOf(.day) are not same.")
|
|
|
|
let startOfWeek1 = currentDate.dateAt(.startOfWeek).date
|
|
let startOfWeek2 = currentDate.dateAtStartOf(.weekOfYear).date
|
|
|
|
XCTAssert( startOfWeek1 == startOfWeek2, "dateAt(.startOfWeek), dateAtStartOf(.weekday) are not same.")
|
|
|
|
let startOfMonth1 = currentDate.dateAt(.startOfMonth).date
|
|
let startOfMonth2 = currentDate.dateAtStartOf(.month).date
|
|
|
|
XCTAssert( startOfMonth1 == startOfMonth2, "dateAt(.startOfMonth), dateAtStartOf(.month) are not same.")
|
|
}
|
|
}
|