我正在使用 Delphi,尝试在数据库中存储记录,并使用 UTC datetime,在客户端读取时将其转换为本地时间。你有什么关于这个双向转换的想法吗?
我正在使用 Delphi,尝试在数据库中存储记录,并使用 UTC datetime,在客户端读取时将其转换为本地时间。你有什么关于这个双向转换的想法吗?
这是我用于将UTC时间转换为本地时间的函数。
function LocalDateTimeFromUTCDateTime(const UTCDateTime: TDateTime): TDateTime;
var
LocalSystemTime: TSystemTime;
UTCSystemTime: TSystemTime;
LocalFileTime: TFileTime;
UTCFileTime: TFileTime;
begin
DateTimeToSystemTime(UTCDateTime, UTCSystemTime);
SystemTimeToFileTime(UTCSystemTime, UTCFileTime);
if FileTimeToLocalFileTime(UTCFileTime, LocalFileTime)
and FileTimeToSystemTime(LocalFileTime, LocalSystemTime) then begin
Result := SystemTimeToDateTime(LocalSystemTime);
end else begin
Result := UTCDateTime; // Default to UTC if any conversion function fails.
end;
end;
如您所见,该函数将UTC日期时间转换为以下内容:
很明显如何反向操作。
请注意,此转换将夏令时视为现在的状态,而不是在转换时是/曾经的状态。XE引入了DateUtils.TTimeZone
类型,试图解决这个问题。代码如下:
LocalDateTime := TTimeZone.Local.ToLocalTime(UniversalDateTime);
在另一个方向上使用 ToUniversalTime
。
这个类似乎(宽泛地)基于 .net 的 TimeZone
类。
一个警告。不要期望尝试转换时考虑到夏令时的尝试 100%准确。这是根本不可能做到的。至少不用时间机器。而且这只考虑了未来的时间。即使是过去的时间也很复杂。Raymond Chen 在这里讨论了这个问题:为什么夏令时是非直观的。
你可以使用来自kernel32的TzSpecificLocalTimeToSystemTime和SystemTimeToTzSpecificLocalTime函数。
var
Form1: TForm1;
function TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation: PTimeZoneInformation; var lpLocalTime, lpUniversalTime: TSystemTime): BOOL; stdcall;
function SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation: PTimeZoneInformation; var lpUniversalTime,lpLocalTime: TSystemTime): BOOL; stdcall;
implementation
function TzSpecificLocalTimeToSystemTime; external kernel32 name 'TzSpecificLocalTimeToSystemTime';
function SystemTimeToTzSpecificLocalTime; external kernel32 name 'SystemTimeToTzSpecificLocalTime';
{$R *.dfm}
Function DateTime2UnivDateTime(d:TDateTime):TDateTime;
var
TZI:TTimeZoneInformation;
LocalTime, UniversalTime:TSystemTime;
begin
GetTimeZoneInformation(tzi);
DateTimeToSystemTime(d,LocalTime);
TzSpecificLocalTimeToSystemTime(@tzi,LocalTime,UniversalTime);
Result := SystemTimeToDateTime(UniversalTime);
end;
Function UnivDateTime2LocalDateTime(d:TDateTime):TDateTime;
var
TZI:TTimeZoneInformation;
LocalTime, UniversalTime:TSystemTime;
begin
GetTimeZoneInformation(tzi);
DateTimeToSystemTime(d,UniversalTime);
SystemTimeToTzSpecificLocalTime(@tzi,UniversalTime,LocalTime);
Result := SystemTimeToDateTime(LocalTime);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Date,Univdate,DateAgain:TDateTime;
begin
Date := Now;
Univdate := DateTime2UnivDateTime(Date);
DateAgain := UnivDateTime2LocalDateTime(Univdate);
Showmessage(DateTimeToStr(Date) +#13#10 + DateTimeToStr(Univdate)+#13#10 + DateTimeToStr(DateAgain));
end;
如果有人需要2020年的答案:
请确保包含以下内容:
Uses
System.DateUtils;
然后是针对协调世界时 (UTC)。
function GetUTC(dt: TDateTime): TDateTime;
begin
result := TTimeZone.Local.ToUniversalTime(dt);
end;
并且为当地时间
function GetLocalTime(dt: TDateTime):TDateTime;
begin
result := TTimeZone.Local.ToLocalTime(dt);
end;
如果您正在使用XE2,您可以使用System.DateUtils.TTimeZone
。
您可以查看这篇好的文章,它解释了方法及其工作原理,并提供了示例: http://alex.ciobanu.org/?p=373
function NowUTC: TDateTime;
Var UTC: TSystemTime;
begin
GetSystemTime(UTC);
Result := SystemTimeToDateTime(UTC);
end;
function UTCToLocal(UTC: TDateTime): TDateTime;
begin
Result := IncMinute(UTC, UTCToLocalTimeOffsetMinutes);
end;
function UTCToLocalTimeOffsetMinutes: Int16;
Var UTC: TSystemTime;
UTC2: TSystemTime;
t : TDateTime;
t2 : TDateTime;
begin
GetSystemTime(UTC);
GetLocalTime(UTC2);
t := SystemTimeToDateTime(UTC);
t2 := SystemTimeToDateTime(UTC2);
Result := System.DateUtils.MinutesBetween(t,t2);
end;
我不知道有SystemTimeToTzSpecificLocalTime,但我刚读到Jon Skeet更喜欢TZDB来处理时区。TZDB提供了一个简单的数据库和专门的时区类,允许访问Time Zone Database项目支持的所有时区。