Advanced Exercism • julia

Dates and Times

Lesson Overview

# Introduction

About

“Dates and times are something we teach to young children. How hard can it be?”

Many programmers have made that mistake, and the subsequent experience tends to have a bad effect on their health and happiness.

Anyone doing non-trivial programming with dates and times should at least be prepared to understand and mitigate potential problems.

This document will attempt to give an overview of how Julia handles dates and times, but consulting the manual will be necessary for further details. There is also a Wikibook which some readers may find clearer in its explanations, and it includes a helpful diagram of the type hierarchy.

The Dates module

Most of the basic functionality is in Dates, a module which is included in a default installation but is not part of the standard library.

To use the module, there are two options:

  1. Add import Dates, then preface all function calls with Dates.
  2. Add using Dates, which brings the entire module into the current namespace (except for a few problem names).
# option 1
julia> import Dates

julia> Dates.now()
2025-06-21T16:02:39.080

julia> now()  # wrong: need to include `Dates.` after an import
ERROR: UndefVarError: `now` not defined in `Main`


# option 2
julia> using Dates

julia> now()  # functions are now in the namespace
2025-06-21T16:03:04.161

The Dates module is big and complicated, but it supplies four main types of functionality:

  • Dates.Date, which includes day-month-year but no time of day.
  • Dates.Time, which includes time of day, to nanosecond resolution, but no date.
  • Dates.DateTime, which combines date and time to millisecond resolution.
  • Dates.Period, which handles time/date intervals.

Please note that a DateTime generally does not include time zone information (Python would call this a “naive” datetime). One exception is now(), which gets local time from your system clock, and can convert this to UTC if required.

Other time zone calculations need the TimeZones.jl package, which is not part of a default installation. As of June 2025, this is not available within Exercism, but may be added in future.

Perhaps the most frequent needs are:

  • Parse some appropriate input format to construct a Dates type.
  • Get the required numerical or string format from a Dates type.
  • Apply an offset to a Date, Time or DateTime value.
  • Calculate the interval between two such values.
  • Get the current date (with today()) and/or time (with now()).
To reduce visual distraction, most of the example code in the rest of this document assumes `using Dates`, and avoids including the module name `Dates.` in function calls.

Date and time formats

There are many ways to write dates and times, which tend to be culturally-specific. All-number dates such as “7/6/23” are ambiguous, confusing, and have led to many expensive mistakes in multinational organizations (the author says this from bitter personal experience).

The international standard is defined in ISO 8601, with two main advantages:

  • Parsing is quick and unambiguous.
  • Sorting is easy, as the datetime can be treated as normal text and sorted alphabetically.

We saw in a previous example that Date.now() returned 2025-06-21T16:03:04.161.

This is built up from various parts:

  • YYYY-MM-DD for the date part.
  • hh:mm:ss for the time part.
  • Optionally, milliseconds (or an even smaller unit) after the decimal point.

Other string formats will be discussed in a later section.

Dates

Dates.Date is a relatively simple date-only type, with no understanding of times or timezones.

# constructor with year, month, day arguments
julia> date1 = Date(2025, 06, 23)
2025-06-23

# constructor with ISO 8601 string
julia> date2 = Date("2025-06-23")
2025-06-23

julia> date1 == date2
true

julia> typeof(date1)
Date

There are accessor functions for individual parts of the date, and these come in pairs distinguished by case.

# lowercase returns an integer
julia> month(date1)
6

julia> typeof(month(date1))
Int64

# Propercase returns a Period type
julia> Month(date1)
6 months

julia> typeof(Month(date1))
Month

# various compound methods are also available, returning a tuple
julia> monthday(date1)
(6, 23)

Query functions

There are a variety of query functions with some understanding of the calendar. The next example shows just a few.

julia> date1
2025-06-23

julia> dayofweek(date1) # 1 = Monday, 7 = Sunday
1

julia> dayname(date1)
"Monday"

julia> daysinmonth(date1)
30

julia> isleapyear(date1)
false

julia> lastdayofmonth(date1)
2025-06-30

Times

Dates.Time is the basic time-only type. It has no understanding of dates: times automatically roll over to Time(0, 0, 0) at midnight.

The full constructor format is Time(hour, min, sec, millisec, microsec) or the ISO 8601 equivalent.

All the parameters are optional and will default to 0.

julia> Time(14, 57, 25, 142)
14:57:25.142

