Friday, February 21, 2014

Convert UTC Date to Seconds

One of the primary ways used to compare dates is to convert the date to seconds and then do the comparison. The GNU date has an option +%s to convert the given date to seconds since epoch. But sometimes GNU date is not available in all the systems and they can't install it due to restrictions in the working environment. The next option is to use strftime() of gawk or perl. The same issue may exist with gawk/perl also, i.e. they may not have the required libs or things of that sort.

The following script which I call utc_to_sec will convert the given UTC date to seconds using the basic arithmetic.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#!/bin/bash
# utc_to_sec v1.0
# Script to convert time to seconds
# TZ should be UTC
#
# ./utc_to_sec 2014-02-20  01:42:02
# 1392860522

EXE=${0#*/}
usage()
{
  echo -e "\nConvert date to seconds since epoch"
  echo "  Usage : $EXE yyyy-mm-dd hh:mm:ss"
  echo -e "  ** TZ default UTC **\n"
  exit
}

do_everything()
{
  echo $1 $2 | awk '

    BEGIN{
      year=hh=1; mon=mm=2; day=ss=3
      m_lo[4]=m_lo[6]=m_lo[9]=m_lo[11]=1
      m_hi[1]=m_hi[3]=m_hi[5]=m_hi[7]=m_hi[8]=m_hi[10]=m_hi[12]=1
    }

    function no_of_days_in_year(input)
    {
      leap=0
      if ( input%4 == 0 ){
        if ( input%100 == 0 ){
          if ( input%400 == 0 ){
            leap=1
          }
        }
        else{
          leap=1
        }
      }

      return (365+leap)
    }

    function no_of_days_in_month(in_mon, in_year)
    {
      if(m_hi[in_mon]) val=31
      else if(m_lo[in_mon]) val=30
      else {
        val= no_of_days_in_year(in_year)>365?29:28
      }
      return val
    }

    function validate(a_date, a_time)
    {
      if( a_date[year]<1970 || a_date[mon]<=0 || a_date[mon]>12 ||
          a_date[day]<=0 || a_date[day]>31 ||
          ( m_lo[a_date[mon]] && (a_date[day]>30) ) ||
          ( a_date[mon]==2 && a_date[day]>29 ) ||
          a_time[hh]<0 || a_time[hh]>23  ||
          a_time[mm]<0 || a_time[mm]>=60 ||
          a_time[ss]<0 || a_time[ss]>=60 ){
        print -1
        exit
      }
      if(no_of_days_in_year(a_date[year])<=365 && a_date[mon]==2 && a_date[day]>28){
        print -2
        exit
      }
    }

    END{
      split($1, a_date, /-/)
      split($2, a_time, /:/)
      validate(a_date, a_time)

      #no of days in a year
      for(i=1970; i<a_date[year]; i++)
        sum_days+=no_of_days_in_year(i)

      for(i=1; i<a_date[mon]; i++)
        sum_days+=no_of_days_in_month(i, a_date[year])

      sum_days=sum_days+a_date[day]-1
      seconds = (sum_days*24*60*60) + (a_time[hh] * 3600) + (a_time[mm] * 60) + a_time[ss]
      print seconds
    }
  '
}

( [[ $# -ne 2 ]] ) && usage
seconds=$( do_everything $* )
[[ ${seconds:-0} -lt 0 ]] && echo "Invalid data..." && usage
echo $seconds

Output

root@maximus:~/scripts# ./utc_to_sec 2037-12-31 23:59:59
2145916799
root@maximus:~/scripts# date +%s -d "2037-12-31 23:59:59 UTC"
2145916799
root@maximus:~/scripts# ./utc_to_sec 2014-02-19 12:13:56
1392812036
root@maximus:~/scripts# date +%s -d " 2014-02-19 12:13:56 UTC"
1392812036
root@maximus:~/scripts# ./utc_to_sec 1970-01-01 00:00:00
0
root@maximus:~/scripts# date +%s -d "1970-01-01 00:00:00 UTC"
0
root@maximus:~/scripts# date +%s -d "1990-01-01 23:40:44 UTC"
631237244
root@maximus:~/scripts# ./utc_to_sec 1990-01-01 23:40:44
631237244

Execution time

root@maximus:~/scripts# time date +%s -d "2037-12-31 23:59:59 UTC"
2145916799

real    0m0.010s
user    0m0.004s
sys     0m0.008s

root@maximus:~/scripts# time ./utc_to_sec 2037-12-31 23:59:59
2145916799

real    0m0.023s
user    0m0.012s
sys     0m0.004s


HTH