Date format in RecordSet - delphi

I need to store a date in a Recordset. I am doing it as follows:
rs: _Recordset;
dt: TDatetime;
Rs := CoRecordset.Create;
Rs.Fields.Append('Date', adDate, 4, adfldupdatable, Unassigned);
Rs.Fields.Item['Date'].Value := FormatDateTime('dd/mm/yyyy', TDatetime)
But, in the Recordset the date appears in format d/m/yyyy
How can I store dd/mm/yyyy in the recordset?

The adDate is not a string format, it is a date format. When you set value, it is performing a conversion from the string you entered into its internal format (which renders as d/m/yyyy when saved to a file).
If you want a specific string format for your date, use a string field to store the date. The disadvantage is that you are going to then need to make sure that you process the string field properly if you change your region settings to one where the date format is in M/D/Y order instead of what you stored as D/M/Y order.

I'm afraid I cannot get your code to function as you claim. In any case, it is clearly in error
because the line
Rs.Fields.Item['Date'].Value := FormatDateTime('dd/mm/yyyy', TDatetime)
is wrong, because you are passing TDateTime as the second argument, not your
dt variable you apparently intend.
So, instead, using Delphi 10.4.2. I have compiled and run this program on Win10
program RecordSetTest;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Variants, System.Win.ComObj, WinAPI.ActiveX, WinAPI.AdoInt;
var
RS: _Recordset;
DT: TDatetime;
begin
CoInitialize(Nil);
DT := Now;
RS:= CoRecordset.Create;
RS.Fields.Append('Date', adDate, 4, adfldupdatable, Unassigned);
RS.Open('c:\temp\rsdata.xml', EmptyParam, adOpenStatic, adLockOptimistic, 0);
RS.AddNew('Date', FormatDateTime('dd/MM/yyyy', DT));
RS.Save('c:\temp\rsdata.xml', adPersistXML);
RS := Nil;
writeln('saved');
readln;
end.
Note the call to AddNew, which is necessary to add a data record to the RecordSet file.
On my system, which has short and long date formats of dd/MM/yyyy and dd MMMM yyyy, the file
written is as follows:
<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882'
xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882'
xmlns:rs='urn:schemas-microsoft-com:rowset'
xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
<s:ElementType name='row' content='eltOnly' rs:updatable='true'>
<s:AttributeType name='Date' rs:number='1' rs:write='true'>
<s:datatype dt:type='dateTime' rs:dbtype='variantdate' dt:maxLength='16' rs:precision='0' rs:fixedlength='true'
rs:maybenull='false'/>
</s:AttributeType>
<s:extends type='rs:rowbase'/>
</s:ElementType>
</s:Schema>
<rs:data>
<z:row Date='2021-10-20T12:32:59'/>
</rs:data>
</xml>
Note the format of the date value
2021-10-20T12:32:59
despite the formatting of the date value in
RS.AddNew('Date', FormatDateTime('dd/MM/yyyy', DT));

Related

Delphi: DateTimeToStr output with zero time (midnight)

I have found a similar question here, but it is unrelated to what I am trying to do. I have done a lot of research on the Internet and I have determined that Delphi is working as designed or intended, where it omits the time if the time is zero. I have an application which displays the date & time in a listview, and when the time is midnight, it doesn't show 00:00:00, and therefore making the results look uneven and out of place.
The way I've gotten around this which is still locale independant is to add a microsecond to the time, see sample code:
program Test11;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Winapi.Windows;
begin
try
Writeln(DateTimeToStr(44167, TFormatSettings.Create(GetThreadLocale)));
Writeln(DateTimeToStr(44167.00000001, TFormatSettings.Create(GetThreadLocale)));
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
And subsequent output:
02/12/2020
02/12/2020 00:00:00
The question is - is there a better, more programatically correct way to do achieve this?
Running Delphi XE6
You can use FormatDateTime function for more control over date time formatting.
FormatDateTime('ddddd tt', 44167, TFormatSettings.Create);
Note: There is no need to call TFormatSettings.Create(GetThreadLocale) with locale parameter because plain TFormattSettings.Create call will internally use GetThreadLocale on Windows platform.
This is "normal" behavior of VCL. Look at the related code from System.SysUtils (it is from Seattle, but I think it hasn't much changes since XE6).
function DateTimeToStr(const DateTime: TDateTime;
const AFormatSettings: TFormatSettings): string;
begin
DateTimeToString(Result, '', DateTime, AFormatSettings);
end;
procedure DateTimeToString(var Result: string; const Format: string;
DateTime: TDateTime; const AFormatSettings: TFormatSettings);
begin
...
if Format <> '' then AppendFormat(Pointer(Format)) else AppendFormat('C');
...
//This is another related part of DateTimeToString.
case Token of
'C':
begin
GetCount;
AppendFormat(Pointer(AFormatSettings.ShortDateFormat));
GetTime;
if (Hour <> 0) or (Min <> 0) or (Sec <> 0) or (MSec <> 0) then
begin
AppendChars(' ', 1);
AppendFormat(Pointer(AFormatSettings.LongTimeFormat));
end;
end;
If time part of datetime value equals to zero, then only date will be in result string.
If you want always show datetime in single format, better use FormatDateTime as suggested by other answers.

Why can't a Variant contain a TDateTime before 100 CE?

Consider the following code:
procedure Test;
function d1: Variant;
var
DDt: TDateTime;
begin
DDt := EncodeDate(100,1,1);
Result := DDt;
end;
function d2: Variant;
var
DDt: TDateTime;
begin
DDt := EncodeDate(99,12,31);
Result := DDt;
end;
procedure Writedate(V: Variant);
begin
Writeln(string(V));
end;
var
V: Variant;
begin
V := d1;
Writedate(V);
V := d2;
Writedate(V);
end;
The first call to Writedate will succeed, and the output will be '01-01-0100'. The second call, however, will fail with an 'invalid argument' failure. Inspecting the code, you can see the Variant of the 99-12-31 date has a EVariantInvalidArgError error.
However, if I call FormatDateTime('c', TDateTime(V)) on either TDateTime, they will both succeed. In fact, at any point when the Variant contains a TDateTime, whose date is before 100 CE, the IDE will display a EVariantInvalidArgError when inspecting its value.
It seems odd that the Variant cannot handle the pre-100 CE date, when TDateTime can. Is this a bug in Delphi? I find it being right between 99 and 100 CE to be a bit suspicious.
Variant can contain any date value, as your code demonstrates (assignment V := d2; produces no error).
The error is raised during the conversion to string which the compiler delegates to the OS on Windows platforms. This fails because OLE Automation specifies midnight, 1 January 0100 as the minimum valid OLE Automation date value.

Is DateTimeToString in Delphi XE5 doesn't work?

I have small piece of code:
DateTimeToString(DiffString, 't.zzz', TDT);
ShowMessage('TDT: ' + DateTimeToStr(TDT));
ShowMessage('DiffString: ' + DiffString);
which result with first ShowMessage gives random nice DateTime TDT value...
second where DiffString is exacly 00:00.000
Could anyone check it in other IDE?
In fact DateTimeToString works just fine and is behaving exactly as designed. It is doing precisely what you asked it to.
Here is the SSCCE that you should have provided:
{$APPTYPE CONSOLE}
uses
SysUtils;
var
DiffString: string;
TDT: TDateTime;
begin
TDT := Date;
DateTimeToString(DiffString, 't.zzz', TDT);
Writeln('TDT: ' + DateTimeToStr(TDT));
Writeln('DiffString: ' + DiffString);
end.
Output:
TDT: 04/02/2014
DiffString: 00:00.000
The reason is, and I am guessing here, that your date time comes from a call to Date. Or perhaps your date time is an uninitialized variable.
Whichever way, it is clear that the time part is zero. Into DiffString you put the time and not the date. That is what the t.zzz format string means.
Try again with a date time containing a non-zero time:
{$APPTYPE CONSOLE}
uses
SysUtils;
var
DiffString: string;
TDT: TDateTime;
begin
TDT := Now;
DateTimeToString(DiffString, 't.zzz', TDT);
Writeln('TDT: ' + DateTimeToStr(TDT));
Writeln('DiffString: ' + DiffString);
end.
Output
TDT: 04/02/2014 11:16:43
DiffString: 11:16.942
Of course, t.zzz is a bad choice of format. It combines the short time format with milliseconds. As you can see, on my machine, the default short time format omits seconds. So you get hours, minutes and milliseconds. You'll need to re-think your format string. Perhaps 'hh:nn:ss.zzz' is what you need.

How to convert UTC to local time

I have to develop an application for ePrescribe and need to convert an UTC Time value (for example '2010-01-01T16:09:04.5Z') to local time. Delphi 2010, any suggestions?
You have to parse the string manually first. Extract the individual values from it, then you can put them into a Win32 SYSTEMTIME record and call SystemTimeToTzSpecificLocalTime() to convert it from UTC to local. You can then use the converted SYSTEMTIME however you need, such as converting it to a TDateTime using SystemTimeToDateTime().
You could use TXSDateTime class from unit XSBuiltIns
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
XSBuiltIns;
var xsDateTime: TXSDateTime;
input, output: string;
date: TDateTime;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
input := '2010-01-01T16:09:04.5Z';
xsDateTime := TXSDateTime.Create;
xsDateTime.XSToNative(input);
date := xsDateTime.AsDateTime;
output := 'Parsed date/time: ' + FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
writeln(output);
readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Output:
Parsed date/time: 2010-01-01 19:09:04
Your computer provides your time zone. You can use them to manipulate UTC time to adjust by adding hours and minutes.

Delphi & ADO: datetime to string conversion

I use Delphi 2006 and ADO to connect to a MS Access database. Some of the fields I retrieve are Date fields (in Access formatted as "Medium Date" i.e. 20-Apr-2010) however I have to retrieve them as Strings:
FValue:=FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsString;
and then the fields are formatted as follows: 4/20/2010.
My question is: when does this formatting take place and how can I customize it? Is it ADO settings (could not find anything there) or the OS (I use Win XP ENG with US locale)? Or maybe it's Delphi?
Thanks!
Lou
the ShortDateFormat and LongTimeFormat variables are used to format an TDateTimeField to string.
you can change the value of theses variables or try something different like this :
Dt :TDateTime;
Ds :String;
begin
//FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsString
Dt:=FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsDateTime;
Ds:=FormatDateTime('dd-mmm-yyyy',dt);
end;
Ok, just found it. It's delphi general settings (if missing then the values are taken from the OS):
DateSeparator := '-';
ShortDateFormat := 'dd-mmm-yyyy';
And now the returned value is "20-Apr-2010".
You can retreive the Value as DateTime and use this function to convert it to your format
FValue:=FAccessADOQuery.Fields.FieldByName(FIELD_NAME).AsDateTime;
function DateToMediumDate(const Date: TDate): string;
var
y, m, d: Word;
begin
DecodeDate(Date, y, m , d);
Result := Format('%d-%s-%d', [d, ShortMonthNames[m], y]);
end;

Resources