Introduction
I decided to create a function that generated expiry info for web cookies and remembered an APL+Win workspace that did something similar. To cut a long story short I ended up taking a subset of the many date functions in that workspace and translating them to J. With the author's permission I've made them available here in case others find them useful. Apart from anything else the exercise was a useful learning experience for me, especially starting to come to terms with calling DLLs.
The functions would most probably benefit from added Jification and it would be nice if the GetTimeZoneInfo verb could be made to work across platforms. Unfortunately my Linux/Mac skills are not up to the task.
Download the dcdates.ijs script or, use the Literate version below.
The information below has been modified only slightly from that accompanying the original workspace.
Usage and Background
Most routines will accept an array of any size, shape, or rank and return an array of the same (or similar) dimensions. Operations on arrays of dates are fast and encouraged. All date routines (except dJulian and dGregorian) use the Julian Day Number style of date for simplicity. The YYYYMMDD format is only useful when passing dates to and from other systems.
Dates in the (calendar) form of YYYYMMDD are single 8-digit numeric values where groups of digits are used to describe the parts of the date. MM is the month number (01-12) and DD is the day number of the month (01-31). The year (YYYY) may vary from _4712 to 9999. If the year is negative, this is indicated by making the entire date a negative number, but without affecting the month or day digits (i.e. they still appear to read as the correct decimal values). Negative dates (years) are used to indicate dates prior to 1 A.D. Since there was no year 0 (A.D. or B.C.), the year number (YYYY) of 0 is used to represent the calendar year 1 B.C. A year number (YYYY) of _1 is used to represent the year 2 B.C., and so on. Dates are calculated properly back to the accepted standard starting date of January 1, 4713 B.C. (_47120101), and theoretically beyond that as well.
Important note: Dates from 15 Oct 1582 and later are assumed to represent dates in the Gregorian (reform) calendar and dates from 4 Oct 1582 and earlier are assumed to represent dates in the (proleptic) Julian calendar, even those that are prior to the official adoption of the Julian calendar in 46 B.C. Other variations and complications in the use of historical dates (such as those surrounding and immediately following the adoption of the Julian calendar system -- 46 B.C. to 4 A.D., or the variable date of switchover to the Gregorian calendar) are summarily ignored.
Julian Day Number
The "Julian Day Number" (also called the Julian Day or JD, but *not* the Julian date) form of the dates (JJJJJJJ) is the standard way of consistently measuring dates regardless of the local calendar in use at any given time. Its origin is at (Julian proleptic date) January 1, 4713 B.C. where the JD = 0. In many Julian Day Number systems (especially those used by astronomers), dates were actually assigned fractional numbers so that a time of day might be so indicated. In such systems, the whole value of the Julian Day represents noon on that day, so the previous midnight would be .5 days less and the following midnight would be .5 days more. Such fractional day numbers are not supported by most of these routines and all dates should be considered to be the whole number (noontime for astronomers) value for that day. However, dJulian and dGregorian have been modified to allow special "timestamp" formats for convenience, and these can accept and produce special fractional-day values which start at *midnight* (and thus are 12 hours different than astronomical day-fractions noted above).
The conversion routines were modified slightly so that a Julian Day (JJJJJJJ) of zero is returned and accepted when the calendar date (YYYYMMDD) is zero. This is to facilitate the handling of "null" (e.g. empty, undefined) dates by use of the value zero in either format. Unfortunately, this means that the exact (Julian proleptic) date of January 1, 4713 B.C. (_47120101) is not available for use since it also equates to day number zero. This is not expected to pose any difficulties to programmers in the real world, however.
References
The basis for the date conversion algorithms was derived from a detailed mathematical analysis by Peter Baum in 1998 and additional formulas and algorithms were derived from the current "calendar FAQ". At this time, Peter's information is available from his web site at and the FAQ is usually multi-posted to the following Usenet news groups: <sci.astro>, <soc.history>, <sci.answers>, <soc.answers>, and <news.answers>. Please consult these extensive sources of information for more details on the inner workings of the algorithms and the historical use of calendars through the centuries.
Literate version
NB. Julian day, date and time utilities NB. Describe n Description of Script and handling Julian days NB. CookieExpires v Format a web cookie's EXPIRES= parameter NB. dGregorian v convert a "Julian Day Number" to a Gregorian-style date (YYYYMMDD). NB. dIStartDT v given year, returns date that Daylight Saving Time starts NB. dIStartST v given year, returns date that Standard Time starts NB. dJulian v convert a Gregorian-style date (YYYYMMDD) to a "Julian Day Number". NB. dSpell v Format a date (a Julian Date Number) in a given format NB. getTimeZoneInfo v function to return Windows time zone info NB. tLocal v Converts a UTC (GMT) date/time to Local one. NB. tSpell v Format a time (in seconds) in a given format. NB. tUTC v Converts a localized date/time to UTC (GMT) one. «Require» «Describe» «CookieExpires» «dGregorian» «handle Daylight saving time» «dJulian» «dSpell» «getTimeZoneInfo» «tLocal» «tSpell» «tUTC»
MS0Date and Linux0DateTime are constants useful for translating to and from dates generated by Microsoft applications (such as Excel, Access, or many VB-language routines) and Linux-style dates respectively. A Microsoft date can be directly added to MS0Date to get a Julian Day Number (JJJJJJJ). Or, MS0Date can be subtracted from a Julian Day Number (JJJJJJJ) to produce a Microsoft date. Be careful of missing dates as these are handled in different ways.
It would be useful to calculate and add the constant for the J dates script in the standard library.
require 'dll winapi' MS0Date=: 2415019 Linux0DateTime=: 2440588
NB.*CookieExpires v Format a web cookie's EXPIRES= parameter
NB. Date/time argument may be provided as any of:
NB. 6!:0 '' form (3, 5, 6 $)
NB. +6!:0 '' form (adds to 6!:0 '' if year < 100)
NB. YYYYMMDD[.HHMMSS] form
NB. dJulian form (integer or fractional)
NB. DDD[.HHMMSS] to add days/hours to current time
NB. Date/time argument is assumed to be in local time (will convert to GMT)
NB. Result is text of the form:
NB. 'expires=Wed, 01-May-2005 00:00:00 GMT'
NB. Reverse conversion is also available. Supply the expiration character
NB. vector in standard form and it will be converted back to (local) 6!:0 '' form.
NB. ('expires=', 'GMT', and day-of-week are optional. No checking is done.)
NB. Written 6 May 2004 by Davin Church of Creative Software Design
NB. Modified 10 October 2004 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
CookieExpires=: 3 : 0
ts=. y
if. ' '={.0$ts do. NB. *****Reverse conversion from text
t=. ;:'expires= Expires= EXPIRES=' NB. Allow prefix with capitalizations
ts=. ((${.t)*(<($t){.ts)e. t)}.ts NB. Remove prefix
t=. ' GMT'
ts=. ((-$t)*t-:(-$t){.ts)}.ts NB. Remove suffix
ts=. (>:(','e.ts)*ts i.',')}.ts NB. Remove day-of-week
ts=. 6{.(ts e.' -:,')<;._1 ts NB. Chop into individual pieces
t=. ;: 'Jan Feb Mar Apr May Jun Jul Aug Sep Aug Oct Nov Dec'
ts=.(<>:t i. 1{ts) (1)}ts NB. Convert month to numeric form
ts=.> ({.each ".each 0 2 3 4 5{ts) (2 0 3 4 5)}ts NB. Convert text to numbers and reorder
ts=. ((0{ts)+2000*100>0{ts) (0)}ts NB. Accept squirrelly 2-digit year
NB. That gives it to us in GMT - now convert to local time
exp=. 2 dGregorian tLocal dJulian ts NB. And that gives us our result
else. NB. *****Normal formatting of date given
if. 3<:#ts do. NB. using 6!:0 '' format
if. ({.ts)<100 do. ts=. (6!:0 '')+6{.ts end. NB. timestamp offset form
date=. dJulian 3{.ts
time=. 60#.3{.3}.ts
else. NB. some singleton form
date=. <.{.ts
time=. 1|{.ts
if. date<10000 do. NB. Allow short offsets from "now"
ts=. (dJulian 6!:0 '')+date+(60#.(3#100)#:<.0.5+time*1000000)%86400
date=. <.{.ts
time=. 1|{.ts
end.
if. date>:10000000 do.
date=. dJulian date
time=. 60#.(3#100)#.<.0.5+time*1000000
else.
time=. <.0.5+86400*time
end.
end.
NB. Convert to UTC/GMT (including DST for given date)
date=. <.ts=. tUTC date+time%86400
time=. <.0.5+86400*1|ts
NB. Format as text
date=. 'ddd, dd-mmm-yyyy' dSpell date
time=. 'hh:mm:ss' tSpell time
exp=. 'expires=',date,' ',time,' GMT'
end.
)
NB.*dGregorian v convert a "Julian Day Number" to a Gregorian-style date (YYYYMMDD).
NB. If optional left argument is specified as a numeric 0 (default), then
NB. the result will have the year, month, and day separated into distinct
NB. elements in 3 columns YYYY MM DD. If it is 1 then YYYYMMDD.
NB. If the left argument is specified as a numeric 2, then the result will
NB. have an expanded date as above, but will include 6 columns/elements
NB. to represent a timestamp in 6!:0 format.
NB. The input values may optionally specify a timestamp value by providing
NB. a fractional number with the fraction representing a particular time of
NB. day (starting at midnight) as a fraction of a whole day. If a time is
NB. specified in this way, then the returned result will also be fractional
NB. and return the timestamp in the form YYYYMMDD.HHMMSSTTT, or as a
NB. 7-column matrix (in 6!:0 form) if the left argument is 2 (see above).
NB. Warning: The YYYYMMDD.HHMMSSTTT format cannot be precisely stored by the
NB. computer and may manifest millisecond rounding errors (up or down) which
NB. can adversely effect computation or display of the resulting values.
NB. Any rank & shape array accepted. See "Describe" for more details.
NB. Constants: 1721117 is 1-Jan-0001; 2299160 is 4-Oct-1582
NB. Constants: 584400 is (10 + 29-Feb-1600) - 1-Jan-0001
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Modified January 2003 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
dGregorian=: 3 : 0
0 dGregorian y
:
JJJ=. y
nz=. JJJ~:0
if. f=. 1 e. ,0~:FFF=. 1||JJJ do.
JJJ=. (*JJJ)*<.|JJJ
end.
JJJ=. (JJJ+10*JJJ>2299160)-1721117
j1600=. 0>.(j=. JJJ-0.25)-584400
t=. >.0.75*<.j1600%36524.25
Y=. <.(t+j)%365.25
d=. t+JJJ-<.Y*365.25
M=. (<:d){(31 30 31 30 31 31 30 31 30 31 31 29#2+>:i.12)
D=. d-(<:M){0 0 0 31 61 92 122 153 184 214 245 275 306 337
Y=. Y+t=. M>12
M=. M-12*t
if. +./t=. 0 2 e. {.x do.
YMD=. nz*Y,"0 1 M,."1 D NB. matrix form
if. 1{t do. NB. add optional time to it
YMD,"1 nz*(0 60 60)#: 0.001*<.0.5+FFF*86400000
end.
else.
YMD=. nz*(_1^Y<:0)*(10000*|Y)+(100*M)+D NB. Normal form
if. f do. NB. Add the optional time to it (if any time was supplied)
FFF=. (0 100 100 1000)#.(0 60 60 1000)#:<.0.5+FFF*86400000
YMD=. (*YMD)*(|YMD)+FFF%1000000000
end.
end.
)
NB.*dIStartDT v given year, returns date that Daylight Saving Time starts
NB. uses current Windows system rules
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
dIStartDT=: 3 : 0
year=. y
rule=. 6{:: getTimeZoneInfo ''
if. 0=1{rule do. NB. Daylight Time not used
date=. 0
elseif. 0=0{rule do. NB. Use day-of-month rule
date=. 1 dJulian (_1^year<0)*(1+100*1{rule)+10000*|year
date=. (7*(3{rule)-1)+date+7|1+(2{rule)-1+7|1+date
date=. date-7*(1{rule)~:100|<.(1 dGregorian date)%100
elseif. do. NB. Use fixed-date rule
date=. (year=0{rule)*1 dJulian 100#:0 1 3{rule
end.
)
NB.*dIStartST v given year, returns date that Standard Time starts
NB. uses current Windows system rules
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
dIStartST=: 3 : 0
year=. y
rule=. 3{:: getTimeZoneInfo ''
if. 0=1{rule do. NB. Daylight Time not used
date=. 0
elseif. 0=0{rule do. NB. Use day-of-month rule
date=. 1 dJulian (_1^year<0)*(1+100*1{rule)+10000*|year
date=. (7*(3{rule)-1)+date+7|1+(2{rule)-1+7|1+date
date=. date-7*(1{rule)~:100|<.(1 dGregorian date)%100
elseif. do. NB. Use fixed-date rule
date=. (year=0{rule)*1 dJulian 100#:0 1 3{rule
end.
)
NB.*dJulian v convert a Gregorian-style date (YYYYMMDD) to a "Julian Day Number".
NB. Dates may optionally include a "time" portion to produce a "timestamp"
NB. value. Standard integers are extended to allow non-integer
NB. values in the format (YYYYMMDD.HHMMSSTTT). If this optional
NB. format is used, the result will not be an integer and the fractional
NB. portion will be the numeric fraction of a day representing that time.
NB. Warning: The YYYYMMDD.HHMMSSTTT format cannot be precisely stored by the
NB. computer. Such inputs are pre-rounded to a multiple of 10 milliseconds
NB. to prevent erroneous rounding during processing.
NB. Constants: 1721117 is 1-Jan-0001; 2299160 is 4-Oct-1582
NB.
NB. Original APL written in 1998 by Davin Church of Creative Software Design
NB. Modified July 2005 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
dJulian=: 3 : 0
0 dJulian y
:
YMD=. y
if. x=0 do.
HMS=. (0 60 60#. 3{."1 (3}."1 YMD))%86400
YMD=. (_1^0>{."1 YMD)*100 #. 3{."1 |YMD
elseif. 1 e. ,0~:HMS=. <.0.5+100000000*1||YMD do. NB. did they give us a timestamp?
YMD=. <.YMD
HMS=. (0 60 60 1000)#.(0 100 100 1000 #: HMS*10)%86400000
end.
Y=. (*YMD)*<.|YMD%10000
M=. <.(YMD=. 10000||YMD)%100
Y=. Y-12<M=. M+12*M<:2
JJJ=. (100|YMD)+ (<:M){0 0 0 31 61 92 122 153 184 214 245 275 306 337
y1600=. 0>.Y-1600
JJJ=. JJJ+1721117+(<.Y*365.25)-(<.y1600%100)-<.y1600%400
JJJ=. HMS+(YMD~:0)*JJJ-10*JJJ>2299160
)
NB.*dSpell v Format a date (a Julian Date Number) in a given format
NB. Specify the date format to be used with the following codes:
NB. d: 1 dd: 01 ddd: Sun dddd: Sunday
NB. m: 1 mm: 01 mmm: Jan mmmm: January
NB. yy: 99 yyyy: 1999
NB. Any rank & shape array accepted. See "Describe" for more details.
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
dSpell=: 3 : 0
'mmmm d, yyyy' dSpell y
:
codes=. ;:'d dd ddd dddd m mm mmm mmmm yy yyyy'
pic=. x
t=. 2</\0, s=. pic='\'
var=. -.}:0,s=. s*.~:/\s~:t &#^:_1 (2~:/\0,t#~:/\}:0,s) NB. Handle '\\*'s
pic=. ((a.i.pic)+(-/a.i.'aA')*var*.pic e.'DMY'){a. NB. force to l/c
pic=. (t=. 1,2~:/\(-.s)#(3*-.var)>.'dmy'i.pic) <;.1 (-.s)#pic NB. Cut into tokens
var=. (pic e. codes)*. -. ;0 e.&.> (t <;.1 (-.s)#var) NB. mark sections as vars
s=. *YMD=. ,1 dGregorian <.y
YMD=. |: 0 100 100 #:|YMD
YMD=. (s*0{YMD) (0)}YMD
t=. 2{YMD
values=. (": each t);<(}.each ": each 100+t) NB. Format values for all codes
s=. ;:'Sunday Monday Tuesday Wednesday Thursday Friday Saturday'
t=. (7|1+<.y){s
values=. values,(3{. each t);<t NB. Embed weekday
t=. 1{YMD
values=. values, (": each t);<(}.each ": each 100 + t)
s=. ;:'January February March April May June July August September October November December'
t=. (0>.<:t){s
values=. values, (3{.each t);<t
s=. 0>:t=. 0{YMD
t=. |t-s
s=. s{('';' BC')
t=. (": each 10000+t),each s
values=. values,(3}.each t) ; <(1}.each t)
t=. (#codes)+ i.$pic
t=. (codes i. var#pic) (I.var)}t
text=. ($y)$>,each each /t{(values,(#1{::values)$each <each pic)
text=. ((0~:y)*each$each text)$each text
if. (0$0)-:$y do. text=. >text end.
text
)
The following verb gets the timezone information as stored by Windows. This information is key to any of the other verbs related to converting local time to UTC/GMT time and back. It would be nice if this verb could be extended to work other platforms too.
NB.*getTimeZoneInfo v function to return Windows time zone info
NB. returns 8-item boxed vector
NB. Daylight saving status (0 unknown, 1 standarddate, 2 daylightdate)
NB. Bias; StarndardName; StandardDate; StandardBias; DaylightName; DaylightBias
NB. eg. getTimeZoneInfo ''
NB. Based on APL+Win func written by Davin Church of Creative Software Design
NB. Written May 2007 by Ric Sherlock
getTimeZoneInfo=: 3 : 0
'info buffer'=. 'GetTimeZoneInformation'win32api <(,43#0)
buffer=. (1 (<:+/\ 1 16 4 1 16 4 1)}43#0) <;.2 buffer NB. 4 byte J integers
uctext=. 6&u: each 2&ic each 1 4 {buffer NB. get unicode text for StandardName & DaylightName
inul=. uctext i. each 0{a. NB. find first NUL in each
buffer=. (inul{.each uctext) (1 4)}buffer NB. amend buffer
buffer=. (_1&ic each 2&ic each 2 5{buffer) (2 5)}buffer NB.convert SYSTEMTIME structures
info;buffer
)
NB.*tLocal v Converts a UTC (GMT) date/time to Local one.
NB. input & output date/time values are floating point Julian days.
NB. Fractional portion is time as a fractional day (e.g. X.5 is noon)
NB. Uses the current Window settings for local time definitions.
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
tLocal=: 3 : 0
utc=. y
rule=. getTimeZoneInfo ''
yr=. <.(1 dGregorian <.utc)%10000
reverse=. (dt=. dIStartDT yr)>st=. dIStartST yr NB. Southern hemisphere?
local=. utc-({.1{::rule)%1440 NB. Use local std time to mark switchover point
rls=. reverse*.local<st+1
rld=. reverse*.local>dt-1 NB. Unwrap years?
st=. (dIStartST yr+rld)+(0 60 60 1000#.4}.3{::rule)%86400000
dt=. (dIStartDT yr-rls)+(0 60 60 1000#.4}.6{::rule)%86400000
usedst=. (local>:dt+({.4{::rule)%1440)*.(local<st+({.7{::rule)%1440)
local=. local-((dt~:0)*((0+usedst){4 7)>@{rule)%1440
local=. ($y)$,local
)
NB.*tSpell v Format a time (in seconds) in a given format.
NB. Specify the time format to be used with the following codes:
NB. (either upper or lower case) to specify the formatting of days ("D"),
NB. hours ("H"), minutes ("M"), seconds ("S"), fractions of a second ("C"),
NB. or AM/PM designator ("P"):
NB. d: 1 h: 1 m: 1 s: 1 c: 1 p: a
NB. hh: 01 mm: 01 ss: 01 cc: 01 pp: am
NB. sss: 1.2 ccc: 001
NB. cccc: 0001
NB. Any rank & shape array accepted. See "Describe" for more details.
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
tSpell=: 3 : 0
'h:mm:ss pp' tSpell y
:
codes=. ;:'d h hh m mm s ss sss c cc ccc p pp'
pic=. x
t=. 2</\0, s=. pic='\'
var=. -.}:0,s=. s*.~:/\s~:t &#^:_1 (2~:/\0,t#~:/\}:0,s) NB. Handle '\\*'s
DCP=. 'dcp' e. var#pic=. ((a.i.pic)+(-/a.i.'aA')*var*.pic e.'DHMSCP'){a. NB. force to l/c
pic=. (t=. 1,2~:/\(-.s)#(3*-.var)>.'dhmscp'i.pic) <;.1 (-.s)#pic NB. Cut into tokens
var=. (pic e. codes)*. -. ;0 e.&.> (t <;.1 (-.s)#var) NB. mark sections as vars
DHMS=. |:(0,(24*0{DCP),60 60) #: <.0>.,y
CCC=. (<.0.5+10000*1|0>.,y)%10
values=. ": each 0{DHMS
t=. (2{DCP)+ (12*2{DCP)|(1{DHMS)-2{DCP NB. Format values
values=. values; (": each t);<(_2{. each (": each 100+ t),each (2* t>99)$ each '*')
t=. 2{DHMS
values=. values,(": each t);<(}.each ": each 100+t)
t=. 3{DHMS
values=. values,(": each t);<(}.each ": each 100+t)
t=. ": each t+CCC%1000
values=. values,< (('.'={.each t)$ each '0'),each t
values=. values,<"1 |:}.each ":each 10 100 1000+"1 <.0.5+CCC%"0 1 (100 10 1)
t=. (12<:24|1{DHMS){ ;:'am pm'
values=. values,(1{.each t);<t
t=. (#codes)+ i.$pic
t=. (codes i. var#pic) (I.var)}t
text=. ($y)$>,each each /t{(values,(#1{::values)$each <each pic)
text=. ((y>:0)*each$each text)$each text
if. (0$0)-:$y do. text=. >text end.
text
)
NB.*tUTC v Converts a localized date/time to UTC (GMT) one.
NB. input & output date/time values are floating point Julian days.
NB. Fractional portion is time as a fractional day (e.g. X.5 is noon)
NB. Uses the current Window settings for local time definitions.
NB. Written in 1998 by Davin Church of Creative Software Design
NB. Translated May 2007 from APL+Win to J by Ric Sherlock
tUTC=: 3 : 0
local=. y
rule=. getTimeZoneInfo ''
yr=. <.(1 dGregorian <.local)%10000
reverse=. (dt=. dIStartDT yr)>st=. dIStartST yr
rls=. reverse*.local<st+1
rld=. reverse*.local>dt-1 NB. Unwrap years?
st=. (dIStartST yr+rld)+(0 60 60 1000#.4}.3{::rule)%86400000
dt=. (dIStartDT yr-rls)+(0 60 60 1000#.4}.6{::rule)%86400000
usedst=. (local>:dt)*.(local<st)
utc=. local+(({.1{::rule)+(dt~:0)*((0+usedst){4 7)>@{rule)%1440
utc=. ($y)$,utc
)
Describe=: 0 : 0 This script contains a subset of the date functions written for APL+Win by Davin Church of Creative Software Design and distributed in his Dates workspace. They were translated to J by Ric Sherlock. Any errors are likely to have been introduced during translation. The functions would most probably benefit from added Jification. The description that follows is modified only slightly from Davin's original. Most routines will accept an array of any size, shape, or rank and return an array of the same (or similar) dimensions. Operations on arrays of dates are fast and encouraged. All date routines (except dJulian and dGregorian) use the Julian Day Number style of date for simplicity. The YYYYMMDD format is only useful when passing dates to and from other systems. Dates in the (calendar) form of YYYYMMDD are single 8-digit numeric values where groups of digits are used to describe the parts of the date. MM is the month number (01-12) and DD is the day number of the month (01-31). The year (YYYY) may vary from _4712 to 9999. If the year is negative, this is indicated by making the entire date a negative number, but without affecting the month or day digits (i.e. they still appear to read as the correct decimal values). Negative dates (years) are used to indicate dates prior to 1 A.D. Since there was no year 0 (A.D. or B.C.), the year number (YYYY) of 0 is used to represent the calendar year 1 B.C. A year number (YYYY) of _1 is used to represent the year 2 B.C., and so on. Dates are calculated properly back to the accepted standard starting date of January 1, 4713 B.C. (_47120101), and theoretically beyond that as well. Important note: Dates from 15 Oct 1582 and later are assumed to represent dates in the Gregorian (reform) calendar and dates from 4 Oct 1582 and earlier are assumed to represent dates in the (proleptic) Julian calendar, even those that are prior to the official adoption of the Julian calendar in 46 B.C. Other variations and complications in the use of historical dates (such as those surrounding and immediately following the adoption of the Julian calendar system -- 46 B.C. to 4 A.D., or the variable date of switchover to the Gregorian calendar) are summarily ignored. The "Julian Day Number" (also called the Julian Day or JD, but *not* the Julian date) form of the dates (JJJJJJJ) is the standard way of consistently measuring dates regardless of the local calendar in use at any given time. Its origin is at (Julian proleptic date) January 1, 4713 B.C. where the JD = 0. In many Julian Day Number systems (especially those used by astronomers), dates were actually assigned fractional numbers so that a time of day might be so indicated. In such systems, the whole value of the Julian Day represents noon on that day, so the previous midnight would be .5 days less and the following midnight would be .5 days more. Such fractional day numbers are not supported by most of these routines and all dates should be considered to be the whole number (noontime for astronomers) value for that day. However, dJulian and dGregorian have been modified to allow special "timestamp" formats for convenience, and these can accept and produce special fractional-day values which start at *midnight* (and thus are 12 hours different than astronomical day-fractions noted above). The conversion routines were modified slightly so that a Julian Day (JJJJJJJ) of zero is returned and accepted when the calendar date (YYYYMMDD) is zero. This is to facilitate the handling of "null" (e.g. empty, undefined) dates by use of the value zero in either format. Unfortunately, this means that the exact (Julian proleptic) date of January 1, 4713 B.C. (_47120101) is not available for use since it also equates to day number zero. This is not expected to pose any difficulties to programmers in the real world, however. A global variable called MS0Date may be found in this workspace and is useful for translating to and from dates generated by Microsoft applications (such as Excel, Access, or many VB-language routines). A Microsoft date can be directly added to MS0Date to get a Julian Day Number (JJJJJJJ). Or, MS0Date can be subtracted from a Julian Day Number (JJJJJJJ) to produce a Microsoft date. Be careful of missing dates as these are handled in different ways. A global variable called Linux0DateTime is also available for performing similar translations to and from Linux-style dates (which are usually stored in seconds). Other constants may be computed for any other special needs. The basis for the date conversion algorithms was derived from a detailed mathematical analysis by Peter Baum in 1998 and additional formulas and algorithms were derived from the current "calendar FAQ". At this time, Peter's information is available from his web site at <vsg.cape.com/~pbaum> and the FAQ is usually multi-posted to the following Usenet news groups: <sci.astro>, <soc.history>, <sci.answers>, <soc.answers>, and <news.answers>. Please consult these extensive sources of information for more details on the inner workings of the algorithms and the historical use of calendars through the centuries. Many structural changes were made to the formulas and algorithms in the process of coding them first in APL and then in J. All rights thus obtained are reserved and the final code is copyright 1998-2005 by Creative Software Design. But these functions are intended to be freely used in commercial applications -- see this workspace's "ReadMe" variable for more details. )
-- RicSherlock 2007-05-24 09:06:44
