Home > Net >  Convert a date string to EPOCH time with POSIX tools
Convert a date string to EPOCH time with POSIX tools

Time:10-22

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 be date ' %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
}
  • Related