BSOne.SFC/Tocsg.Lib/VCL/Tocsg.DateTime.pas

658 lines
17 KiB
Plaintext

{*******************************************************}
{ }
{ Tocsg.DateTime }
{ }
{ Copyright (C) 2022 kkuzil }
{ }
{*******************************************************}
unit Tocsg.DateTime;
interface
uses
System.Generics.Collections, System.Generics.Defaults, System.SysUtils,
Winapi.Windows;
type
TDateTimeList = TList<TDateTime>;
TDateTimeComparer = class(TComparer<TDateTime>)
public
function Compare(const Left, Right: TDateTime): Integer; override;
end;
function ConvStrToDateTime(sDtStr: String): TDateTime; {$IFDEF RELEASE} inline; {$ENDIF}
function ConvSecToProgTime(dwSec: DWORD): String; inline;
function ConvSecBetweenToProgTime(dtNow, dtThen: TDateTime): String;
function GetBootTime: TDateTime;
function GetLocalIncUtcMin: Integer; // 로컬 타임존 UTC+분
function ConvFileTimeToDateTime(const aFileTime: TFileTime): TDateTime;
function ConvFileTimeToDateTime_Local(const aFileTime: TFileTime): TDateTime;
function ConvSystemTimeToDateTime(const st: TSystemTime): TDateTime;
function ConvSystemTimeToDateTime_Local(const st: TSystemTime): TDateTime;
function ConvDateTimeToSystemTime(dt: TDateTime): TSystemTime;
function ConvDateTimeToSystemTime_Local(dt: TDateTime): TSystemTime;
function ConvDateTimeToFileTime(dt: TDateTime): TFileTime;
function ConvUtcToLocal(dt: TDateTime): TDateTime;
function ConvLocalToUtc(dt: TDateTime): TDateTime;
function ConvTimeToDateTime(t: Integer): TDateTime;
function ConvTimestampToDateTime(llTime: LONGLONG): TDateTime;
function ConvChromeTimestampToDateTime(llTime: LONGLONG): TDateTime;
function ConvTimestampToFileTime_Local(llTime: LONGLONG): TFileTime;
function ConvTimestampToFileTime(llTime: LONGLONG): TFileTIme;
function ConvJavaToDelphiDateTime(const llVal: LONGLONG; bUtc0: Boolean = false): TDateTime;
function ConvStrDtToDateTime(const sDateTime: String): TDateTime;
function UnixTimeToDateTime(llDT: LONGLONG): TDateTime;
function AppendTime(dt: TDateTime; wHour, wMin, wSec, wMSec: WORD): TDateTime;
function RawStrToDateTime(const RawStr: string): TDateTime;
function JavaDtToDelphiDt(const dt: int64): TDateTime;
function DelphiDtToJavaDt(const dt: TDateTime): int64;
implementation
uses
System.DateUtils, Tocsg.Exception;
{ TDateTimeComparer }
function TDateTimeComparer.Compare(const Left, Right: TDateTime): Integer;
begin
Result := CompareDateTime(Right, Left);
end;
{ Other }
function ConvStrToDateTime(sDtStr: String): TDateTime;
var
wLen, wYear, wMonth, wDay, wHour, wMin, wSec: WORD;
begin
Result := 0;
sDtStr := StringReplace(sDtStr, '-', '', [rfReplaceAll]);
sDtStr := StringReplace(sDtStr, ':', '', [rfReplaceAll]);
sDtStr := StringReplace(sDtStr, ' ', '', [rfReplaceAll]);
wLen := sDtStr.Length;
if wLen >= 8 then
begin
wYear := StrToIntDef(Copy(sDtStr, 1, 4), 0);
wMonth := StrToIntDef(Copy(sDtStr, 5, 2), 0);
wDay := StrToIntDef(Copy(sDtStr, 7, 2), 0);
if wLen >= 14 then
begin
wHour := StrToIntDef(Copy(sDtStr, 9, 2), 0);
wMin := StrToIntDef(Copy(sDtStr, 11, 2), 0);
wSec := StrToIntDef(Copy(sDtStr, 13, 2), 0);
end else begin
wHour := 0;
wMin := 0;
wSec := 0;
end;
try
Result := EncodeDateTime(wYear, wMonth, wDay, wHour, wMin, wSec, 0);
except
exit;
end;
end;
end;
function ConvSecToProgTime(dwSec: DWORD): String;
var
dwMin,
dwHour, dwDay: DWORD;
begin
dwMin := (dwSec div 60) mod 60;
dwHour := (dwSec div 60 div 60) mod 24;
dwDay := dwSec div 60 div 60 div 24;
dwSec := dwSec mod 60;
if dwDay > 0 then
begin
if (dwHour + dwMin + dwSec) = 0 then
Result := Format('%dDay', [dwDay])
else
Result := Format('%dDay, %.2d:%.2d:%.2d', [dwDay, dwHour, dwMin, dwSec]);
end else
Result := Format('%.2d:%.2d:%.2d', [dwHour, dwMin, dwSec]);
end;
function ConvSecBetweenToProgTime(dtNow, dtThen: TDateTime): String;
begin
Result := ConvSecToProgTime(SecondsBetween(dtNow, dtThen));
end;
function GetBootTime: TDateTime;
begin
Result := IncMilliSecond(now, LONGLONG(-1) * GetTickCount);
end;
function GetLocalIncUtcMin: Integer;
begin
Result := (TTimeZone.Local.UtcOffset.Hours * 60) + TTimeZone.Local.UtcOffset.Minutes;
end;
function ConvFileTimeToDateTime(const aFileTime: TFileTime): TDateTime;
var
nDosTime: Integer;
begin
Result := 0;
try
if WinApi.Windows.FileTimeToDosDateTime(aFileTime, LongRec(nDosTime).Hi,
LongRec(nDosTime).Lo) then
try
Result := FileDateToDateTime(nDosTime);
except
On EConvertError do
exit;
end;
except
exit;
end;
end;
function ConvFileTimeToDateTime_Local(const aFileTime: TFileTime): TDateTime;
var
lcFT : TFileTime;
lc: Integer;
begin
Result := 0;
if WinApi.Windows.FileTimeToLocalFileTime(aFileTime, lcFT) then
begin
if WinApi.Windows.FileTimeToDosDateTime(lcFT, LongRec(lc).Hi, LongRec(lc).Lo) then
try
Result := FileDateToDateTime(lc);
except
On EConvertError do
exit;
end;
end;
end;
function ConvSystemTimeToDateTime(const st: TSystemTime): TDateTime;
var
ft: TFileTime;
begin
Result := 0;
if SystemTimeToFileTime(st, ft) then
Result := ConvFileTimeToDateTime(ft);
end;
function ConvSystemTimeToDateTime_Local(const st: TSystemTime): TDateTime;
var
ft: TFileTime;
begin
Result := 0;
if SystemTimeToFileTime(st, ft) then
Result := ConvFileTimeToDateTime_Local(ft);
end;
function ConvDateTimeToSystemTime(dt: TDateTime): TSystemTime;
begin
try
if not FileTimeToSystemTime(ConvDateTimeToFileTime(dt), Result) then
ZeroMemory(@Result, SizeOf(Result));
except
on E: Exception do
begin
ETgException.TraceException(E, 'Fail .. ConvDateTimeToSystemTime()');
ZeroMemory(@Result, SizeOf(Result));
end;
end;
end;
function ConvDateTimeToSystemTime_Local(dt: TDateTime): TSystemTime;
var
lcFT: TFileTime;
begin
ZeroMemory(@Result, SizeOf(Result));
try
if WinApi.Windows.FileTimeToLocalFileTime(ConvDateTimeToFileTime(dt), lcFT) then
FileTimeToSystemTime(lcFT, Result);
except
on E: Exception do
ETgException.TraceException(E, 'Fail .. ConvDateTimeToSystemTime_Local()');
end;
end;
function ConvDateTimeToFileTime(dt: TDateTime): TFileTime;
var
LocalFileTime, Ft: TFileTime;
SystemTime: TSystemTime;
begin
Result.dwLowDateTime := 0;
Result.dwHighDateTime := 0;
try
DateTimeToSystemTime(dt, SystemTime);
SystemTimeToFileTime(SystemTime, LocalFileTime);
LocalFileTimeToFileTime(LocalFileTime, Ft);
Result := Ft;
except
ZeroMemory(@Result, SizeOf(Result));
end;
end;
function ConvUtcToLocal(dt: TDateTime): TDateTime;
var
tzi: TTimeZoneInformation;
lt, ut: TSystemTime;
begin
Result := 0;
try
GetTimeZoneInformation(tzi);
DateTimeToSystemTime(dt, ut);
SystemTimeToTzSpecificLocalTime(@tzi, ut, lt);
Result := SystemTimeToDateTime(lt);
except
end;
end;
function ConvLocalToUtc(dt: TDateTime): TDateTime;
begin
Result := 0;
try
Result := TTimeZone.Local.ToUniversalTime(Now);
except
end;
end;
function ConvTimeToDateTime(t: Integer): TDateTime;
var
sysTime, sysTime1 : TSystemTime;
begin
DateTimeToSystemTime(EncodeDate(1970, 1, 1) + (t / 86400), sysTime);
SystemTimeToTzSpecificLocalTime(nil, sysTime, sysTime1);
Result := SystemTimeToDateTime(sysTime1);
end;
function ConvTimestampToDateTime(llTime: LONGLONG): TDateTime;
var
nTime: Integer;
ft: TFileTime;
begin
Result := 0;
// 이 값이 들어오는 경우가 있는데..
// 이 경우는 FileDateToDateTime() 여기서 예외가 발생해서 미리 체크함 20_0522 09:26:30 sunk
if llTime <> 116444736000000000 then
begin
CopyMemory(@ft, @llTime, SizeOf(LONGLONG));
try
FileTimeToDosDateTime(ft, LongRec(nTime).Hi, LongRec(nTime).Lo);
if nTime > 0 then
Result := FileDateToDateTime(nTime);
except
// On EConvertError do
// exit;
end;
end;
end;
function ConvChromeTimestampToDateTime(llTime: LONGLONG): TDateTime;
begin
{
u : Unix timestamp eg: 1378615325
j : JavaScript timestamp eg: 1378615325177
c : Chrome timestamp eg: 13902597987770000
w : Windows timestamp eg: 139025979877700000
u = (j / 1000)
u = (c - 116444736000000) / 10000000
u = (w - 1164447360000000) / 100000000
j = (u * 1000)
j = (c - 116444736000000) / 10000
j = (w - 1164447360000000) / 100000
c = (u * 10000000) + 116444736000000
c = (j * 10000) + 116444736000000
c = (w / 10)
w = (u * 100000000) + 1164447360000000
w = (j * 100000) + 1164447360000000
w = (c * 10)
}
if llTime > 116444736000000 then
Result := ConvTimestampToDateTime(llTime * 10)
else
Result := 0;
end;
function ConvTimestampToFileTime_Local(llTime: LONGLONG): TFileTime;
var
ft,
localft: TFileTime;
begin
CopyMemory(@ft, @llTime, SizeOf(LONGLONG));
try
FileTimeToLocalFileTime(ft, localft); // 생성일 기준
Result := localft;
except
ZeroMemory(@Result, SizeOf(Result));
end;
end;
function ConvTimestampToFileTime(llTime: LONGLONG): TFileTIme;
var
ft: TFileTime;
begin
CopyMemory(@ft, @llTime, SizeOf(LONGLONG));
Result := ft;
end;
// superobject.pas에 있는거 가져옴
function ConvJavaToDelphiDateTime(const llVal: LONGLONG; bUtc0: Boolean = false): TDateTime;
var
t: TSystemTime;
begin
DateTimeToSystemTime(25569 + (llVal / 86400000), t);
if not bUtc0 then
SystemTimeToTzSpecificLocalTime(nil, t, t);
Result := SystemTimeToDateTime(t);
end;
function ConvStrDtToDateTime(const sDateTime: String): TDateTime;
var
wLen, wYear, wMonth, wDay, wHour, wMin, wSec: WORD;
begin
Result := 0;
wLen := Length(sDateTime);
if wLen >= 8 then
begin
wYear := StrToIntDef(Copy(sDateTime, 1, 4), 0);
wMonth := StrToIntDef(Copy(sDateTime, 5, 2), 0);
wDay := StrToIntDef(Copy(sDateTime, 7, 2), 0);
if wLen >= 14 then
begin
wHour := StrToIntDef(Copy(sDateTime, 9, 2), 0);
wMin := StrToIntDef(Copy(sDateTime, 11, 2), 0);
wSec := StrToIntDef(Copy(sDateTime, 13, 2), 0);
end else begin
wHour := 0;
wMin := 0;
wSec := 0;
end;
try
Result := EncodeDateTime(wYear, wMonth, wDay, wHour, wMin, wSec, 0);
except
Result := 0;
end;
end;
end;
function UnixTimeToDateTime(llDT: LONGLONG): TDateTime;
begin
Result := (llDT / 86400) + 25569;
end;
function AppendTime(dt: TDateTime; wHour, wMin, wSec, wMSec: WORD): TDateTime;
var
wYear, wMonth, wDay: WORD;
begin
try
DecodeDate(dt, wYear, wMonth, wDay);
Result := EncodeDateTime(wYear, wMonth, wDay, wHour, wMin, wSec, wMSec);
except
Result := dt;
end;
end;
function RawStrToDateTime(const RawStr: string): TDateTime;
var
v: Int64;
begin
v := StrToInt64(RawStr);
Result := PDouble(@v)^;
end;
// superobject.pas에서 가져옴
function DayLightCompareDate(const date: PSystemTime;
const compareDate: PSystemTime): Integer;
var
limit_day, dayinsecs, weekofmonth: Integer;
First: Word;
begin
if (date^.wMonth < compareDate^.wMonth) then
begin
Result := -1; (* We are in a month before the date limit. *)
Exit;
end;
if (date^.wMonth > compareDate^.wMonth) then
begin
Result := 1; (* We are in a month after the date limit. *)
Exit;
end;
(* if year is 0 then date is in day-of-week format, otherwise
* it's absolute date.
*)
if (compareDate^.wYear = 0) then
begin
(* compareDate.wDay is interpreted as number of the week in the month
* 5 means: the last week in the month *)
weekofmonth := compareDate^.wDay;
(* calculate the day of the first DayOfWeek in the month *)
First := (6 + compareDate^.wDayOfWeek - date^.wDayOfWeek + date^.wDay) mod 7 + 1;
limit_day := First + 7 * (weekofmonth - 1);
(* check needed for the 5th weekday of the month *)
if (limit_day > MonthDays[(date^.wMonth=2) and IsLeapYear(date^.wYear)][date^.wMonth]) then
dec(limit_day, 7);
end
else
limit_day := compareDate^.wDay;
(* convert to seconds *)
limit_day := ((limit_day * 24 + compareDate^.wHour) * 60 + compareDate^.wMinute ) * 60;
dayinsecs := ((date^.wDay * 24 + date^.wHour) * 60 + date^.wMinute ) * 60 + date^.wSecond;
(* and compare *)
if dayinsecs < limit_day then
Result := -1 else
if dayinsecs > limit_day then
Result := 1 else
Result := 0; (* date is equal to the date limit. *)
end;
function CompTimeZoneID(const pTZinfo: PTimeZoneInformation;
lpFileTime: PFileTime; islocal: Boolean): LongWord;
var
ret: Integer;
beforeStandardDate, afterDaylightDate: Boolean;
llTime: Int64;
SysTime: TSystemTime;
ftTemp: TFileTime;
begin
llTime := 0;
if (pTZinfo^.DaylightDate.wMonth <> 0) then
begin
(* if year is 0 then date is in day-of-week format, otherwise
* it's absolute date.
*)
if ((pTZinfo^.StandardDate.wMonth = 0) or
((pTZinfo^.StandardDate.wYear = 0) and
((pTZinfo^.StandardDate.wDay < 1) or
(pTZinfo^.StandardDate.wDay > 5) or
(pTZinfo^.DaylightDate.wDay < 1) or
(pTZinfo^.DaylightDate.wDay > 5)))) then
begin
SetLastError(ERROR_INVALID_PARAMETER);
Result := TIME_ZONE_ID_INVALID;
Exit;
end;
if (not islocal) then
begin
llTime := PInt64(lpFileTime)^;
dec(llTime, Int64(pTZinfo^.Bias + pTZinfo^.DaylightBias) * 600000000);
PInt64(@ftTemp)^ := llTime;
lpFileTime := @ftTemp;
end;
FileTimeToSystemTime(lpFileTime^, SysTime);
(* check for daylight savings *)
ret := DayLightCompareDate(@SysTime, @pTZinfo^.StandardDate);
if (ret = -2) then
begin
Result := TIME_ZONE_ID_INVALID;
Exit;
end;
beforeStandardDate := ret < 0;
if (not islocal) then
begin
dec(llTime, Int64(pTZinfo^.StandardBias - pTZinfo^.DaylightBias) * 600000000);
PInt64(@ftTemp)^ := llTime;
FileTimeToSystemTime(lpFileTime^, SysTime);
end;
ret := DayLightCompareDate(@SysTime, @pTZinfo^.DaylightDate);
if (ret = -2) then
begin
Result := TIME_ZONE_ID_INVALID;
Exit;
end;
afterDaylightDate := ret >= 0;
Result := TIME_ZONE_ID_STANDARD;
if( pTZinfo^.DaylightDate.wMonth < pTZinfo^.StandardDate.wMonth ) then
begin
(* Northern hemisphere *)
if( beforeStandardDate and afterDaylightDate) then
Result := TIME_ZONE_ID_DAYLIGHT;
end else (* Down south *)
if( beforeStandardDate or afterDaylightDate) then
Result := TIME_ZONE_ID_DAYLIGHT;
end else
(* No transition date *)
Result := TIME_ZONE_ID_UNKNOWN;
end;
function GetTimezoneBias(const pTZinfo: PTimeZoneInformation;
lpFileTime: PFileTime; islocal: Boolean; pBias: PLongint): Boolean;
var
bias: LongInt;
tzid: LongWord;
begin
bias := pTZinfo^.Bias;
tzid := CompTimeZoneID(pTZinfo, lpFileTime, islocal);
if( tzid = TIME_ZONE_ID_INVALID) then
begin
Result := False;
Exit;
end;
if (tzid = TIME_ZONE_ID_DAYLIGHT) then
inc(bias, pTZinfo^.DaylightBias)
else if (tzid = TIME_ZONE_ID_STANDARD) then
inc(bias, pTZinfo^.StandardBias);
pBias^ := bias;
Result := True;
end;
function TzSpecificLocalTimeToSystemTime(
const lpTimeZoneInformation: PTimeZoneInformation;
const lpLocalTime: PSystemTime; lpUniversalTime: PSystemTime): BOOL;
var
ft: TFileTime;
lBias: LongInt;
t: Int64;
tzinfo: TTimeZoneInformation;
begin
if (lpTimeZoneInformation <> nil) then
tzinfo := lpTimeZoneInformation^
else
if (GetTimeZoneInformation(tzinfo) = TIME_ZONE_ID_INVALID) then
begin
Result := False;
Exit;
end;
if (not SystemTimeToFileTime(lpLocalTime^, ft)) then
begin
Result := False;
Exit;
end;
t := PInt64(@ft)^;
if (not GetTimezoneBias(@tzinfo, @ft, True, @lBias)) then
begin
Result := False;
Exit;
end;
(* convert minutes to 100-nanoseconds-ticks *)
inc(t, Int64(lBias) * 600000000);
PInt64(@ft)^ := t;
Result := FileTimeToSystemTime(ft, lpUniversalTime^);
end;
function SystemTimeToTzSpecificLocalTime(
lpTimeZoneInformation: PTimeZoneInformation;
lpUniversalTime, lpLocalTime: PSystemTime): BOOL;
var
ft: TFileTime;
lBias: LongInt;
llTime: Int64;
tzinfo: TTimeZoneInformation;
begin
if (lpTimeZoneInformation <> nil) then
tzinfo := lpTimeZoneInformation^ else
if (GetTimeZoneInformation(tzinfo) = TIME_ZONE_ID_INVALID) then
begin
Result := False;
Exit;
end;
if (not SystemTimeToFileTime(lpUniversalTime^, ft)) then
begin
Result := False;
Exit;
end;
llTime := PInt64(@ft)^;
if (not GetTimezoneBias(@tzinfo, @ft, False, @lBias)) then
begin
Result := False;
Exit;
end;
(* convert minutes to 100-nanoseconds-ticks *)
dec(llTime, Int64(lBias) * 600000000);
PInt64(@ft)^ := llTime;
Result := FileTimeToSystemTime(ft, lpLocalTime^);
end;
function JavaDtToDelphiDt(const dt: int64): TDateTime;
var
t: TSystemTime;
begin
DateTimeToSystemTime(25569 + (dt / 86400000), t);
SystemTimeToTzSpecificLocalTime(nil, @t, @t);
Result := SystemTimeToDateTime(t);
end;
function DelphiDtToJavaDt(const dt: TDateTime): int64;
var
t: TSystemTime;
begin
DateTimeToSystemTime(dt, t);
TzSpecificLocalTimeToSystemTime(nil, @t, @t);
Result := Round((SystemTimeToDateTime(t) - 25569) * 86400000)
end;
end.