Code listing below.  Click here for program description.

You can cut-n-paste the code below or you can
access an ASCII text version of the code (which you can Save As...) by clicking here.


#!/usr/bin/nawk -f
# SccsId[] = "@(#)epoch_time_awk.awk 1.3 08/03/04"
#----------------------------------------------------------------------#
#                            epoch_time.awk                            #
# -------------------------------------------------------------------- #
#                                                                      #
#   Copyright (c) 2006-2008 by Bob Orlando.  All rights reserved.      #
#                                                                      #
#   Permission to use, copy, modify and distribute this software       #
#   and its documentation for any purpose and without fee is hereby    #
#   granted, provided that the above copyright notice appear in all    #
#   copies, and that both the copyright notice and this permission     #
#   notice appear in supporting documentation, and that the name of    #
#   Bob Orlando not be used in advertising or publicity pertaining     #
#   to distribution of the software without specific, written prior    #
#   permission.  Bob Orlando makes no representations about the        #
#   suitability of this software for any purpose.  It is provided      #
#   "as is" without express or implied warranty.                       #
#                                                                      #
#   BOB ORLANDO DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS           #
#   SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY      #
#   AND FITNESS.  IN NO EVENT SHALL BOB ORLANDO BE LIABLE FOR ANY      #
#   SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES          #
#   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER    #
#   IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,     #
#   ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF     #
#   THIS SOFTWARE.                                                     #
#                                                                      #
#----------------------------------------------------------------------#
BEGIN \
{
  #----------------------------#
  # Process options -h and -z. #
  #----------------------------#
  for (n=1; n<ARGC; n++)
  {
    if (ARGV[1] == "-z") # Timezone option
    {
      if (ARGV[2] !~ /^-?[0-9]+$/)
      {
        Usage_and_exit("Time zone must be integer (represents hours)!")
      }
      tz_adj  = ARGV[2] * 3600
      ARGV[2] = ""
      ARGV[1] = ""
      shift_ARGV()
    }
    else if (ARGV[1] == "-h") # Help
    {
      Usage_and_exit()
    }
  }

  if (ARGV[1] == "") # No options/arguments returns current epoch seconds.
  {
    print secs_since_epoch() # Since Jan. 1, 1970 00:00:00
    exit 0
  }

  julian_table()

  #----------------------------------------------------------#
  # Using user supplied yyyymmddhhmiss (+\%Y\%m\%d\%H\%M\%S) #
  #----------------------------------------------------------#
  if (ARGV[1] ~ /[0-9][0-9][0-9][0-9][0-1][0-9][0-3][0-9][0-2][0-9][0-5][0-9][0-5][0-9]/)
  {
    print secs_since_epoch()
    exit 0
  }

  #----------------------------------------------------------#
  # Looks like epoch seconds argument.                       #
  #----------------------------------------------------------#
  epoch_sss = ARGV[1] + tz_adj # ET timezone adjustment = 5 (hours)
  days = sprintf("%d",(epoch_sss / 86400))
  sss  = sprintf("%d",(epoch_sss % 86400))
  sub(/^-/,"",sss) # Remove any sign.
  int(sss)

  ddddd = (basedate(1970,01,01) + days)

  yyyymmdd = ddddd_to_yyyymmdd(ddddd)

  hh  =  (sss / 3600.000000)
  sss =  (sss % 3600.000000)
  mm  =  (sss / 60.000000)
  ss  =  (sss % 60)
  printf("%8s%02d%02d%02d\n", yyyymmdd, hh, mm, ss)

  exit 0
} # E.O.BEGIN

#======================================================================#
#                     U S E R    F U N C T I O N S                     #
#                        (in alphabetical order)                       #
#----------------------------------------------------------------------#
function basedate(                                               \
                   YYYY,MM,DD,                                   \
                   yyyy,cc,ddddd,leapdays,cc_leapdays,accum_days \
                 )
# -------------------------------------------------------------------- #
# Globals: julian_days[] and julian_leap[]                             #
#   Calls: leap_year()                                                 #
#----------------------------------------------------------------------#
{
  julian_table() # Start with a new julian table.

  #--------------------------------------------------------#
  # First, decrement the year and get the number of days   #
  # for all the years preceeding it (in the Common Era).   #
  #--------------------------------------------------------#
  yyyy = YYYY
  if ((yyyy - 1) > 0)
  {
    yyyy--
    cc          = int(yyyy / 100)
    ddddd       = yyyy * 365
    leapdays    = int(yyyy / 4)
    cc_leapdays = (cc > 0) ? (int(cc / 4)) : 0
    ddddd       = ddddd + leapdays - cc + cc_leapdays
    yyyy++
  }
  ddddd += DD

  #--------------------------------------------------------#
  # Add the previous month YTD days to our ddddd.  If it   #
  # is a leap year, assign julian_leap[] to accum_days[].  #
  # Then add in YTD days for previous months of this year  #
  #--------------------------------------------------------#
  if (leap_year(yyyy))
    for (i=0; i<13; i++) accum_days[i] = julian_leap[i]
  else
    for (i=0; i<13; i++) accum_days[i] = julian_days[i]
  ddddd += accum_days[MM += 0] # 'MM += 0' removes leading zero

  return (ddddd)
} # function basedate

