Using Julian days for calendar calculations

Q: I need to determine days between arbitrary dates. I’ve seen epoch seconds as a possible way but this is limited 1970 through 2038 and converting between MDY forms and epoch seconds is fairly tricky.

A: If all you need are the number of days, the simple way is to use the Julian calendar. Note that Julian term is often confused with day of the year (1-365) but in astronomy, the Julian calendar starts Jan 1, 4713 BC. The definition of the calendar includes fractional values to indicate time of day, but for your requirement, just the integer value (days) is sufficient.

Here’s a calculator from the US Naval Observatory with a background on how to convert between Gregorian calendars and Julian calendars.

http:///service-it-direct.s7.devpreviewr.com.usno.navy.mil/USNO/astronomical-applications/data-services/cal-to-jd-conv

But for your requirement, there is a math procedure to convert in either direction. Here is a reference to the actual math:

http://pmyers.pcug.org.au/General/JulianDates.htm#G2J

But the two functions listed below will accomplish the math for you. They both assume the US calendar date format: MM DD YYYY. Jdate accepts a 3 character month name (Jan-Dec, upper or lower or mixed case) and Jmdy has an option (-m) to display the month name rather than the number.

Jdate is useful in decoding dates (like syslog.log) which are typically in this format:

Nov 30 08:59:09

So to determine the days between two dates:

J1=$(Jdate Nov 30 2011)
J2=$(Jdate Dec 26 2010)
DAYS=$(($J1 – $J2))
or
DAYS=$(($(Jdate Nov 30 2011) – $(Jdate Dec 26 2010)))

(ans: 339)

Here are the two routines. You can paste them into your scripts or use them from a function library by exporting FPPATH=/yourFunctionDirectory

###########

# Jdate #

###########

function Jdate

{

# Usage: Jdate <month_name> <day> <yearYYYY>

# where month is the 3-char month name or 1-12 month number,

# day and year are integers, year is 4 digits.

# Calculate Julian day from MONTH_NAME DAY YEARYYYY

# It is the number of days since 4713 BC

# The Julian date is the ideal way to compute day differences given calendar dates

# The exact Julian day technically starts at 0.5 Universal Time (noon)

# but this calculation is all integers and computes from 0.0 UT.

# See http:///service-it-direct.s7.devpreviewr.com.usno.navy.mil/USNO/astronomical-applications/data-services/ca…

set -u

TRACEME=${TRACEME:-false} # TRACEME non-null = trace on

[ $TRACEME != false ] && set -x && PS4='[$LINENO]: ‘

MONRAW=$1

DAY=$2

YEAR=$3

# Convert month to number

# To allow for UPPER or MiXeD case, store in lowercase

# ZerO is a placeholder for the zero-th array element so

# that jan is element 1 in the array.

if ” | wc -c) -ne 0 ]]

then

typeset -l MONTHNAME=$1 # convert month name to lowercase

set -A MON ZerO jan feb mar apr may jun jul aug sep oct nov dec

MONTH=1

while

do

&& break

MONTH=$(($MONTH+1))

done

else

MONTH=$1

fi

# If we get here with MONTH=13, then the month name was not found

# or the month number was not 1-12

if

then

echo “Month: “$MONRAW” not valid”

exit 1

fi

# perform conversion

######################################################

if ; then

MONTH=$(($MONTH – 3))

else

MONTH=$(($MONTH + 9))

YEAR=$(($YEAR – 1))

fi

CENTURY=$(($YEAR / 100))

CENT2=$(($YEAR % 100))

JULIAN=$(((146097 * $CENTURY) / 4

+ (1461 * $CENT2) / 4

+ (153 * $MONTH + 2) / 5

+ $DAY + 1721119))

echo $JULIAN

return

}

##########

# Jmdy #

##########

function Jmdy

{

# Usage: Jmdy [-m] <Julian day number>

# where -m will return 3-char Month Name, otherwise month=1-12

# JulianDayNumber to be converted to Month Day Year

# Calculate MONTH_NAME DAY YEARYYYY from JulianDayNumber

# JulianDayNumber is the number of days since 4713 BC

# This is the ideal way to compute date differences given calendar dates

# The exact Julian day technically starts at 0.5 Universal Time (noon)

# but this calculation is all integers and computes from 0.0 UT.

# See http:///service-it-direct.s7.devpreviewr.com.usno.navy.mil/USNO/astronomical-applications/data-services/ca…

set -u

TRACEME=${TRACEME:-false} # TRACEME non-null = trace on

[ $TRACEME != false ] && set -x && PS4='[$LINENO]: ‘

set -A MONTH3 ZerO Jan Feb Mar Apr May Jun Jul Aug Sep Oce Nov Dec

MONTHNAME=false

while getopts “:m” OPTCHR

do

case $OPTCHR in

m) MONTHNAME=true

;;

*) eval “ERROPT=$$(($OPTIND-1))”

echo “function Jmdy invalid option: $ERROPT”

return 1

;;

esac

done

shift $(($OPTIND -1))

# Do all the calculations – all integer math

JULIANDATE=$1

JULIANDATE=$((JULIANDATE – 1721119))

YEAR=$(((4 * JULIANDATE – 1) / 146097))

JULIANDATE=$((4 * JULIANDATE – 1 – 146097 * YEAR))

DAY=$((JULIANDATE / 4))

JULIANDATE=$(((4 * DAY + 3) / 1461))

DAY=$((4 * DAY + 3 – 1461 * JULIANDATE))

DAY=$(((DAY + 4) / 4))

MONTH=$(((5 * DAY – 3) / 153))

DAY=$((5 * DAY – 3 – 153 * MONTH))

DAY=$(((DAY + 5) / 5))

YEAR=$((100 * YEAR + JULIANDATE))

if

then

MONTH=$((MONTH + 3))

else

MONTH=$((MONTH – 9))

YEAR=$((YEAR + 1))

fi

&& MONTH=${MONTH3[$MONTH]}

echo “$MONTH $DAY, $YEAR”

return

}

– See more at: http://serviceitdirect.com/blog/using-julian-days-calendar-calculations#sthash.Tv9S2ucB.dpuf


Tags: