Introducing Julia/Working with dates and times

Working with dates and times
Functions for working with dates and times are provided in the standard package Dates. To use any of the time and date functions, you must do one of the following:





If you use  Dates functions, you’ll need to prefix every function with an explicit Dates., e.g. , as shown in this chapter. However, if you add the line  to your code, this brings all exported Dates functions into Main, and they can be used without the   prefix.

Types
This diagram shows the relationship between the various types used to store Times, Dates, and DateTimes.



Date, Time, and DateTimes
There are three main datatypes available:


 * A Dates.Time object represents a precise moment of time in a day. It doesn't say anything about the day of the week, or the year, though. It's accurate to a nanosecond.


 * A Dates.Date object represents just a date: no time zones, no daylight saving issues, etc... It's accurate to, well, a day.


 * A Dates.DateTime object is a combination of a date and a time of day, and so it specifies an exact moment in time. It's accurate to a millisecond or so.

Use one of these constructors to make the type of object you want:

julia> rightnow = Dates.Time(Dates.now) # a Dates.Time object 16:51:56.374

julia> birthday = Dates.Date(1997,3,15)  # a Dates.Date object 1997-03-15 julia> armistice = Dates.DateTime(1918,11,11,11,11,11) # a Dates.DateTime object 1918-11-11T11:11:11

The  function returns a Date object for the current date:

julia> datetoday = Dates.today 2014-09-02

The  function returns a DateTime object for the current instant in time:

julia> datetimenow = Dates.now 2014-09-02T08:20:07.437

(We used  earlier to define , then converted it to a Dates.Time using Dates.Time.)

Sometimes you want UTC (the reference time for the world, without local adjustments for daylight savings):

julia> Dates.now(Dates.UTC) 2014-09-02T08:27:54.14

To create an object from a formatted string, use the  function in Dates, and supply a suitable format string that matches the formatting:

julia> Dates.DateTime("20140529 120000", "yyyymmdd HHMMSS") 2014-05-29T12:00:00 julia> Dates.DateTime("18/05/2009 16:12", "dd/mm/yyyy HH:MM") 2009-05-18T16:12:00 julia> vacation = Dates.DateTime("2014-09-02T08:20:07") # defaults to expecting ISO8601 format 2014-09-02T08:20:07

See Date Formatting below for more examples.

Date and time queries
Once you have a date/time or date object, you can extract information from it with the following functions. For both date and datetime objects, you can obtain the year, month, day, and so on:

julia> Dates.year(birthday) 1997 julia> Dates.year(datetoday) 2014 julia> Dates.month(birthday) 3 julia> Dates.month(datetoday) 9 julia> Dates.day(birthday) 15 julia> Dates.day(datetoday) 2

and, for date/time objects:

julia> Dates.minute(now) 37 julia> Dates.hour(now) 16 julia> Dates.second(now) 8

julia> Dates.minute(rightnow) 37 julia> Dates.hour(rightnow) 16 julia> Dates.second(rightnow) 8

There's also a bunch of other useful ones:

julia> Dates.dayofweek(birthday) 6 julia> Dates.dayname(birthday) "Saturday" julia> Dates.yearmonth(now) (2014,9) julia> Dates.yearmonthday(birthday) (1997,3,15) julia> Dates.isleapyear(birthday) false julia> Dates.daysofweekinmonth(datetoday) 5 julia> Dates.monthname(birthday) "March" julia> Dates.monthday(now) (9,2) julia> Dates.dayofweekofmonth(birthday) 3

Two of those functions are very similarly named: the  (days of week in month) function tells you how many days there are in the month with the same day name as the specified day — there are five Tuesdays in the current month (at the time of writing). The last function,  (day of week of month), tells us that the 15th of March, 1997, was the third Saturday of the month.

You can also find days relative to a date, such as the first day of the week containing that day, using the adjusting functions, described below.

Date arithmetic
You can do arithmetic on dates and date/time objects. Subtracting two dates or datetimes to find the difference is the most obvious one:

julia> datetoday - birthday 6380 days julia> datetimenow - armistice 3023472252000 milliseconds

which you can convert to s or  s or some other unit:

julia> Dates.Period(datetoday - birthday) 7357 days julia> Dates.canonicalize(Dates.CompoundPeriod(datetimenow - armistice)) 5138 weeks, 5 days, 5 hours, 46 minutes, 1 second, 541 milliseconds julia> convert(Dates.Day, Dates.Period(Dates.today - Dates.Date(2016, 1, 1))) 491 days julia> convert(Dates.Millisecond, Dates.Period(Dates.today - Dates.Date(2016, 1, 1))) 42422400000 milliseconds

To add and subtract periods of time to date and date/time objects, use the  constructor functions to specify the period. For example,  defines a period of 20 years, and   defines a period of 6 months. So, to add 20 years and 6 months to the birthday date:

julia> birthday + Dates.Year(20) + Dates.Month(6) 2017-09-15

Here's 6 months ago from now:

julia> Dates.now - Dates.Month(6) 2014-03-02T16:43:08

and similarly for months, weeks:

julia> Dates.now - Dates.Year(2) - Dates.Month(6) 2012-03-02T16:44:03

and similarly for weeks and hours. Here's the date and time for two weeks and 12 hours from now:

julia> Dates.now + Dates.Week(2) + Dates.Hour(12) 2015-09-18T20:49:16

and there are

julia> daystoxmas = Dates.Date(Dates.year(Dates.now), 12, 25) - Dates.today 148 days or 148 (shopping) days till Christmas (at the time this was written).

To retrieve the value as a number, use the function :

julia> Dates.value(daystoxmas) 148

This works with different types of date/time objects too:

julia> lastchristmas = Dates.now - Dates.DateTime(2017, 12, 25, 0, 0, 0) 25464746504 milliseconds julia> Dates.value(lastchristmas) 25464746504

Range of dates
You can make iterable range objects that define a range of dates:

julia> d = Dates.Date(1980,1,1):Dates.Month(3):Dates.Date(2019,1,1) 1980-01-01:3 months:2019-01-01

This iterator yields the first day of every third month. To find out which of these fall on weekdays, you can provide an anonymous function to  that tests the day name against the given day names: julia> weekdays = filter(dy -> Dates.dayname(dy) != "Saturday" && Dates.dayname(dy) != "Sunday", d) 104-element Array{Date,1}: 1980-01-01 1980-04-01 1980-07-01 ⋮         2014-07-01 2014-10-01 2016-04-01 2016-07-01 2018-01-01 2018-10-01 2019-01-01

Similarly, here's a range of times 3 hours apart from now, for a year hence:

julia> d = collect(Dates.DateTime(Dates.now):Dates.Hour(3):Dates.DateTime(Dates.now + Dates.Year(1))) 2929-element Array{DateTime,1}: 2015-09-04T08:30:59 2015-09-04T11:30:59 2015-09-04T14:30:59 ⋮                  2016-09-03T20:30:59 2016-09-03T23:30:59 2016-09-04T02:30:59 2016-09-04T05:30:59 2016-09-04T08:30:59

If you have to pay a bill every 30 days, starting on the 1st of January 2018, the following code shows how the due date creeps forward every month:

julia> foreach(d -> println(Dates.format(d, "d u yyyy")), Dates.Date("2018-01-01"):Dates.Day(30):Dates.Date("2019-01-01")) 1 Jan 2018 31 Jan 2018 2 Mar 2018 1 Apr 2018 1 May 2018 31 May 2018 30 Jun 2018 30 Jul 2018 29 Aug 2018 28 Sep 2018 28 Oct 2018 27 Nov 2018 27 Dec 2018

Date formatting
To specify date formats, you use date formatting codes in a formatting string. Each character refers to a date/time element:

y Year digit eg yyyy => 2015, yy => 15 m Month digit eg m => 3 or 03 u Month name eg Jan U Month name eg January e Day of week eg Tue E Day of week eg Tuesday d Day eg 3 or 03 H Hour digit eg HH => 00 M Minute digit eg MM => 00 S Second digit eg S => 00 s Millisecond digit eg .000

You can use these formatting strings with functions such as  and. For example, you create a DateTime object from a string by identifying the different elements in the incoming string:

julia> Dates.Date("Fri, 15 Jun 2018", "e, d u y") 2018-06-15

julia> Dates.DateTime("Fri, 15 Jun 2018 11:43:14", "e, d u y H:M:S") 2018-06-15T11:43:14

Other characters are used literally. In the second example, the formatting characters matched up as follows:

Fri, 15 Jun 2018 11:43:14 e,  d   u    y  H: M: S

