Compare date strings in hugo

Assume you wish to compare two formatted date strings like 2017-03-15 with 2017-01-12 and you want to compare them which one is older. Or check if a date is in past or future. We jump into this topic here.

To format any date to a proper date string can be done with dateFormat but what if you need to compare two date strings with each other. Like is 1998-01-05 in the past or in the future from now?

Instead of splitting the string into pieces and compare them one by one we will use the time function to make a proper time out of a string.

the time function

From the documentation we learn - time converts a timestamp string into a time.Time structure so you can access its fields.

In a simple case:

{{ time "1998-01-05" }} -> “1998-01-05T00:00:00Z”

Make sure that the time is properly formatted either in ISO8601 (2015-01-21T20:54:45.847Z), YYYY-MM-DD (2015-01-21), RFC822 (21 Jan 06 15:04 MST), RFC822Z (21 Jan 06 15:04 -0700), RFC1123 (Mon, 21 Jan 2006 15:04:05 MST) or RFC339 (Mon, 21 Jan 2006 15:04:05 -0700).

The output is a time.Time structure providing many functions to access. Like:

In all below examples we assumed, that we have a variable called $t where we stored the time. Like:

{{ $t := (time "1998-01-05") }}

Read time and fragments

.String

String returns the time formatted using the format string

{{ $t.String }} -> 1998-01-05 00:00:00 +0000 UTC

.Year

Year returns the year in which t occurs.

{{ $t.Year }} -> 1998

.Month

Month returns the month of the year specified by t.

{{ $t.Minute }} -> January

.Day

Day returns the day of the month specified by t.

{{ $t.Day }} -> 5

.Hour

Hour returns the hour within the day specified by t, in the range [0, 23].

{{ $t.Hour }} -> 0

.Minute

Minute returns the minute offset within the hour specified by t, in the range [0, 59].

{{ $t.Minute }} -> 0

.Second

Second returns the second offset within the minute specified by t, in the range [0, 59].

{{ $t.Second }} -> 0
{{ (mul $t.Second 1000) }} -> 0 (milliseconds)

.Nanosecond

Nanosecond returns the nanosecond offset within the second specified by t, in the range [0, 999999999].

{{ $t.Nanosecond }} -> 0

.Weekday

Weekday returns the day of the week specified by t.

{{ $t.Weekday }} -> Monday

.YearDay

YearDay returns the day of the year specified by t, in the range [1,365] for non-leap years, and [1,366] in leap years.

{{ $t.YearDay }} -> 5

.Unix

Unix returns t as a Unix time, the number of seconds elapsed since January 1, 1970 UTC.

{{ $t.Unix }} -> 883958400
{{ (mul $t.Unix 1000) }} -> 883958400000 (milliseconds)

.UTC

UTC returns t with the location set to UTC.

{{ $t.UTC }} -> 1998-01-05 00:00:00 +0000 UTC

.UnixNano

UnixNano returns t as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC. The result is undefined if the Unix time in nanoseconds cannot be represented by an int64 (a date before the year 1678 or after 2262). Note that this means the result of calling UnixNano on the zero Time is undefined.

{{ $t.UnixNano }} -> 883958400000000000

.UnixNano

UnixNano returns t as a Unix time, the number of nanoseconds elapsed since January 1, 1970 UTC. The result is undefined if the Unix time in nanoseconds cannot be represented by an int64 (a date before the year 1678 or after 2262). Note that this means the result of calling UnixNano on the zero Time is undefined.

{{ $t.UnixNano }} -> 883958400000000000

.Location

Location returns the time zone information associated with t.

{{ $t.Location }} -> UTC

.MarshalBinary

MarshalBinary implements the encoding.BinaryMarshaler interface.

{{ $t.MarshalBinary }} -> [1 0 0 0 14 172 66 25 128 0 0 0 0 255 255]

.MarshalJSON

MarshalJSON implements the json.Marshaler interface. The time is a quoted string in RFC 3339 format, with sub-second precision added if present.

