1. Overview

Daylight saving time is a common practice in many geographical locations of the world. Since it involves advancing the clock (typically by one hour), therefore, we apparently lose one hour on the day when the standard time is switched to daylight saving time.

In this tutorial, we’ll focus on the use case of getting relative dates in a DST-safe manner.

2. Relative Dates

The date command is the de-facto standard to get date and time information on a Unix or Linux system. In this section, we’ll learn how to get relative dates using words from our natural language, such as yesterday and tomorrow. Further, we’ll discover a subtle issue when it comes to getting DST-safe relative dates.

2.1. Basics

Let’s start by taking a 10,000-foot view of the concept associated with getting relative dates using the date command. We can use the yesterday keyword with the –date option to get yesterday’s date:

$ date; date --date "yesterday"
Sat Mar 12 10:15:39 PST 2022
Fri Mar 11 10:15:39 PST 2022

We can see that the default format of the output includes both the date and time.

Similarly, let’s use the tomorrow keyword to get tomorrow’s date:

$ date; date --date "tomorrow"
Sat Mar 12 10:18:45 PST 2022
Sun Mar 13 11:18:45 PDT 2022

As such, the daylight saving is scheduled to switch at 2 AM on March 13th for the year 2022. So, we can notice the PDT time zone reflected for tomorrow’s date. Additionally, as expected, the time is advanced by one hour when compared to the current time.

If we want to limit the output to show only the date value, we can use the %F format:

$ date +%F; date --date "tomorrow" +%F
2022-03-12
2022-03-13

2.2. DST-Unsafe Relative Dates

So far, so good. However, things go haywire when the current time is the last hour of the day in the standard time, and the future date falls in the daylight saving time:

$ date; date --date='3 days'
Thu Mar 10 23:11:01 PST 2022
Mon Mar 14 00:11:01 PDT 2022

To limit the output to date-only, let’s use the %F format:

$ date +%F; date --date='3 days' +%F
2022-03-10
2022-03-14

We’d think that if today is March 10th, then the 3rd day from now will be March 13th. However, the math doesn’t seem to add up without the time zone and time information.

Moreover, a similar inconsistency will be visible if we try to get a date in the past that falls in the standard time, while the current time is in the first hour of a day that falls in the daylight saving time:

$ date; date --date='3 days ago'
Mon Mar 14 00:11:01 PDT 2022
Thu Mar 10 23:11:01 PST 2022

Like earlier, the issue becomes more prominent when we use the %F format to limit the output to date only:

$ date +%F; date --date='3 days ago' +%F
2022-03-14
2022-03-10

Let’s learn how to get relative dates in a DST-safe manner.

3. Using GNU date

The root cause of the issue is that the GNU date command (the default date utility on most Linux distros) is aware of the daylight saving time. So, it tends to do more complex calculations than necessary in this scenario. In contrast, the natural meaning of relative dates is simple arithmetic at the date level without worrying about the current time of the day.

To solve the issue, we can specify the time of the day when getting the relative time in the past or future.

Let’s see this in action by getting a relative date in the future:

$ date; date --date='3 days 00:00'
Thu Mar 10 23:11:01 PST 2022
Sun Mar 13 00:00:00 PDT 2022

Now, when we use the %F format to get the date, it aligns well with the natural notion of relative date:

$ date +%F; date --date='3 days 00:00' +%F
2022-03-10
2022-03-13

Similarly, we can get a relative date in the past:

$ date; date --date='3 days ago 00:00'
Mon Mar 14 00:11:01 PDT 2022
Fri Mar 11 00:00:00 PST 2022

As earlier, we can safely include the %F format to get the DST-safe date value:

$ date +%F; date --date='3 days ago 00:00' +%F
2022-03-14
2022-03-11

4. Using BSD date

As we noted in the previous section, in most Linux distros, the GNU date is the default date utility. However, some of the modern operating systems, such as macOS, use the BSD version as the default date utility.

When it comes to getting the relative dates, the BSD version of the date command seems to be smarter than the GNU-based date command. By using the -v flag, the user always gets a DST-safe date.

Let’s see how to use the -v flag to get a future date in the Los Angeles time zone that’s observing daylight saving starting March 13, 2022:

$ TZ="America/Los_Angeles" date; TZ="America/Los_Angeles" date -v+1d;
Sat Mar 12 23:11:01 PST 2022
Sun Mar 13 23:11:01 PDT 2022

We can notice that although the time zone information changed from PST to PDT, the time value remains the same as the current time. So, the BSD date utility adjusted the one-hour clock advancement internally.

Consequently, we can safely use the %F format when using the -v flag for getting a relative date using the BSD date command:

$ TZ="America/Los_Angeles" date +%F; TZ="America/Los_Angeles" date -v+1d +%F;
2022-03-12
2022-03-13

5. Conclusion

In this tutorial, we explored the subtle issues while getting the relative dates using the date command in a DST-safe manner. Further, we learned simple techniques to solve the issues when using GNU and BSD versions of the date utility.