DelphiScript - String to DateTime/to 18 digit TimeStamp - delphi

I'm trying to automate some of my recurring tasks. Amongst other jobs, i want to automate the LDAP Account Creation. Since we have employes with fixed-term employment contract, i want to automatically set the deactivation Date of these LDAP-Accounts to the last Day working.
Our tool where I do the automation supports DelphiScript, VBScript and JavaScript. Additionaly it would support PowerShell scripts.
I have a Date variable which i could declare as DateTime or as String.
As DateTime it will look like 44366,3996712847 converted as a String it will look like 19.06.2021 09:36:35. In the end I need to convert one of these values as a 18 digit timestamp, so i can set a Account Expiration Date in LDAP.
A few years ago I did a lot in C#, but nothing with Delphi, JScript and VBScript. I'm also out of C# by now.
My approaches, where I first tried to Convert String to DateTime, look like this:
uses
Classes, SysUtils;
var
befristet: datetime;
timestamp: string;
begin
befristet := GetVarContent('DATA.Befristet');
timestamp := DateTimeToTimeStamp(befristet);
// Writing to Log
LogMessage('VarContent: ' + befristet);
// Returns 0 as script result
Result := 0;
end;

Related

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.

StrToDateDef not working

I want to convert the system date time to a specific format. My system format is dd/mm/yy which i wanted to convert to mm/dd/yyyy and so i am using StrToDateDef. I need to use StrToDateDef only because the date comes as string and if there is a string other than date i will use default date. My code is below
str := '30/01/14';
GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, FmtStngs);
FmtStngs.DateSeparator := '/';
FmtStngs.ShortDateFormat := 'mm/dd/yyyy';
FmtStngs.TimeSeparator := ':';
FmtStngs.LongTimeFormat := 'hh:nn';
date := StrToDateDef(str,01/28/2013,FmtStngs);
I am expecting the date to be of '01/30/2014' but it is coming as '30/01/14'. What is that i am doing wrong?
There are several bugs in this code:
First the TFormatSettings you are passing to the StrToDateDef routine are the format settings for the string you are passing (and not for the datetime variable that comes out, more on that later).
As you are passing '30/01/14' your ShortDateFormat should be 'dd/mm/yyyy' and not 'mm/dd/yyyy'
Then the default value you are passing equals to like 1ms after midnight of 30.12.1899 (because you actually are passing 1 divided by 28 divided by 2014). Use EncodeDate(2013, 1, 28) from DateUtils.pas.
Then you are saying
I am expecting the date to be of '01/30/2014' but it is coming as '30/01/14'.
Well you are looking at a TDateTime variable and it will be formatted according to your local settings of your windows system by the debugger. Nothing more. You don't have a string but a float value (which is what TDateTime is) that is presented as string to you to make it readable.
Also I think the result should be the passed default date because the passed ShortDateFormat does not match the value of the string you are passing (trying to put 30 into the month part).

Delphi inifiles ReadDateTime

The essence in the following:
procedure TForm1.Button1Click(Sender: TObject);
var
cfile: TInifile;
Date1: TDateTime;
begin
Date1 := IncYear(Now, -50);
cfile := TInifile.Create(ExtractFilePath(Application.ExeName) + 'Settings.ini');
try
cfile.WriteDateTime('Main', 'DateTime', Date1);
ShowMessage('Recorded in the ini file ' + DateTimeToStr(Date1));
Date1 := cfile.ReadDateTime('Main', 'DateTime', Now);
ShowMessage('Read from ini file ' + DateTimeToStr(Date1));
finally
cfile.Free;
end;
end;
Entry in the ini file passes without problems. In the file is written to 04-Dec-63 17:28:14. Read also from ini file does not work, the message falls "04-Dec-63 17:28:14 is not a valid date and time".
Windows 7 Enterprise х32, Embarcadero Delphi XE Portable
You've written the date/time to the file as text. And formatted it using the locale settings of the user who created that file. You are doomed to fail to read this file reliably since different users have different locale settings. You need to use a robust format for the date that does not depend on locale.
The two options that seem most natural:
Store as a floating point value, using the underlying representation of TDateTime.
Store as text using a pre-determined format.
For option 1 you'll need to make sure you use a pre-determined decimal separator to avoid the exact same problem you have now! That means you'll need to perform your own conversion between TDateTime and string because the WriteFloat and ReadFloat methods use the global format settings which are locale dependent. There are overloads of FloatToStr and StrToFloat in SysUtils that accept a format settings parameter.
For option 2, the RTL contains various functions to perform date/time conversions using specified formats. There are overloads of DateTimeToStr and StrToDateTime in SysUtils that accept a format settings parameter.
Option 2 is to be preferred if you wish the file to be easily read or edited by a human.

Crash in Delphi function - StrToDateTime

Who can to advice in my problem.
I set date format as 'JUL/12 - 12 15:35', but when using StrToDateTime then give EConvertError.
What can I do with this format which contains 2 - date separator ?
Use next code
function LocaleFormatStrToDateTime(const S: string): TDateTime;
var
LFormatSettings: TFormatSettings;
begin
LFormatSettings := GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT);
LFormatSettings.ShortTimeFormat := FormatSettings.ShortTimeFormat;
LFormatSettings.TimeSeparator := FormatSettings.TimeSeparator;
Result := StrToDateTime(S, LFormatSettings);
end;
----------
**
the best solution is use jvDateUtil.StrToDate*
**
Your format is completely non-standard (and almost incomprehensible), so can't be handled by the built-in Date/Time formatters.
You've designed your own format, so you need to write your own code to convert to and from it.
This is nature's way of telling you not to use wacky date and time formats!
Probably the string you're trying to convert is not compatible with the default system format. Taking a look at the method signature and description reveals that you can override it to suit your needs, see an example here.
It would be helpful if you posted a piece of the code you have so far, maybe you overlooked something.
EDIT
I've missed the fact that your're using a complex format, including multiple separators for the date, which I'm not sure that are supported in delphi.
I guess that in this case you could split your string into pieces and then encode them into a TDateTime. To convert your month name to a month number you can iterate through the LFormatSettings.ShortMonthNames array, something like:
String longMonth:= copy(S, 0, 3);
for i := Low(LFormatSettings.ShortMonthNames) to High(LFormatSettings.ShortMonthNames) do
if SameText(longMonth, LFormatSettings.ShortMonthNames[i]) then begin
shortMonth:=FormatFloat('00', i);
Break;
end;

delphi how to convert twitter timestamp to TDatetime

In continue to this question:
How to Convert Twitter Timestamp to DateTime?
what is the code to convert twitter date time stamp to TDateTime?
edit:
StrDateTime(const string;TFormatSettings);
could handle some of it,
now only to figure out how to intoduce new format.
Since we don't have the ParseExact function, you need to parse the components of the timestamp positionally. You could do it with the Copy() function. ex:
TheMonthAsString := Copy(TwitterDate,5,3);
TheDayAsString := Copy(TwitterDate,9,2);
etc..
Convert those pieces to Integers, and then you can use EncodeDateTime (in the DateUtils unit) (Thanks Jens!) to generate a TDateTime.
Summary: Pick the string apart into the individual components of the timestamp, and convert that to a TDateTime using EncodeDateTime or StrToDateTime.

Resources