Chapter 24 Dates and Times
Although generally associated with data, handling dates and times can come up often and it may be necessary to use the
Date and DateTime objects that we will see in this chapter. We will start with the Date object that represents dates and if the time is needed as well, we will use a DateTime object. Lastly, a duration can also be helpful and is shown toward the end of the chapter. For any of these objects to be created and the associated methods, make sure you use the Dates module.
Section 24.1 Dates
A
Date in Julia is a way of representing a date in much the same way. For example, January 9, 2007 can be created with Date(2007, 1, 9) and must be passed in year, month, day order. Notice that the string 2007-01-09 is returned, a common way to represent the date.1
Comment on fights about date formats
If only the year is passed into
Date then the first day of the year (January 1) is assumed and if the year and month is passed it, then the first day of the month is assumed. Try entering Date(2024) and Date(2024,7) and notice that 2024-01-01 and 2024-07-01 are returned.
Subsection 24.1.1 Parsing a string to a date
Also useful is parsing a string as a date. If a date is in the formal "yyyy-mm-dd", then it will be parsed as expected, like
Date("2010-02-12") returns "2010-02-12" and stores the date February 12, 2010. If you have a different format, like the U.S. standard "mm/dd/yyyy", like "2/12/2010" then you can pass in the format to be parsed like: Date("2/12/2010", dateformat"m/d/yyyy").
Details on how to structure strings for parsing are on the the Julia docs for formatting, but here’s a few helpful strings associated with dates:
| Code | Comment | Example |
|---|---|---|
| y | numeric year with a fixed width | 9 |
| Y | numeric year with a minimum width | 2016 |
| m | numeric month with a minimum width | 1, 12 |
| u | Month name shorted to 3 characters. | Jan |
| U | Full month name | January |
| d | Day of the month with minimum width | 25 |
| e | Abbreviated day of the week | Mon, Sat |
| E | Full name of day of the week | Monday, Saturday |
As another example, if we have a date like
"Mar 28, 2015", we can parse it as Date("Mar 28, 2015", dateformat"u d, Y") and it returns in standard form as 2015-03-28. As we will see below, we can use this same formatting to output dates.
Subsection 24.1.2 Accessor and Query Functions
Once we have a date, like from
d = Date("2010-02-12"), then we can access the individual components using the year, month and day. The output year(d), month(d) and day(d) returns 2010, 2 and 10.
If you need more than one of these, there are other accessor functions.
yearmonth(d) returns the tuple (2010,2), monthday(d) returns the tuple (2,10) and yearmonthday(d) returns the tuple (2024,2,10).
The other important things we’d like to know about dates falls into what may be called calendar functions. What day of the week is a day, how many days are in a given month. Is it a leap year? These are collectively query functions of dates and here are some examples:
monthname(d) returns "February", dayofweek(d) returns 5 and dayname(d). Information about the year includes dayofyear(d) which returns 43 and isleapyear(d) returns false.
Subsection 24.1.3 Details of a Date
A
Date is a type that is an immutable Int64. If we have d = Date(1978,11,17), then if we do dump(d) returns
Date
instant: Dates.UTInstant{Day}
periods: Day
value: Int64 722405
This shows that this particular date is the integer
722405 which is in short the number of days since January 1, of the year 1 (first day of the common era). The accessor and query functions calculate whatever is desired from the integer value.
Subsection 24.1.4 Formatting a Date for output
Another feature that might be important is to format a Date in a certain way. The
format method does this and we use the same characters as the parsing table as shown in Subsection 24.1.1. As an example, if we have the date d1 = Date(2021, 10, 25), then we can convert it to a string with the date of the week, month name, day and year with the following:
Dates.format(d1, dateformat"E, U d, yyyy")
which returns
"Monday, October 25, 2021".
Section 24.2 Times during a Day
Julia has a
Time type to handle times during a day. For example, if we know the hours, minutes and seconds during the day, then we can make a Time object. For example,
Time(9, 10, 15)
returns
09:10:15. Note that the hours are in 24 hours (so a range of 0 to 23). For more precise Time objects, you can also include milliseconds, microseconds and nanoseconds. For example, Time(9,10,15,10,500) returns 09:10:15.0105. Also, only the hours are required. The hour "3PM" would be Time(15) and if you want 7:30, then Time(7,30) would do that.
Subsection 24.2.1 Parsing and Formatting Times
Similar to a Date, one can parse a string as a Time. By default if the hours are in 24 hours and the parts of the Time are separated by a ":", then it will parse the string by a default format. For example,
Time("15:30") returns 15:30:00 representing 3:30pm.
If the string is not in default format, we can specify one. let’s say we want to parse "3.30 pm", then
Time("3.30 pm", dateformat"H.M p") will parse and return "15:30:00".
We saw formatting strings for dates above. The following table includes those strings for parsing and formatting times.
| Code | Comment | Examples |
|---|---|---|
| H | Hour (24-hour clock) with minimum width | 7, 18 |
| I | Hour (12-hour clock) used only for outputting | 7, 12 |
| p | AM/PM (matches case insensitive) | AM |
| M | Minute with minimum width | 0, 30 |
| S | Second with minimum width | 0, 30 |
| s | Millisecond with minimum width | 000, 500 |
As another example, let’s say that we have a time "18|45|10.600", the we can parse it with
t2 = Time("18|45|10.600", dateformat"H|M|S.s") to the default format of "18:45:10.6"
We can format a time for output. Using the format example to get this to "6.45 pm", we would execute
Dates.format(t2, dateformat"I.M p") and get "6.45 PM".
Subsection 24.2.2 Details of Time types
Similar to the Date, a Time is an integer under the scene. Because the smallest unit of time for this type is a nanosecond, the integer is the number of nanoseconds (say from the beginning of the day). If
t = Time(15,30), then dump(t) is
Time
instant: Nanosecond
value: Int64 55800000000000
and note that the
instant field says the unit of time and compare this to the Date object which uses a Day as the unit.
Section 24.3 Handling Dates and Times
If instead of a day or a time (of day), you need both, like to represent a calendar event, then a
DateTime is recommended. This object is the combined Date and Time and in general
DateTime(year, month, day, hours, minutes, second)
such as
DateTime(2024,11,20,8,25,37) which returns
2024-11-20T08:25:37
and the date is in year-month-day form with the time in 24-hour notation.
If you don’t have seconds or minutes you can still make a DateTime, such as
DateTime(2024,11,20,8,25) or DateTime(2024,11,20,8). In fact, if you need more precise time, then a 7th argument is the number of milliseconds such as d2 = DateTime(2024,11,20,8,25,37,150) which returns 2024-11-20T08:25:37.150
One can parse a string as a DateTime similar to that of a Date with the constructor. For example:
DateTime("2023-07-04T03:10:15") returns
2023-07-04T03:10:15
One can create a custom date time parser also using the
dateformat string. For example, DateTime("07/04/2023 3:10:15 PM", dateformat"m/d/Y H:M:S p") parses successfully as 2023-07-04T15:10:15.
There are many features for parsing formats. See the Julia documentation on DateTime formatting for more information.
Subsection 24.3.1 Accessor and Query Functions
If we need to know the number of hours, minutes and seconds of a DateTime, we can extract that information with the methods
hour, minute and second. If we defined the d2 object above then hour(d2), minute(d2) and second(d2) returns 8, 25 and 37 respectively. millisecond(d2) will return 150.
Subsection 24.3.2 Details of a DateTime
Similar to that of a Date, a DateTime object is a wrapper (struct) on an integer that represents the number of milliseconds since midnight on the first day of the common era in UTC (Universal Time Coordinates). See Section 24.5 for more information. To see details about the internals of a DateTime object, use
dump. The expression dump(d2) returns
DateTime
instant: Dates.UTInstant{Millisecond}
periods: Millisecond
value: Int64 63867774337150
Section 24.4 Arithmetic with Dates and Times and Comparisons
Although it is fairly straightforward for us humans to know when days or times occur before or after another time, it is often necessary to perform comparisons. For example, let’s say we have two dates,
d1 = Date(2024, 2, 14) and d2 = Date(2024, 3, 17). We can check that d2 occurs after d1 with d2 > d1 and this returns true. The comparison operators <, <=, == and != all can be used with Dates and DateTimes as expected.
Subsection 24.4.1 Durations
We often would like to know the number of days (or amount of time) between two dates or DateTimes. We can find the using the
- operator. Using the two Dates from above, dur = d2 - d1 returns 32 days and typeof(dur) returns Day which is a time duration. Notice that this is returned in days because the internal time period for a Date is a Day.
Another example using DateTime would be
time_diff = DateTime(2024, 3, 17, 7, 0, 0 ) - DateTime(2024,2,14,4,0,0)
which this time returns
2775600000 milliseconds. Although the time difference should be in hours, the result is in milliseconds because the underlying time period for a DateTime is in milliseconds. We’ll see how to convert this below.
We can create a time duration directly using the capitalized versions of
Year, Month, Day, Hour, Minute, Second and Millisecond.
Subsection 24.4.2 Compound Periods
If we (as humans) were asked how long it was between the two dates listed above, we would not return it in milliseconds. It is such a big numbers, that we can’t get a scale for how long it is. Instead, we would use combinations of years, months, and days and that it precisely the idea behind a CompoundPeriod.
An example of a CompoundPeriod would be
cp1 = Day(3) + Hour(5) representing 3 days and 5 hours. Note that the capitalizations on the time periods are important. The output from this is 3 days, 5 hours, a human-readable version of the input.
As shown above, we can use the difference between compound periods, but also the sum. If we have
cp2 = Month(2) + Day(20) + Hour(23) and we add cp1 and cp2, then we get: 2 months, 23 days, 28 hours. Notice that you might scratch your head and wonder why it didn’t convert 28 hours to 1 day and 4 hours. Arithmetic with periods will not automatically do this, but the method canonicalize will do this. Applying this to the difference results in the expected result: 4 weeks, 4 days, 3 hours, and notice that the days were also converted. You can examine the details in
And we can use this method to convert the time difference in the previous section by entering
canonicalize(time_diff) and resulting in 4 weeks, 4 days, 3 hours. Note that since months are not a fixed length of time, weeks are not converted to months, however if more than 11 months are in a compound period, then they can be converted to years. As mentioned above, Dates are difficult and we will see even more so they get more complicated with time zones.
Section 24.5 Time Zones
On the surface, dates and times seem to be pretty straightforward. We understand them because we know dates and times from an entire lifetime of experience. However, they are quite tricky with two respects. The first being leap years and the second is time zones. When using times with the DateTime object, it can be thought of as local time, however, technically it is in Universal Coordinate Time.
MORE HERE
Section 24.6 Summary of Dates and Times
As mentioned a few times in this chapter, dates and times can be tricky, however use of a package like
Dates can make things simpler. In short, I would recommend to use the simplest time object that you need that is and make a clear note of a time instance versus a period.
-
Use time instances (Date, Time, DateTime) only when needed specific dates, times during the date or Date with times respectively.
-
If a time period (how long something occurs) is needed, then time durations should be used and you have all of arithmetic associated with them.
-
Use a DateTime only if specific times on specific dates is needed. Events include specific observations (maybe astronomical) or calendar events. If only a date in needed, use the simpler Date object.
-
Only use Time zones if needed. Note that they are in a separate package due to additional complication. These would only be warranted if either you need precise dates/times on particular location on earth. Examples would include astronomical data collected at different parts on earth or ensuring times (like meetings) occur for people in different locations on earth.
