BASH looping though time
Asked Answered
T

2

0

Using BASH I want to loop from a start to end date at ten-minute intervals. I tried

begin_date="2015-01-01 00:00:00"
end_date="2015-02-20 00:00:00"
d=$begin_date
while [ "$d" != "$end_date" ]; do
    echo $d
    d=$(date -d "${d} + 10 min" +"%Y-%m-%d %H:%M")
done

But it didn't work. Looking at Bash:Looping thru dates

#This works
d=$(date -I -d "${d} + 1 day")

#This doesn't work
d=$(date -d "${d} + 1 day" +"%Y-%m-%d")

What am I missing in the format string?

Trug answered 19/12, 2016 at 11:17 Comment(3)
define "it didn't work". What output does it produce?Kemper
It didn't add ten minutes or day.Trug
The question you link to specifically explains why -I is important, but you took it out anyway.Wingo
S
1

The example you linked to just needs to be adjusted slightly:

#!/bin/bash

## User-specified dates.
# See [GNU Coreutils: Date] for more info
# [GNU Coreutils: Date]: https://www.gnu.org/software/coreutils/manual/html_node/Combined-date-and-time-of-day-items.html#Combined-date-and-time-of-day-items
begin_date="2015-01-01T00:00"
end_date="2015-01-01T00:40"

# Run through `date` to ensure iso-8601 consistency
startdate=$(date --iso-8601='minutes' --date="${begin_date}")
enddate=$(date --iso-8601='minutes' --date="${end_date}")

# Do loop
d="$startdate"
while [ "$d" != "$enddate" ]; do 
  echo $d
  d=$(date --iso-8601='minutes' --date="$d + 10 minutes")
done

Note that the options -I and -d are equivalent to --iso-8601 and --date respectively.

Shackle answered 19/12, 2016 at 11:48 Comment(3)
Thanks, Sigve, your answer also works. Not certain if I can tick multiple answers as correct.Trug
On further testing, I'm changing to accept Sigve's answer. I ran both methods for a month. Sigve's was slightly faster (although not by much) @tripleee's real 1m1.310s user 0m1.156s sys 0m55.953s vs Sigve's real 0m57.308suser 0m0.953s sys 0m52.250s However, the reason I changed the answer was that I'm needing four variables derived from the date (old/new path (YYYYMMDD) and old/new name foo_HHMM) and the code above was easy to reformat the date whereas the other code was not.Trug
Minor correction to start and end dates. Don't use Z (i.e. UTC). code begin_date="2015-01-01T00:00" end_date="2015-01-01T00:40" codeTrug
W
2

The expression date -d "${d} + 10 min" seems not to produce a date with an offset of 10 minutes. In fact, when I run your code, I see a date counter going backwards. (Posting this diagnostic as part of your question would help others see where the problem is; you should not require others to run your code just to see what it does.)

Anyway, the sane way to do this is to convert the dates to Unix epoch, then take it from there.

for ((d=$(date -d "$begin_date" +%s); d <= $(date -d "$end_date" +%s); d += 600))
do
    date -d @$d +"%F %H:%M"
done

Doing date arithmetic in the shell is probably going to be rather inefficient; converting this to e.g. Awk or Perl might be worth your time if you find it's too sluggish, or need to run it lots of times.

Wingo answered 19/12, 2016 at 11:34 Comment(4)
Thanks, code works perfectly. Been battling with that for about 8 hours! I had to google epoch time to understand your answer :-)Trug
Thanks for the feedback; added a Wikipedia link.Wingo
I also experienced this rare feature of time going backwards when saying +10 min.Kemper
I think the reason that +10 min appears to have time go backwards is that the +10 part is parsed as a timezone specification if T00:00 or similar is present in the timestamp. date -d '2015-01-01T00:00 + 10 + 20 minutes' gives me Wed Dec 31 15:20:00 CET 2014, so an apparent 9 hours back in time because CET is +1 and I asked for +10, and then 20 minutes forward as specified.Signal
S
1

The example you linked to just needs to be adjusted slightly:

#!/bin/bash

## User-specified dates.
# See [GNU Coreutils: Date] for more info
# [GNU Coreutils: Date]: https://www.gnu.org/software/coreutils/manual/html_node/Combined-date-and-time-of-day-items.html#Combined-date-and-time-of-day-items
begin_date="2015-01-01T00:00"
end_date="2015-01-01T00:40"

# Run through `date` to ensure iso-8601 consistency
startdate=$(date --iso-8601='minutes' --date="${begin_date}")
enddate=$(date --iso-8601='minutes' --date="${end_date}")

# Do loop
d="$startdate"
while [ "$d" != "$enddate" ]; do 
  echo $d
  d=$(date --iso-8601='minutes' --date="$d + 10 minutes")
done

Note that the options -I and -d are equivalent to --iso-8601 and --date respectively.

Shackle answered 19/12, 2016 at 11:48 Comment(3)
Thanks, Sigve, your answer also works. Not certain if I can tick multiple answers as correct.Trug
On further testing, I'm changing to accept Sigve's answer. I ran both methods for a month. Sigve's was slightly faster (although not by much) @tripleee's real 1m1.310s user 0m1.156s sys 0m55.953s vs Sigve's real 0m57.308suser 0m0.953s sys 0m52.250s However, the reason I changed the answer was that I'm needing four variables derived from the date (old/new path (YYYYMMDD) and old/new name foo_HHMM) and the code above was easy to reformat the date whereas the other code was not.Trug
Minor correction to start and end dates. Don't use Z (i.e. UTC). code begin_date="2015-01-01T00:00" end_date="2015-01-01T00:40" codeTrug

© 2022 - 2024 — McMap. All rights reserved.