You can supply a format string to  to format a date object. In the formatting string, you repeat the characters to control how years and days, for example, are output:

julia> timenow = Dates.now 2015-07-28T11:43:14

julia> Dates.format(timenow, "e, dd u yyyy HH:MM:SS") "Tue, 28 Jul 2015 11:43:14"

When you're creating a formatted date, you can double some of the components of the format string to produce a leading zero for single digit date elements:

julia> anothertime = Dates.DateTime("Tue, 8 Jul 2015 2:3:7", "e, d u y H:M:S") 2015-07-08T02:03:07 julia> Dates.format(anothertime, "e: dd u yy, HH.MM.SS") # with leading zeros "Wed: 08 Jul 15, 02.03.07" julia> Dates.format(anothertime, "e: d u yy, H.M.S") "Wed: 8 Jul 15, 2.3.7"

To convert a date string from one format to another, you can use  and a format string to convert the string to a DateTime object, then   to output the object in a different format:

julia> formatted_date = "Tue, 28 Jul 2015 11:43:14" "Tue, 28 Jul 2015 11:43:14" julia> temp = Dates.DateTime(formatted_date, "e, dd u yyyy HH:MM:SS") 2015-07-28T11:43:14 julia> Dates.format(temp, "dd, U, yyyy HH:MM, e") "28, July, 2015 11:43, Tue"

If you're doing a lot of date formatting (you can apply date functions to an array of strings), it's a good idea to pre-define a DateFormat object and then use that for bulk conversions (this is quicker):

