Skip to content

Commit

Permalink
update rounding
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander-Barth committed Mar 12, 2024
1 parent d0a5174 commit 3674ca7
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 18 deletions.
43 changes: 27 additions & 16 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This package implements the calendar types from the [CF convention](http://cfcon
* Julian calendar (`DateTimeJulian`)

Note that time zones are not supported by `CFTime.jl`.


## Installation

Expand Down Expand Up @@ -108,22 +108,8 @@ Time ranges can be constructed using a start date, end date and a time increment

## Rounding


```julia
using CFTime: DateTimeStandard
using Dates: DateTime
dt = DateTimeStandard(24*60*60*1000*1000 + 123,"microsecond since 2000-01-01")
round(DateTime,dt)
# output

2000-01-02T00:00:00
```



```julia
using Dates: DateTime, Second
using CFTime: DateTimeStandard

dt = DateTimeStandard(24*60*60,"second since 2000-01-01")

Expand All @@ -143,6 +129,31 @@ round(dt+Second(9),Second(10)) == dt + Second(10)
true
```


Julia's `DateTime` records the time relative to a time orgin (January 1st, 1 BC or 0000-01-01 in ISO_8601) with a millisecond accuracy. Converting CFTime date time structures to
Julia's `DateTime` (using `convert(DateTime,dt)`) can trigger an inexact exception if the convertion cannot be done without loss of precision. One can use the `round` function in order to round to the nearest time represenatable by `DateTime`:

```julia
using CFTime: DateTimeStandard
using Dates: DateTime
dt = DateTimeStandard(24*60*60*1000*1000 + 123,"microsecond since 2000-01-01")
round(DateTime,dt)
# output

2000-01-02T00:00:00
```

## Internal API

For CFTime 0.1.3 and before all date-times are encoded using internally milliseconds since a fixed time origin and stored as an `Int64` similar to julia's `Dates.DateTime`.
However, this approach does not allow to encode time with a sub-millisecond precision possible allowed by the CF convention and supported by e.g. [numpy](https://numpy.org/doc/1.25/reference/arrays.datetime.html#datetime-units). While `numpy` allows attosecond precision, it can only encode a time span of ±9.2 around the date 00:00:00 UTC on 1 January 1970. In CFTime the time origin and the number containing the duration and the time precision are now encoded as two additional type parameters.

When wrapping CFTime data-time, it is recommended for performance reasons to make the containg structure also parametric, for example

``` julia
struct MyStuct{T1,T2}
dt::DateTimeStandard{T1,T2}
end
```

Future version of CFTime might add other type parameters.
10 changes: 8 additions & 2 deletions src/rounding.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

"""
dtr = round(::Type{DateTime}, dt::Union{DateTimeProlepticGregorian,DateTimeStandard,DateTimeJulian},r = RoundNearestTiesUp)

function round(::Type{DateTime}, dt::AbstractCFDateTime,r = RoundNearestTiesUp)
Round the date time `dt` to the nearest date time represenatable by julia's `DateTime` using the rounding mode `r` (either `RoundNearest` (default) `RoundDown` or `RoundUp`).
"""
function round(::Type{DateTime}, dt::DateTimeProlepticGregorian,r::RoundingMode = RoundNearest)
function round_ms(t)
t_ms =
if 3+_exponent(t) > 0
Expand Down Expand Up @@ -31,7 +34,10 @@ function round(::Type{DateTime}, dt::AbstractCFDateTime,r = RoundNearestTiesUp)
end

return DateTime(UTInstant{Millisecond}(Dates.Millisecond(duration)))
end

function round(::Type{DateTime}, dt::Union{DateTimeJulian,DateTimeStandard},r = RoundNearest)
round(DateTime,convert(DateTimeProlepticGregorian,dt))
end


Expand Down
8 changes: 8 additions & 0 deletions test/test_resolution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ end




dt1 = DateTimeStandard(1,"day since 2000-01-01")
dt2 = DateTimeStandard(24,"hours since 2001-01-01")
T = typeof(dt1)
# to be implemented
#@test_broken dt2 == convert(T,dt2)


@test daysinmonth(DateTimeAllLeap,2001,2) == 29
@test daysinmonth(DateTimeStandard,1582,10) == 21

Expand Down

0 comments on commit 3674ca7

Please sign in to comment.