Julian Days, Dates, and Calendars

Some rough code for dealing with calendar conversions.  Some things you should know to start out.

Calendar conversion is done most easily by getting a normal, decimal notation number for dates in relation to some absolute.  The normal baseline used for that in the Western world is a Julian day number, calculated from January 1, 4713 BC.

Next, there is a very bad and non-standard habit some people have of referring to the numeric day of a year as a Julian date.  This is incorrect and is highly confusing; don't use that term.

Remember that day lengths vary; over time, the length of a day is increasing.  Counting days is not an absoulte measure of time in the strict sense.

Windows systems use locale-specific time calculations; that means that a file stamped with "3 AM" shows up as 3 AM in any time zone.  Unix/Linux systems in general use an internal UTC stamp and render it into the local time.  One effect of this is that absolute date measurements on Windows systems are done relative to the present time zone, and any absolute times and dates are assumed to refer to the local time zone. For this reason, it is best to convert anything into a UTC time.  A Windows date and time is not complete information about time of an event without accompanying time zone information.

Class calend
' Alex K. Angelopoulos v. 2002.11.24 (UTC Gregorian)
' modifications are encouraged - but please
' remark them and add your name and the date

Dim JdnVBScriptOffSet, JdnModOffset

Private Sub Class_Initialize
'ENUM JULIAN_DAYNUMBER_OFFSETS
' these are numbers which need to be added to a particular day-based
' dating reference to get a Julian Day Number
JdnVBScriptOffSet = 2415018.5
'For VBScript - baseline #1899/12/31 12:00:00 AM#
JdnModOffset = 2400000.5
' modern "modified" Julian date (1858.11.17?)
' source http://maia.usno.navy.mil/
Jdn_FrenchRepublican = 2375838.5
' this is a guess.
'END ENUM CALENDAR_OFFSETS
End Sub

Public Function BesselianDate(ModJdn)
' JDN - is a MODIFIED Julian Day Number
' This converts a julian day number to a besselian date
' besselians are years measured relative to
' 12:00:00 AM Jan 1 2000 UTC
' source:http://maia.usno.navy.mil/
BesselianDate = 2000 + (ModJdn - 51544.03)/365.2422
End Function

public function SolarNumber(y)
'http://www.tondering.dk/claus/cal/calendar24.txt
SolarNumber = (y + 8) mod 28 + 1
End Function

public function GoldenNumber(y)
'http://www.tondering.dk/claus/cal/calendar24.txt
' years with the same golden number have roughly coincidental
' lunar month cycles
GoldenNumber = (y mod 19)+1
End Function

public function JulianEpact(y)
'http://www.tondering.dk/claus/cal/calendar24.txt
' Possible values: 1, 3, 4, 6, 7, 9, 11, 12, 14, 15, 17, 18,
' 20, 22, 23, 25, 26, 28, and 30
JulianEpact = (11 * (GoldenNumber(y)-1)) mod 30
If JulianEpact = 0 Then JulianEpact = 30
End Function

public function GregorianEpact(y)
' Modified Gregorian version of the traditional Julian Epact
' correction base don issues with moon calculations in Julian calendar
Dim Epact, c
Epact = (11 * (GoldenNumber(y) - 1 )) mod 30
'Adjust the Epact, taking into account the fact that 3 out of 4
'centuries have one leap year less than a Julian century:
c = Int(y/100) + 1 ' this is a "bad" century calculation
' incorrectly treats 1900 as 20th C., but needed for thic calculation
Epact = Epact - Int((3*c)/4)
'Adjust the Epact, taking into account the fact that 19 years is not
'exactly an integral number of synodic months:
'This adds one to the Epact 8 times every 2500 years.
Epact = Epact + Int((8*c + 5)/25)
' Add 8 to the Epact to make it the age of the moon on 1 January:
Epact = Epact + 8
' Add or subtract 30 until the Epact lies between 1 and 30.
GregorianEpact = Epact
End Function

public function Century(y)
' simple formula to correctly return the century of a year
' correctly returns 1901-2000 as 20th Century
Century = Int((y + 99)/100)
End Function

public function indiction(y)
Indiction = (y + 2) mod 15 + 1
end function

public function dayOfWeek(jdn)
' returns day of week from the Julian number
' based on Kees Couprie's material at:
' http://www.geocities.com/couprie/calmath
dayOfWeek = (jdn Mod 7) + 1
End Function

