Recent

Author Topic: Date validation  (Read 3012 times)

ranlaborde

  • New Member
  • *
  • Posts: 11
Date validation
« on: July 21, 2017, 01:51:20 pm »
I have read the PDF regarding dates and have reviewed the functions and procedures in DateUtil but I still have a problem:

I expect users to use my program in several countries with different date orders.  The IsValidDate function requires knowing the Month day and year to pass to the function. As the order of these change according to the system settings of each computer I need an example of date validation that works across different international settings.

Could someone point me to an example?

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Date validation
« Reply #1 on: July 21, 2017, 02:14:49 pm »
I use a procedure to isolate the date items stored in a string. I then validate the items based on the system's locale. Here is an idea of how you can do this:
Code: Pascal  [Select][+][-]
  1.   // get date items
  2.   s := Collect(source, tiNumberDigits);
  3.   count := High(s) + 1;
  4.   // define items
  5.   if count = 2 then
  6.     // only day and month
  7.     begin
  8.       // get current year
  9.       d.Year := GetYear(SystemDate());
  10.       if Notation.DateFormat = Notation_DMY then
  11.         begin
  12.           d.Day := IntVal(s[0]);
  13.           d.Month := IntVal(s[1]);
  14.         end
  15.       else
  16.         begin
  17.           d.Month := IntVal(s[0]);
  18.           d.Day := IntVal(s[1]);
  19.         end;
  20.     end
  21.   else
  22.     begin
  23.       case Notation.DateFormat of
  24.         Notation_DMY:
  25.           begin
  26.             d.Day := IntVal(s[0]);
  27.             d.Month := IntVal(s[1]);
  28.             d.Year := IntVal(s[2]);
  29.           end;
  30.         Notation_MDY:
  31.           begin
  32.             d.Day := IntVal(s[1]);
  33.             d.Month := IntVal(s[0]);
  34.             d.Year := IntVal(s[2]);
  35.           end;
  36.         Notation_YMD:
  37.           begin
  38.             d.Day := IntVal(s[2]);
  39.             d.Month := IntVal(s[1]);
  40.             d.Year := IntVal(s[0]);
  41.           end;
  42.       end;
  43.     end;
The procedure Collect works as follows (you can write this yourself): go through each character of the string containing the date; as long as it is a number buffer it into an array element. If an alphanumeric characters is found, skip that character and increment the array index if the previous character was numeric. That will give you an array of three elements, provided the date is valid. This will give you maximum flexibility in treating the date elements. My example allows omitting the year, which will then be assumed to be the system's current year.
« Last Edit: July 21, 2017, 02:24:41 pm by Artie »
keep it simple

wp

  • Hero Member
  • *****
  • Posts: 11917
Re: Date validation
« Reply #2 on: July 21, 2017, 02:33:49 pm »
I suppose you are talking about conversion of a date string to a TDate. The record FormatSettings contains a list of country-specific settings, among them the element ShortDateFormat which, in my country, is the string "dd.mm.yy". This means: two-digit-day, then two-digit-month, then two-digit year, separated by dots. The FormatSettings are initialized when the program starts according to the defaults of the operating system (in Linux/Unix the unit "clocale" must be added to "uses").

The function StrToDate converts a string to a date value based on the ShortDateFormat string. I.e, the date string of today, '21.07.17' will be converted correctly in my country. You could also call TryStrToDate which returns a boolean false if the string cannot be converted successfully, instead of raising an exception.

Both functions are overloaded with versions accepting a local copy of the format settings to try other formats. The following code, for example, would check the date separators '.'. '/', and '-':

Code: Pascal  [Select][+][-]
  1. function MyStrToDate(s: String): TDate;
  2. var
  3.   fs: TFormatSettings;
  4. begin
  5.   // At first check default format
  6.   if TryStrToDate(s, Result) then
  7.     exit;
  8.   fs := FormatSettings;
  9.   // StrToDate replaces the slash ('/') by the DateSeparator of the FormatSettings.
  10.   if pos('/', FormatSettings.ShortDateFormat) > 0 then begin
  11.     if FormatSettings.DateSeparator <> '-' then begin
  12.       fs.DateSeparator := '-';
  13.       if TryStrToDate(s, result) then
  14.         exit;
  15.     end;
  16.     if FormatSettings.DateSeparator <> '/' then begin
  17.       fs.DateSeparator := '/';
  18.       if TryStrToDate(s, Result) then
  19.         exit;
  20.     end;
  21.     if FormatSettings.DateSeparator <> '.' then begin
  22.       fs.Dateseparator := '.';
  23.       if TryStrToDate(s, Result) then
  24.         exit;
  25.     end;
  26.   end;
  27.   // Now check cases where DateSeparator is hard-coded in ShortDateFormat
  28.   if FormatSettings.DateSeparator <> '-' then begin
  29.     fs.ShortDateFormat := StringReplace(FormatSettings.ShortDateFormat, FormatSettings.DateSeparator, '-', [rfReplaceAll]);
  30.     if TryStrToDate(s, Result) then
  31.       exit;
  32.   end;
  33.   if FormatSettings.DateSeparator <> '/' then begin
  34.     fs.ShortDateFormat := StringReplace(FormatSettings.ShortDateFormat, FormatSettings.DateSeparator, '/',  [rfReplaceAll]);
  35.     if TryStrToDate(s, Result) then
  36.       exit;
  37.   end;
  38.   if FormatSettings.DateSeparator <> '.' then begin
  39.     fs.ShortDateFormat := StringReplace(FormatSettings.ShortDateFormat, FormatSettings.DateSeparator, '.',  [rfReplaceAll]);
  40.     if TryStrToDate(s, Result) then
  41.       exit;
  42.   end;
  43.  
  44.   // if we get here the string parts most probably do not lead to a valid date --> raise an exception
  45.   raise EConvertError.Create('Invalid date string ' + s)
  46. end;
 
The function (Try)StrToDate, and thus the function above, is flexible enough to accept also a 4-digit year, or a 1-digit day or month, e.g. '21.7.2017'.

Therefore, I think that above function will cover most of the cases.

ranlaborde

  • New Member
  • *
  • Posts: 11
Re: Date validation
« Reply #3 on: July 21, 2017, 03:00:41 pm »
Thanks so much for the quick responses.  Artie, your solution is very much what I came up with myself.  What unit is Notation located in?  Looks quite useful.

wp:  I looked high and low for TryStrToDate.  This is very useful.  I appreciate the example of using FormatSettings as well as this addresses the problem of some users using Windows and others using Macs (not developing for Linux at this point).

Again, thanks for the responses!!

munair

  • Hero Member
  • *****
  • Posts: 798
  • compiler developer @SharpBASIC
    • SharpBASIC
Re: Date validation
« Reply #4 on: July 21, 2017, 03:13:26 pm »
It is not a unit that comes with Lazarus. I setup some notation definitions like:
Code: Pascal  [Select][+][-]
  1. const
  2.   Notation_MDY = 0;
  3.   Notation_DMY = 1;
  4.   Notation_YMD = 2;
and use that throughout my projects, including date and time formatting etc.
keep it simple

 

TinyPortal © 2005-2018