Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for time zones #1

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1250"
LastUpgradeVersion = "1340"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
10 changes: 7 additions & 3 deletions Sources/FrenchRepublicanCalendarCore/DecimalTime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,13 @@ public struct DecimalTime {
}

public extension DecimalTime {
/// Initializes a new DecimalTime with the current time
init(base: Date = Date()) {
let midnight = Calendar.gregorian.startOfDay(for: base)
/// Initializes a new DecimalTime with the current time and optional time zone
init(base: Date = Date(), timeZone: TimeZone? = nil) {
var gregorianCalendar: Calendar = Calendar.gregorian
if let timeZone = timeZone {
gregorianCalendar.timeZone = timeZone
}
let midnight = gregorianCalendar.startOfDay(for: base)
self.init(timeSinceMidnight: base.timeIntervalSinceReferenceDate - midnight.timeIntervalSinceReferenceDate)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import Foundation

extension FrenchRepublicanDate: CustomDebugStringConvertible {
public static let allMonthNames = ["Vendémiaire", "Brumaire", "Frimaire", "Nivôse", "Pluviôse", "Ventôse", "Germinal", "Floréal", "Prairial", "Messidor", "Thermidor", "Fructidor", "Sansculottide"]
public static let allMonthNames = ["Vendémiaire", "Brumaire", "Frimaire", "Nivôse", "Pluviôse", "Ventôse", "Germinal", "Floréal", "Prairial", "Messidor", "Thermidor", "Fructidor", "Sansculottides"]
public static let sansculottidesDayNames = ["Jour de la vertu", "Jour du génie", "Jour du travail", "Jour de l'opinion", "Jour des récompenses", "Jour de la révolution"]

public static let shortMonthNames = ["Vend.r", "Brum.r", "Frim.r", "Niv.ô", "Pluv.ô", "Vent.ô", "Germ.l", "Flo.l", "Prai.l", "Mes.or", "Ther.or", "Fru.or", "Ss.cu"]
Expand All @@ -40,15 +40,15 @@ extension FrenchRepublicanDate: CustomDebugStringConvertible {

/// Returns string as d MMMM
public func toLongStringNoYear() -> String {
if components.month == 13 {
if components.month == 13 && !self.options.treatSansculottidesAsAMonth {
return "\(FrenchRepublicanDate.sansculottidesDayNames[components.day! - 1])"
}
return "\(components.day!) \(monthName)"
}

/// Returns string as d MMM
public func toShortString() -> String {
if components.month == 13 {
if components.month == 13 && !self.options.treatSansculottidesAsAMonth {
return "\(FrenchRepublicanDate.sansculottidesShortNames[components.day! - 1])"
}
return "\(components.day!) \(shortMonthName)"
Expand Down
52 changes: 37 additions & 15 deletions Sources/FrenchRepublicanCalendarCore/FrenchRepublicanDate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public struct FrenchRepublicanDate {

public private(set) var options: FrenchRepublicanDateOptions

public var timeZone: TimeZone?

// MARK: Component accessors

/// The day in year date component, 1-indexed
Expand All @@ -62,8 +64,10 @@ public struct FrenchRepublicanDate {
// MARK: Initializers

/// Creates a Republican Date from the given Gregorian Date
/// - Parameter date: the Gregorian Date
public init(date: Date, options: FrenchRepublicanDateOptions? = nil) {
/// - Parameters
/// - date: The Gregorian Date
/// - timeZone: The time zone; the default time zone if nil
public init(date: Date, options: FrenchRepublicanDateOptions? = nil, timeZone: TimeZone? = nil) {
self.date = date
if let options = options {
self.options = options
Expand All @@ -72,6 +76,9 @@ public struct FrenchRepublicanDate {
} else {
self.options = .default
}
if let timeZone = timeZone {
self.timeZone = timeZone
}
dateToFrenchRepublican()
}

Expand All @@ -83,21 +90,26 @@ public struct FrenchRepublicanDate {
/// - minute: Minutes
/// - second: Seconds
/// - nanosecond: Nanoseconds
public init(dayInYear: Int, year: Int, hour: Int? = nil, minute: Int? = nil, second: Int? = nil, nanosecond: Int? = nil, options: FrenchRepublicanDateOptions? = nil) {
/// - timeZone: The time zone; the default time zone if nil
public init(dayInYear: Int, year: Int, hour: Int? = nil, minute: Int? = nil, second: Int? = nil, nanosecond: Int? = nil, options: FrenchRepublicanDateOptions? = nil, timeZone: TimeZone? = nil) {
if let options = options {
self.options = options
} else if let type = FrenchRepublicanDateOptions.self as? SaveableFrenchRepublicanDateOptions.Type {
self.options = type.current
} else {
self.options = .default
}
self.date = Date(dayInYear: dayInYear, year: year, hour: hour, minute: minute, second: second, nanosecond: nanosecond, options: self.options)
self.date = Date(dayInYear: dayInYear, year: year, hour: hour, minute: minute, second: second, nanosecond: nanosecond, options: self.options, timeZone: timeZone)
initComponents(dayOfYear: dayInYear - 1, year: year, hour: hour, minute: minute, second: second, nanosecond: nanosecond)
}

/// Logic that converts the `date` value to republican date components. Called by the Gregorian > Republican constructor
private mutating func dateToFrenchRepublican() {
let gregorian = Calendar.gregorian.dateComponents([.year, .month, .day, .hour, .minute, .second, .nanosecond], from: date)
var gregorianCalendar: Calendar = Calendar.gregorian
if self.timeZone != nil {
gregorianCalendar.timeZone = self.timeZone!
}
let gregorian = gregorianCalendar.dateComponents([.year, .month, .day, .hour, .minute, .second, .nanosecond], from: date)

var year: Int
var dayOfYear: Int
Expand Down Expand Up @@ -151,7 +163,7 @@ public struct FrenchRepublicanDate {
public mutating func nextYear() {
components.year! += 1
components.yearForWeekOfYear! += 1
date = Date(dayInYear: dayInYear, year: components.year!, hour: components.hour, minute: components.minute, second: components.second, nanosecond: components.nanosecond, options: options)
date = Date(dayInYear: dayInYear, year: components.year!, hour: components.hour, minute: components.minute, second: components.second, nanosecond: components.nanosecond, options: options, timeZone: self.timeZone)
}
}

Expand Down Expand Up @@ -209,9 +221,14 @@ internal extension Date {
/// - minute: Minute, will directly be copied over
/// - second: Second, will directly be copied over
/// - nanosecond: Nanosecond, will directly be copied over
/// - timeZone: The time zone; the default time zone if nil
/// - Note: Library users: use FrenchRepublicanDate.init(dayInYear: ...).date
init(dayInYear: Int, year: Int, hour: Int?, minute: Int?, second: Int?, nanosecond: Int?, options: FrenchRepublicanDateOptions) {
self = Calendar.gregorian.date(from: Date.dateToGregorian(dayInYear: dayInYear, year: year, hour: hour, minute: minute, second: second, nanosecond: nanosecond, options: options))!
init(dayInYear: Int, year: Int, hour: Int?, minute: Int?, second: Int?, nanosecond: Int?, options: FrenchRepublicanDateOptions, timeZone: TimeZone? = nil) {
var gregorianCalendar: Calendar = Calendar.gregorian
if let timeZone = timeZone {
gregorianCalendar.timeZone = timeZone
}
self = gregorianCalendar.date(from: Date.dateToGregorian(dayInYear: dayInYear, year: year, hour: hour, minute: minute, second: second, nanosecond: nanosecond, options: options, timeZone: timeZone))!
}
}

Expand All @@ -224,8 +241,13 @@ fileprivate extension Date {
/// - minute: Minute, will directly be copied over
/// - second: Second, will directly be copied over
/// - nanosecond: Nanosecond, will directly be copied over
/// - Returns: A DateComponents object containing the gregorian year and day of year, with the additional time components copied over.
static func dateToGregorian(dayInYear rDayInYear: Int, year rYear: Int, hour: Int?, minute: Int?, second: Int?, nanosecond: Int?, options: FrenchRepublicanDateOptions) -> DateComponents {
/// - timeZone: The time zone; the default time zone if nil
/// - Returns: A DateComponents object containing the Gregorian year and day of year, with the additional time components copied over.
static func dateToGregorian(dayInYear rDayInYear: Int, year rYear: Int, hour: Int?, minute: Int?, second: Int?, nanosecond: Int?, options: FrenchRepublicanDateOptions, timeZone: TimeZone? = nil) -> DateComponents {
var gregorianCalendar: Calendar = Calendar.gregorian
if let timeZone = timeZone {
gregorianCalendar.timeZone = timeZone
}

var gYear: Int
var gDayOfYear: Int
Expand All @@ -249,14 +271,14 @@ fileprivate extension Date {
}
case .romme:
// hour: 10 avoids a timezone change issue on 1911-03-11 (9 minutes 21 seconds change)
let date = Calendar.gregorian.date(from: DateComponents(year: rYear + 2000, day: rDayInYear, hour: 10))!
let shifted = Calendar.gregorian.date(byAdding: .day, value: -76071, to: date)!
gYear = Calendar.gregorian.component(.year, from: shifted)
gDayOfYear = Calendar.gregorian.ordinality(of: .day, in: .year, for: shifted)! - 1
let date = gregorianCalendar.date(from: DateComponents(year: rYear + 2000, day: rDayInYear, hour: 10))!
let shifted = gregorianCalendar.date(byAdding: .day, value: -76071, to: date)!
gYear = gregorianCalendar.component(.year, from: shifted)
gDayOfYear = gregorianCalendar.ordinality(of: .day, in: .year, for: shifted)! - 1
let remdays = (rYear - 1) / 4000
gDayOfYear.increment(by: -remdays, year: &gYear, daysInYear: \.daysInGregorianYear)
}

return DateComponents(calendar: Calendar.gregorian, year: gYear, day: gDayOfYear + 1, hour: hour, minute: minute, second: second, nanosecond: nanosecond)
return DateComponents(calendar: gregorianCalendar, year: gYear, day: gDayOfYear + 1, hour: hour, minute: minute, second: second, nanosecond: nanosecond)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@ public struct FrenchRepublicanDateOptions {
public var romanYear: Bool
public var variant: Variant

/// If true, formatted dates in Sansculottides are things like "1 Sansculottides" instead of a holiday name.
public var treatSansculottidesAsAMonth: Bool

public init(romanYear: Bool, variant: Variant) {
self.init(romanYear: romanYear, variant: variant, treatSansculottidesAsAMonth: false)
}

public init(romanYear: Bool, variant: Variant, treatSansculottidesAsAMonth: Bool) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a default value of false for treatSansculottidesAsAMonth instead of duplicating here too

self.romanYear = romanYear
self.variant = variant
self.treatSansculottidesAsAMonth = treatSansculottidesAsAMonth
}

public enum Variant: Int, CaseIterable {
Expand Down