julia> Time(14, 57, 25, 142, 32)`
14:57:25.142032

julia> Time("14:57:25.142")
14:57:25.142

As with Date, individual parts are available with accessor functions:

julia> minute(t) # => integer
57

julia> Minute(t) # => Period
57 minutes

DateTimes

Dates.DateTime combines most of the features of the Date and Time Types.

It is the most versatile of these three classes, though time resolution is limited to milliseconds (Time can resolve to nanoseconds).

julia> dt1 = DateTime(2025, 6, 23, 14, 57, 25, 142)
2025-06-23T14:57:25.142

# date1 and t were defined in previous examples
julia> dt2 = DateTime(date1, t)
2025-06-23T14:57:25.142

The year, month and day parameters default to 1, time parameters all default to 0.

Keeping all these parameters straight can be a challenge, so the ISO format may be preferable.

Alternatively, a DateTime can be constructed from a Date and a Time, as in the above example.

Periods

Each of the date/time components has its own Period type, from Year down to Nanosecond, representing an interval of time. These are particularly useful for arithmetic.

julia> dt1
2025-06-23T14:57:25.142

julia> Year(21)
21 years

julia> Second(61)
61 seconds

julia> dt1 + Year(21) - Second(61)
2046-06-23T14:56:24.142

julia> Date(2025, 07, 03) - Date(2025, 04, 25)
69 days

There is also a CompoundPeriod type, which can combine multiple Period types. Oddly, this currently needs the module name to avoid a namespace error (which feels like a bug).

julia> dt1
2025-06-23T14:57:25.142

julia> offset = Dates.CompoundPeriod(Year(21), Second(-61))
21 years, -61 seconds

julia> dt1 + offset
2046-06-23T14:56:24.142

When subtracting dates/times, the period will be given in days (for dates) or nanoseconds (for times). It may be helpful to canonicalize these to a CompoundPeriod.

julia> diff = Time(23, 14, 00) - Time(1, 23, 52)
78608000000000 nanoseconds

julia> canonicalize(diff)
21 hours, 50 minutes, 8 seconds

julia> typeof(canonicalize(diff))
Dates.CompoundPeriod

When working with Period types, Julia tries to be smart about calendar irregularities such as month length, rounding as necessary.

# February has fewer days than January
julia> Date(2025, 1, 31) + Month(1) 
2025-02-28

Rounding

The functions floor, ceil and round should be familiar from integer/floating-point numbers, but also work with dates and times. Specify the Period to round to, as the second argument.

julia> dt1
2025-06-23T14:57:25.142

julia> floor(dt1, Minute(15))
2025-06-23T14:45:00

julia> ceil(dt1, Minute(30))
2025-06-23T15:00:00

julia> ceil(dt1, Month(1))
2025-07-01T00:00:00

String representations

We previously mentioned the ISO 8601 standard, which is Julia’s default date/time representation when formatting or parsing strings.

Obviously, many other formats are in frequent use, often in culturally-specific ways. To handle these, Julia has the DateFormat type, which can be constructed from a format string of arbitrary complexity.

# dateformat"" string syntax
julia> dtfmt = dateformat"d/m/y"
dateformat"d/m/y"

julia> typeof(dtfmt)
DateFormat{Symbol("d/m/y"), Tuple{Dates.DatePart{'d'}, Dates.Delim{Char, 1}, Dates.DatePart{'m'}, Dates.Delim{Char, 1}, Dates.DatePart{'y'}}}

# DateFormat constructor
julia> DateFormat("d/m/y")
dateformat"d/m/y"

The above example is very simple, just a date in the day/month/year format common in Europe. Two variants on the syntax are shown, but the result is identical.

A DateFormat can then be used in parsing any suitable string:

julia> DateTime("14/7/2023", dtfmt)
2023-07-14T00:00:00

Creating a DateFormat involves significant overhead, so doing this step just once (as here with dtfmt) is helpful for perfomance when looping over many date/time strings.

Formats can be much more complex than this, so see the manual for details.

Anyone familiar with other languages may notice a family resemblance to the decades-old strptime and strftime functions. The Julia version borrows from this legacy, but aims to be more concise and flexible.

The same DateFormat can also be used to output a date/time as any desired string format, using the Dates.format() function.

Unfortunately, there seems to be a namespace conflict with format (at least as of Julia 1.11.5), so it is best to include the Dates. prefix even after using Dates.

julia> dt1
2025-06-23T14:57:25.142

julia> Dates.format(dt1, dateformat"e, u d")
"Mon, Jun 23"

As documented, "e, u d" gives the abbreviated text representation for weekday and month.

Text representation defaults to English. Other languages can be added, though support for this is currently sub-optimal.


Originally from Exercism julia concepts