© J R Stockton, ≥ 2008-09-02

Date and Time Calculation.

Links within this site :-

Calculational Pitfalls

Calculating with dates and times presents a number of possible pitfalls, described on relevant associated pages. Some of them occur more generally than there described. This is a comparatively simple list, to serve as a reminder :-

Critical and Significant Dates (ZIP)
A page of relevant problem dates and periods - past, present and future.
BC/AD and Year 0
There is no BC/AD Year Zero, but astronomers' notation uses ... -2 -1 0 +1 +2 ... .
Y2k
Errors can easily be caused by using two-digit years.
Leap Years
The Gregorian (or other) Rules may be misunderstood.
Year Length
The 366th day of a year may be unexpected.
Ambiguity
DD/MM/YY or MM/DD/YY or YY/MM/DD; DD/MM/YYYY or MM/DD/YYYY - YYYY-MM-DD is standard and unambiguous, but sometimes YYYY/MM/DD is more acceptable.
More Ambiguity
YYYYMMDD is ISO standard, but YYYYDDMM is alas sometimes used.
FFF
Fred Flintstone Format - M/D/Y, and/or the 12-h clock, for data.
Sorting
The ISO 8601 formats YYYY-MM-DD, YYYYMMDD sort easily as strings. So do 24-h, but not 12-h, times; and appropriate date-time combinations.
The Calendar
Old dates may be Gregorian or Julian.
The Start of the Year
Not always January 1st - O.S./N.S., Financial Year.
Month Stepping
Since month lengths vary, stepping by a number of months may need care.
The Week
The First Day and First Day Number are not always given as in ISO 8601, particularly in the USA.
Week Numbers
Should be as in ISO 8601, but other numberings are used in the USA.
Function DatePart
Has Week Number bugs.
Day Counts
JDN changes at GMT midday, MJD changes at GMT midnight. CJD and CMJD change at local midnight; so do Delphi TDateTime and VBScript CDate. Check the count origin.
The Start of the Day
It is not always 00:00:00, for organisations working overnight. In the past, the Nautical Date started 12 hours early, and the GMT Date started 12 hours late. Hebrew and Islamic calendars traditionally use 18:00 or sunset.
Resolution
Beware of rounding errors.
Time Zone
This affects civil time and date. It does not vary with season.
Time Offset
This affects civil time and date. It often varies with season.
Summer Time
This affects civil time and date.
Summer Time
This varies the length of two days each year, generally by plus one hour in Spring and minus one hour in Autumn.
Summer Time
The rules depend on location, can change, and are reversed on the other side of the Equator.
Seasons
Are reversed on the other side of the Equator.
Leap Seconds
These can occasionally vary the day length, by ± 1 second.
AM/PM Times
These can be troublesome; so are best avoided.
Modulus Errors
Operations Mod & Div can give inappropriate results when used with a negative argument.
Start of Time-Scale
Can be local time or GMT. Can be expressed as Gregorian, Julian, or other.

What have I forgotten?

Resolution

One must distinguish between the numerical resolution of the stored quantity and its update interval.

The MS-DOS timer at 40:6C is incremented 1800B0h times per 24 hours, which is 18.2 Hz & 54.9 ms. The DOS time-of-day call therefore returns centiseconds increasing in steps of 5 and 6. Many systems use this timer, even under Win98+ where it may be possible to do better.

Floating-point day counts represent time-of-day inexactly; beware of rounding errors : see Delphi Date and Time, VBScript Date and Time.

In JavaScript, time is stored as integer milliseconds (from 1970.0 UTC); but is not necessarily updated that often.

My Borland Pascal Time and Date also refers to MS-DOS on the PC.

On Algorithms

Avoid unnecessary work :-

Simplify :-

In calculating with just Y & M, it can be easier to work with 12×Y+M or 12×Y+M-1, and convert back with div and mod.

It is often useful to have available a routine to accept, in Y M D, out-of-range M & D and convert to a standard date or a daycount. Some languages have these built-in - JavaScript new Date(Y, M', D) and Date.UTC(Y, M', D) ; VBScript DateSerial for example.

Long Intervals

JulianYearsGregorian
SecondsDaysDaysSeconds
31536000365136531536000
3162240036636631622400
126230400146141460126144000
1461126230400
315576000036525100365243155673600
365253155760000
1262304000014610040014609712622780800
3155760000003652500100003652425315569520000

For the Gregorian Calendar, without Summer Time or Leap Seconds.

To calculate with a long interval, first reduce the interval by a multiple of 400 years (all such are the same length) then use the remainder with due regard to the positioning of Leap Years.

For the Julian Calendar, first reduce by a multiple of 4 years.

Terminology

I have read, from a presumed American :-

..., although they are ambiguous, "bi-weekly" and "semi-monthly" are
fairly standard terms for pay schedules in my part of the world.

  bi-weekly    : every two weeks, typically on Friday.
  semi-monthly : every half month, eg, 15th and last day.

Pascal / Delphi Routines

Pascal

These programs and units are written with BP7, are compilable with TP7, and also work with Delphi 3 in console mode in a Win 98 or XP DOS box, using "DCC32 -cc progname" to compile. All are in my programs/ Web directory, generally with EXE, ZIP, and sometimes TXT files.

Unit DATEPROX.PAS provides many Gregorian/Julian/Civil date routines, derived from first principles; it is tested by MJD_DATE.PAS, and agrees with others. Also, for Leap Years, see LEAPYEAR.PAS, which checks and compares a score of methods; and also see Leap Years.

The following presume the Gregorian calendar.

const DiM : array [1..12] of byte =
  (31,28,31,30,31,30,31,31,30,31,30,31) ;

function ValidDateGreg1(const Yr : word ; const Mo, Dy : byte) :
  boolean ;
begin (* assume, or set, {$B+} *)
  ValidDateGreg1 := (Mo in [1..12]) and (Dy>0) and
    ( (Dy<=DiM[Mo]) or ( (Dy=29) and Leap(Yr) ) ) ;
  end {ValidDateGreg1} ;

For the last day in the Gregorian month, maybe

function UltiMo(const Yr : word ; const Mo : byte) : byte ;
begin UltiMo := DiM[Mo] + Ord((Mo=2) and Leap(Yr)) end {UltiMo} ;

function ValidDateGreg2(const Yr : word ; const Mo, Dy : byte) :
  boolean ;
begin (* assume, or set, {$B+} *)
  ValidDateGreg2 :=
    (Mo in [1..12]) and (Dy>0) and (Dy<=UltiMo(Yr, Mo) ) ;
  end {ValidDateGreg2} ;

These are, slightly, tested.

Program LONGCALC.PAS also does date/time arithmetic, with vast integers; PASCHAL.PAS > has Easter routines; NOWMINUS.PAS sets date information into the Environment.

Program HEBCLNDR.PAS contains code for the Molod and the start of a given Hebrew Calendar Year, and performs various tests and investigations.

Delphi Only

These are Win32 console mode programs (PAS & EXE, in ZIP), compiled with Delphi 3 as above, for running at an MS-DOS prompt.

STD_TIME.PAS - redirect to a batch file, and execute that, to set these environment variables :-

SET CMJD=53428
SET MJD=53428
SET GMT=2005-02-27T12:17:43
SET time_t=1109506663

TZ-CHECK.PAS - test and demonstration of some Win32 Date/Time system routines.

JavaScript Routines

Home Page
Mail: no HTML
© Dr J R Stockton, near London, UK.
All Rights Reserved.
These pages are tested mainly with Firefox and W3's Tidy.
This site, , is maintained by me.
Head.