18

我正在使用 Delphi,我正在尝试在我的数据库中使用 UTC 日期时间存储记录,然后当客户在他的本地日期时间读取它时将其恢复回来?知道如何进行此前后转换吗?

4

6 回答 6

25

这是我用来从 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 日期时间转换如下:

  • 日期时间 -> 系统时间
  • 系统时间 -> 文件时间
  • 文件时间 -> 本地文件时间(这是从 UTC 到本地的转换)
  • 本地文件时间 -> 系统时间
  • 系统时间 -> 日期时间

如何扭转这一点应该很明显。


请注意,此转换将夏令时视为现在而不是转换时的状态。XE 中引入的DateUtils.TTimeZone类型试图做到这一点。代码变为:

LocalDateTime := TTimeZone.Local.ToLocalTime(UniversalDateTime);

在另一个方向使用ToUniversalTime.

这个类似乎(松散地)模仿了 .netTimeZone类。

一句警告。不要期望在转换时考虑夏令时的尝试是 100% 准确的。根本不可能做到这一点。至少没有时间机器。这只是考虑未来的时间。即使是过去的时代也很复杂。Raymond Chen 在这里讨论了这个问题:为什么夏令时不直观

于 2013-03-22T09:57:17.140 回答
12

您可以使用 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;
于 2013-03-22T10:14:34.687 回答
10

当您使用 XE2 时,您可以使用System.DateUtils.TTimeZone.

您可以查看这篇解释方法及其工作原理和示例的 好帖子: http ://alex.ciobanu.org/?p=373

于 2013-03-22T10:11:01.763 回答
10

如果有人在 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;
于 2020-11-02T11:56:07.860 回答
1

如果您的客户使用本地 Delphi 应用程序,这可以通过系统日期函数来完成。

但是,如果您处于客户端/服务器环境中(例如 Delphi 应用程序是 Web 服务器,并且客户端只接收 HTML 页面),则需要以不同的方式转换为用户的本地时间。服务器需要知道用户的时区,并进行适当的转换。

如果应用程序需要转换历史数据,夏令时也会引起头疼——您需要知道用户所在地区是否有夏令时。

在这些用例中,Delphi 时区数据库可能会有所帮助。

TZDB 提供了一个简单的数据库和一个专门的时区类,允许访问时区数据库项目支持的所有时区。

我不知道有 SystemTimeToTzSpecificLocalTime 但只是读到Jon Skeet 更喜欢 TZDB进行时区处理。

于 2013-03-22T09:56:22.860 回答
1
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;
于 2020-08-05T11:28:38.010 回答