Contributor: TREVOR J. CARLSEN        

Unit TCDate;

  { Author: Trevor J Carlsen  Released into the public domain }
  {         PO Box 568                                        }
  {         Port Hedland                                      }
  {         Western Australia 6721                            }
  {         Voice +61 91 732 026                              }

Interface

Uses Dos;

Type
  Date          = Word;
  UnixTimeStamp = LongInt;

Const
  WeekDays   : Array[0..6] of String[9] =
               ('Sunday','Monday','Tuesday','Wednesday','Thursday',
                'Friday','Saturday');
  months     : Array[1..12] of String[9] =
               ('January','February','March','April','May','June','July',
                'August','September','October','November','December');

Function DayofTheWeek(pd : date): Byte;
 { Returns the day of the week For any date  Sunday = 0 .. Sat = 6    }
 { pd = a packed date as returned by the Function PackedDate          }
 { eg...  Writeln('today is ',WeekDays[DayofTheWeek(today))];         }

Function PackedDate(yr,mth,d: Word): date;
 { Packs a date into a Word which represents the number of days since }
 { Dec 31,1899   01-01-1900 = 1                                       }

Function UnixTime(yr,mth,d,hr,min,sec: Word): UnixTimeStamp;
 { Packs a date and time into a four Byte unix style Variable which   }
 { represents the number of seconds that have elapsed since midnight  }
 { on Jan 1st 1970.                                                   }

Procedure UnPackDate(Var yr,mth,d: Word; pd : date);
 { Unpacks a Word returned by the Function PackedDate into its        }
 { respective parts of year, month and day                            }

Procedure UnPackUnix(Var yr,mth,d,hr,min,sec: Word; uts: UnixTimeStamp);
 { Unpacks a UnixTimeStamp Variable into its Component parts.         }

