SiteMap
Page Bottom  Documentation

"Dates.w3" by Davin Church

This contains a description of a set of date manipulation routines written in APL by Davin Church (of Creative Software Design).

The primary function of these routines is the ability to convert between a standard (Gregorian) calendar date and a sequential Julian Day Number representation of that date so that date computations are more easily made. For instance, the number of days elapsed between two dates can be easily computed by subtracting their day numbers; or a date exactly 6 weeks in the future can be calculated by adding 42 to the day number. Additional (related) calculation routines are also available for use.

The following routines are currently available:

  1. dJulian Convert a calendar date (YYYYMMDD) to a Julian Day Number (JJJJJJJ).
  2. dGregorian Convert a Julian Day Number (JJJJJJJ) to a calendar date (YYYYMMDD).
  3. dToday Return today's date as a Julian Day Number (JJJJJJJ).
  4. dNow Return a timestamp, like dToday but including a fractional time.
  5. dDayOfWeek Return the day of the week (Sun=1 thru Sat=7) of a JDN (JJJJJJJ).
  6. dDayOfYear Return the day of the calendar year (1-366) of a JDN (JJJJJJJ).
  7. dAge Return the calendar age from a JDN (JJJJJJJ) as of today.
  8. dBeginWeek Return the JDN of the Sunday on or just before the given JDN.
  9. dBeginMonth Return the date of the first of the month of the date given.
  10. dBeginYear Return the date of the first of the year of the date given.
  11. dNextMonth Return the date that is one (or more) months from the date given.
  12. dNextYear Return the date that is one (or more) years from the date given.
  13. dNextWeekday Return the JDN of the following week day of the given JDN.
  14. dNextWorkday Return a date as in ’dNextWeekday but also skip holidays.
  15. dHolidayUSBank For a given date, decide if it is a U.S. national bank holiday.
  16. dEaster For a given year, return the JDN date of the Easter holiday.
  17. dSpell For a given date, spell out the date in any desired format.
  18. tSpell For a given time (in SSSSS = seconds of the day), format it as text.
  19. dStartDT For any given year, return the beginning date for Daylight Time.
  20. dStartST For any given year, return the beginning date for Standard Time.
  21. dISpell Internationalized version of ’dSpell.
  22. dIStartDT Internationalized version of ’dStartDT.
  23. dIStartST Internationalized version of ’dStartST.
  24. tISpell Internationalized version of ’tSpell.
  25. tLocal Convert a UTC (GMT) date/time to a localized one.
  26. tUTC Convert a local date/time to a UTC (GMT) one.
  27. dDaytime Retrieve the current time & date from a NIST internet server.
  28. dSetTime Modify your computer's hardware clock under program control.
  29. CookieExpires Format a date in a web cookie EXPIRES= form.

All routines will accept an array of any size, shape, or rank and return an array of the same 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.) 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. Other constants may be computed for special needs (such as internal APL file timestamps).

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: , , , , and . 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 in APL. All rights thus obtained are reserved and the final code is copyright 1998-2004 by Creative Software Design. However, use of this code (except for direct financial profit) is hereby granted provided it is not modified and appropriate credit and references are visibly acknowledged.

The syntax of these routines is as follows:

{JJJJJJJ} ← dJulian {YYYYMMDD} Converts a calendar-form date to a serial day number. If YYYYMMDD is negative, this indicates that the year itself is negative (not affecting the month or day) and is used to describe a date B.C. If YYYYMMDD is zero, then the returned JJJJJJJ result will also be zero to facilitate the implementation of "null" dates. While the example format given above is the primary syntax/usage, alternative syntaxes are also available. Input dates may be given in "expanded" (ŒTS-like) format where the dates are provided as separate year, month, and day numbers, as long as they are nested together into an enclosed scalar anywhere a simple 8-digit scalar would normally be expected (e.g. 3↑ŒTS). Also, a time of day may be included to specify a "timestamp" value, either by including a decimal fraction on the date integer with the time in readable form (YYMMDD.HHMMSS) or by extending the "expanded" format to have up to 7 items in the nested vector. In either case, the resulting Julian Day Number will no longer be an integer, but instead will contain the usual day value plus a fractional amount of a day representing the time of day provided.

{YYYYMMDD} ← {openform} dGregorian {JJJJJJJ} Converts a serial day number to a calendar-form date. If JJJJJJJ is zero, the the returned YYYYMMDD result will also be zero to facilitate the implementation of "null" dates. To further enhance the ability to interface this with other code and systems, it is also possible to have the dates returned as nested 3-item numeric vectors which are the usual scalar dates separated into independent year, month, and day values similar to ( 3↑ŒTS). This form of output is produced by specifying a scalar 1 as the optional left argument to the function. If a fractional serial day number is provided, then a fractional result will be returned giving the time in a readable YYYYMMDD.HHMMSS format. If full timestamps are desired in enclosed ŒTS form, supply a left argument of 2.

