for a hobby project I have utc seconds since unix epoch and want to have a Ptime.t * int with correct timezone offset in a given timezone. Daylight saving makes it nasty. Currently I hack something with index (timedesc.index) and Ptime / Erratique but my approach doesn’t feel quite right. Also I want to be lean on dependencies and avoid Jane street’s core.
let tz = "Europe/Zurich" |> Timedesc.Time_zone.make_exn
and of_rfc3339 (str : string) : (Ptime.t * int) =
match str |> Ptime.of_rfc3339 with
| Ok (t, Some tz_s, _) -> (t, tz_s)
| _ -> (Ptime.min, 0)
in
let of_epoch utc_s =
utc_s |> Timedesc.of_timestamp_float_s ~tz_of_date_time:tz
|> Option.get |> Timedesc.to_rfc3339 |> Option.get |> of_rfc3339
in
...
How would I git rid of the rfc3339 string conversion steps?
I don’t understand what you’re asking. A time in seconds-since-the-epoch does not come with a timezone. Maybe you’re asking “if I have an epoch-time, and a time-zone, can I deduce whether that timezone is correct (or needs to be adjusted for DST) ?”
If you can get a Timedesc.t, there appears to be a tz function on that type. Maybe this helps, maybe not – I’m just kibitzing.
@Chet_Murthy I need the offset for a timezone on a given day honouring daylight saving – say Europe/Zurich and day x (as utc epoch). Then I can use Ptime, because when finally serialising (to_rfc3339) I have to add that information.
Ho do I get the timezone offset for a timezone + utc epoch?
“Offset for time zone on a given day honouring daylight saving” is unfortunately not well defined, so really can’t give you a good answer before I finish reading what you’re trying to do.
Also, if you want to get a Ptime.t, you can also use the builtin conversion functions in Utils: Timedesc (timedesc.Timedesc)
Right, so you scrapped a seconds since unix epoch string, converted it into float, and want to process it into a Broadcast.timestamp (which is defined as Ptime.t * int) that makes sense in the Europe/Zurich time zone when read by a human, namely with an offset that makes sense with respect to whether DST is in effect, I’m guessing?
Suppose you obtain said float as x, you can do
module Td = Timedesc
let tz = Td.Time_zone.make_exn "Europe/Zurich" in
let time = Td.of_timestamp_float_s_exn ~tz_of_date_time:tz x in
let offset = match Td.offset_from_utc time with
| `Single x -> Td.Span.get_s x
| `Ambiguous _ -> failwith "Unexpected case"
in
let ptime = Td.to_timestamp_single time time
|> Td.Utils.ptime_of_timestamp
|> Option.get
in
(ptime, offset)
I’ll add I’m curious about the insistence on using Ptime as the base unit of handling time - if you’re using only Ptime then that makes sense since Ptime carries way fewer dependencies, but you’re using Timedesc anyway.
Thanks a lot, that looks great (was struggling with the ambiguous cases).
I may pass around a record with 2 timestamps and may factor some basic operations into a (opam?) package – so I want the visible surface be as lean as possible.
Indeed, the timere build dependency is there anyway because of Time_zone.
Do you mind expanding on your use case? (feel free to ignore it, I’m just curious)
What is the user of such a package supposed to do with the offset? Once you have a timestampt + offset, it’s very tempting to start using it. E.g. applying the same offset to a different timestamp, adding some seconds to the timestamp, etc. All of that could potentially lead to an invalid offset because we crossed a DST boundary.
Why isn’t the solution to have timestamp + zone id, which allows you to retrieve a local time whenever you need to do so?
@beajeanm the broad use-case is handling radio broadcast meta data e.g. persisting and deserialising, computing overlaps, schedule recordings, bundle recordings into RSS feeds etc. for a private DIY internet radio recorder.
Some of those use-cases is tackled with OCaml, currently mostly the scraping of the meta-data from the radio stations websites (at the heart a per-station Unix filter in OCaml). Cross-station meta data storage is flat files (xml) in webroot of a webserver (no DB involved). E.g. http://rec.mro.name/stations/b2/2021/10/03/1905.xml
Times in above xml are rfc3339. Including a timezone numerical offset. So that’s the minimum information needed (for storage).
Source is the station’s broadcast page e.g. Zündfunk extra | Bayern 2 | Radio | BR.de which has UTC epoch times (hidden in page source). And the station is in a known, fixed timezone.
So when scraping the station B2 (more to come), I finally have to turn a utc epoch + timezone into rfc3339. But I use an internal time representation Ptime * int for less conversions when calculating durations etc.
As it is internal, it could be Timedescs, but for now I am with Ptime * int and see where this leads to.
The package I mentioned might be the cross-station parts like what is a broadcast without the scraping that is specific to the station(s). That package will not provide functions about times, zones etc.