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.
Is this for the standard library or for an external library?
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.
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
That is, should our standard library have stuff that stays in Roc forever?
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
Though there are plenty of languages like Python that have "all the basics" in their standard library
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
We already have a few different impls in the wild
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.
Good point. We should probably make a combined time library, since we'll want a single, common type for datetime, date, and times
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
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.
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:
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 NaiveDateTime
s. 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.
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?
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.
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
or like Date
and DateFormat
maybe
and it is just completely not concerned with time zones even a little bit
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
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?
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...
oh I don't think any of these would use anything from the platform
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?"
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
and I think it's totally fine for those 2 functions to just live in a different module from the rest of the API
like with time I'm thinking:
Duration
and Instant
live in their own modules and are all pure functions and data structures (likely should be builtins)Clock
module with Clock.now!()
that returns an Instant
similarly, I think we can have:
Request
and Response
modules that are just data structures for HTTP requests and responses, totally platform-agnostic (could be builtins I suppose if we really wanted to)Http
module with Http.get!()
, Http.request!()
, etc. that uses the platform-agnostic Request
and Response
modulesI 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
for example, new versions of Unicode and HTTP get released periodically
so those are probably better off as packages
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
What’s the motivation for Clock.now! instead of Instant.now! ?
the Instant
module should be in a platform-agnostic package (or a builtin module) that doesn't know how to do effects
I think this is a theme of how we can make nice platform-agnostic APIs without module params
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
and then File
, Clock
, and Http
are platform-specific and do the actual effects, but work with those platform-agnostic data structure modules
Oh oops, I thought you had said Clock would be a built in module not platform supplied
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.
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
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
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