{JJJJJJJ} ← dToday Return the current date (ŒTS) in JJJJJJJ form for use by these routines or any other process that can use "standard" Julian Day Numbers. The implementation is simply "dJulian 100Ā3↑ŒTS".

{JJJJJJJ.FFFFFFFFF} ← dNow Return the current date and time (ŒTS) in fractional JJJJJJJ.FFFFFF form for use where a combined timestamp value is preferred. This is the same as dToday (a Julian Day Number), except that it also contains the current time with a fractional portion indicating the time of day (e.g. X.0 = midnight, X.5 = noon, X.75 = 6pm). This form can be used as a full timestamp and can be processed directly by tLocal & tUTC. It is also recognized by dGregorian (which can then return a fractional result) & dSpell (which ignores the time). The date portion may be extracted with "Ž" and the time portion extracted and converted to seconds (for use with tSpell, for instance) with "86400×1|". The implementation is simply "dJulian ŒTS". Note that most of the other functions here do not accept fractional inputs.

{dayofweek} ← dDayOfWeek {JJJJJJJ} For any given serial day number, return the day of the week on which it falls. Sunday is given as 1 and Saturday as 7.

{dayofyear} ← dDayOfYear {JJJJJJJ} For any given serial day number, return the day of that year on which it falls. January 1 is given as 1 and December 31 as 365 or 366.

{ageinyears} ← [today←dToday] dAge {JJJJJJJ} For a given "starting" date, return a calendar age (in calendar years and fractions thereof) that have elapsed from that date until today (or other specified "ending" date in JJJJJJJ format). Fractional years are computed by counting days between the last "birthday" and the next, and thus there may not always be exact half and quarter years for some dates. Previous, next, and nearest whole ages may be extracted by using floor (Ž), ceiling (—), and round (Ž.5+), respectively.

{JJJJJJJ} ← [startday←1] dBeginWeek {JJJJJJJ} For a given date, return the date of the first day (Sunday) of the week containing that date (i.e. the Sunday on or immediately preceeding the date given). If a week is considered to begin on a different day (on Monday or Saturday for example), then that day number (2, 7, etc.) may be provided as a left argument to the routine to logically shift the notion of the "beginning of the week".

{JJJJJJJ} ← [startday←1] dBeginMonth {JJJJJJJ} For a given date, return the date of the first day of the calendar month containing that date. If a month is to be considered to begin on a different day (on the 5th, or the 25th, or other unusual boundary), then that day number (5, 25, etc.) may be provided as a left argument to the routine to logically shift the notion of the "beginning of the month".

{JJJJJJJ} ← [startmonthday←0101] dBeginYear {JJJJJJJ} For a given date, return the date of the first day of the calendar year containing that date. If a year is to be considered to begin on a different day (on March 1st, or December 25th, or other unusual boundary such as those representing fiscal tax years), then that month and day (as a 4-digit number: 0301, 1225, etc.) may be provided as a left argument to the routine to logically shift the notion of the "beginning of the year".

{JJJJJJJ} ← [months←1] dNextMonth {JJJJJJJ} Add one calendar month to the date given and return its date. More than one month may be added, or one or more months may be subtracted, by giving the routine a left argument specifying the number of months to add (subtract if negative). If the day of the month (e.g. 31st) is not part of the following month (e.g. April), then the first legal day following it is returned instead (e.g. May 1st).

{JJJJJJJ} ← [years←1] dNextYear {JJJJJJJ} Add one calendar year to the date given and return its date. More than one year may be added, or one or more years may be subtracted, by giving the routine a left argument specifying the number of years to add (subtract if negative). If the day of the year (e.g. Feb 29th in a leap year) is not part of the resulting year (e.g. it is not a leap year), then the first legal day following it is returned instead (e.g. March 1st).

{JJJJJJJ} ← [weekdays←1] dNextWeekday {JJJJJJJ} Add one week day (Monday-Friday) to the date given and return its date. Zero, or more than one week day may be added, or one or more week days may be subtracted, by giving the routine a left argument specifying the number of week days to add (subtract if negative). If the starting date is not a week day, then the starting date is shifted forward to Monday before counting begins.

{JJJJJJJ} ← [workdays←1] dNextWorkday {JJJJJJJ} Add one non-holiday week day to the date given and return its date. Zero or more work days may be added, or one or more work days may be subtracted, by giving the routine a left argument specifying the number of work days to add (subtract if negative). If the starting date is not a work day, then the starting date is shifted forward to the next legal work day before counting begins.

