Convert a date from DD-MM-YYYY to YYYY-MM-DD format in bash LINUX
Scenario: I am fetching a date value from a file into a variable and it is in DD-MM-YYYY format by default. I have to substract this date from system date. Subtraction is giving incorrect result if I had both dates in DD-MM-YYYY format. So I read a bit on google and decided to format both dates as YYYY-MM-DD as this will give correct value after subtraction. I have System date formatted successfully in YYYY-MM-DD, but facing a hard time to convert the date obtained from the file to YYYY-MM-DD format. Below solution works fine with single digit dates:
19 - is Date of a month 07 - is Month i.e. July in this case. 2021 - is Year
Desired output as -> 2021-07-19 I am working on RH Linux with Date version as: date (GNU coreutils) 8.22 Please help to provide a solution for above problem.
The first one didn’t work fine as it gave you 1832-09-02 instead of 1832-02-09 for 9-2-1832. IOW, your date interpreted the date as if it was in US format (m-d-y) instead of d-m-y).
@StéphaneChazelas I can reproduce this on my Arch, and using LC_ALL=el_GR.utf8 or LC_ALL=es_ES.utf8 or LC_ALL=fr_FR.utf8 doesn’t make a difference, although all of those use DD-MM-YYYY and print the right format with +%x . This is a bug in GNU date , right?
@terdon, no, it works as documented, GNU date expects date is a limited number of formats, generally US style, and that’s independant of the locale. See info date cal
I find GNU date weird: it jumps through hoops to try to accept all sorts of «intuitive» date inputs, but doesn’t let you tell it what the format is when it’s impossible to guess. (BSD date is much nicer in this respect.) Granged, 19 can’t be a month, but GNU date doesn’t seem to go so far as to assume it must be a day and adjust accordingly.
Do you have no control over the file? If you could arrange for that to be in YYYY-MM-DD format too, then your life would be much easier 🙂 (I don’t understand why everyone isn’t using ISO-format dates everywhere by now…)
10 Answers 10
You could switch to busybox date which allows specifying the input format:
$ date=19-7-2021 $ busybox date -D %d-%m-%Y -d "$date" +%F 2021-07-19
(beware it won’t accept 019-007-2021 dates for instance).
Same with the ast-open implementation of date (unlikely to be available out of the box on your RedHat system):
$ date -p %d-%m-%Y -d "$date" +%F 2021-07-19
If you were on BSD instead of a GNU system, you’d do:
$ date -jf %d-%m-%Y -- "$date" +%F 2021-07-19
Or you could switch to zsh instead of bash as is has a date parsing and formatting builtin:
$ zmodload zsh/datetime $ strftime -rs t %d-%m-%Y $date && strftime %F $t 2021-07-19
( strftime being the builtin that gives access to the standard strftime() and strptime() (with -r ) APIs).
It also has proper splitting operators and a printf that can take arguments in arbitrary orders (using the %n$. syntax as found in most printf() implementations including GNU printf() , but not the printf builtin of bash nor the GNU printf standalone utility), and it doesn’t treat numbers with leading zeros as octal by default:
$ printf '%3$04d-%2$02d-%1$01d\n' $ 2021-07-19
Or using an anonymous function:
Or here, the array reversing operator:
$ printf '%04d-%02d-%02d\n' $ 2021-07-19
In your case, awk might be a better method:
Thanks @Stephane. I have posted my answer which helped solving my problem, but I think yours would work too as the approach looks similar.
Awk is able to do it directly: awk -F’-‘ ‘
If the format is hardcoded as DD-YY-YYYY , you can also hardcode the whole expression and use substring extraction to reformat, which is very fast:
#!/bin/bash dmy=$(date +%d-%m-%Y) ymd="$-$-$" echo "$dmy -> $ymd"
If the format is D[D]-M[M]-YYYY you could use regular expressions to reformat, which is somewhat slower:
#!/bin/bash dmy="9-2-1932" if [[ "$dmy" =~ ^([[:digit:]]+)-([[:digit:]]+)-([[:digit:]]+)$ ]] ; then ymd="$-$-$" fi echo "$dmy -> $ymd"
You can format that a bit nicer with printf :
dmy="9-02-2032" if [[ "$dmy" =~ ^([[:digit:]]+)-([[:digit:]]+)-([[:digit:]]+)$ ]] ; then printf -v ymd '%.4d-%.2d-%.2d' \ "$" "$" "$" fi echo "$dmy -> $ymd"
A former version of this answer is evidence that I didn’t read it fully, converting YYYY-MM-DD to DD-MM-YYYY :
#!/bin/bash ymd=$(date +%Y-%m-%d) dmy="$-$-$" echo "$ymd -> $dmy"
Thanks all you guys for pitching in with various solutions. I believe few of the suggested solutions would work out for me as I correlate them to the approach I used in solving it on my own yesterday, right after posting this question.
This would output 2021-07-19 .
The $ etc. removes an initial 0 from the value of $m , in case the original month happens to be 08 or 09 which would otherwise be interpreted as invalid octal numbers.
Just FYI you can force bash computations to be in base 10 by using $((10#$m)) . It usually helps also because it will not replace single-digit 0 with nothing (as does $ ). Also, even empty variables become 0. But neither can happen in your case, so this suggestion only make the whole slightly more verbose.
$ perl -lne 'print reverse split /(-)/, s/\b\d\b/0$&/rg'
Split on dash /(-)/ , with the dashes included in the split up array but before that change the date to double digits in day and/or month.
A quick and lean solution that uses only sed:
echo 19-07-2021 | sed -E 's/^(2)-(1)-(7)$/\3-\2-\1/'
I had the same problem in many .txt files and wrote a simple one line:
@DanielWerner so the dates are written badly, can't do much about it. Seems like your solution is the right one here. My is more general for proper data.
an universal approach using python: (standalone with User Input)
python3 -c 'import datetime as dt; format="%d-%m-%Y"; print(dt.datetime.strptime(input(f"input time in format : "), format).strftime("%Y-%m-%d")); ' input time in format %d-%m-%Y: 9-2-1832 1832-02-09
for pipable just remove input query (single value conversion):
echo "9-2-1832"| python3 -c 'import datetime as dt; print(dt.datetime.strptime(input(), "%d-%m-%Y").strftime("%Y-%m-%d")); ' 1832-02-09
and finally pipable, to convert list of string-dates:
echo -e "9-2-1832\n1-2-1234\n31-12-2020"| python3 -c 'import sys,datetime;[sys.stdout.write(datetime.datetime.strptime(line.strip(), "%d-%m-%Y").strftime("%Y-%m-%d")+"\n") for line in sys.stdin]' 1832-02-09 1234-02-01 2020-12-31
the power of this unbeautiful solution is that ANY possible formats can be used for input or output, explicitly seen and easily updated if needed, unlike hardcoded regular expressions. for instance, one can convert dates to ISO YEAR-WEEK:
echo -e "9-2-1832\n1-2-1234\n31-12-2020"| python3 -c 'import sys,datetime;[sys.stdout.write(datetime.datetime.strptime(line.strip(), "%d-%m-%Y").strftime("%G-%V")+"\n") for line in sys.stdin]' 1832-06 1234-05 2020-53
for more format specifiers see date --help
convert Linux date to yyyy-MM-dd'T'HH:mm:ss'Z' format
I need to parameter-ize a datetime value with an objective of passing to a constructed URI to make a Smartsheet API call to get data (i.e. sheets) changed in last 24 hours. I want to use Linux date command as I can do something like date -d '1 day ago' %F to get the output of a day before today. How can I use the date command to convert the value to yyyy-MM-dd'T'HH:mm:ss'Z' format to get something like 2018-01-01T00:00:00-07:00 ? If the value is not in this particular format, then Smartsheet API complains:
HTTP_01 - Error fetching resource. Status: 400 Reason: Bad Request : < "errorCode" : 1018, "message" : "The value '/home/my/path/to/param_file/Sysdate' was not valid for the parameter modifiedSince.", "refId" : "1xqawd3s94f4y" >
Stack Overflow is not a code writing service. Please show your code. Since Stack Overflow hides the Close reason from you: Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Complete, and Verifiable example.
4 Answers 4
To output date in ISO 8601 format, you'll probably want to use -I[FMT] / --iso-8601[=FMT] option, if your date supports it (GNU/Linux version does). The FMT is basically a resolution, and in your case you'll want to use s for seconds:
$ date -Is 2018-03-09T09:28:14+01:00
The alternative (for POSIX date , including BSD/OSX date ) is to explicitly specify the format and output the time in UTC time zone ( -u flag):
$ date -u +"%Y-%m-%dT%H:%M:%SZ" 2018-03-09T08:28:14Z
Note the importance of -u and the explicit Z we can append in that case. Without -u , we would need to output the exact time zone in +hh:mm format, but POSIX date provides support only for time zone name output ( %Z ). GNU date extends the format set with %:z which outputs the numeric time zone, but if already using GNU date , the first approach with -Is is simpler.
How do I convert a date from Unix time to string, in a script?
To convert a date from string to Unix time, I can use date --date "2012-02-13" +%s . How do I do the opposite, from Unix time to string?
Be careful, this is just for Bash GNU. As an example on Mac should be something like date -d "2012-02-13" +%s similar but not the same
@DiegoAndrésDíazEspinoza, good warning, but to clarify, this is GNU date, but not bash. The OSX version is derived from FreeBSD, where you could use date -j 021300002012 '+%s' or date -v2012y -v2m -v13d '+%s' to get something equivalent to the command in the question.
@ghoti the main title says "bash script" maybe I got confused with that thanks anyway i made a mistake becasu i wrote "date -d "2012-02-13" +%s" which is pretty the same, anyway my point was: just becareful, bash and gnu has not the same standard
4 Answers 4
You can use date --date @datetime :
[foo@bar ~]$date --date "2012-02-13" +%s 1329055200 [foo@bar ~]$date --date @1329055200 Mon Feb 13 00:00:00 EST 2012 [foo@bar ~]$date --date @1329055200 +"%Y-%m-%d" 2012-02-13
I'm not sure where the '@xxxx' is documented though!
The man page does say "The date string format is more complex than is easily documented here but is fully described in the info documentation."
The options to the date command depend on your operating system.
[ghoti@pc ~]$ date -r 1332468005 '+%Y-%m-%d %T' 2012-03-22 22:00:05
ghoti@wopr$ date -d @1332468005 '+%Y-%m-%d %H:%M:%S' 2012-03-22 22:00:05
In other operating systems, I don't know.
If you want a solution that doesn't rely on the date command, you can use perl or gawk, if they happen to be installed on the machine you're using.
ghoti@wopr$ gawk -v when=1332468005 'BEGIN< print strftime("%Y-%m-%d %T", when); >' 2012-03-22 22:00:05