2

I'm getting the following error

date: invalid date '23/07/2021 14:44'

when running this code

start="23/07/2021 14:44"
startSec=`date +%s -d "${start}"`

I also tried

start="23/07/2021 14:44"
startSec=`date -d "${start}" +%s`

How can I put the string date ($start) in the format '%d/%m/%Y %H:%M' and then convert it into seconds?

Thank you.

2
4

You could use string manipulation to force the input into a format recognized by date.

Or, perl where you must specify the exact input format.

start='23/07/2021 14:44'
perl -MTime::Piece -sE '
    say Time::Piece->strptime($input, "%d/%m/%Y %H:%M")->epoch
' -- -input="$start"
1627051440

string manipulation: I'm using read to break the string into variables, using characters in $IFS as field terminators:

start='23/07/2021 14:44'
IFS='/ ' read -r day month year time <<<"$start"
date -d "$year-$month-$day $time"
Fri Jul 23 14:44:00 EDT 2021

That has a bashism, the <<< here-string redirection. For a more POSIX shell:

IFS='/ ' read -r day month year time <<END_START
$start
END_START
2
  • Thank you Glenn, but I rather not use perl. Could you help me modify the string into a recognized format? I see that 2021-07-23 would be a good format, but I am not sure how to manipulate the string to get to that format.
    – lilek3
    Jul 23 at 19:53
  • 1
    Technically, <<< is a zshism (added to a few other shells including bash later). date -d is a GNUism though (added to a few other date implementations and with variations later). Jul 24 at 11:36
3

The -d option is not a standard option to date, and the standard date specification has no provision for taking a date in one format and reformat it in another format.

BSD, GNU, ast-open and busybox date have options for that, but with different syntax and of the 4, GNU date, which you seem to be using given the wording of your error message, is the only one where you can't specify the input format.

So you can't do what you're asking portably with date alone.

Here, beside resorting to proper programming languages like perl/python/tcl... you could switch from bash to zsh which has a builtin interface to strptime():

$ zmodload zsh/datetime
$ start='23/07/2021 14:44'
$ strftime -r '%d/%m/%Y %H:%M' $start
1627047840

That gives you the epoch time for that date interpreted as local time (for me, Europe/London time) at 14:44:00. If that timestamp is meant to be in UTC time, change to:

$ TZ=UTC0 strftime -r '%d/%m/%Y %H:%M' $start
1627051440

To store into a scalar variable, you can use the -s option.

$ strftime -s startSec -r '%d/%m/%Y %H:%M' $start
$ echo $startSec
1627047840

The ksh93 shell can also parse dates with its printf builtin (or its date builtin if it was built as part of ast-open, though that's rarely the case these days), though specifying the input format is a bit cumbersome, as you need to store the list of formats in a file specified with the $DATEMSK variable (as for standard getdate()), though on systems with /dev/fd/x, you can do:

$ { DATEMSK=/dev/fd/3 printf '%(%s)T\n' "$start"; } 3<<< '%d/%m/%Y %H:%M'
1627047840

(it doesn't work without the command group with my version of ksh93 (from the Ubuntu 20.04 ksh package), a bug apparently fixed in newer versions from https://github.com/ksh93/ksh).

Note that whilst 23/07/2021 14:44 is most likely unambiguous as a local time in your timezone, with that timestamp format, there are going to be ambiguous ones if your timezone implements DST.

For instance, in my Europe/London timezones there are two possible Unix epoch times that correspond to 25/10/2020 01:30:

$ strftime '%d/%m/%Y %H:%M' 1603585800
25/10/2020 01:30
$ strftime '%d/%m/%Y %H:%M' 1603589400
25/10/2020 01:30

On my system, zsh's strftime -r (the system's strptime()) picks the latter:

$ strftime -r '%d/%m/%Y %H:%M' '25/10/2020 01:30'
1603589400
2

The GNU date (and others) is not able to accept a date string in the format you want it. It mostly accept US date formats.

To have a variable format, the easiest solution is to use busybox date, it just needs to be told the format (which you already have: %d/%m/%Y %H:%M) in the -D option:

$ busybox date -u +%s -D '%d/%m/%Y' -d "23/07/2021"
1626998400

better to include the -u (utc) option so the answer is the exact time in seconds given and not the fuzzy result of the local time. Local time is fuzzy because of DST, unexpected time changes, etc.

There are some other date implementations that could accept a variable date string format (BSD). But busybox is a very small executable and quite useful as it could replace many utilities.

1

There's also dateutils with strptime. Just:

$ strptime -i '%d/%m/%Y %H:%M' -e -f '%s\n' <<<'23/07/2021 14:44'
1627051440
1
  • Note that it assumes the input is UTC time. -l switches to local time, but then it doesn't seem to work for summer timestamps in those timezones that implement DST. Jul 24 at 11:25
1

Since you have GNU date you also have or can get GNU awk:

$ start="23/07/2021 14:44"
$ awk -v start="$start" 'BEGIN{split(start,d,"[/ :]"); print mktime(d[3]" "d[2]" "d[1]" "d[4]" "d[5]" 0")}'
1627069440

the above uses your TZ setting, or for UTC you can just set the UTC flag in mktime():

$ awk -v start="$start" 'BEGIN{split(start,d,"[/ :]"); print mktime(d[3]" "d[2]" "d[1]" "d[4]" "d[5]" 0",1)}'
1627051440

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.