The date command appears to convert timezones the wrong way? - bash

I'm working on a script where I need to convert the current time, in UTC to something else:
fmt="%Y-%m-%d %H:%M:%S"
tzone=America/Detroit
nowinutc=$(TZ=UTC date +"$fmt");echo $nowinutc
nowintz=$(date -d "TZ=\"$tzone\" $nowinutc" +"$fmt");echo $nowintz
yestintz=$(date -d "$nowintz - 1 day" +"$fmt");echo $yestintz
I know America/Detroit is 5 hours behind UTC, so 08:00:00 should become 03:00:00, but:
$ fmt="%Y-%m-%d %H:%M:%S"
$ tzone=America/Detroit
$ nowinutc=$(TZ=UTC date +"$fmt");echo $nowinutc
2022-02-11 08:49:51
$ nowintz=$(date -d "TZ=\"$tzone\" $nowinutc" +"$fmt");echo $nowintz
2022-02-11 13:49:51
$ yestintz=$(date -d "$nowintz - 1 day" +"$fmt");echo $yestintz
2022-02-12 14:49:51
It's probably not the date command that gets it wrong, but what am I doing wrong here? It looks like everything works the opposite way of expected. Not only does the timezone conversion add 5 hours, but subtracting 1 day adds 25 hours - what is going on here?

so 08:00:00 should become 03:00:00
Then the input is in UTC and you are in Detroit.
TZ=America/Detroit date -d "08:00:00 UTC" +%H:%M:%S
TZ=America/Detroit date -d "TZ=\"UTC\" 08:00:00" +%H:%M:%S
TZ=America/Detroit date -d "08:00:00+00:00" +%H:%M:%S
From man date examples:
Show the time on the west coast of the US (use tzselect(1) to find TZ)
$ TZ='America/Los_Angeles' date
Show the local time for 9AM next Friday on the west coast of the US
$ date --date='TZ="America/Los_Angeles" 09:00 next Fri'

Related

Round current time up and down

I'm trying to figure out how to get the current hour rounded down to start of the hour and the next hour in bash?
For example, if I run my script:
./printHour.sh
and let's say the current time at execution is 13:04:12 - it would print
current hour is: 13:00:00
next hour is: 14:00:00
Progress so far: (but this gives 1hour ago so it does not work) - any ideas?
lastHour=$(date -d '1 hour ago' "+%H:%M:%S")
echo "current hour is: "$lastHour
You can use this utility function:
hrdt() { date -d "${1?} hour ago" '+%H:00:00'; }
Testing:
> hrdt
bash: 1: parameter not set
> hrdt 0
08:00:00
> hrdt 1
07:00:00
> hrdt 2
06:00:00
> hrdt 3
05:00:00
Could you please try following, written and tested as per shown samples, my date is GNU date version.
cat script.bash
#!/bin/bash
currentHour=$(date "+%H:00:00")
nextHour=$(date -d '+1 hour' "+%H:00:00")
echo "current hour is: $currentHour"
echo "next hour is: $nextHour"
When I run above script I get as follows:
current hour is: 06:00:00
next hour is: 07:00:00
Seems like you don't need anything special so this should do it:
date -d '1 hour ago' "+%H:00:00"
Why bother when you want exactly the hour where both %M and %S are expected to be zero?
You don't need date in this case; as seen below, built-in printf can generate formatted date-time strings too. Here -1 represents current time, and EPOCHSECONDS is a dynamic variable that expands to the number of seconds since epoch.
$ printf 'current hour is: %(%H)T:00:00\n' -1
current hour is: 17:00:00
$
$ printf 'next hour is: %(%H)T:00:00\n' $((EPOCHSECONDS + 3600))
next hour is: 18:00:00
Using awk,
$ awk ' BEGIN { st=systime();
print "current hour=" strftime("%F %H:00:00",st);
print "next hour=" strftime("%F %H:00:00",st+(60*60)) } '
current hour=2020-12-26 23:00:00
next hour=2020-12-27 00:00:00
$

Shell add day to date saved in parameter

