29

I am having trouble grasping how to properly check from a bash script if the current time is between 23:00 and 06:30. I am trying to run an infinite loop to check the time now, and to do something if the time range is between 11pm and 6:30 am. Here's what I have written so far, which doesn't work the next day:

fireup()
{

 local starttime=$(date --date="23:00" +"%s")
 local endtime=$(date --date="06:30" +"%s")

 while :; do
     local currenttime=$(date +%s)
     if [ "$currenttime" -ge "$starttime" -a "$currenttime" -ge "$endtime" ]; then
        do_something
     else
         do_something_else
     fi
     test "$?" -gt 128 && break
     local currenttime=$(date +%s)
 done &
 }

What I am doing wrong?

1
  • "which doesnt work the next day" ... if you use yesterday's 23:00 and 06:30 timestamps and never update those ....
    – muru
    Commented Oct 3, 2017 at 23:21

7 Answers 7

47

If all you need is to check if HH:MM is between 23:00 and 06:30, then don't use Unix timestamps. Just check the HH:MM values directly:

fireup()
{  
  while :; do
   currenttime=$(date +%H:%M)
   if [[ "$currenttime" > "23:00" ]] || [[ "$currenttime" < "06:30" ]]; then
     do_something
   else
     do_something_else
   fi
   test "$?" -gt 128 && break
  done &
}

Notes:

  • Time in HH:MM will be in lexicographic order, so you can directly compare them as strings.
  • Avoid using -a or -o in [ ], use || and && instead.
  • Since this is bash, prefer [[ ]] over [ ], it makes life easier.
13
  • Can you check the current time in a specific time zone? Thanks! Commented Dec 27, 2019 at 14:30
  • 2
    @tommy.carstensen you can get date to use a specific time zone using the TZ variable (e.g., TZ=Asia/Tokyo date or TZ=UTC date +%H:%M
    – muru
    Commented Jan 14, 2020 at 3:00
  • I'd give you an extra plus just for naming the method fireup()
    – jbrahy
    Commented May 12, 2020 at 15:15
  • @jbrahy that was OP's idea (it's in the question)
    – muru
    Commented May 12, 2020 at 15:43
  • 1
    @zakmck yes, you have to phrase your time constraints appropriately: given timestamps of limits A and B for a span that crosses 00:00, are all times before B in a day OK? Then use < with B and > with A (so 0630 is B and 2300 A in the question, 00:30 is B and 0530 is A in your comment). If not, then A and B are swapped: all times before A must be OK, so use < with A and > with B.
    – muru
    Commented Jul 30 at 16:21
9

If you want to be more explicit about the times you want to match, you could use a case statement. Here's a 24-hour loop with a case statement inside that indicates whether the current time matches your range:

for hh in {00..23}
do
  for mm in {00..59}
  do
    case $hh:$mm in
        (23:*)        echo YES $hh:$mm;;
        (0[012345]:*) echo YES $hh:$mm;;
        (06:[012]*)   echo YES $hh:$mm;;
        (*)           echo  NO $hh:$mm;;
    esac
  done
done

To use it in your script, just replace the variables with a call to date:

case $(date +%H:%M) in
    (23:*)        echo YES;;
    (0[012345]:*) echo YES;;
    (06:[012]*)   echo YES;;
    (*)           echo NO;;
esac

You might consider being friendlier to your CPU and computing the amount of time between (now) and the next start time, and sleep for that amount of time.

1
  • Wow. This is a very unexpected and a great solution. Thanks!
    – Unpossible
    Commented Oct 4, 2017 at 19:26
6

You can convert the time to seconds since the start of "this" day and then check that the seconds value is either greater than 23*60*60 (82800) or lower than 6*60*60+30*60 (23400).

To get seconds since the start of "this" day you can do:

secsSinceMidnight=$(( $(date +%s) - $(date -d '00:00:00' +%s) ))

And the test would be:

[[ $secsSinceMidnight -lt 23400 || $secsSinceMidnight -gt 82800 ]] && echo YES
3

Another possible solution. This will work if you cross midnight or not. Function return true if time is between interval. (Otherwise false). See examples in test parts.

#! /bin/bash
date_between() {
    TIME=$1
    FROM=$2
    TO=$3
    
    RESULT=false

    if [[ "$FROM" < "$TO" ]]; thenFROM < $TIME) || ($TIME < $TO) ]]; then
            RESULT=true
        fi
    fi

    echo $RESULT
}

test_date_between_human
        if [[ ("$FROM" < "$TIME") && ("$TIME" < "$TO") ]]; then
            RESULT=true
        fi
    else
        if [[ ("$FROM" < "$TIME") || ("$TIME" < "$TO") ]]; then
            RESULT=true
        fi
    fi

    echo "$RESULT"
}

