Stream: API design

Topic: Date and DateTime


view this post on Zulip Isaac Van Doren (Jan 03 2025 at 03:00):

On the topic of time (#API design > Datatype for representing epochs), in addition to Instants/Timestamps, we should have types for dates (yyyy-MM-dd) and date times (yyyy-MM-dd hh:mm:ss).

I propose these be named Date and DateTime.

Other possibilities for naming them are PlainDate and PlainDateTime, or LocalDate and LocalDateTime to communicate that they don't store timezone information, but I don't think these names are helpful enough to justify the longer and less natural names.

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:00):

Is this for the standard library or for an external library?

view this post on Zulip Isaac Van Doren (Jan 03 2025 at 03:02):

I think they should be in the standard library given how frequently they are used. That way there can be standard conversion functions between all of the different date types.

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:04):

I agree that having them in std is a noble goal, but this is one of the first modules that would require us to really consider the philosophy of our standard library

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:04):

That is, should our standard library have stuff that stays in Roc forever?

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:05):

The reason why Rust is reticent to put Regex and datetime and all into std is because the underlying implementations for those and which library is even popular seems to change a good bit, even now over a decade after its first release as a language

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:06):

Though there are plenty of languages like Python that have "all the basics" in their standard library

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:06):

I think the safer start is to make a roc-lang/datetime library and develop everything there

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:07):

Once that seems stable, we can consider permanently adding it to Roc

view this post on Zulip Luke Boswell (Jan 03 2025 at 03:12):

We already have a few different impls in the wild

  1. https://github.com/hasnep/roc-datetimes
  2. https://github.com/imclerran/Roc-IsoDate
  3. the DateTime module in basic-webserver

view this post on Zulip Sky Rose (Jan 03 2025 at 03:18):

Elixir calls this NaiveDateTime, and their DateTime type has time zone info. https://hexdocs.pm/elixir/1.18.1/NaiveDateTime.html
I don't like the name "NaiveDateTime" but I do like that it's harder to use than the date time with time zone, which creates a pit of success. You only use NaiveDateTime if you really know that you're working with a fictional abstract concept of time, otherwise you use the one with time zones that represents the real world.

view this post on Zulip Sam Mohr (Jan 03 2025 at 03:18):

Good point. We should probably make a combined time library, since we'll want a single, common type for datetime, date, and times

view this post on Zulip Isaac Van Doren (Jan 03 2025 at 03:20):

I think the safer start is to make a roc-lang/datetime library and develop everything there
Once that seems stable, we can consider permanently adding it to Roc

Yeah that sounds reasonable. The design space for Date APIs is quite large

view this post on Zulip Sky Rose (Jan 03 2025 at 03:24):

time zone data also changes a lot, even if the API is stable (similar to Unicode). How to deal with time zone data is gonna be a problem for this library to solve. Even if we want datetimes to eventually be a builtin, the time zone data probably never could be moved to Roc.

view this post on Zulip Richard Feldman (Jan 03 2025 at 03:47):

yeah I think the complexity level of dates, especially with time zones involved, is just way higher than Instant and Duration, which are both fairly thin wrappers around integers :big_smile:

view this post on Zulip Jasper Woudenberg (Jan 03 2025 at 09:07):

I was working on bringing my blog into a static-site platform I'm working on last week. I've blog posts in a directory prefixed with the date, as in yyyy-mm-dd-name-of-the-post.md, and needed to:

I was loth to pull in a dependency for something so small, so I wrote some hacky Str.splitOn ... code to implement that.

I realized that this particular time-related logic had no dependency on locales or time zone databases. I wondered how many uses are like that. There might be a design where builtins include a Time type that could replace my hacky code, but no functions that require a timezone database, such as conversions between Time and Instant. At that point there might no longer be a need for an external time package, but we would instead have a locale package. Either way, it makes sense to me to test out these ideas in a package first.

I guess the Time type I'm describing here is the same as NaiveDateTime that sky mentioned Elixir has, but I don't think that's necessarily a bad thing. In the API documentation for NaiveDateTime it shows that the type implements all sorts of functions for adding intervals to a NaiveDateTime or getting the duration between two NaiveDateTimes. I'd argue it's those functions that are naive, not the type itself. We could omit those kinds of calculation functions for the Roc Time type, and require application authors to convert Time to Instant (using a timezone) before they can do math with it.

view this post on Zulip Sky Rose (Jan 03 2025 at 14:19):

That does sound useful to have NaiveDateTime available for stuff like that, but is it possible to make that easy without disincentivizing people from using the real DateTime when they really do need that correctness?

view this post on Zulip Jasper Woudenberg (Jan 03 2025 at 14:32):

