I'm using Delphi and I'm trying to store records using UTC datetime in my database and then restore it back when a client reads it in his local datetime ? any idea how to do this forth back conversion ?
6 Answers
This is the function that I use to convert from UTC to local.
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;
As you can see the function transforms the UTC date time as follows:
- Date time -> system time
- System time -> file time
- File time -> local file time (this is the conversion from UTC to local)
- Local file time -> system time
- System time -> date time
It should be obvious how to reverse this.
Note that this conversion treats daylight saving as it is now rather than as it is/was at the time being converted. The DateUtils.TTimeZone
type, introduced in XE, attempts to do just that. The code becomes:
LocalDateTime := TTimeZone.Local.ToLocalTime(UniversalDateTime);
In the other direction use ToUniversalTime
.
This class appears to be (loosely) modelled on the .net TimeZone
class.
A word of warning. Do not expect the attempt to account for daylight savings at the time being converted to be 100% accurate. It is simply impossible to achieve that. At least without a time machine. And that's just considering times in the future. Even times in the past are complex. Raymond Chen discusses the issue here: Why Daylight Savings Time is nonintuitive.

- 601,492
- 42
- 1,072
- 1,490
you can use TzSpecificLocalTimeToSystemTime and SystemTimeToTzSpecificLocalTime from kernel32.
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;

- 27,123
- 14
- 62
- 101
-
Do you know if SystemTimeToTzSpecificLocalTime supports "historical" daylight saving time information, like tzdb? – mjn Mar 22 '13 at 10:24
-
The SystemTimeToTzSpecificLocalTime function may calculate the local time incorrectly under the following conditions: The time zone uses a different UTC offset for the old and new years. The UTC time to be converted and the calculated local time are in different years. http://msdn.microsoft.com/de-de/library/windows/desktop/ms724949(v=vs.85).aspx – bummi Mar 22 '13 at 10:31
-
1There is a small mistake in the code above. In UnivDateTime2LocalDateTime, the line DateTimeToSystemTime(d,LocalTime); should read DateTimeToSystemTime(d,UniversalTime); – Bob Moss Nov 06 '13 at 12:34
In case somebody need an answer in 2020:
make sure to include
Uses
System.DateUtils;
then for UTC
function GetUTC(dt: TDateTime): TDateTime;
begin
result := TTimeZone.Local.ToUniversalTime(dt);
end;
and for Local Time
function GetLocalTime(dt: TDateTime):TDateTime;
begin
result := TTimeZone.Local.ToLocalTime(dt);
end;

- 923
- 2
- 13
- 30
As you are using XE2, you can use the System.DateUtils.TTimeZone
.
You can check this good post explaining the methods and how it works and examples : http://alex.ciobanu.org/?p=373

- 7,998
- 4
- 32
- 56
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;

- 3,393
- 1
- 33
- 72
If your client uses a local Delphi application, this can be done with the System date functions.
However if you are in a client/server environment (for example the Delphi app is a web server, and the client only receives the HTML pages), you need to convert to the user's local time differently. The server needs to know the user's time zone, and convert appropriately.
Also daylight saving time can cause headache if the application needs to convert historical data - you need to know if there was DST in effect for the user's region.
In these use cases, the Time Zone Database for Delphi can be helpful.
TZDB provides a simple database and a specialized time zone class that allows accessing all time zones supported by Time Zone Database project.
I did not know that there is SystemTimeToTzSpecificLocalTime but just read that Jon Skeet prefers TZDB for time zone handling.