{{ $t.MarshalJSON }} -> [34 49 57 57 56 45 48 49 45 48 53 84 48 48 58 48 48 58 48 48 90 34]

.MarshalText

MarshalText implements the encoding.TextMarshaler interface. The time is formatted in RFC 3339 format, with sub-second precision added if present.

{{ $t.MarshalText }} -> [49 57 57 56 45 48 49 45 48 53 84 48 48 58 48 48 58 48 48 90]

Compare time

.After

After reports whether the time instant t is after u.

{{ $t.After now }} -> false
{{ $t.After (time "1996-05-15") }} -> true

.Before

Before reports whether the time instant t is before u.

{{ $t.Before now }} -> true
{{ $t.Before (time "1996-05-15") }} -> false

.Equal

Equal reports whether t and u represent the same time instant. Two times can be equal even if they are in different locations. For example, 6:00 +0200 CEST and 4:00 UTC are Equal. Do not use == with Time values.

{{ $t.Equal (time "1998-01-05") }} -> true
{{ $t.Equal (time "2017-05-08") }} -> false

.IsZero

IsZero reports whether t represents the zero time instant, January 1, year 1, 00:00:00 UTC.

{{ $t.IsZero }} -> false
{{ (time "0001-01-01").IsZero }} -> true

Calculations

.Add

Add returns the time t+d. While 1 second equals to 1000000000.

{{ $t.Add 1000000000 }} -> 1998-01-05 00:00:01 +0000 UTC

.AddDate

AddDate returns the time corresponding to adding the given number of years, months, and days to t. For example, AddDate(-1, 2, 3) applied to January 1, 2011 returns March 4, 2010.

{{ $t.AddDate <years> <months> <days> }}
{{ $t.AddDate 10 9 5 }} -> 2008-10-10 00:00:00 +0000 UTC

.Round

Round returns the result of rounding t to the nearest multiple of d (since the zero time). The rounding behavior for halfway values is to round up. If d <= 0, Round returns t unchanged.

Round operates on the time as an absolute duration since the zero time; it does not operate on the presentation form of the time. Thus, Round(Hour) may return a time with a non-zero minute, depending on the time’s Location.

{{ $t.Round 1000000000000000 }} -> 1998-01-10 03:33:20 +0000 UTC

.Sub

Sub returns the duration t-u. If the result exceeds the maximum (or minimum) value that can be stored in a Duration, the maximum (or minimum) duration will be returned. To compute t-d for a duration d, use t.Add(-d).

{{ $t.Sub (time "1997-01-05") }} -> 8760h0m0s

{{ $delta := $t.Sub (time "1997-01-05") }}
{{ int (div $delta.Hours 24)}} -> 365 (full days)
{{ $delta.Hours }} -> 8760
{{ $delta.Minutes }} -> 525600
{{ $delta.Seconds }} -> 3.1536e+07

.Truncate

Truncate returns the result of rounding t down to a multiple of d (since the zero time). If d <= 0, Truncate returns t unchanged.

Truncate operates on the time as an absolute duration since the zero time; it does not operate on the presentation form of the time. Thus, Truncate(Hour) may return a time with a non-zero minute, depending on the time’s Location.

{{ $t.Truncate 1000000000000000 }} -> 1997-12-29 13:46:40 +0000 UTC

Format and Convert

.Format

Format returns a textual representation of the time value formatted according to layout, which defines the format by showing how the reference time, defined to be

Same output as dateFormat.

{{ $t.Format "January 2006" }} -> January 1998

.In

In returns t with the location information set to loc.

In panics if loc is nil.

{{ $t.In now.Location }} -> 1998-01-05 01:00:00 +0100 CET (this is relative to the machine timezone)

.Local

Local returns t with the location set to local time.

{{ $t.Local }} -> 1998-01-05 01:00:00 +0100 CET (results may vary due to your machines local time)

Conclusion

So the magic happens when we convert a date string into time and do all the comparisons (.After, .Before, .Equal, .IsZero).

time empowers also a lot of time calculation functions like .Add, .AddDate or .Sub to name a few.

So, time is up. Article End.