Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

awk with dates before 1970

Tags:

date

bash

awk

from https://www.gnu.org/software/gawk/manual/html_node/Time-Functions.html I understand that gawk has only 2 functions to work on date/time mktime and strftime.

So, I can parse any date using mktime that return a long, so I can make any math op, and so I can format the output desired with strftime

This works like a charm for any date after "1970 01 01 00 00 00"

Using awk, how can I format dates before 1970 ?

$ awk 'BEGIN{t=mktime("1970 01 01 00 00 00"); print t; print strftime("%Y-%m-%d", t) }'
10800
1970-01-01
$ awk 'BEGIN{t=mktime("1960 01 01 00 00 00"); print t; print strftime("%Y-%m-%d", t) }'
-315608400
awk: cmd. line:1: (FILENAME=- FNR=1) fatal: strftime: second argument less than 0 or too big for time_t
like image 646
ton Avatar asked Apr 09 '15 15:04

ton


1 Answers

Unfortunately, as you've seen, gawk just can't do this directly. The gawk manual says:

All known POSIX-compliant systems support timestamps from 0 through 2^31 - 1, which is sufficient to represent times through 2038-01-19 03:14:07 UTC. Many systems support a wider range of timestamps, including negative timestamps that represent times before the epoch.

The manual doesn't say what strftime() does if given an out-of-range date.

But even on my system, which does behave sensibly for negative time_t values, gawk's strftime() function doesn't support them (though mktime() does), and so can't handle dates before 1970. I consider this to be a bug in gawk.

(My advice would be to use Perl instead of Awk, but that doesn't answer the question you asked.)

In principle, you could reinvent the wheel by re-implementing a function like strftime() in awk. But that would be overkill.

If your system has a working GNU coreutils date command, you can invoke it from gawk. Using your example of Jan 1, 1960:

$ cat 1960.awk
#!/usr/bin/awk -f

BEGIN {
    timestamp = mktime("1960 00 00 00 00 00")
    print "mktime() returned " timestamp

    if (0) {
        # This doesn't work
        s = strftime("%Y-%m-%d %H:%M:%S", timestamp)
        print "strftime() returned ", s
    }
    else {
        # This works
        "date '+%Y-%m-%d %H:%M:%S' -d @" timestamp | getline t
        print "The date command printed \"" t "\""
    }
}
$ ./1960.awk
mktime() returned -318355200
The date command printed "1959-11-30 00:00:00"
$

(I gave up on figuring out the sequence of quotes and backslashes needed to do this as a one-liner from a shell prompt.)

This probably makes sense if you have a large existing awk program and you need to add this one feature to it. But if you're not stuck with doing this in awk, you might consider using something else; awk may not be the right tool for what you're trying to accomplish.

Or, if you're really ambitious, you could modify the gawk sources to handle this case correctly.

like image 160
Keith Thompson Avatar answered Sep 26 '22 08:09

Keith Thompson