//
|
// 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 Foundation
|
|
public struct DateInRegion: DateRepresentable, Decodable, Encodable, CustomStringConvertible, Comparable, Hashable {
|
|
/// Absolute date represented. This date is not associated with any timezone or calendar
|
/// but represent the absolute number of seconds since Jan 1, 2001 at 00:00:00 UTC.
|
public internal(set) var date: Date
|
|
/// Associated region which define where the date is represented into the world.
|
public let region: Region
|
|
/// Formatter used to transform this object in a string. By default is `nil` because SwiftDate
|
/// uses the thread shared formatter in order to avoid expensive init of the `DateFormatter` object.
|
/// However, if you need of a custom behaviour you can set a valid value.
|
public var customFormatter: DateFormatter?
|
|
/// Extract date components by taking care of the region in which the date is expressed.
|
public var dateComponents: DateComponents {
|
return region.calendar.dateComponents(DateComponents.allComponentsSet, from: date)
|
}
|
|
/// Description of the date
|
public var description: String {
|
let absISODate = DateFormatter.sharedFormatter(forRegion: Region.UTC).string(from: date)
|
let representedDate = formatter(format: DateFormats.iso8601).string(from: date)
|
return "{abs_date='\(absISODate)', rep_date='\(representedDate)', region=\(region.description)"
|
}
|
|
/// The interval between the date value and 00:00:00 UTC on 1 January 1970.
|
public var timeIntervalSince1970: TimeInterval {
|
return date.timeIntervalSince1970
|
}
|
|
/// Initialize with an absolute date and represent it into given geographic region.
|
///
|
/// - Parameters:
|
/// - date: absolute date to represent.
|
/// - region: region in which the date is represented. If ignored `defaultRegion` is used instead.
|
public init(_ date: Date = Date(), region: Region = SwiftDate.defaultRegion) {
|
self.date = date
|
self.region = region
|
}
|
|
/// Initialize a new `DateInRegion` by parsing given string.
|
/// If you know the format of the string you should pass it in order to speed up the parsing process.
|
/// If you don't know the format leave it `nil` and parse is done between all formats in `DateFormats.builtInAutoFormats`
|
/// and the ordered list you can provide in `SwiftDate.autoParseFormats` (with attempt priority set on your list).
|
///
|
/// - Parameters:
|
/// - string: string with the date.
|
/// - format: format of the date.
|
/// - region: region in which the date is expressed.
|
public init?(_ string: String, format: String? = nil, region: Region = SwiftDate.defaultRegion) {
|
guard let date = DateFormats.parse(string: string,
|
format: format,
|
region: region) else {
|
return nil // failed to parse date
|
}
|
self.date = date
|
self.region = region
|
}
|
|
/// Initialize a new `DateInRegion` by parsing given string with the ordered list of passed formats.
|
/// If you know the format of the string you should pass it in order to speed up the parsing process.
|
/// If you don't know the format leave it `nil` and parse is done between all formats in `DateFormats.builtInAutoFormats`
|
/// and the ordered list you can provide in `SwiftDate.autoParseFormats` (with attempt priority set on your list).
|
///
|
/// - Parameters:
|
/// - string: string with the date.
|
/// - formats: ordered list of formats to use.
|
/// - region: region in which the date is expressed.
|
public init?(_ string: String, formats: [String]?, region: Region = SwiftDate.defaultRegion) {
|
guard let date = DateFormats.parse(string: string,
|
formats: (formats ?? SwiftDate.autoFormats),
|
region: region) else {
|
return nil // failed to parse date
|
}
|
self.date = date
|
self.region = region
|
}
|
|
/// Initialize a new date from the number of seconds passed since Unix Epoch.
|
///
|
/// - Parameters:
|
/// - interval: seconds since Unix Epoch.
|
/// - region: the region in which the date must be expressed, `nil` uses the default region at UTC timezone
|
public init(seconds interval: TimeInterval, region: Region = Region.UTC) {
|
self.date = Date(timeIntervalSince1970: interval)
|
self.region = region
|
}
|
|
/// Initialize a new date corresponding to the number of milliseconds since the Unix Epoch.
|
///
|
/// - Parameters:
|
/// - interval: seconds since the Unix Epoch timestamp.
|
/// - region: region in which the date must be expressed, `nil` uses the default region at UTC timezone
|
public init(milliseconds interval: Int, region: Region = Region.UTC) {
|
self.date = Date(timeIntervalSince1970: TimeInterval(interval) / 1000)
|
self.region = region
|
}
|
|
/// Initialize a new date with the opportunity to configure single date components via builder pattern.
|
/// Date is therfore expressed in passed region (`DateComponents`'s `timezone`,`calendar` and `locale` are ignored
|
/// and overwritten by the region if not `nil`).
|
///
|
/// - Parameters:
|
/// - configuration: configuration callback
|
/// - region: region in which the date is expressed.
|
/// Ignore to use `SwiftDate.defaultRegion`, `nil` to use `DateComponents` data.
|
public init?(components configuration: ((inout DateComponents) -> Void), region: Region? = SwiftDate.defaultRegion) {
|
var components = DateComponents()
|
configuration(&components)
|
let r = (region ?? Region(fromDateComponents: components))
|
guard let date = r.calendar.date(from: components) else {
|
return nil
|
}
|
self.date = date
|
self.region = r
|
}
|
|
/// Initialize a new date with given components.
|
///
|
/// - Parameters:
|
/// - components: components of the date.
|
/// - region: region in which the date is expressed.
|
/// Ignore to use `SwiftDate.defaultRegion`, `nil` to use `DateComponents` data.
|
public init?(components: DateComponents, region: Region?) {
|
let r = (region ?? Region(fromDateComponents: components))
|
guard let date = r.calendar.date(from: components) else {
|
return nil
|
}
|
self.date = date
|
self.region = r
|
}
|
|
/// Initialize a new date with given components.
|
public init(year: Int, month: Int, day: Int, hour: Int = 0, minute: Int = 0, second: Int = 0, nanosecond: Int = 0, region: Region = SwiftDate.defaultRegion) {
|
var components = DateComponents()
|
components.year = year
|
components.month = month
|
components.day = day
|
components.hour = hour
|
components.minute = minute
|
components.second = second
|
components.nanosecond = nanosecond
|
components.timeZone = region.timeZone
|
components.calendar = region.calendar
|
self.date = region.calendar.date(from: components)!
|
self.region = region
|
}
|
|
/// Return a date in the distant past.
|
///
|
/// - Returns: Date instance.
|
public static func past() -> DateInRegion {
|
return DateInRegion(Date.distantPast, region: SwiftDate.defaultRegion)
|
}
|
|
/// Return a date in the distant future.
|
///
|
/// - Returns: Date instance.
|
public static func future() -> DateInRegion {
|
return DateInRegion(Date.distantFuture, region: SwiftDate.defaultRegion)
|
}
|
|
// MARK: - Codable Support
|
|
enum CodingKeys: String, CodingKey {
|
case date
|
case region
|
}
|
|
}
|