Detect daylight saving time in bash

In the northern hemisphere, in regions with daylight savings, then it's active when the offset is greater than the offset is in January. In southern hemisphere time zones, daylight savings is active when the offset is greater than that in July.

You can discover the offsets in January and July, as well as now:

OFF_1=$(date -d '1 Jan' +%z)
OFF_7=$(date -d '1 Jul' +%z)
OFF_NOW=$(date +%z)

If your zone doesn't have daylight savings, all three will be the same (this year). In any case, we can make two comparisons, one for the northern hemisphere and one for the southern; if the current offset is greater than either one of those, then the current zone is in daylight savings time:

if  test $OFF_NOW -gt $OFF_1  ||  test $OFF_NOW -gt $OFF_7
then
  # I'm in summer time
else
  # I'm in winter time
fi

The assumption here is that all regions that observe daylight savings have their transition somewhere between July and January. That's true in all zones, as far as I know. The only case where this might fail is in a year where the winter offset changes (such 1940 in the UK, when the subsequent winter was on GMT+1).


I don't now if this exactly answers your question, but it gives you some tools to help you better understand and test whats going on.

You can use date and environment-var TZ to help you.

So for example I live in Sweden so my timezone location is Europe/Stockholm. So in the winter date +%Z reports CET and in the summer CEST. The nice thing is that you could specify timezone for the environment of a specific command, then you could specify what date the date command should present. So by summing up this you could do any of the following:

TZ=Europe/Stockholm date +%Z # CET or CEST depending of when its run
TZ=Europe/Stockholm date --date=20170101 +%Z # CET
TZ=Europe/Stockholm date --date=20170601 +%Z # CEST
TZ=CET date --date=20170101 +%Z # CET
TZ=CET date --date=20170601 +%Z # CEST, note that its auto adjusted to CEST

If you instead want the time difference to UTC you could use lowercase-z:

TZ=Europe/Stockholm date +%z # +0100 or +0200 depending of when its run
TZ=Europe/Stockholm date --date=20170101 +%z # +0100
TZ=Europe/Stockholm date --date=20170601 +%z # +0200
TZ=CET date --date=20170101 +%z # +0100
TZ=CET date --date=20170601 +%z # +0200

NOTE: You can not use TZ=CEST or TZ=ULFR since that is not a valid TZ:

TZ=CEST date --date=20170101 +%z # +0000
TZ=ULFR date --date=20170101 +%z # +0000

crontab example:

We run our servers on UTC but some of the jobs run by crontab needs to be run at a specified wallclock (CET/CEST) time. So since we want the jobs to be run one hour later in the winter (the clock is put one hour forward in the summer witch makes it reach a specified UTC-time one hour earlier in the summer than in the winter) we do sleep before the actual job is executed in the winter.

We want the job /whatever/bin/foobar to be run at 04:15 wallclock time every day. But since cron runs on UTC the job needs to be set one hour earlier for CET and two hours earlier for CEST. That would be the same as always running the command two hours earlier but sleeping for an hour during winter-time. Ex crontab row:

15 2 * * * [ `TZ=CET date +\%Z` = CET ] && sleep 3600; /whatever/bin/foobar

If you have a nicer solution to this issue, then please feel free to advice me!


for info :

zdump -v Europe/Paris | grep 2020

gives this :

Europe/Paris  Sun Mar 29 00:59:59 2020 UT = Sun Mar 29 01:59:59 2020 CET isdst=0 gmtoff=3600
Europe/Paris  Sun Mar 29 01:00:00 2020 UT = Sun Mar 29 03:00:00 2020 CEST isdst=1 gmtoff=7200
Europe/Paris  Sun Oct 25 00:59:59 2020 UT = Sun Oct 25 02:59:59 2020 CEST isdst=1 gmtoff=7200
Europe/Paris  Sun Oct 25 01:00:00 2020 UT = Sun Oct 25 02:00:00 2020 CET isdst=0 gmtoff=3600

Perl to the rescue:

if perl -e 'exit ((localtime)[8])' ; then
    echo winter
else
    echo summer
fi

Tags:

Bash

Date

Dst