#----------------------------------------------------------------------#
function ddddd_to_yyyymmdd(DDDDD,                   ddddd,yyyy,mmdd,i) #
# -------------------------------------------------------------------- #
# Globals: julian_days[] and julian_leap[].                            #
#----------------------------------------------------------------------#
{
  yyyy = ddddd = reduce(DDDDD)
  sub(/-.*$/,"", yyyy); yyyy  += 0 # "+= 0" ensures numeric context
  sub(/^.*-/,"",ddddd); ddddd += 0

  if (leap_year(yyyy))
    for (i in julian_leap)
      julian[i] = julian_leap[i]
  else
    for (i in julian_days)
      julian[i] = julian_days[i]

  #------------------------------------------------------------#
  # Reduce ddddd to month and day (we already have the year).  #
  #------------------------------------------------------------#
  for (i=13; i>0; i--)
  {
    if (julian[i] < ddddd)
    {
    # if (leap_year(yyyy) && i > 2) ddddd++
      ddddd -= julian[i]
      mmdd = sprintf("%02d%02d", i, ddddd)
      break
    }
  }

  return (yyyy""mmdd)
} # function ddddd_to_yyyymmdd

#----------------------------------------------------------------------#
function julian_table()
#----------------------------------------------------------------------#
{
  split("0 31 59 90 120 151 181 212 243 273 304 334 365", julian_days)
  split("0 31 60 91 121 152 182 213 244 274 305 335 366", julian_leap)
}

#----------------------------------------------------------------------#
function leap_year(YYYY) # YYYY = year.  Returns true|false (1|0).
#----------------------------------------------------------------------#
{
  return (((YYYY%4 == 0 && YYYY%100 != 0) || (YYYY%400 == 0)) ? 1 : 0)
} # function leap_year

#----------------------------------------------------------------------#
function reduce(DDDDD,           yyyy,ddddd,cc,leapdays,cc_leapdays,i) #
#----------------------------------------------------------------------#
{
  for (i=1; ; i++)
  {
    yyyy        = int(DDDDD / 365) - i
    ddddd       = DDDDD - yyyy * 365
    cc          = int(yyyy / 100)
    leapdays    = int(yyyy / 4)
    cc_leapdays = (cc > 0) ? (int(cc / 4)) : 0
    leapdays    = leapdays - cc + cc_leapdays
    ddddd      -= leapdays
    yyyy++
    if (ddddd > 0) break
  }
  return yyyy"-"ddddd
} # function reduce

#----------------------------------------------------------------------#
function secs_since_epoch(ddddd,ddddd_yyyy,sss)
#----------------------------------------------------------------------#
{
  #-------------------------------------------------------#
  # Calling date with "+\%Y\%m\%d" (with escaped percent  #
  # signs) avoids hassles with SCCS and percentYpercent.  #
  #-------------------------------------------------------#
  if (ARGV[1] == "")
    "date +\%Y\%m\%d\%H\%M\%S" | getline yyyymmddhhmiss
  else                                           # yyyymmddhhmiss
    yyyymmddhhmiss = sprintf("%14s",substr(ARGV[1]"00000000000000",1,14))

  yyyy = substr(yyyymmddhhmiss, 1,4)
  mm   = substr(yyyymmddhhmiss, 5,2)
  dd   = substr(yyyymmddhhmiss, 7,2)
  hh   = substr(yyyymmddhhmiss, 9,2)
  mi   = substr(yyyymmddhhmiss,11,2)
  ss   = substr(yyyymmddhhmiss,13,2)

  ddddd = basedate(yyyy,mm,dd)
  ddddd_yyyy = basedate(1970,01,01)
  ddddd -= ddddd_yyyy

  sss = (ddddd * 86400) + (hh * 3600) + (mi * 60) + ss
  sss += tz_adj

  return sss
} # function secs_since_epoch

#----------------------------------------------------------------------#
function shift_ARGV(i,j,k)
#----------------------------------------------------------------------#
{
  k = ARGC
  for (i=1; i<=ARGC; i++)
  {
    if (ARGV[i] == "")     # If the argument is empty, see is we
    {                      # can shift the next one down to it.
      for (j=i+1; j<=k; j++)
      {
        if (ARGV[j] == "") # If the next one is empty, try the one
          continue         # following that.
        ARGV[i] = ARGV[j]  # Once we find an argument, move it down,
        ARGV[j] = ""       # null where it was, decrement arg counter,
        break              # can do this with the next argument.
      }
    }
  }

  for (i=1; i<=k; i++)     # Adjust ARGC
    if (ARGV[i] == "")     # If the argument is empty,
      ARGC--               # decrement ARGC
  return ARGC
} # function shift_ARGV

