Initial revision
[AROS-Contrib.git] / development / compilers / freepascal / rtl / objpas / dati.inc
blob5748a15baca3756c117c5ddcdc77ede21a089682
2     *********************************************************************
3     $Id$
4     Copyright (C) 1997, 1998 Gertjan Schouten
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19     *********************************************************************
21     System Utilities For Free Pascal
24 {==============================================================================}
25 {   internal functions                                                         }
26 {==============================================================================}
28 const
29    DayTable: array[Boolean, 1..12] of longint =
30       ((0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334),
31        (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335));
33 function DoEncodeDate(Year, Month, Day: Word): longint;
34 var
35   c, ya: cardinal;
36 begin
37   if (Month > 0) and (Month < 13) and (Day > 0) and (Day < 32) then
38    begin
39      if month > 2 then
40       Dec(Month,3)
41      else
42       begin
43         Inc(Month,9);
44         Dec(Year);
45       end;
46      c:= Year DIV 100;
47      ya:= Year - 100*c;
48      result := (146097*c) SHR 2 + (1461*ya) SHR 2 + (153*cardinal(Month)+2) DIV 5 + cardinal(Day) - 693900;
49    end
50   else
51    result:=0;
52 end;
54 function DoEncodeTime(Hour, Minute, Second, MilliSecond: word): longint;
55 begin
56   If ((hour>=0) and (Hour<24)) and
57      ((Minute>=0) and (Minute<60)) and
58      ((Second>=0) and (Second<60)) and
59      ((MilliSecond>=0) and (Millisecond<1000)) then
60     Result := (Hour * 3600000 + Minute * 60000 + Second * 1000 + MilliSecond)
61   else
62     Result:=0;
63 end;
66 {==============================================================================}
67 {   Public functions                                                           }
68 {==============================================================================}
70 {   DateTimeToTimeStamp converts DateTime to a TTimeStamp   }
72 function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;
73 begin
74   result.Time := Trunc(Frac(DateTime) * MSecsPerDay);
75   result.Date := 1 + DateDelta + Trunc(System.Int(DateTime));
76 end ;
78 {   TimeStampToDateTime converts TimeStamp to a TDateTime value   }
80 function TimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;
81 begin
82   result := (TimeStamp.Date - DateDelta - 1) + (TimeStamp.Time / MSecsPerDay);
83 end ;
85 {   MSecsToTimeStamp   }
87 function MSecsToTimeStamp(MSecs: comp): TTimeStamp;
88 begin
89   result.Date := Round(msecs / msecsperday);
90   msecs:= comp(msecs-result.date*msecsperday);
91   result.Time := Round(MSecs);
92 end ;
94 {   TimeStampToMSecs   }
96 function TimeStampToMSecs(const TimeStamp: TTimeStamp): comp;
97 begin
98   result := TimeStamp.Time + timestamp.date*msecsperday;
99 end ;
101 {   EncodeDate packs three variables Year, Month and Day into a
102     TDateTime value the result is the number of days since 12/30/1899   }
104 function EncodeDate(Year, Month, Day: word): TDateTime;
105 begin
106   result := DoEncodeDate(Year, Month, Day);
107 end ;
109 {   EncodeTime packs four variables Hour, Minute, Second and MilliSecond into
110     a TDateTime value     }
112 function EncodeTime(Hour, Minute, Second, MilliSecond:word):TDateTime;
113 begin
114   Result := DoEncodeTime(hour, minute, second, millisecond) / MSecsPerDay;
115 end ;
117 {   DecodeDate unpacks the value Date into three values:
118     Year, Month and Day   }
120 procedure DecodeDate(Date: TDateTime; var Year, Month, Day: word);
122   j : cardinal;
123 begin
124   j := pred((Trunc(System.Int(Date)) + 693900) SHL 2);
125   Year:= j DIV 146097;
126   j:= j - 146097 * cardinal(Year);
127   Day := j SHR 2;
128   j:=(Day SHL 2 + 3) DIV 1461;
129   Day:= (cardinal(Day) SHL 2 + 7 - 1461*j) SHR 2;
130   Month:=(5 * Day-3) DIV 153;
131   Day:= (5 * Day +2 - 153*Month) DIV 5;
132   Year:= 100 * cardinal(Year) + j;
133   if Month < 10 then
134    inc(Month,3)
135   else
136     begin
137       dec(Month,9);
138       inc(Year);
139     end;
140 end;
142 {   DecodeTime unpacks Time into four values:
143     Hour, Minute, Second and MilliSecond    }
145 procedure DecodeTime(Time: TDateTime; var Hour, Minute, Second, MilliSecond: word);
147   l : cardinal;
148 begin
149  l := Round(Frac(time) * MSecsPerDay);
150  Hour   := l div 3600000;
151  l := l mod 3600000;
152  Minute := l div 60000;
153  l := l mod 60000;
154  Second := l div 1000;
155  l := l mod 1000;
156  MilliSecond := l;
157 end;
159 {   DateTimeToSystemTime converts DateTime value to SystemTime   }
161 procedure DateTimeToSystemTime(DateTime: TDateTime; var SystemTime: TSystemTime);
162 begin
163   DecodeDate(DateTime, SystemTime.Year, SystemTime.Month, SystemTime.Day);
164   DecodeTime(DateTime, SystemTime.Hour, SystemTime.Minute, SystemTime.Second, SystemTime.MilliSecond);
165 end ;
167 {   SystemTimeToDateTime converts SystemTime to a TDateTime value   }
169 function SystemTimeToDateTime(const SystemTime: TSystemTime): TDateTime;
170 begin
171   result := DoEncodeDate(SystemTime.Year, SystemTime.Month, SystemTime.Day) +
172             DoEncodeTime(SystemTime.Hour, SystemTime.Minute, SystemTime.Second, SystemTime.MilliSecond) / MSecsPerDay;
173 end ;
175 {   DayOfWeek returns the Day of the week (sunday is day 1)  }
177 function DayOfWeek(DateTime: TDateTime): integer;
178 begin
179   Result := 1 + (Abs(Trunc(DateTime) - 1) mod 7);
180 end ;
182 {   Date returns the current Date   }
184 function Date: TDateTime;
186   SystemTime: TSystemTime;
187 begin
188   GetLocalTime(SystemTime);
189   result := DoEncodeDate(SystemTime.Year, SystemTime.Month, SystemTime.Day);
190 end ;
192 {   Time returns the current Time   }
194 function Time: TDateTime;
196   SystemTime: TSystemTime;
197 begin
198   GetLocalTime(SystemTime);
199   Result := DoEncodeTime(SystemTime.Hour,SystemTime.Minute,SystemTime.Second,SystemTime.MilliSecond) / MSecsPerDay;
200 end ;
202 {   Now returns the current Date and Time    }
204 function Now: TDateTime;
206   SystemTime: TSystemTime;
207 begin
208   GetLocalTime(SystemTime);
209   result := DoEncodeDate(SystemTime.Year,SystemTime.Month,SystemTime.Day) +
210             DoEncodeTime(SystemTime.Hour,SystemTime.Minute,SystemTime.Second,SystemTime.MilliSecond) / MSecsPerDay;
211 end ;
213 {   IncMonth increments DateTime with NumberOfMonths months,
214     NumberOfMonths can be less than zero   }
216 function IncMonth(const DateTime: TDateTime; NumberOfMonths: integer): TDateTime;
218   Year, Month, Day: word;
219   S : Integer;
220 begin
221   If NumberOfMonths>=0 then
222     s:=1
223   else
224     s:=-1;
225   DecodeDate(DateTime, Year, Month, Day);
226   inc(Year,(NumberOfMonths div 12));
227   inc(Month,(NumberOfMonths mod 12)-1); // Mod result always positive
228   if Month>11 then
229    begin
230      Dec(Month, S*12);
231      Inc(Year, S);
232    end;
233   Inc(Month);                            {   Months from 1 to 12   }
234   if (Month = 2) and (IsLeapYear(Year)) and (Day > 28) then
235    Day := 28;
236   result := Frac(DateTime) + DoEncodeDate(Year, Month, Day);
237 end ;
239 {  IsLeapYear returns true if Year is a leap year   }
241 function IsLeapYear(Year: Word): boolean;
242 begin
243   Result := (Year mod 4 = 0) and ((Year mod 100 <> 0) or (Year mod 400 = 0));
244 end;
246 {  DateToStr returns a string representation of Date using ShortDateFormat   }
248 function DateToStr(Date: TDateTime): string;
249 begin
250   result := FormatDateTime('ddddd', Date);
251 end ;
253 {  TimeToStr returns a string representation of Time using ShortTimeFormat   }
255 function TimeToStr(Time: TDateTime): string;
256 begin
257   result := FormatDateTime('t', Time);
258 end ;
260 {   DateTimeToStr returns a string representation of DateTime using ShortDateTimeFormat   }
262 function DateTimeToStr(DateTime: TDateTime): string;
263 begin
264   result := FormatDateTime('c', DateTime);
265 end ;
267 {   StrToDate converts the string S to a TDateTime value
268     if S does not represent a valid date value
269     an EConvertError will be raised   }
271 function StrToDate(const S: string): TDateTime;
273    df:string;
274    d,m,y:word;
275    n,i:longint;
276    c:word;
277    dp,mp,yp,which : Byte;
278    s1:string[4];
279    values:array[1..3] of longint;
280    LocalTime:tsystemtime;
281 begin
282   df := UpperCase(ShortDateFormat);
283   { Determine order of D,M,Y }
284   yp:=0;
285   mp:=0;
286   dp:=0;
287   Which:=0;
288   i:=0;
289   while (i<Length(df)) and (Which<3) do
290    begin
291      inc(i);
292      Case df[i] of
293        'Y' :
294          if yp=0 then
295           begin
296             Inc(Which);
297             yp:=which;
298           end;
299        'M' :
300          if mp=0 then
301           begin
302             Inc(Which);
303             mp:=which;
304           end;
305        'D' :
306          if dp=0 then
307           begin
308             Inc(Which);
309             dp:=which;
310           end;
311      end;
312    end;
313   if Which<>3 then
314    Raise EConvertError.Create('Illegal format string');
315 { Get actual values }
316   for i := 1 to 3 do
317     values[i] := 0;
318   s1 := '';
319   n := 0;
320   for i := 1 to length(s) do
321    begin
322      if (s[i] in ['0'..'9']) then
323       s1 := s1 + s[i];
324      if (s[i] in [dateseparator,' ']) or (i = length(s)) then
325       begin
326         inc(n);
327         if n>3 then
328          Raise EConvertError.Create('Invalid date format');
329         val(s1, values[n], c);
330         if c<>0 then
331          Raise EConvertError.Create('Invalid date format');
332         s1 := '';
333       end ;
334    end ;
335   // Fill in values.
336   If N=3 then
337    begin
338      y:=values[yp];
339      m:=values[mp];
340      d:=values[dp];
341    end
342   Else
343   begin
344     getLocalTime(LocalTime);
345     y := LocalTime.Year;
346     If n<2 then
347      begin
348        d:=values[1];
349        m := LocalTime.Month;
350      end
351     else
352      If dp<mp then
353       begin
354         d:=values[1];
355         m:=values[2];
356       end
357     else
358       begin
359         d:=values[2];
360         m:=values[1];
361       end;
362   end;
363   if (y >= 0) and (y < 100) then
364    inc(y,1900);
365   Result := DoEncodeDate(y, m, d);
366 end ;
369 {   StrToTime converts the string S to a TDateTime value
370     if S does not represent a valid time value an
371     EConvertError will be raised   }
373 function StrToTime(const s: string): TDateTime;
375    Len, Current: integer; PM: boolean;
377    function GetElement: integer;
378    var
379      j: integer; c: word;
380    begin
381    result := -1;
382    Inc(Current);
383    while (result = -1) and (Current < Len) do begin
384       if S[Current] in ['0'..'9'] then begin
385          j := Current;
386          while (Current < Len) and (s[Current + 1] in ['0'..'9']) do
387             Inc(Current);
388          val(copy(S, j, 1 + Current - j), result, c);
389          end
390       else if (S[Current] = TimeAMString[1]) or (S[Current] in ['a', 'A']) then begin
391          Current := 1 + Len;
392          end
393       else if (S[Current] = TimePMString[1]) or (S[Current] in ['p', 'P']) then begin
394          Current := 1 + Len;
395          PM := True;
396          end
397       else if (S[Current] = TimeSeparator) or (S[Current] = ' ') then
398          Inc(Current)
399       else
400         raise EConvertError.Create('Invalid Time format');
401       end ;
402    end ;
405    i: integer;
406    TimeValues: array[0..4] of integer;
408 begin
409 Current := 0;
410 Len := length(s);
411 PM := False;
412 for i:=0 to 4 do
413   timevalues[i]:=0;
414 i := 0;
415 TimeValues[i] := GetElement;
416 while (i < 5) and (TimeValues[i] <> -1) do begin
417    i := i + 1;
418    TimeValues[i] := GetElement;
419    end ;
420 If (i<5) and (TimeValues[I]=-1) then
421   TimeValues[I]:=0;
422 if PM then Inc(TimeValues[0], 12);
423 result := EncodeTime(TimeValues[0], TimeValues[1], TimeValues[2], TimeValues[3]);
424 end ;
426 {   StrToDateTime converts the string S to a TDateTime value
427     if S does not represent a valid date and time value
428     an EConvertError will be raised   }
430 function StrToDateTime(const s: string): TDateTime;
431 var i: integer;
432 begin
433 i := pos(' ', s);
434 if i > 0 then result := StrToDate(Copy(S, 1, i - 1)) + StrToTime(Copy(S, i + 1, length(S)))
435 else result := StrToDate(S);
436 end ;
438 {   FormatDateTime formats DateTime to the given format string FormatStr   }
440 function FormatDateTime(FormatStr: string; DateTime: TDateTime): string;
442    ResultLen: integer;
443    ResultBuffer: array[0..255] of char;
444    ResultCurrent: pchar;
446    procedure StoreStr(Str: pchar; Len: integer);
447    begin
448    if ResultLen + Len < SizeOf(ResultBuffer) then begin
449       StrMove(ResultCurrent, Str, Len);
450       ResultCurrent := ResultCurrent + Len;
451       ResultLen := ResultLen + Len;
452       end ;
453    end ;
455    procedure StoreString(const Str: string);
456    var Len: integer;
457    begin
458    Len := Length(Str);
459    if ResultLen + Len < SizeOf(ResultBuffer) then begin
460       StrMove(ResultCurrent, pchar(Str), Len);
461       ResultCurrent := ResultCurrent + Len;
462       ResultLen := ResultLen + Len;
463       end;
464    end;
466    procedure StoreInt(Value, Digits: integer);
467    var S: string; Len: integer;
468    begin
469    S := IntToStr(Value);
470    Len := Length(S);
471    if Len < Digits then begin
472       S := copy('0000', 1, Digits - Len) + S;
473       Len := Digits;
474       end ;
475    StoreStr(pchar(@S[1]), Len);
476    end ;
478    Function TimeReFormat(Const S : string) : string;
479    // Change m into n for time formatting.
480    Var i : longint;
482    begin
483      Result:=S;
484      For I:=1 to Length(Result) do
485        If Result[i]='m' then
486          result[i]:='n';
487    end;
490    Year, Month, Day, DayOfWeek, Hour, Minute, Second, MilliSecond: word;
492    procedure StoreFormat(const FormatStr: string);
493    var
494       Token: char;
495       FormatCurrent: pchar;
496       FormatEnd: pchar;
497       Count: integer;
498       Clock12: boolean;
499       P: pchar;
501    begin
502    FormatCurrent := Pchar(FormatStr);
503    FormatEnd := FormatCurrent + Length(FormatStr);
504    Clock12 := false;
505    P := FormatCurrent;
506    while P < FormatEnd do begin
507       Token := UpCase(P^);
508       if Token in ['"', ''''] then begin
509          P := P + 1;
510          while (P < FormatEnd) and (P^ <> Token) do
511             P := P + 1;
512          end
513       else if Token = 'A' then begin
514          if (StrLIComp(P, 'A/P', 3) = 0) or
515             (StrLIComp(P, 'AMPM', 4) = 0) or
516             (StrLIComp(P, 'AM/PM', 5) = 0) then begin
517             Clock12 := true;
518             break;
519             end ;
520          end ;
521       P := P + 1;
522       end ;
523    while FormatCurrent < FormatEnd do begin
524       Token := UpCase(FormatCurrent^);
525       Count := 1;
526       P := FormatCurrent + 1;
527          case Token of
528             '''', '"': begin
529                while (P < FormatEnd) and (p^ <> Token) do
530                   P := P + 1;
531                P := P + 1;
532                Count := P - FormatCurrent;
533                StoreStr(FormatCurrent + 1, Count - 2);
534                end ;
535             'A': begin
536                if StrLIComp(FormatCurrent, 'AMPM', 4) = 0 then begin
537                   Count := 4;
538                   if Hour < 12 then StoreString(TimeAMString)
539                   else StoreString(TimePMString);
540                   end
541                else if StrLIComp(FormatCurrent, 'AM/PM', 5) = 0 then begin
542                   Count := 5;
543                   if Hour < 12 then StoreStr('am', 2)
544                   else StoreStr('pm', 2);
545                   end
546                else if StrLIComp(FormatCurrent, 'A/P', 3) = 0 then begin
547                   Count := 3;
548                   if Hour < 12 then StoreStr('a', 1)
549                   else StoreStr('p', 1);
550                   end
551                else
552                  Raise EConvertError.Create('Illegal character in format string');
553                end ;
554             '/': StoreStr(@DateSeparator, 1);
555             ':': StoreStr(@TimeSeparator, 1);
556             ' ', 'C', 'D', 'H', 'M', 'N', 'S', 'T', 'Y': begin
557                while (P < FormatEnd) and (UpCase(P^) = Token) do
558                   P := P + 1;
559                Count := P - FormatCurrent;
560                   case Token of
561                      ' ': StoreStr(FormatCurrent, Count);
562                      'Y': begin
563                            case Count of
564                               1: StoreInt(Year, 0);
565                               2: StoreInt(Year mod 100, 2);
566                               4: StoreInt(Year, 4);
567                            end ;
568                         end ;
569                      'M': begin
570                            case Count of
571                               1: StoreInt(Month, 0);
572                               2: StoreInt(Month, 2);
573                               3: StoreString(ShortMonthNames[Month]);
574                               4: StoreString(LongMonthNames[Month]);
575                            end ;
576                         end ;
577                      'D': begin
578                            case Count of
579                               1: StoreInt(Day, 0);
580                               2: StoreInt(Day, 2);
581                               3: StoreString(ShortDayNames[DayOfWeek]);
582                               4: StoreString(LongDayNames[DayOfWeek]);
583                               5: StoreFormat(ShortDateFormat);
584                               6: StoreFormat(LongDateFormat);
585                            end ;
586                         end ;
587                      'H': begin
588                         if Clock12 then begin
589                            if Count = 1 then StoreInt(Hour mod 12, 0)
590                            else StoreInt(Hour mod 12, 2);
591                            end
592                         else begin
593                            if Count = 1 then StoreInt(Hour, 0)
594                            else StoreInt(Hour, 2);
595                            end ;
596                         end ;
597                      'N': begin
598                         if Count = 1 then StoreInt(Minute, 0)
599                         else StoreInt(Minute, 2);
600                         end ;
601                      'S': begin
602                         if Count = 1 then StoreInt(Second, 0)
603                         else StoreInt(Second, 2);
604                         end ;
605                      'T': begin
606                         if Count = 1 then StoreFormat(timereformat(ShortTimeFormat))
607                         else StoreFormat(TimeReformat(LongTimeFormat));
608                         end ;
609                      'C':
610                        begin
611                          StoreFormat(ShortDateFormat);
612                          if (Hour<>0) or (Minute<>0) or (Second<>0) then
613                           begin
614                             StoreString(' ');
615                             StoreFormat(TimeReformat(ShortTimeFormat));
616                           end;
617                        end;
618                   end ;
619                end ;
620             else
621               StoreStr(@Token, 1);
622          end ;
623       FormatCurrent := FormatCurrent + Count;
624       end ;
625    end ;
627 begin
628   DecodeDate(DateTime, Year, Month, Day);
629   DecodeTime(DateTime, Hour, Minute, Second, MilliSecond);
630   DayOfWeek := SysUtils.DayOfWeek(DateTime);
631   ResultLen := 0;
632   ResultCurrent := @ResultBuffer;
633   StoreFormat(FormatStr);
634   ResultBuffer[ResultLen] := #0;
635   result := StrPas(@ResultBuffer);
636 end ;
638 {   DateTimeToString formats DateTime to the given format in FormatStr   }
640 procedure DateTimeToString(var Result: string; const FormatStr: string; const DateTime: TDateTime);
641 begin
642   Result := FormatDateTime(FormatStr, DateTime);
643 end ;
646 Function DateTimeToFileDate(DateTime : TDateTime) : Longint;
648 Var YY,MM,DD,H,m,s,msec : Word;
650 begin
651   Decodedate (DateTime,YY,MM,DD);
652   If (YY<1980) or (YY>2099) then
653     Result:=0
654   else
655     begin
656     DecodeTime (DateTime,h,m,s,msec);
657     Result:=(s shr 1) or (m shl 5) or (h shl 11);
658     Result:=Result or DD shl 16 or (MM shl 21) or ((YY-1980) shl 25);
659     end;
660 end;
663 Function FileDateToDateTime (Filedate : Longint) : TDateTime;
665 Var Date,Time : Word;
667 begin
668   Date:=FileDate shr 16;
669   Time:=FileDate and $ffff;
670   Result:=EncodeDate((Date shr 9) + 1980,(Date shr 5) and 15, Date and 31) +
671           EncodeTime(Time shr 11, (Time shr 5) and 63, (Time and 31) shl 1,0);
672 end;
675   $Log$
676   Revision 1.1  2002/02/19 08:25:44  sasu
677   Initial revision
679   Revision 1.1.2.1  2000/12/16 15:57:15  jonas
680     * removed 64bit evaluations when range checking is on
682   Revision 1.1  2000/07/13 06:30:58  michael
683   + Initial import
685   Revision 1.22  2000/06/18 18:02:54  peter
686     * fixed decodetime which used trunc instead of round
688   Revision 1.21  2000/02/27 14:41:25  peter
689     * removed warnings/notes
691   Revision 1.20  2000/02/09 16:59:32  peter
692     * truncated log
694   Revision 1.19  1999/11/29 16:59:27  pierre
695    * fix for form bug 719
697   Revision 1.18  1999/11/06 14:41:30  peter
698     * truncated log
700   Revision 1.17  1999/10/28 09:52:29  peter
701     * fixed dayofweek
703   Revision 1.16  1999/08/11 21:53:04  peter
704     * fixed formatdatetime('c',...)
705     * fixed strtodate
706     * dateencode/decode is now delphi compatible
708   Revision 1.15  1999/07/24 11:21:14  peter
709     * fixed encode/decode date/time