julia> dformat = Dates.DateFormat("y-m-d"); julia> Dates.Date.([  # broadcast       "2010-01-01",        "2011-03-23",        "2012-11-3",        "2013-4-13",        "2014-9-20",        "2015-3-1"       ], dformat)

6-element Array{Date,1}: 2010-01-01 2011-03-23  2012-11-03  2013-04-13  2014-09-20  2015-03-01 There are some built-in formats that you can use. For example, there's  to give you the ISO8601 format:

julia> Dates.DateTime.([            "2010-01-01",            "2011-03-23",            "2012-11-3",            "2013-4-13",            "2014-9-20",            "2015-3-1"            ], Dates.ISODateTimeFormat)  6-element Array{DateTime,1}: 2010-01-01T00:00:00 2011-03-23T00:00:00 2012-11-03T00:00:00 2013-04-13T00:00:00 2014-09-20T00:00:00 2015-03-01T00:00:00

and here's good old RFC1123:

julia> Dates.format(Dates.now, Dates.RFC1123Format) "Sat, 30 Jul 2016 16:36:09"

Date adjustments
Sometimes you want to find a date nearest to another - for example, the first day of that week, or the last day of the month that contains that date. You can do this with the functions like  and. So, if we're currently in the middle of the week:

julia> Dates.dayname(now) "Wednesday"

the first day of the week is returned by this:

julia> Dates.firstdayofweek(now) 2014-09-01T00:00:00

which you could also write using the function chain operator:

julia> Dates.now |> Dates.firstdayofweek |> Dates.dayname  "Monday"

A more general solution is provided by the,  ,  , and   methods.

With  and , you can provide a (possibly anonymous) function that returns true when a date has been correctly adjusted. For example, the function:

returns true if the day  is a Tuesday. Use this with the  method:

julia> Dates.tonext(d->Dates.dayofweek(d) == Dates.Tuesday, birthday) 1997-03-18 # the first Tuesday after the birthday

Or you can find the next Sunday following the birthday date:

julia> Dates.tonext(d->Dates.dayname(d) == "Sunday", birthday) 1997-03-16 # the first Sunday after the birthday

With  and , you can find the first Sunday, or Thursday, or whatever, of a month. Monday is 1, Tuesday 2, etc.

julia> Dates.tofirst(birthday, 1) # the first Monday (1) of that month 1997-03-03

Supply the keyword argument  to get the first matching weekday of the year.

julia> Dates.tofirst(birthday, 1, of=Year) # the first Monday (1) of 1997 1997-01-06

Rounding dates and times
You can use,  , and  , usually used to round numbers up or down to the nearest preferred values, to adjust dates forward or backwards in time so that they have 'rounder' values.

julia> Dates.now 2016-09-12T17:55:11.378 julia> Dates.format(round(Dates.DateTime(Dates.now), Dates.Minute(15)), Dates.RFC1123Format) "Mon, 12 Sep 2016 18:00:00"

The  adjusts dates or times forward in time:

julia> ceil(birthday, Dates.Month) 1997-04-01 julia> ceil(birthday, Dates.Year) 1998-01-01 julia> ceil(birthday, Dates.Week) 1997-03-17

Recurring dates
It's useful to be able to find all dates in a range of dates that satisfy some particular criteria. For example, you can work out the second Sunday in a month by using the  and   functions.

For example, let's create a range of dates from the first of September 2014 until Christmas Day, 2014:

julia> dr = Dates.Date(2014,9,1):Dates.Day(1):Dates.Date(2014,12,25) 2014-09-01:1 day:2014-12-25

Now an anonymous function similar to the ones we used in  earlier finds a selection of those dates in that range that satisfy that function:

julia> filter(d -> Dates.dayname(d) == "Sunday", dr) 16-element Array{Date,1}: 2014-09-07 2014-09-14  2014-09-21  2014-09-28  2014-10-05  2014-10-12  2014-10-19  2014-10-26  2014-11-02  2014-11-09  2014-11-16  2014-11-23  2014-11-30  2014-12-07  2014-12-14  2014-12-21

These are the dates of every Sunday between September 1st 2014 until Christmas Day, 2014.

By combining criteria in the anonymous function, you can build up more complicated recurring events. Here's a list of all the Tuesdays in that period which are on days that are odd numbered and greater than 20:

julia> filter(d->Dates.dayname(d) == "Tuesday" && isodd(Dates.day(d)) && Dates.day(d) > 20, dr) 4-element Array{Date,1}: 2014-09-23 2014-10-21 2014-11-25 2014-12-23

and here's every second Tuesday in 2016 between April and November:

Unix time
You sometimes have to deal with another type of timekeeping: Unix time. Unix time is a count of the number of seconds that have elapsed since the beginning of the year 1970 (the birth of Unix). In Julia the count is stored in a 64 bit integer, and we'll never see the end of Unix time. (The universe will have ended long before 64 bit Unix time reaches the maximum possible value, which will be in approximately 292 billion years from now, at 15:30:08 on Sunday, 4 December 292,277,026,596.)

In Julia, the  function, used without arguments, returns the Unix time value of the current second:

julia> time 1.414141581230945e9

The  ("string format time") function, which lives in the Libc module, converts a number of seconds in Unix time to a more readable form:

julia> Libc.strftime(86400 * 365.25 * 4) # 4 years worth of Unix seconds "Tue 1 Jan 00:00:00 1974"

You can choose a different format by supplying a format string, with the different components of the date and time defined by '%' letter codes:

julia> Libc.strftime("%A, %B %e at %T, %Y", 86400 * 365.25 * 4) "Tuesday, January 1 at 00:00:00, 1974"

The  function takes a format string and a date string, and returns a TmStruct expression. This can then be converted to a Unix time value by passing it to :

julia> Libc.strptime("%A, %B %e at %T, %Y", "Tuesday, January 1 at 00:00:00, 1974") Base.Libc.TmStruct(0,0,0,1,0,74,2,0,0,0,0,0,0,0) julia> time(ans) 1.262304e8 julia> time(Libc.strptime("%Y-%m-%d","2014-10-1")) 1.4121216e9

The Dates module also offers a  function, which converts a Unix time value to a date/time object:

julia> Dates.unix2datetime(time) 2014-10-24T09:26:29.305

Moments in time
s are stored as milliseconds, in the field. Use  to obtain the value.

julia> moment=Dates.now 2017-02-01T12:45:46.326 julia> Dates.value(moment) 63621636346326

julia> moment.instant Base.Dates.UTInstant{Base.Dates.Millisecond}(63621636346326 milliseconds)

If you use the more precise  type, you can access nanoseconds.

julia> moment = Dates.Time(Dates.now) 17:38:44.33

julia> Dates.value(moment) 63524330000000 julia> moment.instant 63524330000000 nanoseconds

Timing and monitoring
The  macro returns the number of seconds an expression took to evaluate:

julia> @elapsed test(100000000) 1.309819509

The  macro tells you how long an expression took to evaluate, and how memory was allocated.

julia> @time test(100000000) 2.532941 seconds (4 allocations: 160 bytes)