Skip to content
NorscodeNorscode

Date and time

ExampleBy the Norscode project

Compute with points in time, and avoid the three traps that catch most people: local time, calendar days in milliseconds, and measuring with the wall clock.

Time looks simple until it isn't. This recipe shows the basics, and the three traps that catch most people.

funksjon start() -> heltall {
    la naa = builtin.now_ms()
    skriv("Millisekunder siden 1970: " + tekst(naa))

    la ett_dogn = 24 * 60 * 60 * 1000
    la i_morgen = naa + ett_dogn
    skriv("Om et døgn: " + tekst(i_morgen))
    returner 0
}

What happens here

builtin.now_ms gives the number of milliseconds since midnight 1 January 1970 in UTC. It is a single number, without time zone, without daylight saving, without format. That is exactly why it is a good storage format: two such numbers can always be compared and subtracted from each other, no matter where in the world they were made.

Trap 1: storing local time

The most common mistake is to store points in time as local text, for example the time and date as a Norwegian would write them.

Then you get two problems every year. In autumn the clock is set back, and the hour between two and three occurs twice — you cannot know which of them a stored value means. In spring the clock jumps forward, and that hour does not exist at all. If you have stored a point in time there, it is invalid.

Add users in several countries and it gets worse: two events stored as local time cannot be compared without knowing which zone each of them came from.

The rule: always store in UTC as milliseconds. Convert to local time only at the moment a human is to read it.

Trap 2: computing calendar days in milliseconds

The code above adds 86,400,000 milliseconds and calls it a day. That is usually correct — but not always.

The two days a year when the clock is changed are one day of 23 and 25 hours respectively in local time. Add 86,400,000 milliseconds to a date right before the transition, and you end up at the wrong time. For a reminder meant to arrive at nine every morning, that means it suddenly arrives at eight or ten.

If you are to add a calendar day — that is, the same time the next day — use the calendar functions in std.kalender. If you are to add exactly 24 hours, milliseconds are correct. They are two different things, and the difference shows only two days a year. Those two days are enough to destroy trust in your system.

Trap 3: measuring time with the wall clock

If you are to measure how long something took, now_ms is not ideal. The system clock can be adjusted while you measure — by time synchronization, or by an administrator — and then the difference can become negative or absurdly large.

For duration measurement and performance testing, the standard library has std.timeit, which uses a clock that only moves forward.

In brief

  • Store in UTC milliseconds. Always.
  • Show local time, as late as possible, only for humans.
  • Compute in calendar functions when you mean calendar days, in milliseconds when you mean exact time.
  • Measure duration with std.timeit, not with the wall clock.

Related

Back to the overview