This routine is identical to the ’dNextWeekday function except that it skips over holidays as well as weekends. The definition of "holiday" in this context will be defined by a user-defined subroutine called ’dHoliday. It should use the same syntax as ’dHolidayUSBank and may call that as a subroutine.

{isholiday} ← dHolidayUSBank {JJJJJJJ} Return a 1 if the given date is a recognized U.S. national banking holiday or a 0 otherwise. The following holidays are recognized (on date observed):
New Years Day Independence Day Thanksgiving
MLK's Birthday Labor Day Christmas
President's Day Columbus Day
Memorial Day Veterans Day

{JJJJJJJ} ← dEaster {YYYY} Return the date that Easter falls on in a given year. This is a difficult calculation. The rule used is: Easter Sunday is the first Sunday after the ecclesiastical full moon on or after the ecclesiastical vernal equinox. The ecclesiastical vernal equinox is always March 21st. The astronomical vernal equinox may actually occur on the 19th or 20th and the ecclesiastical full moon may differ from the actual astronomical full moon by a day either way. There are also variances to take into account for effective longitude and the effects of the international date line. The ecclesiastical rules define how these effects are all handled. This function uses the above rule for Gregorian calendar dates (after 1582) and a simpler calculation for older Julian calendar dates (before 1583).

{text} ← [picture] dSpell {JJJJJJJ} Format a given date as text. The format to be used is specified by use of a "picture" phrase provided as the left argument. The picture phrase is a character vector containing "keywords" indicating the data to be placed in the formatted output text along with any other characters that are considered decoration and copied to the output text exactly as written. The following "keywords" (either upper or lower case) may be included in the picture to specify the formatting of
days ("D"), months ("M"), or years ("Y"):
d: 1 dd: 01 ddd: Sun dddd: Sunday
m: 1 mm: 01 mmm: Jan mmmm: January
yy: 99 yyyy: 1999

