1

I'm really confused. What I want to do (seems pretty simple) is to display a date + time in a TDateTimePicker, with the calendar integrated, and be able to read it after its value has changed.

I'd like to use the dd/MM/yyyy hh:mm format.

Since the TDateTimePicker isn't a TDateOrTimePicker, a used one with :

  • Kind : dtkDate (displays the calendar)
  • Format : 'dd/MM/yyyy hh:mm'

Looks good, now let's read it :

showmessage(DateTimeToStr(DateTimePicker1.Date)); 

Only displays the date correctly. Changes on the hh:mm part is not taken into account. I always get the default value. Then I thought : There is also a Time property, maybe I need to read the value in 2 times.

 showmessage(DateTimeToStr(DateTimePicker1.Time));
 showmessage(DateTimeToStr(DateTimePicker1.Date));

But I have the exact same output (and problem) as before. So now, let's try to change this Kind property to dtkTime, then read the time, then switch it back to dtkDate then read the date.

But no, it still doesn't work. Switching the Kind property from dtkDate to dtkTime erases my input on the hh:mm part.


I know I'm actually supposed to work with 2 TDateTimePickers : one for the date, one for the time.

So my question is :

  • Is there an easy way to solve my simple and very common problem (a real DateTimePicker) ?

And a Delphi design questions :

  • Why is there a Format property that is basically useless and misleading ? Having a TimeFormat, DateFormat, and proper format validation process isn't that hard.

Please note that I post this question mainly to avoid beginners doing the same loooong process of debugging and frustration.

Etsitpab Nioliv
  • 354
  • 1
  • 4
  • 15
  • 1
    Well, reading the docs could have spared you some time: *TDateTimePicker displays a list box for entering dates or times.* Note the **or**. Further: *Because TDateTimePicker is a wrapper for a Windows control, these formats can't be changed by changing the formatting variables in the SysUtils unit. However, you can use the Windows API call DateTime_SetFormat to programmatically specify these settings.* – Tom Brunberg Mar 23 '17 at 09:40
  • @TomBrunberg Yes I should have seen that before "trying to make it work". The fact that I can't access the documentation from the IDE didn't help. – Etsitpab Nioliv Mar 23 '17 at 09:51
  • Well again, sorry for you. You must have messed up your installation somehow. I specifically use Delphi XE7 right now and copied the text from a very functioning help documentation, accessible straight from the IDE. – Tom Brunberg Mar 23 '17 at 09:54
  • Either way, the docs are available online, just search "Delphi docwiki TDateTimePicker" on your favourite browser. – LU RD Mar 23 '17 at 10:03
  • The format is not correct, it should be 'hh:nn'. look [here](http://www.delphibasics.co.uk/RTL.asp?Name=formatdatetime) – Christine Ross Mar 23 '17 at 10:07
  • Trying to do this without consulting the documentation is pointless. Don't do that. How can you hope to succeed like that? How will you ever learn anything without documentation? http://stackoverflow.com/questions/21403628/how-can-i-search-for-delphi-documentation – David Heffernan Mar 23 '17 at 10:09
  • Yes I should have checked the documentation. I'm simply pointing out that this component behaviour doesn't seem natural to me. A well designed component / class shouldn't require you to read the documentation (for such a basic use case).Should I delete my question ? Can someone post RTFM as an answer ? – Etsitpab Nioliv Mar 23 '17 at 10:13
  • Why are you reading the `Date` property and the `Time` property instead of the `DateTime` property? – Disillusioned Mar 23 '17 at 10:20
  • @CraigYoung They are the same, see System.pas TDate = type TDateTime; TTime = type TDateTime; and TDateTime = type Double;. It all comes down to how you read it (DateTimeToStr, TimeToStr, DateToStr) – Etsitpab Nioliv Mar 23 '17 at 10:33
  • 1
    @EtsitpabNioliv I don't have Delphi on hand to check the implementation. But the documentation indicates: (1) `DateTime` returns the underlying field as is. (2) `Date` uses a `Get...` method; an opportunity to strip out the time from the underlying field. (3) `Time` similarly would be able to strip out the date portion of the underlying field value. – Disillusioned Mar 23 '17 at 10:40
  • @ChristineRoss Please don't refer to that site. Refer instead to the [correct page](http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.ComCtrls.TDateTimePicker.Format) of the documentation. Note that this is specifically for the `TDateTimePicker`. Also note that in other places where 'nn' stands for minutes, 'mm' **can be used** immediately after a preceding 'hh:'. – Tom Brunberg Mar 23 '17 at 10:57
  • 2
    *A well designed component / class shouldn't require you to read the documentation* Lord help us!! – David Heffernan Mar 23 '17 at 11:07
  • 1
    @CraigYoung: The `DateTime` property simply returns the underlying `TDateTime` member as-is. The `Date` property returns the `TDateTime` type-casted to `TDate` (which doesn't really strip anything) and the `Time` property returns the `TDateTime` type-casted to `TTime` (which also doesn't really strip anything). They *should* have used the `DateOf()` and `TimeOf()` functions (or equivalent math) instead. This has caused bugs in my apps in the past, to which I've had to use `DateOf()`/`TimeOf()` manually to address. – Remy Lebeau Mar 23 '17 at 16:26

4 Answers4

11

What I want to do (seems pretty simple) is to display a date + time in a TDateTimePicker, with the calendar integrated, and be able to read it after its value has changed.

I'd like to use the dd/MM/yyyy hh:mm format

Sorry, but you cannot. A TDateTimePicker is designed to only work with EITHER a date OR a time, but not BOTH.

If you set the Kind property to dtkDate, you can specify a custom DATE format but any TIME format is ignored, and using the Time property is undefined.

If you set the Kind property to dtkTime, you can specify a custom TIME format but any DATE format is ignored, and using the Date property is undefined.

When reading the Date property, only the DATE portion of the value is valid. In a perfect world, the TIME portion will always be zero, but I have seen it contain garbage sometimes. However, you can easily ignore that using the DateUtils.DateOf() function:

Value := DateOf(DateTimePicker1.Date);

Same thing with the Time property and the DATE portion of the value. You can use the DateUtils.TimeOf() function to ignore any potential garbage:

Value := TimeOf(DateTimePicker1.Time);

Is there an easy way to solve my simple and very common problem (a real DateTimePicker) ?

The only real solution with TDateTimePicker is to use two separate TDateTimePicker controls, one for a date the other for a time. You can combine the two values together when needed:

Value := DateOf(DateTimePicker1.Date) + TimeOf(DateTimePicker2.Time)

If you want to display a TDateTime, just do the opposite:

DateTimePicker1.Date := DateOf(Value);
DateTimePicker2.Time := TimeOf(Value);

Otherwise, find a 3rd party picker (or write your own) that supports what you need.

Why is there a Format property that is basically useless and misleading ?

It is not useless and misleading. It is perfectly useful, but only within the confines of the current Kind setting.

Having a TimeFormat, DateFormat, and proper format validation process isn't that hard.

TDateTimePicker is just a thin wrapper for a Win32 Date and Time Picker control, which has no such functionality. Certainly they could have written custom logic to allow switching between the Kind values with different setups preserved, but that is not really how a DTP control is meant to be used. You set the Kind once, the Format once, and then handle input and output as needed. No switching back and forth. Just because you can switch the Kind dynamically doesn't mean you should. If you think writing such custom logic is easy, you are welcome to do it in your own code.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

I have exactly the same issue. Andre solution works like a charm thanks to Andre).

