Sleep until a specific time/date Sleep until a specific time/date bash bash

Sleep until a specific time/date


As mentioned by Outlaw Programmer, I think the solution is just to sleep for the correct number of seconds.

To do this in bash, do the following:

current_epoch=$(date +%s)target_epoch=$(date -d '01/01/2010 12:00' +%s)sleep_seconds=$(( $target_epoch - $current_epoch ))sleep $sleep_seconds

To add precision down to nanoseconds (effectively more around milliseconds) use e.g. this syntax:

current_epoch=$(date +%s.%N)target_epoch=$(date -d "20:25:00.12345" +%s.%N)sleep_seconds=$(echo "$target_epoch - $current_epoch"|bc)sleep $sleep_seconds

Note that macOS / OS X does not support precision below seconds, you would need to use coreutils from brew instead → see these instructions


As this question was asked 4 years ago, this first part concerns old bash versions:

Last edit: Wed Apr 22 2020, something between 10:30 and 10h:55 (Important for reading samples)

General method (Avoid useless forks!)

(Nota: this method use date -f wich is no POSIX and don't work under MacOS! If under Mac, goto my pure function)

In order to reduce forks, instead of running date two times, I prefer to use this:

Simple starting sample

sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))

where tomorrow 21:30 could be replaced by any kind of date and format recognized by date, in the future.

With high precision (nanosec)

Nearly same:

sleep $(bc <<<s$(date -f - +'t=%s.%N;' <<<$'07:00 tomorrow\nnow')'st-t')

Reaching next time

For reaching next HH:MM meaning today if possible, tomorrow if too late:

sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))

This works under , and other modern shells, but you have to use:

sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))

under lighter shells like or .

Pure way, no fork!!

Tested under MacOS!

I wrote one two little functions: sleepUntil and sleepUntilHires

 Syntax: sleepUntil [-q] <HH[:MM[:SS]]> [more days]     -q Quiet: don't print sleep computed argument     HH          Hours (minimal required argument)     MM          Minutes (00 if not set)     SS          Seconds (00 if not set)     more days   multiplied by 86400 (0 by default)

As new versions of bash do offer a printf option to retrieve date, for this new way to sleep until HH:MM whithout using date or any other fork, I've build a little function. Here it is:

sleepUntil() { # args [-q] <HH[:MM[:SS]]> [more days]    local slp tzoff now quiet=false    [ "$1" = "-q" ] && shift && quiet=true    local -a hms=(${1//:/ })    printf -v now '%(%s)T' -1    printf -v tzoff '%(%z)T\n' $now    tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))    slp=$((       ( 86400+(now-now%86400) + 10#$hms*3600 + 10#${hms[1]}*60 +          ${hms[2]}-tzoff-now ) %86400 + ${2:-0}*86400    ))    $quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+slp))    sleep $slp}

Then:

sleepUntil 10:37 ; date +"Now, it is: %T"sleep 49s, -> Wed Apr 22 10:37:00 2020Now, it is: 10:37:00sleepUntil -q 10:37:44 ; date +"Now, it is: %T"Now, it is: 10:37:44sleepUntil 10:50 1 ; date +"Now, it is: %T"sleep 86675s, -> Thu Apr 23 10:50:00 2020^C

If target is before this will sleep until tomorrow:

sleepUntil 10:30 ; date +"Now, it is: %T"sleep 85417s, -> Thu Apr 23 10:30:00 2020^CsleepUntil 10:30 1 ; date +"Now, it is: %T"sleep 171825s, -> Fri Apr 24 10:30:00 2020^C

HiRes time with under GNU/Linux

Recent , from version 5.0 add new $EPOCHREALTIME variable with microseconds. From this there is a sleepUntilHires function.

sleepUntilHires () { # args [-q] <HH[:MM[:SS]]> [more days]    local slp tzoff now quiet=false musec musleep;    [ "$1" = "-q" ] && shift && quiet=true;    local -a hms=(${1//:/ });    printf -v now '%(%s)T' -1;    IFS=. read now musec <<< $EPOCHREALTIME;    musleep=$[2000000-10#$musec];    printf -v tzoff '%(%z)T\n' $now;    tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})));    slp=$(((( 86400 + ( now - now%86400 ) +            10#$hms*3600+10#${hms[1]}*60+10#${hms[2]} -            tzoff - now - 1            ) % 86400 ) + ${2:-0} * 86400          )).${musleep:1};    $quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1));    read -t $slp foo}

Please note: this use read -t wich is built-in, instead of sleep. Unfortunely, this won't work when running in background, without real TTY. Feel free to replace read -t by sleep if youplan to run this in background scripts... (But for background process, consider using cron and/or at instead of all this)

Skip next paragraph for tests and warning about $ËPOCHSECONDS!

Recent kernel avoid using /proc/timer_list by user!!

Under recent Linux kernel, you will find a variables file named `/proc/timer_list` where you could read an `offset` and a `now` variable, in **nanoseconds**. So we may compute sleep time to reach the *very top* desired time.

(I wrote this to generate and track specific events on very big log files, containing thousand line for one second).

mapfile  </proc/timer_list _timer_listfor ((_i=0;_i<${#_timer_list[@]};_i++));do    [[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_SKIP=$_i    [[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \    TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \     breakdoneunset _i _timer_listreadonly TIMER_LIST_OFFSET TIMER_LIST_SKIPsleepUntilHires() {    local slp tzoff now quiet=false nsnow nsslp    [ "$1" = "-q" ] && shift && quiet=true    local hms=(${1//:/ })    mapfile -n 1 -s $TIMER_LIST_SKIP nsnow </proc/timer_list    printf -v now '%(%s)T' -1    printf -v tzoff '%(%z)T\n' $now    nsnow=$((${nsnow//[a-z ]}+TIMER_LIST_OFFSET))    nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))    tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))    slp=$(( ( 86400 + ( now - now%86400 ) +            10#$hms*3600+10#${hms[1]}*60+${hms[2]} -            tzoff - now - 1        ) % 86400)).${nsslp:1}    $quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1))    sleep $slp}

After defining two read-only variables, TIMER_LIST_OFFSET and TIMER_LIST_SKIP, the function will access very quickly the variable file /proc/timer_list for computing sleep time:

Little test function

tstSleepUntilHires () {     local now next last    printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1))    sleepUntilHires $next    date -f - +%F-%T.%N < <(echo now;sleep .92;echo now)    printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1))    sleepUntilHires $next    date +%F-%T.%N}

May render something like:

sleep 0.244040s, -> Wed Apr 22 10:34:39 20202020-04-22-10:34:39.0016853122020-04-22-10:34:39.922291769sleep 0.077012s, -> Wed Apr 22 10:34:40 20202020-04-22-10:34:40.004264869
  • At begin of next second,
  • print time, then
  • wait 0.92 seccond, then
  • print time, then
  • compute 0.07 seconds left, to next second
  • sleep 0.07 seconds, then
  • print time.

Care to not mix $EPOCHSECOND and $EPOCHREALTIME!

Read my warning about difference between $EPOCHSECOND and $EPOCHREALTIME

This function use $EPOCHREALTIME so don't use $EPOCHSECOND for establishing next second:

Sample issue: Trying to print time next rounded by 2 seconds:

for i in 1 2;do    printf -v nextH "%(%T)T" $(((EPOCHSECONDS/2)*2+2))    sleepUntilHires $nextH    IFS=. read now musec <<<$EPOCHREALTIME    printf "%(%c)T.%s\n" $now $musecdone

May produce:

sleep 0.587936s, -> Wed Apr 22 10:51:26 2020Wed Apr 22 10:51:26 2020.000630sleep 86399.998797s, -> Thu Apr 23 10:51:26 2020^C


Use sleep, but compute the time using date. You'll want to use date -d for this. For example, let's say you wanted to wait until next week:

expr `date -d "next week" +%s` - `date -d "now" +%s`

Just substitute "next week" with whatever date you'd like to wait for, then assign this expression to a value, and sleep for that many seconds:

startTime=$(date +%s)endTime=$(date -d "next week" +%s)timeToWait=$(($endTime- $startTime))sleep $timeToWait

All done!