Formatted text is returned as a variable-length character vector. A vector (or matrix) of dates is returned as a vector (or matrix) of character vectors. However, a scalar (not a 1½ vector!) date value is returned as an unnested character vector for convenience. A "null" date (Julian Day Number = 0) is formatted as an empty character vector (regardless of the picture format requested) to conveniently deal with "missing" or "undefined" dates. A fractional input (such as that returned from dNow) is accepted as valid, but the fractional portion is ignored when formatting. Note: The "keyword" letters mentioned above are not normally available to use as constant text within the picture. However, the special "character escape" symbol "backslash" ("\") may be used immediately in front of any of these letters (or "\" itself) to indicate that the following character is to be treated as a literal and not otherwise processed.

{text} ← [picture] tSpell {SSSSS} Format a given time (in seconds) as text. The format to be used is specified by use of a "picture" phrase provided as the left argument. The picture phrase is a character vector containing "keywords" indicating the data to be placed in the formatted output text along with any other characters that are considered decoration and copied to the output text exactly as written. The following "keywords" (either upper or lower case) may be included in the picture to specify the formatting of
days ("D"), hours ("H"), minutes ("M"), seconds ("S"), fractions of a second ("C"), or AM/PM designator ("P").
d: 1 h: 1 m: 1 s: 1 c: 1 p: a
hh: 01 mm: 01 ss: 01 cc: 01 pp: am
sss: 1.2 ccc: 001
cccc: 0001

If a "D" code is not included, then "H" may exceed 24 (or even 99) hours (however "HH" produces "**" if it exceeds 99). If a "P" code is included, then 12-hour civilian time is given, otherwise 24-hour military time is assumed. The "SSS" code will use the same formatting as "S", except that any fractions of a second will be included (for as many decimal places as needed, out to 0.0001). For instance, 4.75 seconds will format as "4.75". The "C"-based codes format tenths, hundredths, thousandths, and ten- thousandths of seconds with a fixed number of digits and no decimal point. For instance, 0.75 seconds will format as "8" ("C"), "75" ("CC"), "750" ("CCC"), or "7500" ("CCCC"). This can be used to format fractions of a second with a given precision (as opposed to "SSS" which gives a variable precision). For instance, "S.CC" will format 1.0234 as "1.02" and "SS/CCCC" will format it as "01/0234".

Formatted text is returned as a variable-length character vector. A vector (or matrix) of times is returned as a vector (or matrix) of character vectors. However, a scalar (not a 1½ vector!) time value is returned as an unnested character vector for convenience. A "null" time (any value < 0) is formatted as an empty character vector (regardless of the picture format requested) to conveniently deal with "missing" or "undefined" times. A fraction of a day (such as that returned by 1|dNow) should be converted to seconds with code similar to "Ž0.5+86400×1|dNow" before providing it as input to tSpell. Note: The "keyword" letters mentioned above are not normally available to use as constant text within the picture. However, the special "character escape" symbol "backslash" ("\") may be used immediately in front of any of these letters (or "\" itself) to indicate that the following character is to be treated as a literal and not otherwise processed.

{JJJJJJJ} ← dStartDT {year} For a given year, return the date (JDN) on which U.S. Daylight Savings Time begins during that year (using standard rules as a constant).

{JJJJJJJ} ← dStartST {year} For a given year, return the date (JDN) on which U.S. Standard Time begins during that year (using standard rules as a constant).

{text} ← [picture] dISpell {JJJJJJJ} This is an internationalized version of ’dSpell. The syntax and usage is the same as ’dSpell, except that results are returned with local-specific language. The locale ID # may be specified in optional global variable LOCALE_LCID. If not specified, it defaults to the user's default locale.

{JJJJJJJ} ← dIStartDT {year} Internationalized version of ’dStartDT. For a given year, return the date (JDN) on which Daylight Savings Time begins during that year, as Windows defines it. Note: For some locales (e.g. in the Southern hemisphere) this date may occur after the start of Standard Time (’dIStartST).

{JJJJJJJ} ← dIStartST {year} Internationalized version of ’dStartST. For a given year, return the date (JDN) on which Standard Time begins during that year, as Windows defines it. Note: For some locales (e.g. in the Southern hemisphere) this date may occur before the start of Daylight Savings Time (’dIStartDT).

{text} ← [picture] tISpell {SSSSS} This is an internationalized version of ’tSpell. The syntax and usage is the same as ’tSpell, except that results are returned with local-specific language. The locale ID # may be specified in optional global variable LOCALE_LCID. If not specified, it defaults to the user's default locale.

{local} ← tLocal {UTC} Convert a UTC (GMT) date/time to a localized one using the Windows-defined time zone. Note: Unlike Windows' built-in conversion, Daylight Savings Time shifts are computed based on the specified date rather than on the current (ŒTS) date. The date/time is specified and returned as a Julian Day Number with an optional fractional portion indicating the time of day (e.g. X.0 = midnight, X.5 = noon, X.75 = 6pm).

{UTC} ← tUTC {local} Convert a local date/time to a UTC (GMT) one using the Windows-defined time zone. Note: Unlike Windows' built-in conversion, Daylight Savings Time shifts are computed based on the specified date rather than on the current (ŒTS) date. The date/time is specified and returned as a Julian Day Number with an optional fractional portion indicating the time of day (e.g. X.0 = midnight, X.5 = noon, X.75 = 6pm).

{local} ← dDaytime {server} Obtain the current time and date from a NIST server (or any other server that uses the NIST form of the "Daytime" protocol). The local time is returned as a fractional JDN (comparable to dNow, which obtains ŒTS time) and should be accurate to about a second. The argument should be the name or IP address of a NIST-like server that provides this service. (A list of sample NIST servers is included in the function comments.) An argument of '' can be supplied to use a default value of 'time.nist.gov' ('192.43.244.18'). This result can be used with dSetTime to change your current computer's hardware clock to the correct time.

dSetTime {newtimestamp} This function permanently *CHANGES* your computer's hardware clock to the given _local_ date & time! Use with caution. The new date & time may be supplied in either ŒTS form or a fractional JDN form (such as that returned by ’dDaytime). The timestamp to be set should normally be obtained from ’dDaytime or a similar time-synchronization source.

{expires} ← CookieExpires {local}
{local} ← CookieExpires {expires} This function formats a local time in a format suitable for use with the EXPIRES= parameter of a web browser set-cookie command. This is primarily useful when writing APL-based web applications that use cookies. It can also be used to convert such a format back to a local timestamp.

The date/time argument may be provided in any of the following forms: ŒTS form (3, 5, 6, or 7 ½) +ŒTS form (adds to ŒTS if year < 100) YYYYMMDD[.HHMMSS] form dJulian form (integer or fractional) (dToday + nnn may also be useful here for future days) (dNow + nn÷24 may also be useful here for future hours) DDD[.HHMMSS] to add days/hours to current time The argument is assumed to be in local time and will be converted to UTC (GMT) for formatting.

The result is text exactly of the (required) form: 'expires=Wed, 01-May-2005 00:00:00 GMT' To perform a reverse conversion, supply the expiration character vector in the above standard form and it will be converted back to (local) ŒTS form. ('expires=', 'GMT', and day-of-week are optional. No checking is done.)

horizontal line
to home page e-mail Page Top