By reading the specification thoroughly I found two problems:
tm_yday
value should be date +%j
minus one:
days since January 1 of the year
- Integer arithmetic is needed:
The divisions in the formula are integer divisions
The following code now works; I also added an optional support for timezones in %z
format:
#!/bin/bash
date $'+ date: %Y-%m-%d %T %z \nexpect: %s \n %Y %j %H %M %S %z' |
awk '
NR <= 2 { print; next }
{
# $0: "YYYY jjj HH MM SS zzzzz"
tm_year = $1 - 1900
tm_yday = $2 - 1
tm_hour = $3
tm_min = $4
tm_sec = $5
zoffset = \
int(substr($6,1,1) "1") * \
(substr($6,2,2)*3600 + substr($6,4,2)*60)
epoch = \
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + \
(tm_year-70)*31536000 + int((tm_year-69)/4)*86400 - \
int((tm_year-1)/100)*86400 + int((tm_year+299)/400)*86400 - \
zoffset
print "result:", epoch
}
'
date: 2022-10-22 01:19:23 +0200
expect: 1666394363
result: 1666394363
ASIDE
Here's a sample implementation of GNU's mktime
function for POSIX awk
. It converts a date in the format %Y %m %d %H %M %S %z
to seconds since EPOCH. When %z
isn't provided, it assumes that the timezone is UTC
(ie. +0000
):
function posix_mktime(datespec, a,tm_year,tm_yday,tm_hour,tm_min,tm_sec,tm_zoff) {
# One-time initialization during the first execution
if ( !(12 in _MKTIME_monthToDaysSinceJan1st) )
split("0 31 59 90 120 151 181 212 243 273 304 334", _MKTIME_monthToDaysSinceJan1st, " ")
# datespec: "YYYY mm dd HH MM SS zzzzz"
split(datespec, a, " ")
tm_year = a[1] - 1900
tm_yday = _MKTIME_monthToDaysSinceJan1st[a[2]+0] + a[3] - 1
if (a[2] > 2 && ((a[1]%4 == 0 && a[1]%100 != 0) || a[1]%400 == 0))
tm_yday += 1
tm_hour = a[4]
tm_min = a[5]
tm_sec = a[6]
tm_zoff = (substr(a[7],1,1) "1") * (substr(a[7],2,2)*3600 + substr(a[7],4,2)*60)
return tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 \
+ (tm_year-70)*31536000 + int((tm_year-69)/4)*86400 \
- int((tm_year-1)/100)*86400 + int((tm_year+299)/400)*86400 \
- tm_zoff
}