How I can format a date in shell - bash

I want to convert a custom date format DD-%%%-YYYY to a standard one: YYYYMMDD
Possible values of %%% are:
Jan Fev Mar Avr Mai Jun Jui Aou Sep Oct Nov Dec
Assuming the input is a bash variable, how do I transform it to the standard format?
Example:
$ fr_date='09-Aou-2018'
$ # [transformation]
# sql_date should now contain 20180809
$ echo "$sql_date"
20180809

You can use the date utility.
fr_date='09-Aug-2018'
sql_date="$(date --date=$fr_date +%Y%m%d)"
echo $sql_date
20180809
Please also refer to the date man page for more information.
Additionally, date does not support custom locales, format must be locale independent. Try to store dates as simple Unix epoch.

Solution 1: Rewrite the french months into english, then use date to read and format it:
Pure bash:
tmp=${fr_date/Fev/Feb} tmp=${tmp/Avr/Apr} tmp=${tmp/Mai/May}
tmp=${tmp/Jui/Jul} tmp=${tmp/Aou/Aug}
sql_date=$(date +%Y%m%d -d "$tmp")
With sed:
tmp=$(sed 's/Fev/Feb;s/Avr/Apr;s/Mai/May;s/Jui/Jul;s/Aou/Aug' <<<"$fr_date")
sql_date=$(date +%Y%m%d -d "$tmp")
Solution 2: Assign to each month its corresponding number:
#!/bin/bash
# Requires bash 4 for associative arrays
declare -A month_map=(
[Jan]=01 [Fev]=02 [Mar]=03 [Avr]=04 [Mai]=05 [Jun]=06
[Jui]=07 [Aou]=08 [Sep]=09 [Oct]=10 [Nov]=11[Dec]=12
)
IFS=- read -r day month year <<<"$fr_date"
sql_date=$year${month_map[$month]}$day

Related

Date arithmetic not working properly in macos bash

I am trying to subtract 5 minutes from date but its giving unexpected output.
$ date -j -f "%Y/%m/%d %H:%M:%s" -v "-5M" "2021/03/01 09:11:14"
Thu Jan 1 05:25:14 IST 1970
Please suggest the correction.
Converting my comment to answer so that solution is easy to find for future visitors.
This date command should work on BSD date:
date -j -f "%Y/%m/%d %H:%M:%S" -v "-5M" "2021/03/01 09:11:14"
Issue in your command was use of .%s instead of .%S for the second component.

bash substring of command instead of variable?

I can do this in bash:
foo=bar
echo ${foo:0:2}
which prints 'ba' (the first two characters of 'bar').
Now I want to do the same with a script/command output instead of a variable, like so:
echo ${$(date):0:10}
But then I get an error: "bad substitution".
Of course I can use an intermediary variable:
foo=$(date)
echo ${foo:0:10}
But is there a way to do this directly?
P.S. The date command is just an example, this is not about generating some date string in a particular format. Just the general concept of taking a substring from an arbitrary shell command output.
No, BASH syntax doesn't allow any kind of nesting. You can do so using external utilities like cut:
date | cut -c 1-10
Wed Jun 13
To replace date:
$ date
Wed Jun 13 14:57:38 EEST 2018
you can use printf:
$ printf "%(%a %b %d%n)T"
Wed Jun 13
man strftime for more format modifiers.
If you want the output Wed Jun 13 for today's date (which this is), then
$ date +'%a %b %e'
Wed Jun 13
See the manual for date and/or for strftime on your system.

Convert date String to number on Solaris shell script gives No such file or directory

I have a date in the format "Thu Sep 22 3:50 2016", and I want to convert it to format: "2016-09-22"
I tried the following shell script, which works fine for 'date', but gives error for user specified string: (I am working on Solaris platform). Any inputs will be helpful.
Input:
#!/usr/bin/sh
mydate="Thu Sep 22 3:50 2016"
echo `date +"%Y-%m-%d"`
echo `$mydate +"%Y-%m-%d"`
Output
./testShell.sh
**2016-09-22**
./testShell.sh[6]: Thu: not found **[No such file or directory]**
Any pointers please?
Under Solaris 11, many GNU utilities are available under the /usr/gnu/bin directory so you just need to slightly modify your script to either use the full path the the GNU variant :
#!/bin/sh
mydate="Thu Sep 22 3:50 2016"
date +"%Y-%m-%d"
/usr/gnu/bin/date -d "$mydate" +"%Y-%m-%d"
or use the already existing symlink prefixed by g (for GNU):
gdate -d "$mydate" +"%Y-%m-%d"
or set your PATH to look at /usr/gnu/bin first and keep your script unchanged.
PATH=/usr/gnu/bin:$PATH
You can try something like this;
#!/bin/bash
mydate="Thu Sep 22 3:50 2016"
date +"%Y-%m-%d"
date -d "$mydate" "+%Y-%m-%d"

date: illegal option -- d, Find difference between two dates

