{*******************************************************} { } { Tocsg.DateTime } { } { Copyright (C) 2022 kkuzil } { } {*******************************************************} unit Tocsg.DateTime; interface uses System.Generics.Collections, System.Generics.Defaults, System.SysUtils, Winapi.Windows; type TDateTimeList = TList; TDateTimeComparer = class(TComparer) 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.