Delphi Firemonkey get UTC time - delphi

How does one get the UTC time in Firemonkey?
I tried this code from another Stackoverflow answer but it appears GetSystemTime() is not available in FMX.
function NowUTC: TDateTime;
Var UTC: TSystemTime;
begin
GetSystemTime(UTC);
Result := SystemTimeToDateTime(UTC);
end;

If you add DateUtils to the uses clause, you can use the TTimeZone class, its Local class property, and the ToUniversalTime method:
ShowMessage(DateTimeToStr(TTimeZone.Local.ToUniversalTime(Now)));

TTimeZone.Local.ToUniversalTime(Now) doesn't work during DST change on 30. October 3:00 >> 2:00. Beacause during changing, there is 2:00-3:00 time twice and to get correct UTC, You have to set ForceDaylight in function ToUniversalTime to true/false during transient time.
There is a TDateTime helper in DateUtils unit, which reflects this DST transient time on a UNIX (Android) system too and return the correct UTC time. On Unix are used functions gettimeofday and gmtime_r.
result:=TDateTime.NowUTC;

Related

How to convert Date and time from one timezone to another in Delphi? [duplicate]

Regardless of what the user's local time zone is set to, using Delphi 2007, I need to determine the time (TDateTime) in the Eastern time zone.
How can I do that? Of course, needs to be daylight savings time aware.
If I understand you correctly, you want the Eastern Time equivalent of the current system time.
To do this, use the WiNAPI function GetSystemTime() to get the current time of the computer in UTC. UTC is independent of time zones and will always get you the time at the prime meridian.
You can then use the WinAPI function SystemTimeToTzSpecificLocalTime() to calculate the local time in any other given time zone from the UTC time. In order that SystemTimeToTzSpecificLocalTime() can work, you need to give it a TTimeZoneInformation record that is populated with the correct information for the time zone you want to convert to.
The following sample will always gives you the local time in Eastern Time as per Energy Policy Act of 2005.
function GetEasternTime: TDateTime;
var
T: TSystemTime;
TZ: TTimeZoneInformation;
begin
// Get Current time in UTC
GetSystemTime(T);
// Setup Timezone Information for Eastern Time
TZ.Bias:= 0;
// DST ends at First Sunday in November at 2am
TZ.StandardBias:= 300;
TZ.StandardDate.wYear:= 0;
TZ.StandardDate.wMonth:= 11; // November
TZ.StandardDate.wDay:= 1; // First
TZ.StandardDate.wDayOfWeek:= 0; // Sunday
TZ.StandardDate.wHour:= 2;
TZ.StandardDate.wMinute:= 0;
TZ.StandardDate.wSecond:= 0;
TZ.StandardDate.wMilliseconds:= 0;
// DST starts at Second Sunday in March at 2am
TZ.DaylightBias:= 240;
TZ.DaylightDate.wYear:= 0;
TZ.DaylightDate.wMonth:= 3; // March
TZ.DaylightDate.wDay:= 2; // Second
TZ.DaylightDate.wDayOfWeek:= 0; // Sunday
TZ.DaylightDate.wHour:= 2;
TZ.DaylightDate.wMinute:= 0;
TZ.DaylightDate.wSecond:= 0;
TZ.DaylightDate.wMilliseconds:= 0;
// Convert UTC to Eastern Time
Win32Check(SystemTimeToTzSpecificLocalTime(#TZ, T, T));
// Convert to and return as TDateTime
Result := EncodeDate(T.wYear, T.wMonth, T.wDay) +
EncodeTime(T.wHour, T.wMinute, T.wSecond, T.wMilliSeconds);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Caption:= 'In New York Citiy, it is now ' + DateTimeToStr(GetEasternTime);
end;
TDateTime does not have any time zone information (it is just a double - date as whole number, time as decimal), so you would need that separately. You would need your own logic for DST as well, I don't believe there is any in Delphi. Then use the IncHour function in DateUtils.pas to alter the TDateTime to the Eastern Timezone.
There are probably web services that will do this for you. Does your application need to be self contained, or can it connect to the web to do it?
To be specific, TDateTime isn't object, it's just an alias for double.

Is it possible to get the FileAge to the millisecond?

I am trying to get the time stamp of a file when it was last modified to the millisecond. Currently if I use:
FileAge(Filename);
I will get the last modified date/time to a second. For example: 12:00:54. Is it possible to get the last modified date and time with milliseconds?
Use TFile.GetLastWriteTime or TFile.GetLastWriteTimeUtc from the System.IOUtils unit.
If the system supports your desired accuracy then these functions will provide dates to that accuracy. Of course it all depends on the file system in use.
There are two versions of FileAge():
function FileAge(const FileName: string): Integer;
This version is deprecated.
On Windows, it uses GetFileAttributesEx() and/or FindFirstFile() to retrieve the timestamp as a FILETIME (which has 100-nanosecond precision), but then coverts it to a DOS-encoded integer using FileTimeToDosDateTime(), which has seconds precision.
On POSIX, it uses stat(), which returns timestamps as seconds elapsed since the epoch.
function FileAge(const FileName: string; out FileDateTime: TDateTime; FollowLink: Boolean = True): Boolean;
On Windows, it retreives the same timestamp as the deprecated version, but coverts the FILETIME to a SYSTEMTIME and then to a TDateTime using milliseconds precision.
On POSIX, it uses stat(), and thus has seconds precision.

How do I extract just the time from a datetimepicker component in Delphi?

I have a datetimepicker component in a Delphi form and I would like to just get the Time. When i look at the date in debug mode I see 42544.621701, and I would like just to get 0.621701 without the date value.
You can use the Frac() function:
var
Time: TTime;
...
Time := Frac(DateTimePicker1.DateTime);
Or, you can use the System.DateUtils.TimeOf() function, which is merely an inlined wrapper around Frac() with a more descriptive name:
uses
..., DateUtils;
var
Time: TTime;
...
Time := TimeOf(DateTimePicker1.DateTime);
The question is not actually about a date time picker. The control returns you a date time value. You are looking for a way to extract just the time portion. Do that with the TimeOf function from the System.DateUtils unit.
MyTime := TimeOf(MyDateTime);
EditHoo.Text:=timetostr(DateTimePicker2.Time);

How to avoid TDateTime Data Rounding

I am writing column and cell classes for FMX TGrid that will contain TCalendarEdit and TTimeEdit instances in every cell. Everything works fine except the proper processing of changes done in these child controls.
type
TFMTValue<T> = record
FieldValue: T;
Modified: boolean;
Appended: boolean;
Deleted: boolean;
end;
TDateTimeCell = class(TStyledControl)
private
FDate_Time: TFMTValue<TDateTime>;
procedure SetDateTime(const Value: TFMTValue<TDateTime>);
function GetDateTime: TFMTValue<TDateTime>;
protected
procedure SetData(const Value: TValue); override;
public
property Date_Time: TFMTValue<TDateTime> read GetDateTime write SetDateTime;
...
end;
...
function TDateTimeCell.GetDateTime: TFMTValue<TDateTime>;
begin
FDate_Time.Modified := (FDate_Time.Modified) or
(FDate_Time.FieldValue <> FCalendarEdit.Date +
+ FTimeEdit.Time);
FDate_Time.FieldValue := FCalendarEdit.Date + FTimeEdit.Time;
Result := FDate_Time;
end;
procedure TDateTimeCell.SetData(const Value: TValue);
begin
Date_Time := Value.AsType<TFMTValue<TDateTime>>;
inherited SetData(TValue.From<TDateTime>(FDate_Time.FieldValue));
ApplyStyling;
end;
procedure TDateTimeCell.SetDateTime(const Value: TFMTValue<TDateTime>);
begin
FDate_Time := Value;
FCalendarEdit.Date := DateOf(FDate_Time.FieldValue);
FTimeEdit.Time := TimeOF(FDate_Time.FieldValue);
FDate_Time.FieldValue:=FCalendarEdit.Date + FTimeEdit.Time; //this line helps but not in all cases
end;
The idea is that data is assigned via TGrid OnGetValue event handler. Both date and time are displayed. The user activity is catched and Modified flag is set. The problem is that this flag is set to true sometimes even without any user activities. I suspect it is due to the rounding of time part of TDateTime. There are no other ways the code assignes values to FCalendarEdit.Date and FTimeEdit.Time.
How can I properly compare the data stored in FCalendarEdit.Date and FTimeEdit.Time with that stored in FDate_Time.FieldValue?
Appended
Setting the flag in this way does not resolve the issue.
FDate_Time.Modified := (FDate_Time.Modified) or
(DateOf(FDate_Time.FieldValue) <> FCalendarEdit.Date) or
(TimeOf(FDate_Time.FieldValue)<> FTimeEdit.Time);
Appended 2. On a valued advice of #Ken-White.
If we replace the comparison line by
FDate_Time.Modified := (FDate_Time.Modified) or
(not SameDateTime(FDate_Time.FieldValue,
FCalendarEdit.Date + FTimeEdit.Time));
It works fine. So the TDataTime comparison must be done by this function only.
TDateTime is of type Double, which means it's a floating point value, and therefore is subject to the usual issues of binary representation when doing comparisons for equality without specifying an acceptable delta (difference)..
Specifically for TDateTime values, you can use DateUtils.SameDateTime to compare equality down to less than one millisecond:
FDate_Time.Modified := (FDate_Time.Modified) or
(not SameDateTime(FDate_Time.FieldValue,
FCalendarEdit.Date + FTimeEdit.Time));
There is a bug in TCalendarEdit (a few actually) which is the underlying cause of your problem, but you can fix it with only a small change to your code.
The Problem
The TCalendarEdit makes a number of crucial errors when it applies a new Date value.
A TDate type is actually just an ordinary TDateTime in which you are supposed to ignore the time portion. Similarly a TTime is a TDateTime where you are supposed to ignore the date portion.
But you have to use these types correctly in your code - there is nothing that magically makes a TTime ignore the date or a TDate ignore the time.
For example, if you examine the constructor of the TCalendarEdit, you will see that it initialises the internal date/time to the current system date and time using Now, but truncates this to eliminate the time element:
Date := Trunc(Now);
So far so good.
But when you apply a new value via the Date property, it performs the following (simplified):
if Date <> Value then
FDateTime := Value + Time;
Both of these lines of code contain serious bugs:
It compares the Date (property returning the Date value of the control) with the Value being assigned - including any time value in that date/time. It should instead compare only the date part of the Value.
When assigning the new value to the internal date/time it adds Time to the Value you specified.
The first bug results in unnecessary changes to the internal property but is otherwise relatively innocuous. The second bug however is far more serious and is what is causing your problem.
I presume that the intention of the author of the control was to leave the time portion of the internal date/time value unchanged. However, the Value is not truncated, so it retains the time value specified in the assignment to the property. To make matters even worse, there is no Time property on this control, so this in fact adds the current system time to whatever time is specified in Value.
How This Affects Your Code and Test Case
Since your test case involved a time of midday - 12 hours - the result is that when you run this code in the afternoon, the Date of your TCalendarEdit is actually set to 25-Sep-2015 + 12 hours + the time when the control was initialised.
If you run the code in the morning, it seems to work because the time added results in a value that is still on the 25th Sep.
But when you run the code in the afternoon, the 12 hours are added to the current time and so the date rolls over to the next day!
With a more helpful diagnostic error message, or if you had inspected the properties in your code via the debugger, you would have seen this occurring.
DT := EncodeDate(2015, 9, 25) + EncodeTime(12, 0, 0, 0);
CalendarEdit1.Date := DT;
ShowMessage(DateTimeToString(CalendarEdit1.Date));
// When executed at e.g. 9am, displays: 25 Sep 2015
// When executed at e.g. 1pm, displays: 26 Sep 2015
So the reason your comparison then fails is because the date is actually completely different!
If you had tried simply using SameDateTime() for the comparison it may have appeared to have worked if you tested it in the morning but your problem would have returned in the afternoon !!
The Solution
You can work around these bugs in TCalendarEdit by ensuring that you respect the intended use of the property values yourself, assigning only those parts of the DT date/time value as appropriate in each case:
TimeEdit1.Time := TimeOf(DT);
CalendarEdit1.Date := DateOf(DT);
Although not strictly necessary in the case of the TTimeEdit, this will prevent these bugs in TCalendarEdit from causing these problems and makes it clear in your code that you are aware of what is required (consider it self documenting code if you like). :)
If you do not have TimeOf() and DateOf() functions in your version of Delphi, then the following is equivalent:
TimeEdit1.Time := DT - Trunc(DT);
CalendarEdit1.Date := Trunc(DT);
You could of course write your own versions of TimeOf() and DateOf() based on this, to make the intention clearer.
NOTE
There are precision complications arising from the floating point nature of date/time values in Delphi that could cause problems with direct comparisons with some specific values of date and time and for that reason it is highly recommended that you use the SameDateTime() function for performing such comparisons.
But this was absolutely not the cause of your problem in this case and SameDateTime() does not solve your problem.
SameDateTime() eliminates problems arising from differences in date/time values of less than 1 millisecond. The difference in this case was 24 hours!
Worth noting is that the TCalendarEdit control was deprecated in XE7 and has been removed entirely from XE8.

encodetime, incmilliseconds in delphi

i have create a coding to set a 20 millisecond faster than windows system. i'm using encodetime.
here's the code
procedure TForm1.Button1Click(Sender: TObject);
Var
delphi_datetime :tDateTime;
t_date : tdatetime ;
windows_datetime : tSystemTime;
begin
t_date := dATE;
delphi_datetime := encodetime(8,44,59,980);
delphi_datetime := incmillisecond(delphi_datetime, 20);
replacedate(t_date , delphi_datetime);
datetimetosystemtime( delphi_datetime , windows_datetime );
setlocaltime( windows_datetime );
showmessage('time now = ' + timetostr(delphi_datetime));
end;
aftr i run it, show the correct time. but the date goes to 30 dec 1899.. but i want to the current today date but with the time faster 20 milliseconds. any help.. please...
You have the arguments of ReplaceDate backward. It reads the date of the second parameter and assigns the date portion of the first parameter. The date portion of delphi_datetime is 0 because that's how EncodeTime works. You take that zero value and assign it to t_date, but then you continue working with delphi_datetime.
Reverse the arguments of ReplaceDate, and you should see that your current system time gets set to 8:45:00.000 with the current date.
ReplaceDate(delphi_datetime, t_date);
You could have noticed the mistake sooner if you hadn't used a separate t_date variable. If you'd called Date directly, your code would have failed to compile:
ReplaceDate(Date, delphi_datetime); // can't pass function result as "var" parameter
This works:
ReplaceDate(delphi_datetime, Date);
Rob has identified problems with your existing code. However your existing approach is needlessly complex. If you chose a simpler approach you would find it easier to get the code right.
If you want a date time that is 20 milliseconds greater than now, do it like this:
MyDateTime := IncMillisecond(Now, 20);
If you want a date time representing 0845 today, then you write:
MyDateTime := Date + EncodeTime(8, 45, 0, 0);

Resources