Search code examples
bashdatetimezone

Time Zones and Daylight Savings Time on restricted server


I'm running a bash script on a Github runner that I have no control over. In it, I need to compare timestamps to make certain that the current time is within an accepted timeframe.

I get start and end timestamps with date, and the script should only continue if the current date/time is within those timestamps.

But the server is so locked down that I cannot use TZ=America/Chicago date - it refuses to let me update TZ or anything else that I was able to find to temporarily set a time zone. I read that the time zone can be set to read-only, so that would be my guess.

What finally worked was this:

now=$(date '+%s' -d "now CST")
startdate=$(date '+%s' -d "$startraw CST")
finishdate=$(date '+%s' -d "$finishraw CST")

if [ $now -ge $startdate ] && [ $now -lt $finishdate ]; then
  echo "Current time is allowed"
else
  echo "ERROR - current time is not allowed"
fi

$startraw and $finishraw are in this format: 03/21/2024 08:00:00.

I tried inserting America/Chicago instead of CST but that could not be parsed, so I used CST.

Now I had a UTC timestamp that I could compare correctly - until Daylight Savings hit when the current time no longer fit into the start and end time values even when it should.

So I found that there is CDT which is CST with daylight savings time included (if I understood it correctly).

Except now my results are even more off...

I just ran some test code:

now1=$(date '+%s' -d "now CST")
now2=$(date '+%s' -d "now CDT")

echo "Human readable times:"
echo "Now CST: $(date -d @$now1)"
echo "Now CDT: $(date -d @$now2)"

It is currently 8:11am CST [edit: CST in colloquial terms, not in computer terms]. This should be 1:11pm UTC according to Google.

The output when I convert the timestamp back into human readable:

Now CST: Thu Mar 21 19:11:44 UTC 2024
Now CDT: Thu Mar 21 18:11:44 UTC 2024

So that is 7pm and 6pm instead of the expected 1pm.

The server should be set to use UTC since my test code resulted in an output with "UTC" in it.

I guess it is possible that the server they are using is out of date on its time zone definitions, but still, there shouldn't be a 5 or 6 hour difference between actual UTC and what the server is giving me.

The CST part worked 2 weeks ago (i.e. before DST hit).

It is possible that the server owners made changes to their time zone settings, but that does not explain why the debug output returns UTC.

Can someone help me figure out what is going on here and how to make this work with daylight savings time?

Thanks!


Solution

  • It is possible that the server owners made changes to their time zone settings, but that does not explain why the debug output returns UTC.

    More likely, the change to DST in your area is what produced the breakage.

    I just ran some test code:

    now1=$(date '+%s' -d "now CST")
    now2=$(date '+%s' -d "now CDT")
    
    echo "Human readable times:"
    echo "Now CST: $(date -d @$now1)"
    echo "Now CDT: $(date -d @$now2)"
    

    It is currently 8:11am CST [edit: CST in colloquial terms, not in computer terms]. This should be 1:11pm UTC according to Google.

    The output when I convert the timestamp back into human readable:

    Now CST: Thu Mar 21 19:11:44 UTC 2024
    Now CDT: Thu Mar 21 18:11:44 UTC 2024
    

    So that is 7pm and 6pm instead of the expected 1pm.

    The command ...

    date '+%s' -d "now CDT"
    

    ... says "take the current local time, reinterpret it as being expressed in CDT, and print the corresponding timestamp." If that makes you scratch your head, then it may make more sense if you think about what you would expect when an explicit time is specified instead of now; for example, 13:01:00 EDT.

    Such timestamps are defined relative to UTC, however, regardless of the local time zone, so the command ...

    date -d @$now2
    

    ... says "expand variable now2, interpret the result as a timestamp (relative to UTC, regardless of local time zone), and print the resulting date and time in the default format." The default format uses the local time zone, which in your case happens to be UTC.

    Chaining those together produces what the time is / was / will be in the local time zone when the CDT time is what the local time is now. That's a bit of a mouthful, so here's a breakdown:

    • you ran the first date command at 13:11:44 according to the server's local time
    • a timestamp corresponding to 13:11:44 CDT today was stored
    • you read back that timestamp with the second date command, and asked for it to be formatted in the default format
    • the default format uses the local time zone, which happens to be UTC in your case. The UTC time corresponding to 13:11:44 CDT is 18:11:44 UTC, which is what was printed.

    That's not what you wanted, of course.

    Of course, you can, and I guess did, work with that by expressing your start and end times in UTC or in the server's local time zone, or with a fixed offset from one of those. But if the start and end times relevant to you are expressed in the local time of an area that observes different DST behavior than your time reference does than that breaks when the two time references diverge.


    As far as I am aware, the only way to request date to format dates in a different zone than is specified in /etc/localtime is to use the TZ environment variable. The conventional way to do that is to specify it as a command-line prefix:

    TZ=America/Chicago date
    

    That may not work if your shell is configured to prevent TZ being modified, and in that case it is worth trying to use env to bypass the shell for controlling date's environment:

    env TZ=America/Chicago date
    

    In particular, that still works for me in a Bash shell in which I have used declare -r TZ to mark the TZ variable read-only.

    If using env does not do the job then you probably need to explore alternatives to the date command. For example, if you can run Python scripts then Python's rather more flexible date / time utilities could be brought to bear.