test_date_between_human_readable_time_in_range() {
    assertTrue $(date_between '07:10' '07:00' '07:15')
}
test_date_between_human_readable_time_out_of_range() {
    assertFalse $(date_between '07:40' '07:00' '07:15')
}
test_date_between_human_readable_over_midnight_time_in_range() {
    assertTrue $(date_between '23:30' '23:00' '01:00')
}
test_date_between_human_readable_over_midnight_time_out_of_range() {
    assertFalse $(date_between '07:00' '23:00' '01:00')
}

. shunit2

Run:

./date_test.sh                
test_date_between_human_readable_time_in_range
test_date_between_human_readable_time_out_of_range
test_date_between_human_readable_over_midnight_time_in_range
test_date_between_human_readable_over_midnight_time_out_of_range

Ran 4 tests.

OK
2
  • What is not what, when?   What is the “whole interval”?  I’m often in favor of generalizing questions, and not restricting answers to example data, but I believe that you’re taking that philosophy too far.   It really doesn’t help that you use/reference things you haven’t defined, and that your answer is 10% text and 90% code.   Also, you should quote variables. Commented Jul 18, 2020 at 9:02
  • Hi, Scott for feedback I expected that examples in test would by enough. At least I add a sort description and quote variables. As you point out. Thx for comment to improve. (I admit my first version was confusing, sorry)
    – Alteccz
    Commented Jul 18, 2020 at 9:31
0

I have a similar situation. My goal is to have a solution which is very simple for non-unix geeks to maintain.

When my Linux server restarts, it runs a script which sends an alert. But if this happens as part of our"Maintenance Window" which is defined as between 11pm Saturday and 6am Sunday, I do not want to send the page, because a restart during this window is Okay (patches applied, etc.). I have tested it and it does work as intended (as far as I can tell).

Here is the calendar for reference:

      June 2018
Su Mo Tu We Th Fr Sa
                1  2
 3  4  5  6  7  8  9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Here is my test code:

FMT='+%k:%a' # i.e. Sat:23 for Saturday at 11pm local time

TESTDATE1=$(date -d '2018-06-23 14:30:00' $FMT)
TESTDATE2=$(date -d '2018-06-23 23:59:59' $FMT)
TESTDATE3=$(date -d '2018-06-24 00:00:00' $FMT)
TESTDATE4=$(date -d '2018-06-24 05:59:59' $FMT)
TESTDATE5=$(date -d '2018-06-24 08:01:00' $FMT)

for VAL in "${TESTDATE1}" $TESTDATE2 $TESTDATE3 $TESTDATE4 $TESTDATE5
do

    #extract the 24 hour value and the day
    HH="$(echo ${VAL} | cut -d':' -f1)"
    DAY="$(echo ${VAL} | cut -d':' -f2)"

    echo Testing $VAL  $HH  $DAY

    if [[ "$DAY" = "Sat" && $HH -ge 23 ]]; then
            echo '    Shhhh People are trying to sleep ... Sat window'
    elif [[ "$DAY" = "Sun" && $HH -le 6 ]]; then
            echo '    People are trying to sleep ... Sun window'
    else
            echo '    PAGE SOMEONE - time is outside the Maintenance Window'
    fi

done

Here is the output

Testing 14:Sat 14 Sat
    PAGE SOMEONE - time is outside the Maintenance Window
Testing 23:Sat 23 Sat
    Shhhh People are trying to sleep ... Sat window
Testing 0:Sun 0 Sun
    People are trying to sleep ... Sun window
Testing 5:Sun 5 Sun
    People are trying to sleep ... Sun window
Testing 8:Sun 8 Sun
    PAGE SOMEONE - time is outside the Maintenance Window
2
  • 1
    Since this appears to attempt to answer the question, I've left it, with some edits. If you truly have a desire to have someone review your code, I'd suggest codereview.stackexchange.com
    – Jeff Schaller
    Commented Jun 22, 2018 at 18:50
  • Thanks Jeff, yes, I was looking for suggestions.I was not aware of the codereview site. Thanks
    – Daniel
    Commented Jun 22, 2018 at 21:02
-1

In this particular case, an alternative to make the test simpler is to add an hour and test once for less than 07:30

if [[ $(date -d "now + 60 minutes" +'%H:%M') < "07:30" ]]
    then echo "wake up"
else
    echo "Zzzzzz"
fi
-2

Hint: muru's answer above worked for me, but instead of || (pipes, "or"), i had to use && ("and" operator).

like this: if [[ "$currenttime" > "23:00" ]] && [[ "$currenttime" < "06:30" ]]

1
  • 3
    That doesn’t make any sense.   What possible value of currenttime is both greater than “23:00” and also less than “06:30”?   Hint: there aren’t any. Commented Nov 8, 2019 at 4:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .