Incus J 10 Posted February 7, 2023 (edited) var fmt:TFormatSettings; dt:TDate; begin fmt:=TFormatSettings.Create('en-GB'); fmt.DateSeparator:='/'; fmt.ShortDateFormat:='mmm/d/yyyy'; dt:=StrToDate('Feb/7/2023',fmt); end; I'm having trouble converting dates in a string to TDate. The above test code raises an EConvertError - 'Feb/7/2023' is not a valid date. Where am I going wrong? Edited February 7, 2023 by Incus J Share this post Link to post
Lajos Juhász 293 Posted February 7, 2023 Using Delphi 11.2 update 1 the code works correctly. Share this post Link to post
Der schöne Günther 316 Posted February 7, 2023 Can confirm, works for me (Delphi 11.1). Share this post Link to post
Attila Kovacs 629 Posted February 7, 2023 11.x pfffff.... #forbes I can't see any month name parsing up to version 20. 1 Share this post Link to post
programmerdelphi2k 237 Posted February 7, 2023 (edited) using mask "MMM/d/yyyy" it's works! = no exception! Now, "Names" = month-names short! MSWindows Datetime formats: MM = nn MMM = Sss NOTE: in my RAD11.2 "mmm" works too! Edited February 7, 2023 by programmerdelphi2k 2 1 Share this post Link to post
aehimself 396 Posted February 7, 2023 In Delphi 11 they fixed the date separator, I had to include a new helper to bridge the difference between 10.4 and 11. Let's see if I can dig it up for you. Share this post Link to post
aehimself 396 Posted February 7, 2023 This is the method I ended up using: Procedure FixDateSeparator; Var Buffer: Array[0..2] Of Char; Begin If GetLocaleInfo(GetThreadLocale, LOCALE_SDATE, Buffer, 3) > 0 Then Begin FormatSettings.DateSeparator := Buffer[0]; FormatSettings.ShortDateFormat := FormatSettings.ShortDateFormat.Replace(FormatSettings.DateSeparator, '/'); End Else FormatSettings.DateSeparator := '/'; End; It's a close copy to Delphi 11.2's GetLocaleChar implementation which will fix the buffer underrun in prior versions. Add a TFormatSettings parameter and call it with "fmt" before you call StrToDate. This is the good news. The bad news is, parsing strings as dates was heavily refactored in D11. If the above won't fix your problem, you'll have to start playing around. For me the date format was "yyyy. mm. dd." D10.4 was happy to parse "2023. 02. 07" but D11 required the final dot. 1 Share this post Link to post
Lajos Juhász 293 Posted February 8, 2023 13 hours ago, Attila Kovacs said: I can't see any month name parsing up to version 20. If you are looking how the Delphi is converting the month names you can search for @AFormatSettings.ShortMonthNames in the System.SysUtils. 1 Share this post Link to post
Attila Kovacs 629 Posted February 8, 2023 (edited) 2 hours ago, Lajos Juhász said: If you are looking how the Delphi is converting the month names you can search for @AFormatSettings.ShortMonthNames in the System.SysUtils. As I said, up to BDE 20 the firt check is number/separator/number. If it doesn't match, Exit then raise. Edited February 8, 2023 by Attila Kovacs Share this post Link to post
Lajos Juhász 293 Posted February 8, 2023 2 minutes ago, Attila Kovacs said: BDE BDE (Borland Database Enginge) is deprecated for about 20 years you should not use that. If you are referencing to the Delphi version you should use the official one that everyone can understand. Share this post Link to post
Attila Kovacs 629 Posted February 8, 2023 (edited) sorry BDS 😄 Edited February 8, 2023 by Attila Kovacs Share this post Link to post
Attila Kovacs 629 Posted February 8, 2023 (edited) 2 hours ago, Lajos Juhász said: If you are referencing to the Delphi version you should use the official one that everyone can understand. BDS Version 20 If you can't deal with this information, just don't comment on it. Edited February 8, 2023 by Attila Kovacs Share this post Link to post
Incus J 10 Posted February 8, 2023 Thank you for all the responses. I'm in 10.4.1 and so far can't get either mmm nor MMM to parse the months - but this is an FMX project, so I can probably risk taking it into 11 Alexandria. My general take away from this is that the behaviour of Delphi's built in StrToDate function differs between versions, and might be buggy. I think I'm going to roll my own StrToDate function, since ideally I'd like to parse dates containing characters like commas too, such as "Feb 7, 2023". Interestingly I found that VarToDateTime (variant to date time) parses dates with commas OK on Windows, but sadly not on macOS - so I'm guessing the OS provides the conversion for that function. Share this post Link to post
Sherlock 663 Posted February 8, 2023 1 minute ago, Incus J said: but this is an FMX project, so I can probably risk taking it into 11 Alexandria. I could readily accept such a statement for VCL, but still FMX tends to change a lot. Less than in the XE versions of Delphi but still. Share this post Link to post
Lajos Juhász 293 Posted February 8, 2023 FMX is just another reason to keep up with the Delphi version. Unlike Windows, other platforms are not backward compatible. Share this post Link to post
Incus J 10 Posted February 8, 2023 I agree FMX tends to change more between versions, and it can be a challenge to keep up. But to clarify (hopefully) - my development system is HiDPI and I find when I open any existing VCL form in Alexandria it modifies the position and dimension values (the actual stored x/y/width/height) of all objects on the form. The forms then no longer work on older versions of Windows (e.g. 7/8) - so I avoid Alexandria for VCL projects. Don't get me wrong - I love the look of the HiDPI IDE in Alexandria, and FMX forms behave fine in it, but feel I can not use it for VCL projects. I may have strayed off topic. 1 Share this post Link to post
Incus J 10 Posted February 8, 2023 I've created a general purpose string to date function that is quite tolerant and can parse a variety of date strings using just a couple of parameters like this: 'February 7th, ''23', TDateOrder.doMDY Its current tolerance for variations (e.g. 'Feb 7 2023' '02/07/23' "7th.02 '23") risks it getting creative if fed a string that isn't really a date, but I can tighten it up later if it needs to be stricter (famous last words - what could possibly go wrong). Share this post Link to post
programmerdelphi2k 237 Posted February 8, 2023 I think you could create "input patterns" so you could eliminate error expectations. For each "input pattern", you could create the pre-analysis procedure/function, and at the end, the final completion code, either to show the error, or to return the value to the user in the expected format. as Delphi does, that is, procedures that call sub-procedures to find the appropriate use case (a kind of "overloads"). in general, a simple call to Delphi's standard function would be enough to check the validity or not of a date or time, otherwise, your function could continue after this first evaluation by default. Share this post Link to post
Incus J 10 Posted February 16, 2023 Yes - at the moment I have a private InitialParse subroutine that basically attempts to grab three date components from whatever string it is fed, and drops them into a TStringlist (e.g. from 'Feb 7th, ''23' it will extract 'Feb' and '7th' and '23'). What to do with those three components (how to interpret them) is decided later. It works, but if necessary this subroutine could be made much stricter in terms of what it will accept as input. class procedure TDateUtils.InitialParse(s: string; var ts:TStringlist); {Pull three components from the date string.} var i:integer; c:char; bSkip:boolean; sPart:string; begin sPart:=''; bSkip:=true; for i:=1 to Length(s) do begin c:=s[i]; if c.IsLetterOrDigit then begin if bSkip then begin bSkip:=false; AddPart(ts,sPart); end; sPart:=sPart+c; end else bSkip:=true; end; AddPart(ts,sPart); while ts.Count<3 do ts.Add('0'); //Ensure 3 date components. end; class procedure TDateUtils.AddPart(var ts:TStringlist; var s:string); {Add a complete component e.g. '7th' to the list.} begin if s<>'' then begin if ts.Count<3 then ts.Add(s); s:=''; //Reset. end; end; It could also handle the 'more than three components present' situation more efficiently. Share this post Link to post
programmerdelphi2k 237 Posted February 16, 2023 (edited) hi @Incus J in fact, the Delphi give to you System.Date.utils and others with many functions/procedures to do many tasks, then, why not use it? note: isn't it overkill to use a TStringList to store so little data? I think it would be suitable to use Arrays uses System.DateUtils; procedure TForm1.Button1Click(Sender: TObject); const LArrOfXX: array [1 .. 31] of string = { } ('st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', { } 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th', 'th'); var FS : TFormatSettings; LMyDateTimeValue : string; LMyDateTimeResult: TDateTime; begin LMyDateTimeValue := 'Feb/16/2023'; // FS := TFormatSettings.Create('en-GB'); FS.ShortDateFormat := 'MMM/dd/yyyy'; try // first test using Delphi function, of course! LMyDateTimeResult := StrToDateTime(LMyDateTimeValue, FS); // is valid? not... then do it another way! // Memo1.Lines.Add( { your format... } FS.ShortMonthNames[LMyDateTimeResult.Month] + ' ' + { } LMyDateTimeResult.Day.ToString + LArrOfXX[LMyDateTimeResult.Day] + ' ' + { } LMyDateTimeResult.Year.ToString); // Memo1.Lines.Add(LMyDateTimeResult.ToISO8601); // // NOTE: dont use letters used on format function, like this... or use! you decide! Memo1.Lines.Add(LMyDateTimeResult.Format('Hello World: MMMM, dd, yyyy', FS)); // dd/mm/yyyy, MMM/dd/yyyy ... all valid format! // // etc ... except // what to do? Memo1.Lines.Add('Invalid datetime format...'); end; end; try some like this: Quote FS.ShortDateFormat := 'My Year: yy Hello: mm World: dd, dd, mm, yyyy, ss, hh, mm, ss, z'; Memo1.Lines.Add(DateTimeToStr(LMyDateTimeResult, FS)); Edited February 16, 2023 by programmerdelphi2k Share this post Link to post
Incus J 10 Posted February 16, 2023 (edited) Thank you for the suggestions and code example - I will have an explore. Yes I agree TStringlist is slightly heavy for the job - though I do find it useful when stepping through code in the debugger as there is a visualiser to display the contents. I started out with a dynamic array, but got confused trying to pass the array into and out of subroutines, and the TArray type seemed to lack useful methods such as myArray.IndexOf ...basically my lack of experience with arrays led me to switch to TStringlist to save time! My main reason for rolling my own is that I've discovered the built in StrToDate function differs in behaviour between Delphi versions - so I want to create my own function that I can rely on to maintain the same behaviour going forward. Edited February 16, 2023 by Incus J Share this post Link to post
programmerdelphi2k 237 Posted February 16, 2023 (edited) for each edition you can have your StrToDate function or any other similar for the edition, for this u can use "compiler directives" {$IFDEF xxxx } .... {$ELSE / $ELSEIF }...{$ENDIF} for arrays, you can have yourself functions, like IndexOF(); NOTE: you can have the same approach to Dates/Times types!!! as have yourself "StrToDate(...)' for this types not matters the edition!!! type TMyArrayStrings = TArray<string>; TMyHelperToArray = record helper for TMyArrayStrings function MyIndexOf(const AValue: string): integer; end; function TMyHelperToArray.MyIndexOf(const AValue: string): integer; begin result := -1; // for var i: integer := 0 to high(Self) do if (Self[i] = AValue) then exit(i); end; //... var LMyArrStrings : TArray<string>; // array of string i: integer; begin i := LMyArrStrings.MyIndexOf('hello'); // if (i > -1) then Delete(LMyDateTimeValue, i, 1); end; Edited February 16, 2023 by programmerdelphi2k Share this post Link to post