Hoping someone can help me work out what on earth is happening here.
I've got a script which receives a date as a parameter in this format "2016-09-01 00:00:00" and should create another variable containing the date for one day in the future, code is below
currentDate=$1
currentDate=$(date +"%Y-%m-%d %H:%M:%S" -d "$currentDate")
nextDate=$(date +"%Y-%m-%d %H:%M:%S" -d "$currentDate + 1 day")
echo $currentDate
echo $nextDate
Sometimes this works perfectly fine for example
2016-09-01 00:00:00 - date given as parameter
2016-09-02 00:00:00 - output for next day
But sometimes it'll only add 23 hours depending on the date provided
2016-02-01 00:00:00 - date given as parameter
2016-02-01 23:00:00 - output for next day
if I change the nextDay variable to add three days as below
nextDate=$(date +"%Y-%m-%d %H:%M:%S" -d "$currentDate + 3 day")
it gives the output as below adding only 21 hours instead of 3 days
2016-02-01 00:00:00 - date given as parameter
2016-02-01 21:00:00 - output for next day
Could someone help me understand why this is happening, is it related to timezones?
The reason appears to be how difficult it is to do free-form date parsing:
A baseline date: (I'm in the America/Toronto time zone)
$ date -d "2016-11-06 01:00:00" "+%F %T%z"
2016-11-06 01:00:00-0400
Try adding a day
$ date -d "2016-11-06 01:00:00 + 1 day" "+%F %T%z"
2016-11-06 19:00:00-0500
Hmm, that's strange, looks like it's adding a day but then expressing it as midnight, then subtracting 5 hours.
Let's try adding a day to just the date part
$ date -d "2016-11-06 + 1 day 01:00:00" "+%F %T%z"
2016-11-07 01:00:00-0500
That looks better.
In your script try this:
read current_date current_time < <(date -d "$1" +"%F %T%z")
echo "$current_date $current_time"
next_day=$(date -d "$current_date + 1 day $current_time" +"%F %T%z")
echo "$next_day"
three_days=$(date -d "$current_date + 3 day $current_time" +"%F %T%z")
echo "$three_days"

Parse non standard date format

Say I have the following format outputted for my date:
date --utc +%d.%m.%Y,\ %H:%M\ UTC
# Outputs: 12.06.2014, 09:03 UTC
How can I display the outputted date above, in another date call, in another format? I tried:
date --utc --date="12.06.2014, 09:03 UTC" +%d.%m.%Y,\ %H:%M\ UTC
but with no success (it says invalid date).
I am primarily trying to do this in order to be able to tell from an outputted date how many hours have passed (or days, or whatever time measuring unit).
Here is what the man date page says about format for the --date option:
The --date=STRING is a mostly free format human readable date string such as
"Sun, 29 Feb 2004 16:21:42 -0800" or "2004-02-29 16:21:42" or even "next
Thursday". A date string may contain items indicating calendar date, time of day,
time zone, day of week, relative time, relative date, and numbers. An empty
string indicates the beginning of the day. The date string format is more
complex than is easily documented here but is fully described in the info
documentation.
Hence you can use, for example:
date --date "2014-06-12 09:03 UTC" --utc +%d.%m.%Y,\ %H:%M\ UTC
# Output: 12.06.2014, 09:03 UTC
to get what you desire.
You could get this second form easily from your first output with a sed line as follows:
sed 's/\([0-9]\{2\}\)\.\([0-9]\{2\}\)\.\([0-9]\{4\}\), \(.*\)/\3-\2-\1 \4/'
<<< '12.06.2014, 09:03 UTC'
# Output: 2014-06-12 09:03 UTC
Note that it would probably be faster to output date at ISO 8601 format in the first time for reuse, e.g. with:
date --utc +%F\ %H:%M\ UTC
# Output: 2014-06-12 10:12 UTC
I think you cannot specify input format, so you'll have to change it with another command like this:
date --utc --date="$(echo "12.06.2014, 09:03 UTC" | sed -r 's/(..).(..).(....), (..):(..) UTC/\3-\2-\1 \4:\5 UTC/')"
Also if you want to make arithmethic on this you could use +%s:
DATE1=$(date "+%s" --date="$(echo "12.06.2014, 09:03 UTC" | sed -r 's/(..).(..).(....), (..):(..) UTC/\3-\2-\1 \4:\5 UTC/')")
DATE2=$(date "+%s" --date="$(echo "17.06.2014, 08:30 UTC" | sed -r 's/(..).(..).(....), (..):(..) UTC/\3-\2-\1 \4:\5 UTC/')")
DIFF_IN_SECONDS=$(($DATE2-$DATE1))
DIFF_IN_RAW_DAYS=$(( ($DATE2-$DATE1)/86400 ))
DIFF_IN_DATES=$(( (($DATE2/86400) - ($DATE1/86400)) ))

convert input timestamp from ist to utc (UNIX)

I am getting date as an input parameter which is IST from some source but I have to convert it into UTC format i.e., I have to substract 5 hours 30 minutes to the date which I am receiving.
echo "Date format DD/MM/YYYY HH:SS"
read $input
I can do this writing small function which will grep HH and SS from the $input and then add which checking conditions which is tedious task, instead I want to know is there any way to do this ?
I found date command option which works like this:
date -d "15/05/2014 10:12 -5 hours -30 minutes"
not working
date -d "15/05/2014 10:12 5 hours ago 30 minutes ago" also not working.
Assuming you are getting your IST date in $date variable you can do this to get GMT date:
date="15/05/2014 10:15"
As a 1st step convert your dd/mm/yyyy date to mm/dd/yyyy:
date=$(awk 'BEGIN{FS=OFS="/"} {print $2, $1, $3}' <<< "$date")
Convert to GMT:
( export TZ='GMT' && date -d 'TZ="Asia/Kolkata" '"$date" )
Thu May 15 04:45:00 GMT 2014
To get EPOCH GMT value:
( export TZ='GMT' && date -d 'TZ="Asia/Kolkata" '"$date" '+%s' )
1400129100

Bash: subtracting 10 mins from a given time

In a bash script, if I have a number that represents a time, in the form hhmmss (or hmmss), what is the best way of subtracting 10 minutes?
ie, 90000 -> 85000
This is a bit tricky. Date can do general manipulations, i.e. you can do:
date --date '-10 min'
Specifying hour-min-seconds (using UTC because otherwise it seems to assume PM):
date --date '11:45:30 UTC -10 min'
To split your date string, the only way I can think of is substring expansion:
a=114530
date --date "${a:0:2}:${a:2:2}:${a:4:2} UTC -10 min"
And if you want to just get back hhmmss:
date +%H%M%S --date "${a:0:2}:${a:2:2}:${a:4:2} UTC -10 min"
why not just use epoch time and then take 600 off of it?
$ echo "`date +%s` - 600"| bc; date
1284050588
Thu Sep 9 11:53:08 CDT 2010
$ date -d '1970-01-01 UTC 1284050588 seconds' +"%Y-%m-%d %T %z"
2010-09-09 11:43:08 -0500
Since you have a 5 or 6 digit number, you have to pad it before doing string manipulation:
$ t=90100
$ while [ ${#t} -lt 6 ]; do t=0$t; done
$ echo $t
090100
$ date +%H%M%S --utc -d"today ${t:0:2}:${t:2:2}:${t:4:2} UTC - 10 minutes"
085100
Note both --utc and UTC are required to make sure the system's timezone doesn't affect the results.
For math within bash (i.e. $(( and ((), leading zeros will cause the number to be interpreted as octal. However, your data is more string-like (with a special format) than number-like, anyway. I've used a while loop above because it sounds like you're treating it as a number and thus might get 100 for 12:01 am.
My version of bash doesn't support -d or --date as used above. However, assuming a correctly 0-padded input, this does work
$ input_time=130503 # meaning "1:05:03 PM"
# next line calculates epoch seconds for today's date at stated time
$ epoch_seconds=$(date -jf '%H%M%S' $input_time '+%s')
# the 600 matches the OP's "subtract 10 minutes" spec. Note: Still relative to "today"
$ calculated_seconds=$(( epoch_seconds - 600 )) # bc would work here but $((...)) is builtin
# +%H%M%S formats the result same as input, but you can do what you like here
$ echo $(date -r $calculated_seconds '+%H%M%S')
# output is 125503: Note that the hour rolled back as expected.
For MacOS users you can do the following:
$(date -v -10M +"%H:%M:%S")
Date time without a specific format:
$(date -v -10M)
For non-macOS users:
Date time without a specific format:
date --date '-10 min'

Resources