The "Failed to set calendar min/max range"
error means that DateTime_SetRange()
failed. It was likely given an array of invalid SYSTEMTIME
values as just a side effect of the MaxDate
property failing to load its value from the DFM properly.
The "not a valid date and time"
error comes from StrToDateTime()
. According to your call stack,
>> EConvertError, '23:59:59' is not a valid date and time 0044d219 +019 app.exe
System.SysUtils 5387 +1 ConvertErrorFmt 00453b74 +02c app.exe
System.SysUtils 19596 +2 StrToDateTime 005dd4e1 +0f5 app.exe
Vcl.ComCtrls 27328 +6 TCommonCalendar.SetMaxDate 004e873d +06d app.exe
SetMaxDate()
is calling StrToDateTime()
. I have never seen TDateTimePicker
do that before, so I checked the VCL source and found that the call was added in XE5 (and still exists in the recently released XE6):
procedure TCommonCalendar.SetMaxDate(Value: TDate);
begin
if (FMinDate <> 0.0) and (Value < FMinDate) then
raise CalExceptionClass.CreateFmt(SDateTimeMin, [DateToStr(FMinDate)]);
if FMaxDate <> Value then
begin
Value := Trunc(Value);
Value := Value + StrToDateTime('23:59:59'); // <-- HERE
SetRange(FMinDate, Value);
FMaxDate := Value;
end;
end;
Prior to XE5, SetMaxDate()
looked like this instead:
procedure TCommonCalendar.SetMaxDate(Value: TDate);
begin
if (FMinDate <> 0.0) and (Value < FMinDate) then
raise CalExceptionClass.CreateFmt(SDateTimeMin, [DateToStr(FMinDate)]);
if FMaxDate <> Value then
begin
SetRange(FMinDate, Value);
FMaxDate := Value;
end;
end;
That was just plain stupid on Embarcadero's part, because StrToDateTime()
is subject to user locale settings. Obviously, the time format on the failing machines does not match the hard-coded value that Embarcadero is using. EncodeTime()
should have been used instead:
Value := Value + EncodeTime(23, 59, 59, 0);
In fact, I think Embarcadero should have used ReplaceTime()
as well:
ReplaceTime(Value, EncodeTime(23, 59, 59, 0));
I have filed a bug report in QC:
#124326 MaxDate: '23:59:59' is not a valid date and time
Until Embarcadero fixes it, you have two choices:
patch your VCL manually to fix the bug. If you do not compile your app with Runtime Packages enabled, you can make a copy of Vcl.ComCtrls.pas, edit it as needed, and then add the copy to your project. If you want a more permanent patch, you can compile the edited Vcl.ComCtrls.pas and place the new DCU file(s) in the IDE's lib folder(s).
leave the
MaxDate
set to 0.0 at design-time setSetMaxDate()
will not be called at DFM load time, then setMaxDate
manually in your code at startup, since as in the form'sOnCreate
event. You will have to temporary change the globalSysUtils.FormatSettings
variables (most likely justFormatSettings.TimeSeparator
) to match the format thatSetMaxDate()
is using, then assignMaxDate
to the desired value, and then change the variables back to their original values:var TS: Char; TS := FormatSettings.TimeSeparator; FormatSettings.TimeSeparator := ':'; try DateTimePicker1.MaxDate := ...; finally FormatSettings.TimeSeparator := TS; end;
.