So I see that Utc.now
provides time in Zulu time. Just wondering if there's a way to access the system's timezone?
There isn't currently, but we could add it.
How would you do this using rust?
Looks like chrono::offset::TimeZone
If we are happy adding chrono
as a dependency I can add that.
Or happy to talk you through how to add another effect for the platform
It's pretty straightforward
I have never written any Rust, but been thinking its high time to learn as I participate more in the Roc community...
On a little more digging, looks like chrono doesn't actually give anything useable?
https://stackoverflow.com/questions/59603665/how-do-you-find-the-local-timezone-offset-in-rust
Looks like this will get the TZ offset, not sure yet if thats part of chrono or std lib, or something else.
Local.timestamp(0, 0).offset().fix().local_minus_utc()
Anyway, I'd love to go through the process of adding a new effect for the platform.
Can confirm Local is part of the chrono library.
I'm not sure how we want to represent an Offset
... so this might need tweaking...
Add host<->platform interface in platform/Effect.roc
, something like
offsetTime : Effect U128
Add platform<->app interface in platform/Utc.roc
, something like
## Some offset thing...
offset : Task U128 *
offset =
Effect.offsetTime
|> Effect.map Ok
|> InternalTask.fromEffect
Add the Fx implemenation in the host crates/host/src/lib.rs
, something like
#[no_mangle]
pub extern "C" fn roc_fx_offsetTime() -> roc_std::U128 {
let offset: u128 = 0;
// implement using chrono::offset::TimeZone
// return value to roc
roc_std::U128::from(offset)
}
chrono is just giving offset in seconds from Zulu time.
So shouldn't exceed 60 * 60 * 14
which could be as small as an I16
.
Offset can be +14 hours -12 hours I believe. Its either that or -14/+12.
Thanks for the pointers on adding an effect to the platform. I'll give it a go. Gonna have to learn the basics of setting up my rust dev environment, etc, before I can actually implement it lol.
This looks relevant
https://docs.rs/chrono/latest/chrono/offset/struct.FixedOffset.html
Yeah, I also picked up rust so I could contribute to roc :crab:
I'm not familiar with chrono, but at a glance I'd recommend not using FixedOffset for any reason :big_smile:
one of the things I've learned the hard way is that "fixed UTC offset" is the same thing as UTC, but it has a name that incorrectly suggests it's taking time zones into account when it isn't
for example, the information contained in "UTC time foo, with an offset of +2" is:
I'm seeing chrono::Local
:
Local.timestamp(0, 0).offset().fix().local_minus_utc()
which returns 3600 seconds for one particular poster....
in contrast, a date plus a time zone (e.g. "time foo in the Chicago time zone") can be converted to/from an UTC time - and of course people often want to specify dates add times without the time zone, but you know what their time zone is and so can apply it to the time they specified to get UTC
but if the user gives you a particular date and time, and all you have recorded is their "UTC offset" - that doesn't tell you their time zone
many time zones have rules that are more complex than a fixed UTC offset that changes once or twice per year
and even in the common case in the US (for example), if all you have is a fixed UTC offset, you don't know whether that offset was recorded during daylight savings time or not, so you don't know which direction to adjust it in if you cross a DST boundary
to sum up:
Ok, this looks like the better solution then https://docs.rs/iana-time-zone/latest/iana_time_zone/fn.get_timezone.html
That all makes a lot of sense. Definitely don't want to do away with UTC time, but I think it would be useful to have a way to move between UTC time and Local time.
I can see that pure Zulu offset has some definite ambiguities, if you are trying to determine the timezone from the offset. Depending on your usecase, zulu offset may be exactly what you want. If you simply want to convert a zulu time to a local time to display to the user, offset is exactly what you're looking for. If you want to store a time longterm, and particularly if you're recording it without date info attached.
See also https://www.iana.org/time-zones
Seems to me that both offset and timezone are useful? If all the platform provides is timezone string, but you want to display a time in local time to the user, you still need a way to convert TZ string to an offset. Maybe that should be in a package rather than the platform, but still has its applications.
Screenshot-2024-04-30-at-13.20.28.png
So the solution is to build a Roc package that provides the tz data
The data files look like ^^ in a standardised format so they can be parsed. We could code gen the implementation similar to how we generate the mapping between codepoints and unicode properties like in the roc-land/unicode package.
Correct me if I'm wrong, I believe what we want is:
The Time Zone Database (called tz, tzdb or zoneinfo) contains code and
data that represent the history of local time for many representative
locations around the globe. It is updated periodically to reflect
changes made by political bodies to time zone boundaries, UTC offsets,
and daylight-saving rules.
Though, I'm not sure it's worth including the full history for every single time zone
Luke Boswell said:
Though, I'm not sure it's worth including the full history for every single time zone
Hmm... depending on how big the dataset is, could include the data as a CSV or similar and perform a query on that for historical dates, while hardcoding current info.
Alternatively it could be network dependent, but that seems sub optimal.
If it's a package then application and platform authors can opt out of using it if they dont need it. Once the source is compiled it's probably not that large. Maybe there is a more compact way of representing it in source code too? like encode into a data table and include that in the source code?
That makes sense… are you thinking like a large dict of TZ data? Or how would Roc store a data table like that?
I'm thinking we could hash the TZ string, and use that as a Dict key
Maybe for the value we have a List {utcFrom: U64, offset:U16}
or something
Where from is the beginning date as offset from epoch when the offset came into use?
That would still require list iteration… maybe hash the from and TZ string for O(1) lookup
There's an implementation for the Hasher
ability floating around somewhere. I don't think it needs to be cryptographically secure or anything. In crates/compiler/builtins/roc/Dict.roc
we have the LowLevelHasher
which is an implementation of wyhash. I'm not sure if there is any different using a Str
key or hashing it first. @Brendan Hansknecht would know
Maybe there is a smarter data structure to put all this in? I'm not great with this stuff
Just caught up here. How big is this database in practice and how often does it actually change. Are we talking 10s of timezones, 100s, other?
Just trying to get a feel for the data before suggesting anything.
Thousands of TimeZones
Maybe between 5-20 rules per timezone
Maybe only hundreds of TZ's
Ok, so 597 timezones
Ok
A bunch of timezones share common rules
https://en.wikipedia.org/wiki/Tz_database
This is the best summary I've found
My gut feeling is that the best would be to write a program to read the timezones database and generate roc code for a package (can also try having the roc package directly read the time zone database, but I would guess that would be slow).
I would push for using a tag instead of a string for the timezones.
Of course with conversion
Cause if a specific version of the time zone package doesn't know about a timezone, won't matter if it is a string or a tag. That said, I guess there could be value in making this more pluggable if the database changes often and you would rather load it at runtime.
Anyway, this is small enough that if you use a tag, linear search (or binary for that matter) might beat a dictionary. If you use a string, probably should just make the string the key to a dictionary
Another approach that seems common is to use the database on the users host system. For example the go std library has this
func LoadLocation(name string) (*Location, error)
LoadLocation returns the Location with the given name.
If the name is "" or "UTC", LoadLocation returns UTC. If the name is "Local", LoadLocation returns Local.
Otherwise, the name is taken to be a location name corresponding to a file in the IANA Time Zone database, such as "America/New_York".
LoadLocation looks for the IANA Time Zone database in the following locations in order:
I think it would be nice to have a package that includes the database, then it would just work wherever you write Roc code.
Yeah, I think you want both.
One that is 100% roc with nicer types
One for loading from the system and being able to update with a file
This is how it's done in Elm https://github.com/justinmimbs/timezone-data/blob/10.1.1/src/TimeZone.elm
A platform could provide:
Utc := I128
TimeZone := Str
Local := I128
now : Task Utc *
name : TimeZone -> Task Str [NotFound]
systemTimeZone : Task TimeZone [NotAvailable]
offset : Utc, TimeZone -> Local
A package could provide:
Utc := I128
TimeZone := Str
Local := I128
name : TimeZone -> Result Str [NotFound]
offset : Utc, TimeZone -> Local
Using the platform to get the offset would use less memory as the IANA database is not included, at the expense of requiring a Task
which would search for the database on the system - which may not be available in some environments.
I think the database could be ingested as code which would cost binary size, but would be fast and as long as timezones don't update too much, not that bad for dependency Management
Ian McLerran said:
If you simply want to convert a zulu time to a local time to display to the user, offset is exactly what you're looking for.
it's a trap!
offsets are not stable over time; the offset for a time in this week may be different to the offset for a time next week.
to accurately convert any UTC time to a local time, you need to know the user's local Timezone Identifier (like Australia/Sydney
)
Last updated: Jul 06 2025 at 12:14 UTC