Function DateStr(pd: date; Format: Byte): String;
 { Unpacks a Word returned by the Function PackedDate into its        }
 { respective parts of year, month and day and then returns a String  }
 { Formatted according to the specifications required.                }
 { if the Format is > 9 then the day of the week is prefixed to the   }
 { returned String.                                                   }
 { Formats supported are:                                             }
 {     0:  dd/mm/yy                                                   }
 {     1:  mm/dd/yy                                                   }
 {     2:  dd/mm/yyyy                                                 }
 {     3:  mm/dd/yyyy                                                 }
 {     4:  [d]d xxx yyyy   (xxx is alpha month of 3 Chars)            }
 {     5:  xxx [d]d, yyyy                                             }
 {     6:  [d]d FullAlphaMth yyyy                                     }
 {     7:  FullAlphaMth [d]d, yyyy                                    }
 {     8:  [d]d-xxx-yy                                                }
 {     9:  xxx [d]d, 'yy                                              } 
 
Function ValidDate(yr,mth,d : Word; Var errorcode : Byte): Boolean;
 { Validates the date and time data to ensure no out of range errors  }
 { can occur and returns an error code to the calling Procedure. A    }
 { errorcode of zero is returned if no invalid parameter is detected. }
 { Errorcodes are as follows:                                         }

 {   Year out of range (< 1901 or > 2078) bit 0 of errorcode is set.  }
 {   Month < 1 or > 12                    bit 1 of errorcode is set.  }
 {   Day < 1 or > 31                      bit 2 of errorcode is set.  }
 {   Day out of range For month           bit 2 of errorcode is set.  }

Procedure ParseDateString(Var dstr; Var y,m,d : Word; Format : Byte);
 { Parses a date String in several Formats into its Component parts   }
 { It is the Programmer's responsibility to ensure that the String    }
 { being parsed is a valid date String in the Format expected.        }
 { Formats supported are:                                             }
 {     0:  dd/mm/yy[yy]                                               }
 {     1:  mm/dd/yy[yy]                                               } 

Function NumbofDaysInMth(y,m : Word): Byte;
 { returns the number of days in any month                            }

Function IncrMonth(pd: date; n: Word): date;
 { Increments pd by n months.                                         }

Function today : date;
 { returns the number of days since 01-01-1900                        }

Function ordDate (Y,M,D : Word):LongInt; { returns ordinal Date yyddd }

Function Dateord (S : String) : String;    { returns Date as 'yymmdd' }



{============================================================================= }

Implementation

 Const
  TDays       : Array[Boolean,0..12] of Word =
                ((0,31,59,90,120,151,181,212,243,273,304,334,365),
                (0,31,60,91,121,152,182,213,244,274,305,335,366));
  UnixDatum   = LongInt(25568);
  SecsPerDay  = 86400;
  SecsPerHour = LongInt(3600);
  SecsPerMin  = LongInt(60);
  MinsPerHour = 60;

Function DayofTheWeek(pd : date): Byte;
  begin
    DayofTheWeek := pd mod 7;
  end; { DayofTheWeek }

Function PackedDate(yr,mth,d : Word): date;
  { valid For all years 1901 to 2078                                  }
  Var
    temp  : Word;
    lyr   : Boolean;
  begin
    lyr   := (yr mod 4 = 0);
    if yr >= 1900 then
      dec(yr,1900);
    temp  := yr * Word(365) + (yr div 4) - ord(lyr);
    inc(temp,TDays[lyr][mth-1]);
    inc(temp,d);
    PackedDate := temp;
  end;  { PackedDate }

Function UnixTime(yr,mth,d,hr,min,sec: Word): UnixTimeStamp;
  { Returns the number of seconds since 00:00 01/01/1970 }
  begin
    UnixTime := SecsPerDay * (PackedDate(yr,mth,d) - UnixDatum) +
                SecsPerHour * hr + SecsPerMin * min + sec;
  end;  { UnixTime }

Procedure UnPackDate(Var yr,mth,d: Word; pd : date);
  { valid For all years 1901 to 2078                                  }
  Var
    julian : Word;
    lyr    : Boolean;
  begin
    d      := pd;
    yr     := (LongInt(d) * 4) div 1461;
    julian := d - (yr * 365 + (yr div 4));
    inc(yr,1900);
    lyr    := (yr mod 4 = 0);
    inc(julian,ord(lyr));
    mth    := 0;
    While julian > TDays[lyr][mth] do
      inc(mth);
    d      := julian - TDays[lyr][mth-1];
  end; { UnPackDate }

Procedure UnPackUnix(Var yr,mth,d,hr,min,sec: Word; uts: UnixTimeStamp);
  Var
    temp : UnixTimeStamp;
  begin
    UnPackDate(yr,mth,d,date(uts div SecsPerDay) + UnixDatum);
    temp   := uts mod SecsPerDay;
    hr     := temp div SecsPerHour;
    min    := (temp mod SecsPerHour) div MinsPerHour;
    sec    := temp mod SecsPerMin;
  end;  { UnPackUnix }

Function DateStr(pd: date; Format: Byte): String;

  Var
    y,m,d    : Word;
    YrStr    : String[5];
    MthStr   : String[11];
    DayStr   : String[8];
    TempStr  : String[5];
  begin
    UnpackDate(y,m,d,pd);
    str(y,YrStr);
    str(m,MthStr);
    str(d,DayStr);
    TempStr := '';
    if Format > 9 then 
      TempStr := copy(WeekDays[DayofTheWeek(pd)],1,3) + ' ';
    if (Format mod 10) < 4 then begin
      if m < 10 then 
        MthStr := '0'+MthStr;
      if d < 10 then
        DayStr := '0'+DayStr;
    end;
    Case Format mod 10 of  { Force Format to a valid value }
      0: DateStr := TempStr+DayStr+'/'+MthStr+'/'+copy(YrStr,3,2);
      1: DateStr := TempStr+MthStr+'/'+DayStr+'/'+copy(YrStr,3,2);
      2: DateStr := TempStr+DayStr+'/'+MthStr+'/'+YrStr;
      3: DateStr := TempStr+MthStr+'/'+DayStr+'/'+YrStr;
      4: DateStr := TempStr+DayStr+' '+copy(months[m],1,3)+' '+YrStr;
      5: DateStr := TempStr+copy(months[m],1,3)+' '+DayStr+' '+YrStr;
      6: DateStr := TempStr+DayStr+' '+months[m]+' '+YrStr;
      7: DateStr := TempStr+months[m]+' '+DayStr+' '+YrStr;
      8: DateStr := TempStr+DayStr+'-'+copy(months[m],1,3)+'-'+copy(YrStr,3,2);
      9: DateStr := TempStr+copy(months[m],1,3)+' '+DayStr+', '''+copy(YrStr,3,2);
    end;  { Case }  
  end;  { DateStr }

Function ValidDate(yr,mth,d : Word; Var errorcode : Byte): Boolean;
  begin
    errorcode := 0;
    if (yr < 1901) or (yr > 2078) then
      errorcode := (errorcode or 1);
    if (d < 1) or (d > 31) then
      errorcode := (errorcode or 2);
    if (mth < 1) or (mth > 12) then
      errorcode := (errorcode or 4);
    Case mth of
      4,6,9,11: if d > 30 then errorcode := (errorcode or 2);
             2: if d > (28 + ord((yr mod 4) = 0)) then
                  errorcode := (errorcode or 2);
      end; {Case }
    ValidDate := (errorcode = 0);
    if errorcode <> 0 then Write(#7);
  end; { ValidDate }

Procedure ParseDateString(Var dstr; Var y,m,d : Word; Format : Byte);
  Var
    left,middle       : Word;
    errcode           : Integer;
    st                : String Absolute dstr;
  begin
    val(copy(st,1,2),left,errcode);
    val(copy(st,4,2),middle,errcode);
    val(copy(st,7,4),y,errcode);
    Case Format of
      0: begin
           d := left;
           m := middle;
         end;
      1: begin
           d := middle;
           m := left;
         end;
    end; { Case }
  end; { ParseDateString }
    
Function NumbofDaysInMth(y,m : Word): Byte;
  { valid For the years 1901 - 2078                                   }
  begin
    Case m of
      1,3,5,7,8,10,12: NumbofDaysInMth := 31;
      4,6,9,11       : NumbofDaysInMth := 30;
      2              : NumbofDaysInMth := 28 +
                       ord((y mod 4) = 0);
    end;
  end; { NumbofDaysInMth }

Function IncrMonth(pd: date; n: Word): date;
  Var y,m,d : Word;
  begin
    UnpackDate(y,m,d,pd);
    dec(m);
    inc(m,n);
    inc(y,m div 12); { if necessary increment year }
    m := succ(m mod 12);
    if d > NumbofDaysInMth(y,m) then
      d := NumbofDaysInMth(y,m);
    IncrMonth := PackedDate(y,m,d);
  end;  { IncrMonth }

Function today : date;
  Var y,m,d,dw : Word;
  begin
    GetDate(y,m,d,dw);
    today := PackedDate(y,m,d);
  end;  { today }

Function ordDate (Y,M,D : Word): LongInt;     { returns ordinal Date as yyddd }
Var LYR  : Boolean;
    TEMP : LongInt;
begin
  LYR := (Y mod 4 = 0) and (Y <> 1900);
  Dec (Y,1900);
  TEMP := LongInt(Y) * 1000;
  Inc (TEMP,TDays[LYR][M-1]);    { Compute # days through last month }
  Inc (TEMP,D);                                  { # days this month }
  ordDate := TEMP
end;  { ordDate }

Function Dateord (S : String) : String;    { returns Date as 'yymmdd' }
Var LYR   : Boolean;
    Y,M,D : Word;
    TEMP  : LongInt;
    N     : Integer;
    StoP  : Boolean;
    SW,ST : String[6];
begin
  Val (Copy(S,1,2),Y,N); Val (Copy(S,3,3),TEMP,N);
  Inc (Y,1900); LYR := (Y mod 4 = 0) and (Y <> 1900); Dec (Y,1900);
  N := 0; StoP := False;
  While not StoP and (TDays[LYR][N] < TEMP) do
    Inc (N);
  M := N;                                                     { month }
  D := TEMP-TDays[LYR][M-1];        { subtract # days thru this month }
  Str(Y:2,SW); Str(M:2,ST);
  if ST[1] = ' ' then ST[1] := '0'; SW := SW+ST;
  Str(D:2,ST);
  if ST[1] = ' ' then ST[1] := '0'; SW := SW+ST;
  Dateord := SW
end;  { Dateord }




end.  { Unit TCDate }