Linux: How to round off the date() to nearest interval

Viewed 305

I want to round down the minutes to the nearest 15 min interval i.e. 00,15,30,45. I'm currently doing the below:

echo $(date +'%Y/%m/%d/%H/')$((($(($(date +'%M') / 15))-0)*15))

But at the start of the hour between 1-14 minutes, I get "/2021/11/03/21/0" instead of 00.

Also, I'm not sure if this is the best way to do this. Are there any alternatives?

4 Answers

Would you please try the following:

mod=$(( 10#$(date +%M) \% 15 ))
date -d "-${mod} minutes" +%Y/%m/%d/%H/%M
  • The variable mod holds the remainder of the minutes divided by 15.
  • Then round down to the nearest 15 minute interval by subtracting mod.

[Edit]
The manpage of crontab says:

Percent-signs (%) in the command, unless escaped with backslash (), will be changed into newline characters, and all data after the first % will be sent to the command as standard input.

If you want to execute the command within crontab, please modify the command as:

mod=$(( 10#$(date +\%M) \% 15 ))
date -d "-${mod} minutes" +\%Y/\%m/\%d/\%H/\%M

[Edit2]
If you want to embed the code in crontab file, please add a line which look like:

0 12 * * * username bash -c 'mod=$(( 10#$(date +\%M) \% 15 )); DATEVAR=$(date -d "-${mod} minutes" +\%Y/\%m/\%d/\%H/\%M); write.sh "$DATEVAR"'
  • Please modify the execution time/date and the username accordingly.
  • The default shell to execute crontab command may be /bin/sh. Then you will need to explicitly switch it to /bin/bash to execute bash commands.
  • My apology that a backslash in front of % 15 (modulo operation) was missing in my previous post.

Another approach:

min=$(printf "%0.2d" $(( ($(date +'%M') / 15) * 15 )))
echo "$(date +'%Y/%m/%d/%H/')$min"
date -d "@$((($(date +%s) + 450) / 900 * 900))"

This uses the properties of integer division to “subtract a modulus” and adds half of the desired interval to (almost) mimic a rounding operation.

A bit of extra sub-second rounding precision (for no good reason) can be achieved by taking %N (nanoseconds) into account. But it will not matter, because the exact half of the rounding interval (450 seconds) is already aligned with the default epoch resolution (1 second). (If the number of seconds in the desired rounding interval was odd, then the following would increase the time rounding precision.)

date -d "@$((($(date +%s%N) + 45*10**10) / (9*10**11) * 900))"

Pure bash, bash version 4.3 or higher:

printf '%(%Y/%m/%d/%H/%M)T\n' "$(( $(printf '%(%s)T') /(15*60)*(15*60) ))"

Using GNU date (any bash version or POSIX shell):

date -d @$(( $(date +%s) /(15*60)*(15*60) )) +%Y/%m/%d/%H/%M
  • Truncates the current epoch date (seconds since 1970-01-01 00:00:00) to a 15 minute (900 second) interval, then converts to desired format.
  • Retrieves the current date/time once only.
  • If you build a date/time from two separate date/times, it can be wrong, when a unit ticks over in between.
  • The printf date-time format string was added in bash 4.2, and was changed in 4.3 to also print the current time, if no input date was given.
  • Note that bash arithmetic treats numbers that start with zero as octals, and numbers like 08 and 09 will cause an error (because they are not octal numbers).
Related