Ian Branch 127 Posted December 28, 2020 Hi Team, I needed a routine that would tell me if either of two dates from a database table were valid before being tested for other characteristics. I came up with the following.. function AreValidDates(const DateString1, DateString2: string): Boolean; var DT: TDateTime; // unused date time value begin // Result := TryStrToDate(DateString1, DT); // if Result then Result := TryStrToDate(DateString2, DT); // end; The Function is called by "if AreValidDates(MyTable.FieldByName('Date1').AsString, MyTable.FieldByName('Date2').AsString) then..." The function is used in many places in my Apps. Is there a better/more efficient way to do this? Regards & TIA, Ian Share this post Link to post
Guest Posted December 29, 2020 First, the best validator for your dates is the Date type field where they reside. Otherwise, the field would not accept such dates or generate a value of "null". Second, I would recommend that you check your IDE's HELP SYSTEM for support for DateTime. You could find an interesting answer to your question, since the best for one would not necessarily be the best for another. A well-planned database would have tables with fields suitable for native types, and, in a more general case, most likely, you would use other types to archive data, the "string" type being one of the most used. However, a type prone to result in errors, if not properly handled on the "Client" side. You better detail your programming environment: IDE, Database, how the dates are archived, there is some way of validation for such archiving, etc ... more information pertinent to what you want to use on your system. In a more conservative case, I would advise you, first, to emphasize the care in data entry in your tables. That is, if a field needs to have a date (or date-and-time), then this field must in no way accept a null date, for example. This way, you are already guaranteeing 99.99% of the success in your task, the rest, 0.01% I would leave due to some bug in the system. What is not uncommon! I think that, "TryStrToDate( 'Date Time to test', DateTimeValid_IfSomeError)" already it's great to valid any datetime value! hug Share this post Link to post
Ian Branch 127 Posted December 29, 2020 As you suggest, one solution is not necessarily the answer for all circumstances. I had considered various options and this was what I considered an appropriate solution. 😉 I had two considerations.. 1. Are either of the date fields NULL; and 2. If they weren't, are they properly constituted date(s). This mechanism gave me coverage for both. It is a little of 'Belts & Braces' as the date entries are via TDateTimePickers. From here, if all good, various 'validity/comparison' checks are done, if they fail the User is sent back to correct the date(s) as appropriate. Thank you for your input. Regards, Ian Share this post Link to post
Guest Posted December 29, 2020 you're wellcome! NOTE: if the entry is verifyed, on the "out" it should be good! Share this post Link to post
Guest Posted December 29, 2020 6 hours ago, Ian Branch said: Is there a better/more efficient way to do this? I would suggest a helper here, so your "if ..." will be this clean if MyTable.AreValidDates('Date1', 'Date2') then But if you are talking about performance, then i would to depend on SQL and the DB engine itself to validate and invalidate dates. Share this post Link to post
Ian Branch 127 Posted December 29, 2020 Hi Kas, Now that's an intriguing idea. Perhaps not for this case but I would be interested in how it is done. Can you point me to appropriate material? A Tutorial or similar.. Regards & TIA, Ian Share this post Link to post
Guest Posted December 29, 2020 23 minutes ago, Ian Branch said: Can you point me to appropriate material? Start here to see the power and usefulness of the helpers http://docwiki.embarcadero.com/RADStudio/Sydney/en/Class_and_Record_Helpers_(Delphi) and here what it will look like type TUniTableHelper = class helper for TUniTable function AreValidDBDates(const DateString1, DateString2: string): Boolean; end; { TUniTableHelper } // Parameters are FieldName in this case function TUniTableHelper.AreValidDBDates(const DateString1, DateString2: string): Boolean; var DT1, DT2: TDateTime; begin Result := TryStrToDate(FieldByName(DateString1).AsString, DT1) and TryStrToDate(FieldByName(DateString2).AsString, DT2); if Result then begin // Do your extra checks, like DT2 should be after DT1, the time span between them should be 5 years at max .. etc end; end; // to be used like this if MyTable.AreValidDBDates('Date1', 'Date2') then begin end; Share this post Link to post
Ian Branch 127 Posted December 29, 2020 Now that's neat. I did have a look at that link but my knowledge depth wasn't up to it and I got confused. 😞 I can follow your example though and can see how it could be extended/used in other areas. Regards & Tks, Ian Share this post Link to post
Guest Posted December 29, 2020 (edited) RAD have a function to validate a String transformed in TDateTime: http://docwiki.embarcadero.com/Libraries/Sydney/en/System.DateUtils.IsValidDate or using my sample below, you can decode a string transformed in TDateTime and verify Year, Month and Day, if any value = 0, then the date is "invalid", the time can be 0 = then we have 0hrs another thing, verifying the Millisecond TOTAL in a DateTime, if = 0 or 0.0 (using Double function) then we have a DateTime invalid too! Edited December 29, 2020 by Guest Share this post Link to post
dummzeuch 1505 Posted December 29, 2020 (edited) Why are these dates stored as strings in the first place? If they are in a database they should be in a date field. If they were entered with a date picker, they should be TDate or TDateTime variables. If you want to store them as strings in a database for whatever reason, store them in a standard format: ISO 8601, and document that format. Don't rely on your users having Windows configured for any particular date format, you will find somebody who didn't, especially if your program is used in more than one country. And keep in mind, that date formats can be ambiguous: 01/12/15 can be 12 January 1915 or 12 January 2015 (American format), 1 December 2015 (British format), 15 December 2001 (some MSSQL format that looks like they got ISO 8601 wing). The only one I haven't seen so far is using the middle term for the year, but I'm sure some idiot will come up with that too. TryStringToDate by default uses the Windows settings, so that's out in this case. I wrote my own conversion and checking code for ISO 8601. It's in my dzlib if you are interested. Edited December 29, 2020 by dummzeuch 2 Share this post Link to post
Fr0sT.Brutal 900 Posted December 29, 2020 +1 to the post above. Storing dates as strings is bad idea in most cases. Just don't do it. If you really REALLY have to do it, then use carved in stone format that won't depend on current locale settings. And don't forget the timestamps could contain timezones 2 Share this post Link to post
Ian Branch 127 Posted December 29, 2020 Hi Thomas, Thank you for your input. They are stored as Dates. My original code at the start of this thread was simply where I ended up after considering some alternatives. I have now implemented the Helper per Kas' suggestion. Something new I have learnt today. 🙂 I will have a look in dzLib, I have it here. Ian Share this post Link to post
Guest Posted December 29, 2020 13 minutes ago, Ian Branch said: Something new I have learnt today. 🙂 Somehow i can feel your excitement and happiness with the use of helpers, and i miss that feeling very much ! Last time Embarcadero tried to introduce something similar in excitement, they reviewed FreeAndNil. Share this post Link to post
Ian Branch 127 Posted December 29, 2020 Hi Team, Just to close this one off. I revisited all the various Date data entry points, 7 different dates in all, and confirmed they can only be either Null or a valid date. Then using my new Helper knowledge I created the following Helper based on Kas' suggestion.. { TQueryHelper } // Parameters are FieldNames in this case function TQueryHelper.AreDBDatesNull(const DateString1, DateString2: string): Boolean; begin // Result := (not FieldByName(DateString1).IsNull) and (not FieldByName(DateString2).IsNull); // end; // // Implemented by.. if MyQuery.AreDBDatesNull('Date1', 'Date2') then ..... Simpler, and testing the data fields directly rather than 'converting' them. Thank you all for your input & contributions. Have a Great 2021. Regards, Ian 1 Share this post Link to post
Attila Kovacs 629 Posted December 29, 2020 9 hours ago, Fr0sT.Brutal said: Storing dates as strings is bad idea in most cases Time without time zones are like numbers without units. Like the numbers on the street. (30) I always interpret them as weight limits. Share this post Link to post
Guest Posted December 29, 2020 (edited) maybe can be used: implementation {$R *.dfm} uses System.DateUtils; var lMyYear : word = 0; lMyMonth : word = 0; lMyDay : word = 0; lMyHour : word = 0; lMyMinute : word = 0; lMySecond : word = 0; lMyMilliSecond: word = 0; procedure prcZeringValues; begin lMyYear := 0; lMyMonth := 0; lMyDay := 0; lMyHour := 0; lMyMinute := 0; lMySecond := 0; lMyMilliSecond := 0; end; function fncTestMyDateTimeInString(lDTinString: string): boolean; begin result := false; // try prcZeringValues; // // StrToDateTimeDef(lDTinString, now); // always will be valid! // // DecodeDateTime() for test each part of date in another part of your app, as below in my code! // else, use just "StrToDateTime(lDTinString)" // DecodeDateTime(StrToDateTime(lDTinString), lMyYear, lMyMonth, lMyDay, lMyHour, lMyMinute, lMySecond, lMyMinute); // result := true; // for example: result := (lMyYear > 0) ... etc - you can compare another values except // // comment here for have a "False" return... Else, you'll have a "exception" and nothing of return value! // // if you dont like this way! Sorry, find another! // // raise; // or rasie Exception.Create('Invalid convertion: string to TDateTime'); end; end; procedure TForm1.btn_Testing_String_as_DateTime_ValidClick(Sender: TObject); var lMyDateTimeInString: string; begin lMyDateTimeInString := ''; // if fncTestMyDateTimeInString(lMyDateTimeInString) then ShowMessage('"lMyDateTimeInString" is a valid DateTime value') else ShowMessage('"lMyDateTimeInString" is NOT a valid DateTime value'); end; procedure TForm1.Button1Click(Sender: TObject); var lMyDateTimeInString: string; begin lMyDateTimeInString := ''; // fncTestMyDateTimeInString(lMyDateTimeInString); // provoking verification... // if System.DateUtils.IsValidDateTime(lMyYear, lMyMonth, lMyDay, lMyHour, lMyMinute, lMySecond, lMyMinute) then ShowMessage('"lMyDateTimeInString" is a valid DateTime value') else ShowMessage('"lMyDateTimeInString" is NOT a valid DateTime value'); end; hug Edited December 30, 2020 by Guest Share this post Link to post
Fr0sT.Brutal 900 Posted December 30, 2020 (edited) 15 hours ago, Attila Kovacs said: Time without time zones are like numbers without units. Like the numbers on the street. (30) I always interpret them as weight limits. Generally it's true but timezone could be "global" per-app / per-DB (ideal case is UTC) 11 hours ago, emailx45 said: function fncTestMyDateTimeInString(lDTinString: string): boolean; I still can't get what you're checking. Global variables are bad practice anyway and in your case they appear useless. If StrToDateTime haven't risen any exception, you're already sure the date string is valid. Edited December 30, 2020 by Fr0sT.Brutal add 1 Share this post Link to post
Guest Posted December 30, 2020 5 hours ago, Fr0sT.Brutal said: Global variables are bad practice anyway and in your case they appear useless. IF YOU DONT LIKE, DONT USE! PERIOD! Share this post Link to post
aehimself 396 Posted December 30, 2020 (edited) 3 hours ago, emailx45 said: IF YOU DONT LIKE, DONT USE! PERIOD! But Frost is right about global variables. I would change this to... use them ONLY if you can not solve your issue without them. Edit: wtf spelling...?! Edited December 30, 2020 by aehimself Share this post Link to post
Guest Posted December 30, 2020 (edited) PERIOD! YOU RE RIGHT. PERIOD! Please use the car-brake only if strictly necessary. Otherwise, use your life insurance! Edited December 30, 2020 by Guest Share this post Link to post