I am trying to convert timestamps read from a file from string to date format so that I can find the difference of 2 dates/timestamps. most of the threads/discussions on web show usage of date argument '-d' to convert the string to epoch or to find the difference of two timestamps Find difference between two dates in bash
But it looks like my environment/OS doesn't support -d date argument. Below are the details of my env:
bash --version
GNU bash, version 3.2.52(1)-release (i386-pc-solaris2.10)
Copyright (C) 2007 Free Software Foundation, Inc.
uname -a
SunOS s01***** 5.10 Generic_147148-26 i86pc i386 i86pc
Sample dates read from file:
START_TIME="09/03/16 - 01:04:56"
END_TIME="09/03/16 - 05:10:44"
Code that I have tried
I have tried to mimic the below code from Find difference between two dates in bash
!# /usr/bin/sh
date1="Sat Dec 28 03:22:19 2013"
date2="Sun Dec 29 02:22:19 2013"
date -d #$(( $(date -d "$date2" +%s) - $(date -d "$date1" +%s) )) -u +'%H:%M:%S'
bash test.sh
date: illegal option -- d
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
date: illegal option -- d
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
test.sh: line 5: - : syntax error: operand expected (error token is " ")
I don't think syntax error on line 5 is the main culprit cause I didnot find option -d in my date's man page.
In response to comments:
>>> date --version
date: illegal option -- version
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
>>> date --help
date: illegal option -- help
usage: date [-u] mmddHHMM[[cc]yy][.SS]
date [-u] [+format]
date -a [-]sss[.fff]
>>> echo $0
bash
Even these arguments are not supported. Apologies if I am committing any silly mistake.
Could someone please give me the equivalent of -d for the env details shared above or a way to find the difference between two dates without using -d.
Thanks in advance
awk mktime has a decent chance of existing on your system:
#!/bin/bash
START_TIME="09/03/16 - 01:04:56"
END_TIME="09/03/16 - 05:10:44"
echo -e "$START_TIME\n$END_TIME" |
tr '/:-' ' ' |
awk '{print "20"$3" "$2" "$1" "$4" "$5" "$6}' |
awk '{printf "%s ", mktime($0)}' |
awk '{print $2 - $1}'
explanation:
echo both time strings
tr converts 09/03/16 - 01:04:56 to 09 03 16 01:04:56
first awk changes 09 03 16 01 04 56 to 2016 03 09 01 04 56
second awk converts 2016 03 09 01 04 56 to epoch time: 1457514296 and prints both on one line: 1457514296 1457529044
third awk subtracts first from second, giving difference in seconds: 14748
the awks could also easily be merged, but here i kept each separate for clarity.
According to the POSIX standard, date does not do date and time math for you. It gets or sets the system date and time, possibly with timezone adjustments. See date(1) and note the lack of a -d option (or indeed, any interesting options!).
The question becomes "How do we do date and time math without date?" The timestamps you provided do not have time zone information, so the usual behavior is to assume local time. Local time is bad. Seriously. Some time zones have crazy discontinuities or didn't meaningfully exist hundreds of years ago (e.g. most American time zones). So while you might be able to hack together a custom solution that works in your particular part of the world during the recent past, it simply will not be robust.
If you can get your timestamps into Unix time, you can just subtract them, and that will give you a mostly but not entirely correct answer. Unfortunately, to the best of my knowledge, that can't be done at the command line. Unix provides strptime(3) to do this from C (and from there you go on to mktime(3), as shown in this answer), but I don't believe there is any fully standard utility which provides a command-line interface for this. You may have to write and compile your own.
Correcting for leap seconds is difficult in the extreme because, to the best of my knowledge, POSIX has never provided a standard interface for finding out when leap seconds have happened in the past. Such an interface would require internet connectivity in order to remain up-to-date, which is likely a non-starter for a number of implementations. Without knowing more about your system and its capabilities, I simply cannot begin to guess at what will or will not work for your use case.

Convert a date in Shell

This is a hart one, how do I convert a date like
12-23-11 13:37
In something like(seconds should always be 00)
Fri Dec 23 13:18:58 CET 2011
?
With gnu date 5.97, you can do:
$ date -d '11-12-23 13:37'
to get what you want, so all you need to do is massage your input.
Since gnu date is not ubiquitous, here's a quick perl script that does what you want:
$ echo 12-23-11 13:37 |
perl -MTime::Local -wnE '
y/-:/ /;
#F=split;
say scalar localtime timelocal( 0, $F[4], $F[3], $F[1], $F[0] - 1,$F[2]);
'
Fri Dec 23 13:37:00 2011
(Requires perl 5.10 for -E and say, but should work in older perl using -e and print.)
If this is a script for yourself and not something that will have to run in a million different environments, then depending on what version of date you have available, you should be able to use it.
Read the man page for your particular version of date. For example, if it's the version documented at http://ss64.com/bash/date.html, you can use --date for the input string, etc.
On Mac OS X, use the -f option to specify the input format, the -j option so that it doesn't try to set the date, and with specifying the output format on the command line.

Resources