public function HijriMonths
HijriMonths = Array("Muharram", "Safar", "Rabi-al-Awwal", "Rabi-al-Thani", _
"Jumada-al-Oola", "Jumada-al-Akhir", "Rajab", "Sha'ban", _
"Ramadan", "Shawwal", "Zul-Qa'da", "Zul-Hijja")
End Function

public Function gregorian_jdn( iYear, iMonth, iDay)
' NOTE: This is a Universal Gregorian - no implicit
' conversion to Julian prior to #1582/10/14#
Dim lYear, lMonth, lDay
lYear = CLng(iYear)
lMonth = CLng(iMonth)
lDay = CLng(iDay)
gregorian_jdn = ((1461 * (lYear + 4800 + ((lMonth - 14) \ 12))) \ 4) _
+ ((367 * (lMonth - 2 - 12 * (((lMonth - 14) \ 12)))) \ 12) _
- ((3 * (((lYear + 4900 + ((lMonth - 14) \ 12)) \ 100))) \ 4) _
+ lDay - 32075
End Function

public Function julian_jdn( iYear, iMonth, iDay )
Dim lYear, lMonth, lDay
lYear = CLng(iYear)
lMonth = CLng(iMonth)
lDay = CLng(iDay)
julian_jdn = 367 * lYear - _
((7 * (lYear + 5001 + ((lMonth - 9) \ 7))) \ 4) + ((275 * lMonth) \ 9) _
+ lDay + 1729777
End Function

public Function jdn_Gregorian(jdn)
Dim l, n, i, j, y, m, d
l = jdn + 68569
n = ((4 * l) \ 146097)
l = l - ((146097 * n + 3) \ 4)
i = ((4000 * (l + 1)) \ 1461001)
l = l - ((1461 * i) \ 4) + 31
j = ((80 * l) \ 2447)
d = l - ((2447 * j) \ 80)
l = (j \ 11)
m = j + 2 - 12 * l
y = 100 * (n - 49) + i + l
jdn_Gregorian = Array(y, m, d)
End Function

public Function jdn_julian( jdn)
Dim l, k, n, i, j, d, m, y
j = jdn + 1402
k = ((j - 1) \ 1461)
l = j - 1461 * k
n = ((l - 1) \ 365) - (l \ 1461)
i = l - 365 * n + 30
j = ((80 * i) \ 2447)
d = i - ((2447 * j) \ 80)
i = (j \ 11)
m = j + 2 - 12 * i
y = 4 * k + n + i - 4716
jdn_julian = Array(y, m, d)
End Function


function JdnToHijri(jd)
' I don't trust this function yet...
l=jd-1948440+10632
n=int((l-1)/10631)
l=l-10631*n+354
j= (10985-l)\5316 * (50*l)\17719 + (l\5670) * (43*l)\15238
l=l-(Int((30-j)/15))*(Int((17719*j)/50)) _
-(Int(j/16))*(Int((15238*j)/43))+29
m= (24*l)\709
d=l-((709*m)/24)
y=30*n+j-30
JdnToHijri = Array(y\1, m\1, d\1)
end function

' ==============================================================================

' REFERENCE DATES
' Julian start: 12:00 noon on 1 January 4713 B.C (proleptic Julian calendar)
' http://www.jsc.nasa.gov/er/seh/math16.html
' reference numbers from that page:
' 25 December 1981 is JDN 2444964
' 1 February 1958 is JDN 2436236
' 18 June 1983 is JDN 2445504


' FORTRAN ALGORITHM
' A clever computer algorithm for converting calendar dates to Julian
' days was developed using FORTRAN integer arithmetic (H. F. Fliegel'
' and T. C. Van Flandern, "A Machine Algorithm for Processing'
' Calendar Dates," Communications of the ACM 11 [1968]: 657). In
' FORTRAN integer arithmetic, multiplication and division are
' performed left to right in the order of occurrence, and the
' absolute value of each result is truncated to the next lower
' integer value after each operation, so that both 2/12 and -2/12
' become 0. If I is the year, J the numeric order value of the month,
' and K the day of the month, then the algorithm is: [WRAPPED]
' [JD = K - 32075 + 1461 * (I + 4800 + (J -14)/12)/4 + 367 *
' (J-2-(J-14)/12*12)/12 - 3 * ((I + 4900 + (J-14)/12)/100)/4. ]

' ==============================================================================
' NOTE - COMPARISON DATE/JULIAN DAY NUMBERS
' http://ecsinfo.gsfc.nasa.gov/sec2/papers/noerdlinger2.html
' 1990-07-01 2448073.5
' 1994-02-01 2449484.5
' 1999-11-01 2451483.5
' 2000-01-01 2451544.5

End Class