ISSUE : We need to create a new unit, create a new component and register the component to another package to be able to setup the form at desgin time.

SOLUTION : To avoid this, just declare in the form where you are using TDateTimePicker component the following class : THackDateTimePicker

...
type
  // this class is just to access protected Caption property
  THackDateTimePicker = class(TDateTimePicker);

  TSymbolInfoForm = class(TForm)
  ...
end;

Then just implement OnChange event like :

procedure TSymbolInfoForm.dtpChange(Sender: TObject);
begin
   TDatetimePicker(Sender).Datetime := StrToDateTime(THackDateTimePicker(Sender).Caption);
end;

you can link all OnChange event of all TDateTimePicker components on the same form to the sane OnChange event... and voila!

och
  • 33
  • 3
1

I had the same frustrating problem these days. But in my case I just needed to get the complete Date and Time from the TDateTimePicker field (didn't needed to set it programatically, but it would not be very difficult). So what I did is to create my own TDateTimePicker and create a new public property that gets the field Caption and sets into DateTime (Caption is protected accordind to the documentation). Here's my code:

unit uZDateTimePicker;

interface

uses
 Vcl.ComCtrls, SysUtils;

type
  TZDateTimePicker = class(TDateTimePicker)
  private
    procedure SetDateAndTime;

  protected
    procedure Change; override;

  end;

implementation

{ TZDateTimePicker }

procedure TZDateTimePicker.Change;
begin
  SetDateAndTime
end;

procedure TZDateTimePicker.SetDateAndTime;
begin
  DateTime := StrToDateTime(Caption);
  inherited;
end;


end.

As simple as that.

Also, if you use the DevExpress components you would use the TcxDateEdit that works just like you want.

Andre Batista
  • 336
  • 3
  • 11
-1

The above answers are all in correct , you can get datetime from the datetimepicker.DateTime Property , you can show date and time by setting the "Format" property too dd/MM/yyyy hh:mm