1.4.3 Time zones
Avoid time zones when you can. A datetime column has none (it is naive), "UTC", or an area/location zone from the tz database such as "Asia/Kathmandu"; its dtype is '(datetime unit "Zone/Name"). Fixed offsets such as +02:00 are better avoided: Polars keeps one as "Etc/GMT-2", which knows no daylight saving. A column has one zone, so data with several offsets parses to UTC (Mixed offsets).
dt-convert-time-zone converts from one zone to another, keeping each instant; dt-replace-time-zone sets, changes or, given #f, unsets the zone, keeping each wall-clock time:
> (define ts '("2021-03-27 03:00" "2021-03-28 03:00"))
> (define tz-naive (~> (dataframe (list (series ts #:name "tz_naive"))) (select (str->datetime "tz_naive")) (ref "tz_naive"))) > (define tz-aware (~> tz-naive (dt-replace-time-zone "UTC") (rename "tz_aware"))) > (define time-zones-df (dataframe (list tz-naive tz-aware))) > time-zones-df
shape: (2, 2)
┌─────────────────────┬─────────────────────────┐
│ tz_naive ┆ tz_aware │
│ --- ┆ --- │
│ datetime[μs] ┆ datetime[μs, UTC] │
╞═════════════════════╪═════════════════════════╡
│ 2021-03-27 03:00:00 ┆ 2021-03-27 03:00:00 UTC │
│ 2021-03-28 03:00:00 ┆ 2021-03-28 03:00:00 UTC │
└─────────────────────┴─────────────────────────┘
> (select time-zones-df (~> (col "tz_aware") (dt-replace-time-zone "Europe/Brussels") (alias "replace time zone")) (~> (col "tz_aware") (dt-convert-time-zone "Asia/Kathmandu") (alias "convert time zone")) (~> (col "tz_aware") (dt-replace-time-zone #f) (alias "unset time zone")))
shape: (2, 3)
┌───────────────────────────────┬──────────────────────────────┬─────────────────────┐
│ replace time zone ┆ convert time zone ┆ unset time zone │
│ --- ┆ --- ┆ --- │
│ datetime[μs, Europe/Brussels] ┆ datetime[μs, Asia/Kathmandu] ┆ datetime[μs] │
╞═══════════════════════════════╪══════════════════════════════╪═════════════════════╡
│ 2021-03-27 03:00:00 CET ┆ 2021-03-27 08:45:00 +0545 ┆ 2021-03-27 03:00:00 │
│ 2021-03-28 03:00:00 CEST ┆ 2021-03-28 08:45:00 +0545 ┆ 2021-03-28 03:00:00 │
└───────────────────────────────┴──────────────────────────────┴─────────────────────┘
1.4.3.1 Zoned values from Racket
A gregor moment is an instant in a zone, as Python’s aware datetime is, and a zoned column’s values come back as moments in its zone. A list takes the zone of its first moment, as Python’s does; a fixed offset gives UTC:
> (define landings (series (list (moment 2021 3 27 9 #:tz "Europe/Brussels") (moment 2021 3 28 9 #:tz "Europe/Brussels") (moment 2021 3 27 18 #:tz "Asia/Kathmandu")) #:name "landed")) > landings
shape: (3,)
Series: 'landed' [datetime[us, Europe/Brussels]]
[
2021-03-27 09:00:00 CET
2021-03-28 09:00:00 CEST
2021-03-27 13:15:00 CET
]
> (series->list landings)
'(#<moment 2021-03-27T09:00:00+01:00[Europe/Brussels]>
#<moment 2021-03-28T09:00:00+02:00[Europe/Brussels]>
#<moment 2021-03-27T13:15:00+01:00[Europe/Brussels]>)
> (dtype (series (list (moment 2021 3 27 9 #:tz 3600)))) '(datetime microseconds "UTC")
A moment read back carries Polars’ offset, from chrono-tz’s tz database. gregor reads the system’s zoneinfo, which can disagree before 1970 or after 2037, so a moment built there can name another instant than Polars would (Dates, times, datetimes and durations).