#----------------------------------------------------------------------#
function Usage_and_exit(SPECIFIC_ERROR)
#----------------------------------------------------------------------#
{
  if (SPECIFIC_ERROR) print "\n"SPECIFIC_ERROR

  print "\nPurpose: Return current Unix Epoch time (ssssssssss) or",
      "\n         convert specified Unix Epoch time to yyyymmddhhmiss",
      "\n         datetime, or convert yyyymmddhhmiss datetime to",
      "\n         Unix Epoch seconds.\n",
      "\n  Usage: epoch_time.awk -h -z [-]hh [yyyymmddhhmiss|ssssssssss]",
      "\n    e.g. epoch_time.awk [no options or arguments]",
      "\n         epoch_time.awk 20070121132053",
      "\n         epoch_time.awk 1169385653"
  exit 1
}


#======================================================================#
#                       D O C U M E N T A T I O N                      #
#======================================================================#
#                                                                      #
#      Author: Bob Orlando (Bob@OrlandoKuntao.com)                     #
#                                                                      #
#        Date: May 2, 2006                                             #
#                                                                      #
#  Program ID: epoch_time.awk                                          #
#                                                                      #
#     Purpose: Return current Unix Epoch time (ssssssssss) or          #
#              convert an Epoch seconds argument (seconds since        #
#              January 1, 1970 00:00:00) into yyyymmddhhmiss datetime  #
#              form -- OR -- convert yyyymmddhhmiss datetime argument  #
#              into Unix Epoch seconds.                                #
#                                                                      #
#              The script uses the host's time zone, but adjustments   #
#              can be made via the time zone (-z) option.  The optarg  #
#              is specified in hours and may be specified negatively.  #
#                                                                      #
#       Usage: [gn]awk -f epoch_time.awk -- -h -z [-]hh \              #
#                                        [yyyymmddhhmiss|ssssssssss]   #
#                                                                      #
#                 --             = This option must come first for it  #
#                                  tells Awk to pass on all options    #
#                                  that follow to the script itself.   #
#                 -h             = Help (usage brief)                  #
#                 -z [-]hh       = Timezone adjustment in hours        #
#                 yyyymmddhhmiss = Converts to epoch seconds           #
#                 ssssssssss     = Converts to yyyymmddhhmiss datetime #
#                                                                      #
#    Examples: 1. epoch_time.awk # No options or argument              #
#              2. epoch_time.awk          1164718505                   #
#              3. epoch_time.awk          20061128125505               #
#              4. epoch_time.awk -- -z  2 1164718505                   #
#              5. epoch_time.awk -- -z  2 20061128125505               #
#              6. epoch_time.awk -- -z -2 1164718505                   #
#                                                                      #
#    Displays: 1. 1164718505     # Current datetime to Epoch seconds   #
#              2. 20061128125505 # Epoch seconds to yyyymmddhhmiss     #
#              3. 1164718505     # yyyymmddhhmiss to epoch seconds     #
#              4. 20061128145505 # Adjusted by 2 hour timezone         #
#              5. 1164725705     # Adjusted by 2 hours                 #
#              6. 20061128105505 # Adjusted by -2 hours                #
#                                                                      #
#    Modified: 2008-03-04 Bob Orlando                                  #
#                 v1.3  * Bug fix: Thanks to Mahir Aydin for           #
#                         pointing this out and identifying the fix.   #
#                         The following line was commented out from    #
#                         ddddd_to_yyyymmdd function:                  #
#                                                                      #
#                           if (leap_year(yyyy) && i > 2) ddddd++      #
#                                                                      #
#                         The line was "adding one day if the year     #
#                         is a leap year and the month is after        #
#                         February, which is unnecessary, since you    #
#                         already have two separate tables for leap    #
#                         years and normal years, and [you] use the    #
#                         correct one."                                #
#                                                                      #
#              2007-04-25 Bob Orlando                                  #
#                 v1.2  * Bug fix: Timezone option (-z).  Changed      #
#                           if (tz_adj !~ /^[0-9]+$/)                  #
#                         to                                           #
#                           if (ARGV[2] !~ /^-?[0-9]+$/)               #
#                                                                      #
#              2006-05-02 Bob Orlando                                  #
#                 v1.1  * Initial release.                             #
#                                                                      #
#----------------------------------------------------------------------#

©Copyright Bob Orlando, 2006-2008.
All rights reserved.
http://www.OrlandoKuntao.com
E-mail: Bob@OrlandoKuntao.com
Last update:  Mar. 4, 2008
by Bob Orlando