Dateutil.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:17k
- {*******************************************************}
- { }
- { Delphi VCL Extensions (RX) }
- { }
- { Copyright (c) 1995, 1996 AO ROSNO }
- { Copyright (c) 1997, 1998 Master-Bank }
- { }
- {*******************************************************}
- unit DateUtil;
- {$I RX.INC}
- {$B-,V-,R-,Q-}
- interface
- uses RTLConsts;
- function CurrentYear: Word;
- function IsLeapYear(AYear: Integer): Boolean;
- function DaysPerMonth(AYear, AMonth: Integer): Integer;
- function FirstDayOfPrevMonth: TDateTime;
- function LastDayOfPrevMonth: TDateTime;
- function FirstDayOfNextMonth: TDateTime;
- function ExtractDay(ADate: TDateTime): Word;
- function ExtractMonth(ADate: TDateTime): Word;
- function ExtractYear(ADate: TDateTime): Word;
- function IncDate(ADate: TDateTime; Days, Months, Years: Integer): TDateTime;
- function IncDay(ADate: TDateTime; Delta: Integer): TDateTime;
- function IncMonth(ADate: TDateTime; Delta: Integer): TDateTime;
- function IncYear(ADate: TDateTime; Delta: Integer): TDateTime;
- function ValidDate(ADate: TDateTime): Boolean;
- procedure DateDiff(Date1, Date2: TDateTime; var Days, Months, Years: Word);
- function MonthsBetween(Date1, Date2: TDateTime): Double;
- function DaysInPeriod(Date1, Date2: TDateTime): Longint;
- { Count days between Date1 and Date2 + 1, so if Date1 = Date2 result = 1 }
- function DaysBetween(Date1, Date2: TDateTime): Longint;
- { The same as previous but if Date2 < Date1 result = 0 }
- function IncTime(ATime: TDateTime; Hours, Minutes, Seconds, MSecs: Integer): TDateTime;
- function IncHour(ATime: TDateTime; Delta: Integer): TDateTime;
- function IncMinute(ATime: TDateTime; Delta: Integer): TDateTime;
- function IncSecond(ATime: TDateTime; Delta: Integer): TDateTime;
- function IncMSec(ATime: TDateTime; Delta: Integer): TDateTime;
- function CutTime(ADate: TDateTime): TDateTime; { Set time to 00:00:00:00 }
- type
- TDateOrder = (doMDY, doDMY, doYMD);
- TDayOfWeekName = (Sun, Mon, Tue, Wed, Thu, Fri, Sat);
- TDaysOfWeek = set of TDayOfWeekName;
- { String to date conversions }
- function GetDateOrder(const DateFormat: string): TDateOrder;
- function MonthFromName(const S: string; MaxLen: Byte): Byte;
- function StrToDateDef(const S: string; Default: TDateTime): TDateTime;
- function StrToDateFmt(const DateFormat, S: string): TDateTime;
- function StrToDateFmtDef(const DateFormat, S: string; Default: TDateTime): TDateTime;
- function DefDateFormat(FourDigitYear: Boolean): string;
- function DefDateMask(BlanksChar: Char; FourDigitYear: Boolean): string;
- {$IFDEF WIN32}
- function FormatLongDate(Value: TDateTime): string;
- function FormatLongDateTime(Value: TDateTime): string;
- {$ENDIF}
- const
- DefaultDateOrder = doDMY;
- {$IFDEF USE_FOUR_DIGIT_YEAR}
- var
- FourDigitYear: Boolean;
- {$ELSE}
- function FourDigitYear: Boolean;
- {$ENDIF USE_FOUR_DIGIT_YEAR}
- const
- CenturyOffset: Byte = 60;
- {$IFDEF WIN32}
- NullDate: TDateTime = {-693594} 0;
- {$ELSE}
- NullDate: TDateTime = 0;
- {$ENDIF}
- implementation
- uses SysUtils, {$IFDEF WIN32} Windows, {$ENDIF} Consts, rxStrUtils;
- function IsLeapYear(AYear: Integer): Boolean;
- begin
- Result := (AYear mod 4 = 0) and ((AYear mod 100 <> 0) or (AYear mod 400 = 0));
- end;
- function DaysPerMonth(AYear, AMonth: Integer): Integer;
- const
- DaysInMonth: array[1..12] of Integer =
- (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
- begin
- Result := DaysInMonth[AMonth];
- if (AMonth = 2) and IsLeapYear(AYear) then Inc(Result); { leap-year Feb is special }
- end;
- function FirstDayOfNextMonth: TDateTime;
- var
- Year, Month, Day: Word;
- begin
- DecodeDate(Date, Year, Month, Day);
- Day := 1;
- if Month < 12 then Inc(Month)
- else begin
- Inc(Year);
- Month := 1;
- end;
- Result := EncodeDate(Year, Month, Day);
- end;
- function FirstDayOfPrevMonth: TDateTime;
- var
- Year, Month, Day: Word;
- begin
- DecodeDate(Date, Year, Month, Day);
- Day := 1;
- if Month > 1 then Dec(Month)
- else begin
- Dec(Year);
- Month := 12;
- end;
- Result := EncodeDate(Year, Month, Day);
- end;
- function LastDayOfPrevMonth: TDateTime;
- var
- D: TDateTime;
- Year, Month, Day: Word;
- begin
- D := FirstDayOfPrevMonth;
- DecodeDate(D, Year, Month, Day);
- Day := DaysPerMonth(Year, Month);
- Result := EncodeDate(Year, Month, Day);
- end;
- function ExtractDay(ADate: TDateTime): Word;
- var
- M, Y: Word;
- begin
- DecodeDate(ADate, Y, M, Result);
- end;
- function ExtractMonth(ADate: TDateTime): Word;
- var
- D, Y: Word;
- begin
- DecodeDate(ADate, Y, Result, D);
- end;
- function ExtractYear(ADate: TDateTime): Word;
- var
- D, M: Word;
- begin
- DecodeDate(ADate, Result, M, D);
- end;
- function IncDate(ADate: TDateTime; Days, Months, Years: Integer): TDateTime;
- var
- D, M, Y: Word;
- Day, Month, Year: Longint;
- begin
- DecodeDate(ADate, Y, M, D);
- Year := Y; Month := M; Day := D;
- Inc(Year, Years);
- Inc(Year, Months div 12);
- Inc(Month, Months mod 12);
- if Month < 1 then begin
- Inc(Month, 12);
- Dec(Year);
- end
- else if Month > 12 then begin
- Dec(Month, 12);
- Inc(Year);
- end;
- if Day > DaysPerMonth(Year, Month) then Day := DaysPerMonth(Year, Month);
- Result := EncodeDate(Year, Month, Day) + Days + Frac(ADate);
- end;
- procedure DateDiff(Date1, Date2: TDateTime; var Days, Months, Years: Word);
- { Corrected by Anatoly A. Sanko (2:450/73) }
- var
- DtSwap: TDateTime;
- Day1, Day2, Month1, Month2, Year1, Year2: Word;
- begin
- if Date1 > Date2 then begin
- DtSwap := Date1;
- Date1 := Date2;
- Date2 := DtSwap;
- end;
- DecodeDate(Date1, Year1, Month1, Day1);
- DecodeDate(Date2, Year2, Month2, Day2);
- Years := Year2 - Year1;
- Months := 0;
- Days := 0;
- if Month2 < Month1 then begin
- Inc(Months, 12);
- Dec(Years);
- end;
- Inc(Months, Month2 - Month1);
- if Day2 < Day1 then begin
- Inc(Days, DaysPerMonth(Year1, Month1));
- if Months = 0 then begin
- Dec(Years);
- Months := 11;
- end
- else Dec(Months);
- end;
- Inc(Days, Day2 - Day1);
- end;
- function IncDay(ADate: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := ADate + Delta;
- end;
- function IncMonth(ADate: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := IncDate(ADate, 0, Delta, 0);
- end;
- function IncYear(ADate: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := IncDate(ADate, 0, 0, Delta);
- end;
- function MonthsBetween(Date1, Date2: TDateTime): Double;
- var
- D, M, Y: Word;
- begin
- DateDiff(Date1, Date2, D, M, Y);
- Result := 12 * Y + M;
- if (D > 1) and (D < 7) then Result := Result + 0.25
- else if (D >= 7) and (D < 15) then Result := Result + 0.5
- else if (D >= 15) and (D < 21) then Result := Result + 0.75
- else if (D >= 21) then Result := Result + 1;
- end;
- function IsValidDate(Y, M, D: Word): Boolean;
- begin
- Result := (Y >= 1) and (Y <= 9999) and (M >= 1) and (M <= 12) and
- (D >= 1) and (D <= DaysPerMonth(Y, M));
- end;
- function ValidDate(ADate: TDateTime): Boolean;
- var
- Year, Month, Day: Word;
- begin
- try
- DecodeDate(ADate, Year, Month, Day);
- Result := IsValidDate(Year, Month, Day);
- except
- Result := False;
- end;
- end;
- function DaysInPeriod(Date1, Date2: TDateTime): Longint;
- begin
- if ValidDate(Date1) and ValidDate(Date2) then
- Result := Abs(Trunc(Date2) - Trunc(Date1)) + 1
- else Result := 0;
- end;
- function DaysBetween(Date1, Date2: TDateTime): Longint;
- begin
- Result := Trunc(Date2) - Trunc(Date1) + 1;
- if Result < 0 then Result := 0;
- end;
- function IncTime(ATime: TDateTime; Hours, Minutes, Seconds,
- MSecs: Integer): TDateTime;
- begin
- Result := ATime + (Hours div 24) + (((Hours mod 24) * 3600000 +
- Minutes * 60000 + Seconds * 1000 + MSecs) / MSecsPerDay);
- if Result < 0 then Result := Result + 1;
- end;
- function IncHour(ATime: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := IncTime(ATime, Delta, 0, 0, 0);
- end;
- function IncMinute(ATime: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := IncTime(ATime, 0, Delta, 0, 0);
- end;
- function IncSecond(ATime: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := IncTime(ATime, 0, 0, Delta, 0);
- end;
- function IncMSec(ATime: TDateTime; Delta: Integer): TDateTime;
- begin
- Result := IncTime(ATime, 0, 0, 0, Delta);
- end;
- function CutTime(ADate: TDateTime): TDateTime;
- begin
- Result := Trunc(ADate);
- end;
- function CurrentYear: Word; {$IFNDEF WIN32} assembler; {$ENDIF}
- {$IFDEF WIN32}
- var
- SystemTime: TSystemTime;
- begin
- GetLocalTime(SystemTime);
- Result := SystemTime.wYear;
- end;
- {$ELSE}
- asm
- MOV AH,2AH
- INT 21H
- MOV AX,CX
- end;
- {$ENDIF}
- { String to date conversions. Copied from SYSUTILS.PAS unit. }
- procedure ScanBlanks(const S: string; var Pos: Integer);
- var
- I: Integer;
- begin
- I := Pos;
- while (I <= Length(S)) and (S[I] = ' ') do Inc(I);
- Pos := I;
- end;
- function ScanNumber(const S: string; MaxLength: Integer; var Pos: Integer;
- var Number: Longint): Boolean;
- var
- I: Integer;
- N: Word;
- begin
- Result := False;
- ScanBlanks(S, Pos);
- I := Pos;
- N := 0;
- while (I <= Length(S)) and (Longint(I - Pos) < MaxLength) and
- (S[I] in ['0'..'9']) and (N < 1000) do
- begin
- N := N * 10 + (Ord(S[I]) - Ord('0'));
- Inc(I);
- end;
- if I > Pos then begin
- Pos := I;
- Number := N;
- Result := True;
- end;
- end;
- function ScanChar(const S: string; var Pos: Integer; Ch: Char): Boolean;
- begin
- Result := False;
- ScanBlanks(S, Pos);
- if (Pos <= Length(S)) and (S[Pos] = Ch) then begin
- Inc(Pos);
- Result := True;
- end;
- end;
- {$IFDEF RX_D3}
- procedure ScanToNumber(const S: string; var Pos: Integer);
- begin
- while (Pos <= Length(S)) and not (S[Pos] in ['0'..'9']) do begin
- if S[Pos] in LeadBytes then Inc(Pos);
- Inc(Pos);
- end;
- end;
- {$ENDIF}
- function GetDateOrder(const DateFormat: string): TDateOrder;
- var
- I: Integer;
- begin
- Result := DefaultDateOrder;
- I := 1;
- while I <= Length(DateFormat) do begin
- case Chr(Ord(DateFormat[I]) and $DF) of
- {$IFDEF RX_D3}
- 'E': Result := doYMD;
- {$ENDIF}
- 'Y': Result := doYMD;
- 'M': Result := doMDY;
- 'D': Result := doDMY;
- else
- Inc(I);
- Continue;
- end;
- Exit;
- end;
- Result := DefaultDateOrder; { default }
- end;
- function ExpandYear(Year: Integer): Integer;
- var
- N: Longint;
- begin
- Result := Year;
- if Result < 100 then begin
- N := CurrentYear - CenturyOffset;
- Inc(Result, N div 100 * 100);
- if (CenturyOffset > 0) and (Result < N) then
- Inc(Result, 100);
- end;
- end;
- function ScanDate(const S, DateFormat: string; var Pos: Integer;
- var Y, M, D: Integer): Boolean;
- var
- DateOrder: TDateOrder;
- N1, N2, N3: Longint;
- begin
- Result := False;
- Y := 0; M := 0; D := 0;
- DateOrder := GetDateOrder(DateFormat);
- {$IFDEF RX_D3}
- if ShortDateFormat[1] = 'g' then { skip over prefix text }
- ScanToNumber(S, Pos);
- {$ENDIF RX_D3}
- if not (ScanNumber(S, MaxInt, Pos, N1) and ScanChar(S, Pos, DateSeparator) and
- ScanNumber(S, MaxInt, Pos, N2)) then Exit;
- if ScanChar(S, Pos, DateSeparator) then begin
- if not ScanNumber(S, MaxInt, Pos, N3) then Exit;
- case DateOrder of
- doMDY: begin Y := N3; M := N1; D := N2; end;
- doDMY: begin Y := N3; M := N2; D := N1; end;
- doYMD: begin Y := N1; M := N2; D := N3; end;
- end;
- Y := ExpandYear(Y);
- end
- else begin
- Y := CurrentYear;
- if DateOrder = doDMY then begin
- D := N1; M := N2;
- end
- else begin
- M := N1; D := N2;
- end;
- end;
- ScanChar(S, Pos, DateSeparator);
- ScanBlanks(S, Pos);
- {$IFDEF RX_D3}
- if SysLocale.FarEast and (System.Pos('ddd', ShortDateFormat) <> 0) then
- begin { ignore trailing text }
- if ShortTimeFormat[1] in ['0'..'9'] then { stop at time digit }
- ScanToNumber(S, Pos)
- else { stop at time prefix }
- repeat
- while (Pos <= Length(S)) and (S[Pos] <> ' ') do Inc(Pos);
- ScanBlanks(S, Pos);
- until (Pos > Length(S)) or
- (AnsiCompareText(TimeAMString, Copy(S, Pos, Length(TimeAMString))) = 0) or
- (AnsiCompareText(TimePMString, Copy(S, Pos, Length(TimePMString))) = 0);
- end;
- {$ENDIF RX_D3}
- Result := IsValidDate(Y, M, D) and (Pos > Length(S));
- end;
- function MonthFromName(const S: string; MaxLen: Byte): Byte;
- begin
- if Length(S) > 0 then
- for Result := 1 to 12 do begin
- if (Length(LongMonthNames[Result]) > 0) and
- (AnsiCompareText(Copy(S, 1, MaxLen),
- Copy(LongMonthNames[Result], 1, MaxLen)) = 0) then Exit;
- end;
- Result := 0;
- end;
- procedure ExtractMask(const Format, S: string; Ch: Char; Cnt: Integer;
- var I: Integer; Blank, Default: Integer);
- var
- Tmp: string[20];
- J, L: Integer;
- begin
- I := Default;
- Ch := UpCase(Ch);
- L := Length(Format);
- if Length(S) < L then L := Length(S)
- else if Length(S) > L then Exit;
- J := Pos(MakeStr(Ch, Cnt), AnsiUpperCase(Format));
- if J <= 0 then Exit;
- Tmp := '';
- while (UpCase(Format[J]) = Ch) and (J <= L) do begin
- if S[J] <> ' ' then Tmp := Tmp + S[J];
- Inc(J);
- end;
- if Tmp = '' then I := Blank
- else if Cnt > 1 then begin
- I := MonthFromName(Tmp, Length(Tmp));
- if I = 0 then I := -1;
- end
- else I := StrToIntDef(Tmp, -1);
- end;
- function ScanDateStr(const Format, S: string; var D, M, Y: Integer): Boolean;
- var
- Pos: Integer;
- begin
- ExtractMask(Format, S, 'm', 3, M, -1, 0); { short month name? }
- if M = 0 then ExtractMask(Format, S, 'm', 1, M, -1, 0);
- ExtractMask(Format, S, 'd', 1, D, -1, 1);
- ExtractMask(Format, S, 'y', 1, Y, -1, CurrentYear);
- Y := ExpandYear(Y);
- Result := IsValidDate(Y, M, D);
- if not Result then begin
- Pos := 1;
- Result := ScanDate(S, Format, Pos, Y, M, D);
- end;
- end;
- function InternalStrToDate(const DateFormat, S: string;
- var Date: TDateTime): Boolean;
- var
- D, M, Y: Integer;
- begin
- if S = '' then begin
- Date := NullDate;
- Result := True;
- end
- else begin
- Result := ScanDateStr(DateFormat, S, D, M, Y);
- if Result then
- try
- Date := EncodeDate(Y, M, D);
- except
- Result := False;
- end;
- end;
- end;
- function StrToDateFmt(const DateFormat, S: string): TDateTime;
- begin
- if not InternalStrToDate(DateFormat, S, Result) then
- raise EConvertError.CreateFmt({$IFDEF RX_D3} SInvalidDate {$ELSE}
- LoadStr(SInvalidDate) {$ENDIF}, [S]);
- end;
- function StrToDateDef(const S: string; Default: TDateTime): TDateTime;
- begin
- if not InternalStrToDate(ShortDateFormat, S, Result) then
- Result := Trunc(Default);
- end;
- function StrToDateFmtDef(const DateFormat, S: string; Default: TDateTime): TDateTime;
- begin
- if not InternalStrToDate(DateFormat, S, Result) then
- Result := Trunc(Default);
- end;
- function DefDateFormat(FourDigitYear: Boolean): string;
- begin
- if FourDigitYear then begin
- case GetDateOrder(ShortDateFormat) of
- doMDY: Result := 'MM/DD/YYYY';
- doDMY: Result := 'DD/MM/YYYY';
- doYMD: Result := 'YYYY/MM/DD';
- end;
- end
- else begin
- case GetDateOrder(ShortDateFormat) of
- doMDY: Result := 'MM/DD/YY';
- doDMY: Result := 'DD/MM/YY';
- doYMD: Result := 'YY/MM/DD';
- end;
- end;
- end;
- function DefDateMask(BlanksChar: Char; FourDigitYear: Boolean): string;
- begin
- if FourDigitYear then begin
- case GetDateOrder(ShortDateFormat) of
- doMDY, doDMY: Result := '!99/99/9999;1;';
- doYMD: Result := '!9999/99/99;1;';
- end;
- end
- else begin
- case GetDateOrder(ShortDateFormat) of
- doMDY, doDMY: Result := '!99/99/99;1;';
- doYMD: Result := '!99/99/99;1;';
- end;
- end;
- if Result <> '' then Result := Result + BlanksChar;
- end;
- {$IFDEF WIN32}
- function FormatLongDate(Value: TDateTime): string;
- var
- Buffer: array[0..1023] of Char;
- SystemTime: TSystemTime;
- begin
- {$IFDEF RX_D3}
- DateTimeToSystemTime(Value, SystemTime);
- {$ELSE}
- with SystemTime do begin
- DecodeDate(Value, wYear, wMonth, wDay);
- DecodeTime(Value, wHour, wMinute, wSecond, wMilliseconds);
- end;
- {$ENDIF}
- SetString(Result, Buffer, GetDateFormat(GetThreadLocale, DATE_LONGDATE,
- @SystemTime, nil, Buffer, SizeOf(Buffer) - 1));
- Result := TrimRight(Result);
- end;
- function FormatLongDateTime(Value: TDateTime): string;
- begin
- if Value <> NullDate then
- Result := FormatLongDate(Value) + FormatDateTime(' tt', Value)
- else Result := '';
- end;
- {$ENDIF WIN32}
- {$IFNDEF USE_FOUR_DIGIT_YEAR}
- function FourDigitYear: Boolean;
- begin
- Result := Pos('YYYY', AnsiUpperCase(ShortDateFormat)) > 0;
- end;
- {$ENDIF}
- {$IFDEF USE_FOUR_DIGIT_YEAR}
- initialization
- FourDigitYear := Pos('YYYY', AnsiUpperCase(ShortDateFormat)) > 0;
- {$ENDIF}
- end.