Sleep until a specific time/date

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!


Here is a simple Bash one-liner:

sleep $(expr $(date -d "03/21/2014 12:30" +%s) - $(date +%s))

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


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

General method (Avoid useless forks!)

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

(Nota: this method use date -f wich is no POSIX and don't work under MacOS! If under Mac, goto my pure bash 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 .

if read -rp "Sleep until: "  targetTime ;then
    sleep $(( $(date -f - +%s- <<< "$targetTime"$'\nnow') 0 ))
fi

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 bash, ksh 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 ash or dash.

Pure bash 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 bash 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 2020
Now, it is: 10:37:00

sleepUntil -q 10:37:44 ; date +"Now, it is: %T"
Now, it is: 10:37:44

sleepUntil 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
^C

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

HiRes time with bash under GNU/Linux

Recent bash, 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 you plan 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!

Older method using /proc/timer_list, avoided to normal user, by recent Kernel!!

Under 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_list
for ((_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.: ]} && \
     break
done
unset _i _timer_list
readonly TIMER_LIST_OFFSET TIMER_LIST_SKIP

sleepUntilHires() {
    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 2020
2020-04-22-10:34:39.001685312
2020-04-22-10:34:39.922291769
sleep 0.077012s, -> Wed Apr 22 10:34:40 2020
2020-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 $musec
done

May produce:

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

Tags:

Bash

Sleep

Wait