简体   繁体   中英

bash script looping through dates gets stuck?

I'm using the bash script below to kick off a python program. It loops through date/time exactly as I would expect. But, then, it get's stuck at "2018-11-04 01:45:00". Adding 15 minutes to "2018-11-04 01:45:00" returns "2018-11-04 01:00:00", not "2018-11-04 02:00:00". Has anyone experienced this?

    #!/bin/bash

    dEND=$(date +"%F %T" -d "2020-03-15 00:00:00")
    dCUR=$(date +"%F %T" -d "2018-11-04 01:00:00")
    echo $dCUR $dEND

    typeset -i sEND=1
    typeset -i sCUR=0
    echo $sCUR $sEND

    while( (( $sCUR < $sEND )) );do
       dCUR=$(date +"%F %T" -d "$dCUR 15 minutes")

       typeset -i sEND=$(date +%s -d "$dEND")
       typeset -i sCUR=$(date +%s -d "$dCUR")
       echo $dCUR $dEND $sCUR
    done
    echo done
    $ dCUR=$(date +"%F %T" -d "2018-11-04 00:00:00")
    $ echo $dCUR
    2018-11-04 00:00:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 00:15:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 00:30:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 00:45:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 01:00:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 01:15:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 01:30:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 01:45:00
    $ dCUR=$(date +"%F %T" -d "$dCUR 15 minutes");echo $dCUR
    2018-11-04 01:00:00

I suspect you (or, more correctly, your machine ) may be located (or, more correctly, be configured as if it's located) in the Bahamas, Bermuda, Brazil, Canada, Cuba, Fiji, Greenland, Haiti, Mexico, Saint Pierre and Miquelon, Turks and Caicos islands, and the USA.

I can't tell for sure since your profile has no information on your location, but I'd be curious to know how close I am to reality.


That code of yours works fine on my system but my geographical region does not do daylight savings.

That changeover tends to happen around 2-3am in many places so it may just be an artefact of the date you've chosen. From this page here , it appears a few places did indeed do a switch on November 4 (hence my list of locations in the first paragraph of this answer).

You can probably confirm this by running the exact same experiment but using November 5 rather than the date you've chosen. You can also add the %z format specifier and you should see it change over from (for example) +08 to +07 .


As with most problems with world times, the best solution is convert all local times into UTC as quickly as possible, and turn them back into local times as late as possible. That way, you mostly have a consistent time base except where you're interacting with end users (who generally think in terms of local time).

As an example, here's a script that runs from 1am to 5am, in a region that had a timezone switch in the range.

#!/bin/bash

# My region doesn't do DST but New York does, so fake it.

export TZ=America/New_York

# Helper functions for UTC/local conversion.

utcFmt="%F %T %z"
localFmt="%F %T"

utcToLocal() {
    echo "$(date +"${localFmt}" -d "$(date +"${utcFmt}" -d "$1")")"
}

localToUtc() {
    echo "$(date -u +"${utcFmt}" -d "$(date +"${utcFmt}" -d "$1")")"
}

# Init UTC values based on local time.

utcCur="$(localToUtc "2018-11-04 01:00:00")"
utcEnd="$(localToUtc "2018-11-04 05:00:00")"

echo "Going from ${utcCur} to ${utcEnd}"

while [[ "${utcCur}" < "${utcEnd}" ]] ; do
    # Display local time, but adjust UTC time.

    echo "At $(utcToLocal "${utcCur}")"
    utcCur="$(date -u +"${utcFmt}" -d "$utcCur 15 minutes")"
done

As you can see from the output, there is one rolling back to an earlier time because that's what happens when you turn the clock back (see * markers):

Going from 2018-11-04 01:00:00 to 2018-11-04 05:00:00
At 2018-11-04 01:00:00
At 2018-11-04 01:15:00
At 2018-11-04 01:30:00
At 2018-11-04 01:45:00
At 2018-11-04 01:00:00 * Roll back one hour (once).
At 2018-11-04 01:15:00
At 2018-11-04 01:30:00
At 2018-11-04 01:45:00
At 2018-11-04 02:00:00 * Keep going.
At 2018-11-04 02:15:00
At 2018-11-04 02:30:00
At 2018-11-04 02:45:00
At 2018-11-04 03:00:00
At 2018-11-04 03:15:00
At 2018-11-04 03:30:00
At 2018-11-04 03:45:00
At 2018-11-04 04:00:00
At 2018-11-04 04:15:00
At 2018-11-04 04:30:00
At 2018-11-04 04:45:00

And, if you go to the other end of the daylight savings (where an hour disappears), you can see that as well (again, watch the * markers):

utcCur="$(localToUtc "2018-03-11 01:00:00")"
utcEnd="$(localToUtc "2018-03-11 05:00:00")"

Going from 2018-03-11 01:00:00 to 2018-03-11 05:00:00
At 2018-03-11 01:00:00
At 2018-03-11 01:15:00
At 2018-03-11 01:30:00
At 2018-03-11 01:45:00
At 2018-03-11 03:00:00 * Put clock foward one hour.
At 2018-03-11 03:15:00
At 2018-03-11 03:30:00
At 2018-03-11 03:45:00
At 2018-03-11 04:00:00
At 2018-03-11 04:15:00
At 2018-03-11 04:30:00
At 2018-03-11 04:45:00

2018-11-04 was the first Sunday in November. The regions of North America and nearby territories which switch between daylight saving and standard time currently do so on the first Sunday in November (and on the second Sunday in March for the other transition). That means that in November, the time goes from 01:59:59 xDT to 01:00:00 xST (spring forward; fall back). The addition and result is correct — even though it wasn't what you expected.

Places in Europe that switch currently spring forward on the last Sunday in March and fall back on the last Sunday in October, thus changing 2 or 3 weeks later in the spring and 1 week earlier in the autumn than the USA changes. If the 1st of March falls on Monday through Thursday, the gap is 2 weeks; Friday through Sunday, 3 weeks.

Other countries have different rules still, sometimes influenced by the lunar calendar and religious observations.

GNU date format string is very flexible, but in the long run is not reliable.

To do what you want, convert the date to number of seconds since epoch, add 15*60 seconds and then convert back to the format you want.

That would be:

dCUR=$(date -d "@$(($(date --date="$dCUR" +%s) + 15 * 60))" +"%F %T"); echo $dCUR

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM