How to change the system shortdatetime format using delphi xe3 - delphi

I am going to try and make the question as simple as possible.
How do i either convert the system date to a format i would like, and still keep it a date and not a string.
Or how do i get the system's date format, to adjust my dates accordingly.
When i call
FormatShortdateTime('d/M/yyyy',Date);
I get the correct date as string, but cannot convert it back to a Tdate and use it, then it clashes with the system date settings.
If i can get the system shortdate format the problem would be solved.

Update
The answer below refers to the original question that you asked. You've edited the question multiple times now to ask different questions. You really should not do that and it is a sign that you should spend more time understanding the problem before asking questions.
Even so, the final part of the answer, probably still contains the advice that you need. Namely to stop using conversion functions that rely on the global FormatSettings variable, and always use conversion functions that are passed format settings as a parameter.
The date values in your database are not stored in the format that you expect. This program:
{$APPTYPE CONSOLE}
uses
System.SysUtils;
var
fs: TFormatSettings;
begin
fs := TFormatSettings.Create;
fs.ShortDateFormat := 'd/M/yyyy';
Writeln(DateToStr(StrToDate('18/2/2014', fs)));
end.
produces the following output on my machine:
18/02/2014
I guess on your machine it would produce a different output because your short date format is different from mine. But the point is that the call to StrToDate succeeds.
Clearly the values stored in your database are not in the form you claim. Because you claim that the above code would lead to a date conversion error in the call to StrToDate.
Let's step back and look at the initial part of your question:
I would like to know how to convert dates without having the system date influence the software.
The code in the answer above gives you an example of how to do that. Modern versions of Delphi overload the date and time conversion functions. There are overloads that accept TFormatSettings parameters, and overloads without such a parameter. The TFormatSettings parameter is used to control the format used for the conversion. The overloads that do not receive such a parameter use the global FormatSettings instance declared in the SysUtils unit.
What you should do to avoid having the system settings influence your conversions is to only use conversion functions that accept a format settings parameter.
On the other hand, if you want the system settings to influence your conversions, then feel free to call the overloads that use the global format settings variable.
My code above illustrates both. The conversion from string to date requires a fixed format, not varying with local system settings. The conversion from date to string is designed to show a date using the prevailing local system settings.

I would advice creating own TFormatSettings variable
older delphi versions
var
fs: TFormatSettings;
begin
fs := TFormatSettings.Create;
GetLocaleFormatSettings(LOCALE_SYSTEM_DEFAULT, fs);
end.
This will create your own formatSettings (filled with system format) so you won't alter the default one.
Now what's important is that '/' in shortDateFormat will be replaced by dateSeparator so if you do this
fs.ShortDateFormat:='M/d/yyyy';
fs.dateSeparator:='?';
your input should be like this
strtodate('12?30?1899', fs);
or if other way around you will end up with string: '12?30?1899';
I think that your problems originate from that you don't ask what dateSeparator is used. That would lead to problems if what program expects is '30-12-1899' but he gets '30/12/1899' from you.

Related

How to get correct DateSeparator in Delphi 5?

I am trying to get the correct DateSeparator when the system locale is set to Cenz Republic. The current date is formatted as 9.3.2017, but DateSeparator is always '/' instead of '.'. What can I do to get the correct date separator?
Regarding date presentations there are several separate global variables affecting the output. You mention two of them:
`DateSeparator: Char;` (initialized from registry with reference `LOCALE_SDATE`)
`ShortDateFormat: string;` (initialized from registry with reference `LOCALE_SSHORTDATE`)
The other ones you can see in the documentation or in code in unit SysUtils starting on line 490 (in Delphi 7, might be different in Delphi 5).
Since the DateSeparator and ShortDateFormat are separate variables, it is possible that you see dates presented correctly according your locale, while the DateSeparator return an erroneous character.
To rectify, you can assign the correct character to DateSeparator yourself in your code, but beware if you are using 3rd party libraries, that those might possibly also want to change it.

Overriding the system Date Format

In my application I am reading from one database and writing to a second. The app is quick and dirty so I am reading / writing using AsString on both the FieldByName and ParamByName of the queries.
This works for all my use cases apart from where the data type is Date or DateTime
As far as I can tell FieldByName.AsString uses the system ShortDateTime format to return dates as (in my case) dd/mm/yyyy. The database expects the date to be written in as yyyy-mm-dd
According to Delphi Basics I should be able to set ShortDateFormat to what I need, but it appears that in XE5 this is no longer the case (correct?)
Further digging on here returns these two questions that use TFormatSettings to override the local settings. However, both of these use the resulting FormatSettings in StrToDate and FormatDateTime directly.
So two questions
1) Can I tell my application to override the System ShortDateFormat?
2) If so, How (I have a Plan B if not)?
The use of a global variable for the date and time formats was a mistake committed long ago by the original RTL designers. Functions that rely on the global format settings, like the single parameter StrToDate are retained for backwards compatibility, but you should not be using them.
For conversions between date/time and string you should:
Initialise a TFormatSettings instance with your date format.
Call the two parameter StrToDate, passing your TFormatSettings to convert from a string to a date.
Call FormatDateTime overload that accepts a TFormatSettings when converting in the other direction.
Now, to the main thrust of your question. You should not be using strings at all for your dates and times in the scenario you describe. Use AsDateTime rather than AsString. If you happen to have a database column that does store a date/time as a string, then you'll should use the TFormatSettings based conversion functions to work around that design fault.
If you are absolutely dead set on doing this all with strings, and I cannot persuade you otherwise, then you need to use FormatSettings.ShortDateFormat from SysUtils to control your short date formatting.

TDictionary.ContainsKey returning false, even though the key exists

The problem I'm facing now has totally made me feel like I don't understand programming at all. I have a TDictionary object which consists of pairs of TDate => TEvent (custom class). The object is used in a class which is shared by two separate applications (they don't communicate). In one application, the following works correctly:
// Get recipes from the very event.
Tmp := FCalendar.ContainsKey(D);
if (Tmp) then
begin
E := FCalendar[D];
CopyRecipes(E);
end;
On the other hand, the same piece of code doesn't work in the other application ! How is that possible? I have attached a screenshot of what is happening in the debugger:
As you can see, the key is present in the dictionary, and yet ContainsKey() returns FALSE.
What is causing this?
Regards,
Patryk.
The reason is that TDate is actually just a TDateTime. That means it is a double precision value holding number of days since the Delphi epoch. The IDE debug hints for a TDate show just the date part and omit the time part. You are comparing for equality and so two values that are in the same day, but have different time components will not be equal. Here's a screenshot to illustrate:
Solve the problem by using DateOf to remove the time part of the date time. You will have to use DateOf whenever you receive a date value, and clearly before you add to the dictionary.
possibly in D not 2011-11-11
possibly D:=2011-11-11 00-00-00

Optimal way to store datetime values in SQLite database (Delphi)

I will be storing datetime values in an SQLite database (using Delphi and the DISqlite library). The nature of the db is such that it will never need to be transferred between computers or systems, so interoperability is not a constraint. My focus instead is on reading speed. The datetime field will be indexed and I will be searching on it a lot, as well as reading in thousands of datetime values in sequence.
Since SQLite does not have an explicit data type for datetime values, there are several options:
use REAL data type and store Delphi's TDateTime values directly: fastest, no conversion from string on loading; impossible to debug dates using a db manager such as SQLiteSpy, since dates will not be human-readable. Cannot use SQLite date functions (?)
use a simple string format, e.g. YYYYMMDDHHNNSS: conversion is required but relatively easy on the CPU (no need to scan for separators), data is human-readable. Still cannot use SQLite date functions.
do something else. What's the recommended thing to do?
I have read http://www.sqlite.org/lang_datefunc.html but there's no mention of what data type to use, and, not being formally schooled in programming, I don't quite grok the focus on Julian dates. Why the additional conversion? I will be reading in these values a lot, so any additional conversions between strings and TDateTime adds a significant cost.
You could use one of the SQLite supported string formats, eg. YYYY-MM-DD HH:MM:SS.SSS.
It would be just as easy as YYYYMMDDHHNNSS - you still wouldn't need to scan for separators, since all the numbers are fixed length - and you would get SQLite date function support.
If you need SQLite date function support, I would go with that method.
If not, I'd recommend using REAL values. You can still compare them to each other (higher numbers are later in time), and consider date and time separately (before and after the decimal point respectively) without converting to TDateTime.
One compromise would be to stick with REAL values, but store them as julian dates by using Delphi's DateTimeToJulianDate. That way they remain fast for reading, there's little performance lost in the converation, and they're still in a format that makes sense outside of Delphi.
For this I usually use an Integer data type and store the Unix timestamp value alike (eq # seconds since 1-1-2000 for example). Calculating this t/from a TDateTime is equal to multiplying/diving with/by 86400 and adding a constant for the 'since'.
If you need more precision You could use the DateTime as a FILETIME (eg int64) which has 100 ns increments. There are conversion routines in SysUtils for that and your timestamp is stored in UTC.
If your concern is only human readable format at database level, you can store two separate fields, for example:
DELPHI_DATE REAL (DOUBLE, if possible, but I don't know SQLite), Indexed. All your programmatic queries and comparisons should use this field.
HUMAN_READABLE_DATE Varchar(23) with format 'YYYY-MM-DD HH:MM:SS.SSS'. Maybe indexed (if really necessary). Majority of human input queries should include this field and you can use (as said by others) SQLite date functions.
It has some drawbacks:
Space consumption at database and network traffic grows,
insert operations will take a bit more because necessary conversion,
no automatic synch between values if updated outside your program
If it is suitable for your particular needs, is up to you.
I don't know if this answer is applicable to the DISqlite library but...
Below is some code that illustrates what works for me using both Delphi 2010 and Tim Anderson's SQLite3 wrapper.
SQL to create field:
sSQL := 'CREATE TABLE [someTable] (' +
' [somefield1] VARCHAR(12),' +
' [somefield2] VARCHAR(12),' +
' [myDateTime] DATETIME );';
SQL to populate field:
sSQL := 'INSERT INTO someTable(somefield1, somefield2, myDateTime)' +
' VALUES ( "baloney1", "baloney2","' + FloatToStr(Now) + '");';
Example of retrieving data from field:
var
sDBFilePathString: string;
sl3tbl: TSqliteTable;
fsldb : TSQLiteDatabase;
FromdbDTField : TDateTime;
begin
...
...
fsldb := TSQLiteDatabase.Create(sDBFilePathString);
sl3tbl := fsldb.GetTable('SELECT * FROM someTable');
FromdbDateTime := StrToFloat(sl3tbl.FieldAsString(sl3tbl.FieldIndex['myDateTime']));
Showmessage('DT: ' + DateTimeToStr(FromdbDTField));
end;
Result:
**DT: 10/10/2013 1:09:53 AM**
Like I mentioned on the first line - I don't know if this will work with the DISqlite library but if it does its a pretty clean way of handling things.
I leave it to you to make things prettier or more elegant.

How does WriteLn() really work?

Since the age of the dinosaurs, Turbo Pascal and nowadays Delphi have a Write() and WriteLn() procedure that quietly do some neat stuff.
The number of parameters is variable;
Each variable can be of all sorts of types; you can supply integers, doubles, strings, booleans, and mix them all up in any order;
You can provide additional parameters for each argument:
Write('Hello':10,'World!':7); // alignment parameters
It even shows up in a special way in the code-completion drowdown:
Write ([var F:File]; P1; [...,PN] )
WriteLn ([var F:File]; [ P1; [...,PN]] )
Now that I was typing this I've noticed that Write and WriteLn don't have the same brackets in the code completion dropdown. Therefore it looks like this was not automatically generated, but it was hard-coded by someone.
Anyway, am I able to write procedures like these myself, or is all of this some magic hardcoded compiler trickery?
Writeln is what we call a compiler "magic" function. If you look in System.pas, you won't find a Writeln that is declared anything like what you would expect. The compiler literally breaks it all down into individual calls to various special runtime library functions.
In short, there is no way to implement your own version that does all the same things as the built-in writeln without modifying the compiler.
As the Allen said you can't write your own function that does all the same things.
You can, however, write a textfile driver that does something custom and when use standard Write(ln) to write to your textfile driver. We did that in ye old DOS days :)
("Driver" in the context of the previous statement is just a piece of Pascal code that is hooked into the system by switching a pointer in the System unit IIRC. Been a long time since I last used this trick.)
As far as I know, the pascal standards don't include variable arguments.
Having said that, IIRC, GNU Pascal let's you say something like:
Procecdure Foo(a: Integer; b: Integer; ...);
Try searching in your compiler's language docs for "Variable Argument Lists" or "conformant arrays". Here's an example of the later: http://www.gnu-pascal.de/demos/conformantdemo.pas.
As the prev poster said, writeln() is magic. I think the problem has to do with how the stack is assembled in a pascal function, but it's been a real long time since I've thought about where things were on the stack :)
However, unless you're writing the "writeln" function (which is already written), you probably don't need to implement a procedure with a variable arguments. Try iteration or recursion instead :)
It is magic compiler behaviour rather than regular procedure. And no, there is no way to write such subroutines (unfortunately!). Code generation resolves count of actual parameters and their types and translates to appropriate RTL calls (eg. Str()) at compile time. This opposes frequently suggested array of const (single variant array formal parameter, actually) which leads to doing the same at runtime. I'm finding later approach clumsy, it impairs code readability somewhat, and Bugland (Borland/Inprise/Codegear/Embarcadero/name it) broke Code Insight for variant open array constructors (yes, i do care, i use OutputDebugString(PChar(Format('...', [...])))) and code completion does not work properly (or at all) there.
So, closest possible way to simulate magic behaviour is to declare lot of overloaded subroutines (really lot of them, one per specific formal parameter type in the specific position). One could call this a kludge too, but this is the only way to get flexibility of variable parameter list and can be hidden in the separate module.
PS: i left out format specifiers aside intentionally, because syntax doesn't allow to semicolons use where Str(), Write() and Writeln() are accepting them.
Yes, you can do it in Delphi and friends (e.g. free pascal, Kylix, etc.) but not in more "standard" pascals. Look up variant open array parameters, which are used with a syntax something like this:
procedure MyProc(args : array of const);
(it's been a few years and I don't have manuals hand, so check the details before proceeding). This gives you an open array of TVarData (or something like that) that you can extract RTTI from.
One note though: I don't think you'll be able to match the x:y formatting syntax (that is special), and will probably have to go with a slightly more verbose wrapper.
Most is already said, but I like to add a few things.
First you can use the Format function. It is great to convert almost any kind of variable to string and control its size. Although it has its flaws:
myvar := 1;
while myvar<10000 do begin
Memo.Lines.Add(Format('(%3d)', [myVar]));
myvar := myvar * 10;
end;
Produces:
( 1)
( 10)
(100)
(1000)
So the size is the minimal size (just like the :x:y construction).
To get a minimal amount of variable arguments, you can work with default parameters and overloaded functions:
procedure WriteSome(const A1: string; const A2: string = ''; const A3: string = '');
or
procedure WriteSome(const A1: string); overload;
procedure WriteSome(const A1: Integer); overload;
You cannot write your own write/writeln in old Pascal. They are generated by the compiler, formatting, justification, etc. That's why some programmers like C language, even the flexible standard functions e.g. printf, scanf, can be implemented by any competent programmers.
You can even create an identical printf function for C if you are inclined to create something more performant than the one implemented by the C vendor. There's no magic trickery in them, your code just need to "walk" the variable arguments.
P.S.
But as MarkusQ have pointed out, some variants of Pascal(Free Pascal, Kylix, etc) can facilitate variable arguments. I last tinker with Pascal, since DOS days, Turbo Pascal 7.
Writeln is not "array of const" based, but decomposed by the compiler into various calls that convert the arguments to string and then call the primitive writestring. The "LN" is just a function that writes the lineending as a string. (OS dependant). The procedure variables (function pointers) for the primitives are part of the file type (Textrec/filerec), which is why they can be customized. (e.g. AssignCrt in TP)
If {$I+} mode is on, after each element, a call to the iocheck function is made.
The GPC construct made above is afaik the boundless C open array. FPC (and afaik Delphi too) support this too, but with different syntax.
procedure somehting (a:array of const);cdecl;
will be converted to be ABI compatible to C, printf style. This means that the relevant function (somehting in this case) can't get the number of arguments, but must rely on formatstring parsing. So this is something different from array of const, which is safe.
Although not a direct answer to you question, I would like to add the following comment:
I have recently rewritten some code using Writeln(...) syntax into using a StringList, filling the 'lines' with Format(...) and just plain IntToStr(...), FloatToStr(...) functions and the like.
The main reason for this change was speed improvement. Using a StringList and SaveFileTo is much, much more quicker than the WriteLn, Write combination.
If you are writing a program which creates a lot of text files (I was working on a web site creation program), this makes a lot of difference.

Resources