Date functions: datenum


datenum() #@ Convert DATE to day NUMBER in C.E.
{         #@ USAGE: datenum [DATE [VAR]]
  local _y _m _d junk var num base=-306 ymd _dn
  local invalid="Invalid date" jg_m=175209 j2g_skip=11
  local j2g='Invalid date (Julian to Gregorian changeover)'

  [[ $1 == -- ]] && shift

  if (( $# == 0 )) || is_var "$1"
  then
    today ymd
  elif is_date "$1"
  then
    ymd=$1
    is_var "$2"
  else
    err 1 "$invalid: $1" || return
  fi
  read _y _m _d junk <<< "${ymd//[!0-9]/ }"
  if [[ $_y == [0-9][0-9][0-9][0-9][0-1][0-9][0-3][0-9] ]]
  then
    _d=${_y: -2}
    _m=${_y:4:2}
    _y=${_y:0:4}
  fi
  _d=${_d#0}
  _m=${_m#0}
  _m=$((12 * _y + _m - 3))      ## Calculate number of months from March
  _y=$(( _m / 12))              ## and adjust year if necessary
  _ly=$(( _y/4-_y/100+_y/400 )) ## number of leap days
  _dn=$(( (734 * _m + 15) / 24 -  2 * _y + _ly + _d + base ))
  if [[ $_dn -ge 639786 && $_dn -le 639795 ]]
  then ## date was swallowed by Julian to Gregorian changeover
    err 1 "$j2g" || return
  elif [[ $_dn -lt 1 ]]
  then
    err 2 "$invalid $ymd" || return
  fi
  (( _dn > 639796 )) && _dn=$(( _dn - j2g_skip ))
  [[ $var ]] && printf -v "$var" %d "$_dn" || printf '%s\n' "$_dn"
}

The datenum function can be called in four different ways:

  1. datenum
  2. datenum VAR
  3. datenum DATE
  4. datenum DATE VAR

The date, if supplied, must be in the ISO format: YYYY-MM-DD, though there is some lattitude. Any non-numeric separator may be used, and leading zeroes may be omitted.

If no date is supplied, the current date is used.

If no variable name is supplied, the result is printed to stdout.

The function is valid from 1-01-01 (using the Julian calendar until the changover to Gregorian in September 1752) until long after the sun explodes (or until the Gregorian calendar is replaced by some other system).