diff --git a/src/period.jl b/src/period.jl index 539f66e..deccdef 100644 --- a/src/period.jl +++ b/src/period.jl @@ -52,6 +52,12 @@ end return sum(divi[1:length(tuf)] .* tuf) end +""" + days,h,mi,s,ms,... = timetuplefrac(t::Period) + +Return a tuple with the number of whole days, hours (`h`), minutes (`mi`), +seconds (`s`) and millisecods (`ms`),... from the time period `t`. +""" function timetuplefrac(t::Period{T,Tfactor}) where {T,Tfactor} # for integers factor = _factor(t) diff --git a/src/query.jl b/src/query.jl index 2cc1919..68fa3fd 100644 --- a/src/query.jl +++ b/src/query.jl @@ -1,23 +1,5 @@ # query functions -@inline isleap(::Type{DateTimeAllLeap},year,has_year_zero) = true -@inline isleap(::Type{DateTimeNoLeap},year,has_year_zero) = false - -@inline function isleap(::Type{DateTimeProlepticGregorian},year,has_year_zero) - if (year < 0) && !has_year_zero - year = year + 1 - end - return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 !== 0)) -end - -@inline function isleap(::Type{DateTimeJulian},year,has_year_zero) - if (year < 0) && !has_year_zero - year = year + 1 - end - return year % 4 == 0 -end - - # https://web.archive.org/web/20240304171516/https://unidata.github.io/cftime/api.html # https://github.com/cf-convention/cf-conventions/issues/298 @inline _hasyear0(::Type{T}) where T <: Union{DateTimeJulian,DateTimeStandard} = false diff --git a/test/reference_algorithm.jl b/test/reference_algorithm.jl index 55e8cd8..5b06f1e 100644 --- a/test/reference_algorithm.jl +++ b/test/reference_algorithm.jl @@ -1,15 +1,40 @@ module Reference +import CFTime import CFTime: DateTimeJulian, DateTimeProlepticGregorian, - _hasyear0, - isleap + DateTimeStandard, + DateTimeNoLeap, + DateTimeAllLeap, + DateTime360Day, + _hasyear0 -# Adapted -# from https://github.com/Unidata/cftime/blob/dc75368cd02bbcd1352dbecfef10404a58683f94/src/cftime/_cftime.pyx -# Licence MIT -# by Jeff Whitaker (https://github.com/jswhit) +@inline isleap(::Type{DateTimeAllLeap},year,has_year_zero) = true +@inline isleap(::Type{DateTimeNoLeap},year,has_year_zero) = false +@inline isleap(::Type{DateTime360Day},year,has_year_zero) = false + +@inline function isleap(::Type{DateTimeProlepticGregorian},year,has_year_zero) + if (year < 0) && !has_year_zero + year = year + 1 + end + return (year % 400 == 0) || ((year % 4 == 0) && (year % 100 !== 0)) +end + +@inline function isleap(::Type{DateTimeJulian},year,has_year_zero) + if (year < 0) && !has_year_zero + year = year + 1 + end + return year % 4 == 0 +end + +@inline function isleap(::Type{DateTimeStandard},year,has_year_zero) + if year < 1582 + isleap(DateTimeJulian,year,has_year_zero) + else + isleap(DateTimeProlepticGregorian,year,has_year_zero) + end +end @inline function month_lengths(::Type{T}, year::Integer, has_year_zero) where T if isleap(T, year, has_year_zero) @@ -19,11 +44,19 @@ import CFTime: end end +function month_lengths(::Type{DateTime360Day}, year::Integer, has_year_zero) + ntuple(i -> 30,12) +end + +# Adapted +# from https://github.com/Unidata/cftime/blob/dc75368cd02bbcd1352dbecfef10404a58683f94/src/cftime/_cftime.pyx +# Licence MIT +# by Jeff Whitaker (https://github.com/jswhit) + @inline function datetuple_ymd(::Type{T}, delta_days, julian_gregorian_mixed, has_year_zero) where T - year = 1858 - month = 11 - day = 17 + # use the same origin + year,month,day = CFTime.datetuple_ymd(T,0) month_length = month_lengths(T, year, has_year_zero) @@ -34,8 +67,14 @@ end 0 end + # The Julian calendar day Thursday, 4 October 1582 was + # followed by the first day of the Gregorian calendar, + # Friday, 15 October 1582 + + # counting down @inbounds while delta_days < 0 if (year == 1582) && (month == 10) && (day > 14) && (day + delta_days < 15) + #@show n_invalid_dates delta_days -= n_invalid_dates # skip over invalid dates end @@ -61,6 +100,7 @@ end @inbounds while delta_days > 0 if (year == 1582) && (month == 10) && (day < 5) && (day + delta_days > 4) + #@show n_invalid_dates delta_days += n_invalid_dates # skip over invalid dates end @@ -86,10 +126,10 @@ end return year,month,day end -function datetuple_ymd(::Type{DateTimeProlepticGregorian},Z) - has_year_zero = _hasyear0(DateTimeProlepticGregorian) - julian_gregorian_mixed = false - return datetuple_ymd(DateTimeProlepticGregorian, Z, julian_gregorian_mixed, has_year_zero) +function datetuple_ymd(::Type{T},Z) where T + has_year_zero = _hasyear0(T) + julian_gregorian_mixed = T <: DateTimeStandard + return datetuple_ymd(T, Z, julian_gregorian_mixed, has_year_zero) end end diff --git a/test/test_time.jl b/test/test_time.jl index 1123e86..ed39435 100644 --- a/test/test_time.jl +++ b/test/test_time.jl @@ -484,15 +484,17 @@ for T in [DateTimeStandard, DateTimeJulian, DateTimeProlepticGregorian, end +# comparision with reference algorithm +#Δ = 1 +Δ = 1000 -Z = CFTime.datenum(DateTimeProlepticGregorian,-1000,1,1):CFTime.datenum(DateTimeProlepticGregorian,4000,1,1) - -Z = CFTime.datenum(DateTimeProlepticGregorian,-1000,1,1):100:CFTime.datenum(DateTimeProlepticGregorian,4000,1,1) - -MYMD = CFTime.datetuple_ymd.(DateTimeProlepticGregorian,Z); -RYMD = Reference.datetuple_ymd.(DateTimeProlepticGregorian,Z); - -@test MYMD == RYMD +for T in [DateTimeStandard, DateTimeJulian, DateTimeProlepticGregorian, + DateTimeAllLeap, DateTimeNoLeap, DateTime360Day] + Z = CFTime.datenum(T,-1000,1,1):Δ:CFTime.datenum(T,4000,1,1) + MYMD = CFTime.datetuple_ymd.(T,Z); + RYMD = Reference.datetuple_ymd.(T,Z); + @test MYMD == RYMD +end #= for dt = DateTime(-1000,1,1):Day(100):DateTime(2300,3,1)