I think so! If the builtin Time/NaiveDateTime type does not provide any operations that would return ambiguous results that would make it a quite a limited API. For any function that needs a timezone for an unambiguous result you'd need to make us types/functions that track timezones provided by external packages.

view this post on Zulip Richard Feldman (Jan 03 2025 at 15:12):

that's interesting - one way to split things up could be to have a Date module that literally just stores month/day/year and offers an API for translating to/from strings with various formats

view this post on Zulip Richard Feldman (Jan 03 2025 at 15:12):

or like Date and DateFormat maybe

view this post on Zulip Richard Feldman (Jan 03 2025 at 15:12):

and it is just completely not concerned with time zones even a little bit

view this post on Zulip Richard Feldman (Jan 03 2025 at 15:13):

and as soon as you want to do anything with time zones, UTC, etc., including converting a Date to an Instant, there's a third-party package for that

view this post on Zulip Sky Rose (Jan 03 2025 at 15:17):

What about supporting different calendars (Julian vs Gregorian, Hebrew calendar, Japanese years)? What about places that skipped a date (switching from Julian to Gregorian calendars skipped 11 days, and sometimes countries change which side of the international date line they're on). The builtins probably shouldn't try to do that stuff, so is it okay if the main date library can't do that?

view this post on Zulip Anthony Bullard (Jan 03 2025 at 15:36):

Makes me wish there was a concept of "builtins that aren't actually builtin", i.e., a set of abstract packages that declare (opaque/nominal) types and functions that work on them - but the language runtime does not implement them at all. All platforms would be required to implement them (hopefully through shared crates / zig packages). maybe some way for a platform to opt out? Not sure...

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:21):

oh I don't think any of these would use anything from the platform

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:22):

the only relevant effects for any of these are "what is the time zone set to on my OS right now?" and "what is the current system time?"

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:22):

those 2 functions are platform-specific, and then the other 99.9% of the APIs (wherever they live) are just data structures and pure functions

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:23):

and I think it's totally fine for those 2 functions to just live in a different module from the rest of the API

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:23):

like with time I'm thinking:

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:25):

similarly, I think we can have:

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:28):

I do think it's a good idea to keep things that depend on third-party versioning out of builtins, so those can be released independently of language releases

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:28):

for example, new versions of Unicode and HTTP get released periodically

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:28):

so those are probably better off as packages

view this post on Zulip Richard Feldman (Jan 03 2025 at 16:29):

but there aren't new versions of UTF-8, ASCII, or units of time (milliseconds etc.) getting released periodically, so those all seem safe to include in builtins

view this post on Zulip Isaac Van Doren (Jan 03 2025 at 17:46):

What’s the motivation for Clock.now! instead of Instant.now! ?

view this post on Zulip Richard Feldman (Jan 03 2025 at 17:47):

the Instant module should be in a platform-agnostic package (or a builtin module) that doesn't know how to do effects

view this post on Zulip Richard Feldman (Jan 03 2025 at 17:48):

I think this is a theme of how we can make nice platform-agnostic APIs without module params

view this post on Zulip Richard Feldman (Jan 03 2025 at 17:49):

e.g. Path, Instant, Duration, Request, and Response are all just data structures and pure functions, and none of them know how to do any effects at all, and are all completely platform-agnostic

view this post on Zulip Richard Feldman (Jan 03 2025 at 17:49):

and then File, Clock, and Http are platform-specific and do the actual effects, but work with those platform-agnostic data structure modules

view this post on Zulip Isaac Van Doren (Jan 03 2025 at 17:49):

Oh oops, I thought you had said Clock would be a built in module not platform supplied

view this post on Zulip Richard Feldman (Jan 03 2025 at 17:50):

and then you have things which need actual effects, like Bugsnag, Logger, and Pg, which depend directly the platform-agnostic data structure modules, and then have initialization functions which say "pass me me an effectful function that does the effects I need, and I'll store it and use it whenever I need to do an effect" - which is still platform-agnostic, and accomplishes the sandboxing, capabilities, etc.

view this post on Zulip Kilian Vounckx (Jan 03 2025 at 17:59):

I really like this separation! Gleam does something similar where all HTTP types are in the gleam/http package. And then both client and server packages can just use that one package, while third party packages can target any of the clients/servers. This seems to have the same benefits! Love it

view this post on Zulip Isaac Van Doren (Jan 07 2025 at 01:46):

I like the idea of having Date and DateTime types in the builtins so that packages can agree, and then leaving the gritty parts to external packages. I think that gets a good portion of the benefit of having dates and datetimes in the builtins without the downsides

view this post on Zulip Isaac Van Doren (Jan 07 2025 at 01:59):

I'd argue it's those functions that are naive, not the type itself.

Completely agree


Last updated: Jul 06 2025 at 12:14 UTC