The goal is to convert the date format %Y-%m-%s %H:%M:%S %z
to seconds since EPOCH with POSIX tools.
I simplified the code because I have a problem with the calculation using the formula taken from Seconds Since the Epoch; I can't get the result that GNU/BSD date
provides:
#!/bin/bash
TZ=UTC 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"
$1 -= 1900
epoch = $5 $4*60 $3*3600 int($2 ($1-70)*365 ($1-69)/4 - ($1-1)/100 ($1 299)/400)*86400
print "result:", epoch
}
'
date: 2022-10-21 22:02:56 0000
expect: 1666389776
result: 1666476176
What could be the problem?
CodePudding user response:
@Fravadona
: you don't need to hard-code in the cumulative julian days by month-ends :
function ____(__,___,_)
{
# __| mm:
# ___| yyyy:
# |--> cumulative julian days by month-end
return \
((__=int(__))<(_ =_^=_<_) ? !_ : -_ (___=="" ||
(___=int(___)) % (_ _) ? !_ : \
! (___%((_ _)*((_ _*_*_)^_)^!(___%(_ _*_*_)^_)))))\
\
( _ _^_--)*__\
\
substr((_=((--_ ( _ _*_*_)^_)^_ _)*(_^ _ _)) \
(_ (((_ =_^=!_)* _)^ _* _*(_- - _ _* _)- --_)),__,_^(_<_))
}
Assuming pre-Gregorian years follow the same leap-year rules, and assuming month input to be [1, 12]
and year-input is [1, 2^53)
, this function alone should return the exact cumulative # of julian days till end of month, inclusive, for any year-month combination
(missing year-input defaults to being treated as non-leap year):
1900 1 31 2000 1 31 2002 1 31 2023 1 31 2024 1 31
1900 2 59 2000 2 60 2002 2 59 2023 2 59 2024 2 60
1900 3 90 2000 3 91 2002 3 90 2023 3 90 2024 3 91
1900 4 120 2000 4 121 2002 4 120 2023 4 120 2024 4 121
1900 5 151 2000 5 152 2002 5 151 2023 5 151 2024 5 152
1900 6 181 2000 6 182 2002 6 181 2023 6 181 2024 6 182
1900 7 212 2000 7 213 2002 7 212 2023 7 212 2024 7 213
1900 8 243 2000 8 244 2002 8 243 2023 8 243 2024 8 244
1900 9 273 2000 9 274 2002 9 273 2023 9 273 2024 9 274
1900 10 304 2000 10 305 2002 10 304 2023 10 304 2024 10 305
1900 11 334 2000 11 335 2002 11 334 2023 11 334 2024 11 335
1900 12 365 2000 12 366 2002 12 365 2023 12 365 2024 12 366
CodePudding user response:
By reading the specification thoroughly I found two problems:
tm_yday
value should bedate ' %j'
minus one:
days since January 1 of the year (tm_year)
- Integer arithmetic is needed:
The divisions in the formula are integer divisions
The following code now works; I also added the 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 basic mktime
implementation for POSIX awk
that converts a date in the format %Y %m %d %H %M %S %z
to epoch time:
function mktime(datespec, arr,tm_year,tm_yday,tm_hour,tm_min,tm_sec,zoffset) {
if ( !(1 in _monthYearDay) )
split("0 31 59 90 120 151 181 212 243 273 304 334",_monthYearDay," ")
split(datespec,arr," ")
arr[2] = int(arr[2])
tm_year = arr[1] - 1900
tm_yday = \
arr[3] - 1 _monthYearDay[arr[2]] \
(arr[2] > 2 && ((arr[1]%4 == 0 && arr[1]0 != 0) || arr[1]@0 == 0))
tm_hour = arr[4]
tm_min = arr[5]
tm_sec = arr[6]
zoffset = \
int(substr(arr[7],1,1) "1") * \
(substr(arr[7],2,2)*3600 substr(arr[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 - \
zoffset
}