Files
cod-backend/Sources/App/Libraries/SwiftDate/DateInRegion/DateInRegion+Components.swift
2020-06-14 21:46:44 -05:00

165 lines
6.3 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 Foundation
public extension DateInRegion {
/// Indicates whether the month is a leap month.
var isLeapMonth: Bool {
let calendar = region.calendar
// Library function for leap contains a bug for Gregorian calendars, implemented workaround
if calendar.identifier == Calendar.Identifier.gregorian && year > 1582 {
guard let range: Range<Int> = calendar.range(of: .day, in: .month, for: date) else {
return false
}
return ((range.upperBound - range.lowerBound) == 29)
}
// For other calendars:
return calendar.dateComponents([.day, .month, .year], from: date).isLeapMonth!
}
/// Indicates whether the year is a leap year.
var isLeapYear: Bool {
let calendar = region.calendar
// Library function for leap contains a bug for Gregorian calendars, implemented workaround
if calendar.identifier == Calendar.Identifier.gregorian {
var newComponents = dateComponents
newComponents.month = 2
newComponents.day = 10
let testDate = DateInRegion(components: newComponents, region: region)
return testDate!.isLeapMonth
} else if calendar.identifier == Calendar.Identifier.chinese {
/// There are 12 or 13 months in each year and 29 or 30 days in each month.
/// A 13-month year is a leap year, which meaning more than 376 days is a leap year.
return ( dateAtStartOf(.year).toUnit(.day, to: dateAtEndOf(.year)) > 375 )
}
// For other calendars:
return calendar.dateComponents([.day, .month, .year], from: date).isLeapMonth!
}
/// Julian day is the continuous count of days since the beginning of
/// the Julian Period used primarily by astronomers.
var julianDay: Double {
let destRegion = Region(calendar: Calendars.gregorian, zone: Zones.gmt, locale: Locales.english)
let utc = convertTo(region: destRegion)
let year = Double(utc.year)
let month = Double(utc.month)
let day = Double(utc.day)
let hour = Double(utc.hour) + Double(utc.minute) / 60.0 + (Double(utc.second) + Double(utc.nanosecond) / 1e9) / 3600.0
var jd = 367.0 * year - floor( 7.0 * ( year + floor((month + 9.0) / 12.0)) / 4.0 )
jd -= floor( 3.0 * (floor( (year + (month - 9.0) / 7.0) / 100.0 ) + 1.0) / 4.0 )
jd += floor(275.0 * month / 9.0) + day + 1_721_028.5 + hour / 24.0
return jd
}
/// The Modified Julian Date (MJD) was introduced by the Smithsonian Astrophysical Observatory
/// in 1957 to record the orbit of Sputnik via an IBM 704 (36-bit machine)
/// and using only 18 bits until August 7, 2576.
var modifiedJulianDay: Double {
return julianDay - 2_400_000.5
}
/// Return elapsed time expressed in given components since the current receiver and a reference date.
/// Time is evaluated with the fixed measumerent of each unity.
///
/// - Parameters:
/// - refDate: reference date (`nil` to use current date in the same region of the receiver)
/// - component: time unit to extract.
/// - Returns: value
func getInterval(toDate: DateInRegion?, component: Calendar.Component) -> Int64 {
let refDate = (toDate ?? region.nowInThisRegion())
switch component {
case .year:
let end = calendar.ordinality(of: .year, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .year, in: .era, for: date)
return Int64(end! - start!)
case .month:
let end = calendar.ordinality(of: .month, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .month, in: .era, for: date)
return Int64(end! - start!)
case .day:
let end = calendar.ordinality(of: .day, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .day, in: .era, for: date)
return Int64(end! - start!)
case .hour:
let interval = refDate.date.timeIntervalSince(date)
return Int64(interval / 1.hours.timeInterval)
case .minute:
let interval = refDate.date.timeIntervalSince(date)
return Int64(interval / 1.minutes.timeInterval)
case .second:
return Int64(refDate.date.timeIntervalSince(date))
case .weekday:
let end = calendar.ordinality(of: .weekday, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .weekday, in: .era, for: date)
return Int64(end! - start!)
case .weekdayOrdinal:
let end = calendar.ordinality(of: .weekdayOrdinal, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .weekdayOrdinal, in: .era, for: date)
return Int64(end! - start!)
case .weekOfYear:
let end = calendar.ordinality(of: .weekOfYear, in: .era, for: refDate.date)
let start = calendar.ordinality(of: .weekOfYear, in: .era, for: date)
return Int64(end! - start!)
default:
debugPrint("Passed component cannot be used to extract values using interval() function between two dates. Returning 0.")
return 0
}
}
/// The interval between the receiver and the another parameter.
/// If the receiver is earlier than anotherDate, the return value is negative.
/// If anotherDate is nil, the results are undefined.
///
/// - Parameter date: The date with which to compare the receiver.
/// - Returns: time interval between two dates
func timeIntervalSince(_ date: DateInRegion) -> TimeInterval {
return self.date.timeIntervalSince(date.date)
}
/// Extract DateComponents from the difference between two dates.
///
/// - Parameter rhs: date to compare
/// - Returns: components
func componentsTo(_ rhs: DateInRegion) -> DateComponents {
return calendar.dateComponents(DateComponents.allComponentsSet, from: rhs.date, to: date)
}
/// Returns the difference between two dates (`date - self`) expressed as date components.
///
/// - Parameters:
/// - date: reference date as initial date (left operand)
/// - components: components to extract, `nil` to use default `DateComponents.allComponentsSet`
/// - Returns: extracted date components
func componentsSince(_ date: DateInRegion, components: [Calendar.Component]? = nil) -> DateComponents {
if date.calendar != calendar {
debugPrint("Date has different calendar, results maybe wrong")
}
let cmps = (components != nil ? Calendar.Component.toSet(components!) : DateComponents.allComponentsSet)
return date.calendar.dateComponents(cmps, from: date.date, to: self.date)
}
}