MS Outlook style date edit control/date picker






4.92/5 (13 votes)
Simulates the MS Outlook date edit/picker control.
Screen Shots
How I arrived at this solution
The project I am currently working on is data entry intensive and I have used the opportunity to perform extensive usability testing on the application we are developing. I have learned quite a bit from the experience, although I think the user sample I have to work with is somewhat limited.
In the process of user testing I uncovered numerous issues users were having using the application. One of the problems users were having was entering dates and times. At first, the application used a masked edit control. I tried various masks for various users and basically discovered that any given user is likely to enter a date any one of many possible ways. I could have just considered this a training problem but because this application will be shipped nation-wide (USA), this was not a good option. I tried the date time picker control only to find that this slowed down data entry significantly.
I began looking at other applications and how they allow dates/times to be entered. I focused in on MS Outlook since it has mass appeal (or at least mass distribution and usage.) I discovered that MS Outlook allows dates to be entered in just about any valid format. I also found that it allows for some interesting data entry situations as well. Try entering "2 weeks from now" in a date entry control in MS Outlook, also try using "Christmas" or "2 weeks before Christmas". To my surprise, outlook correctly calculated the date. It also dealt with some date calculation situations like "2 weeks from now + 3 days".
I looked around on the web for a control that worked this way to no avail. Since my date entry problem was important, I took a day to try and simulate the MS Outlook functionality. This is the result of that effort.
My Solution
My application is a MFC app, so I decided to derive a class off of
COleDateTime and re-implement the ParseDateTime function to extend its
abilities. I created a COleDateTimeEx
class. This class
implements many of the capabilities of the MS Outlook control (although not
all). Once I had this implemented, I wrote a CEdit derived class (CFPSDateTimeCtrl
)
to utilize the COleDateTimeEx
class. The CFPSDateTimeCtrl
control
allows the user to enter a date in an open-ended fashion and then when the
control looses focus, reformats the entered date into a specific format.
What it can do
The core text parser is contained entirely in the COleDateTimeEx
class.
This class can deal with all of the situations the normal COleDateTime class can
parse as well as the following situations.
DATE SITUATIONS MMDDYY or YYMMDD Example: 091601, 010123 MMDDYYYY Example: 09162001 "today" or "now" "yesterday" "day before yesterday" "tomorrow" "day after tomorrow" "next week" 7 days from today "last week" 7 days ago "Christmas" or "xmas" or "x-mass" or "xmass" or "x mass" December 25 of current year (if before 12-25) or 12-25 of next year otherwise. "Christmas eve" or "xmas eve" or "x-mas eve" or "x-mass eve" or "xmass eve" or "x mass eve" December 24 of current year (if before 12-24) or 12-24 of next year otherwise. * Other holidays handled: New Years, New Years Eve, 4th of July, Easter, Good Friday, Ash Wednesday, Veterans Day, Memorial Day, Labor Day, Columbus Day, Thanksgiving, and Presidents day. "last [Day of Week]" Example: "Last Sunday" or "Last Tuesday", etc. "next [Day of Week]" Example: "Next Sunday" or "Next Tuesday", etc. "[X] days ago" Note: Also works for weeks, months and years "[X] days from now" Note: Also works for weeks, months and years "[X] [Day of Week] in [Month]" Example: "2nd Sunday in December", "1st Tuesday in January", etc. "[X] days from [N]" or "[X days after [N]" Example: "2 days from Christmas", "2 days from 12/01/2002".
Note: Also works for weeks, months and years."[X] days before [N]" Example "2 days before Christmas", "2 days before 12/01/2001".
Note: Also works for weeks, months and years."IN [X] days" Example: "in 12 days", "in 30 days", etc.
Note: Also works for weeks, months and years."[X] [Day of week] Ago" Example: "2 Sundays ago", "4 Fridays ago" "[X] [Day of Week] From Now" Example: "2 Sundays from now", "4 Fridays from now" Simple date arithmetic situations. Example: "Christmas + 2 days", "New Years + 2 days", etc.
Example: "Haloween - 2 weeks", etc.TIME SITUATIONS "noon" 12:00 PM "midnight" 12:00 AM "XXX" or "XXXX" Example: "430", "515", "1625", "0710", etc. "X" or "XX" Example: "6", "7", "11", etc. "[X] O'clock" Example: 6 o'clock
Note: Also works with "oclock" and "o clock""[X] minutes from now" or "[X] hours from now" Example: "2 hours from now", "15 minutes from now", etc. "[X] minutes ago" or "[X] hours ago" Example: "2 hours ago", "15 minutes ago", etc. "[X] minutes until [XXX]" or "[X] minutes till [XXX]" or "[X] till [XXX]" or "[X] until [XXX]" Example: "15 minutes until noon", 20 minutes till 6", "10 minutes til 1300" "[X] minutes past [XXX]" or "[X] minutes after [XXX]" or "[X] after [XXX]" or "[X] past [XXX]" Example: "15 minutes after 6", "20 minutes past noon" "Half past [XXX]" Example: "Half past 6"
What it can't do
The MS Outlook control allows for some complex date calculations (like "2 days from now + 2 weeks + 1 month"). My implementation does not deal with this (although some limited arithmetic is supported.)
More importantly, my class is limited to English only. In order to support other languages it would be necessary to write a completely new parsing function. Because this is not needed for my application, I have not attempted to do this.
This class is not fully UNICODE compliant, although I don't believe it would be very difficult to make it UNICODE compliant. Again, because this is not needed for my application, I have not attempted to do this.
How to use
Using this code is very easy. If you just wish to use the parser and
not the edit control, you only need to use COleDateTimeEx
instead
of COleDateTime. The ParseDateTime function has been re-implemented to
deal with the new situations.
If you wish to use the edit control, the simplest way to do this is to
1. Setup the dialog/form resource as normal with an edit control where you want the date edit control to be.
2. Add the IDB_DATEPICKER_BUTTON
bitmap (from demo project) to your project.
3. Add a member to your dialog/view/control class of type CFPSDateTimeCtrl
.
MyDialog.h // // a member of type CFPSDateTimeCtrl to be attached to an edit control CFPSDateTimeCtrl m_wndDate; CFPSDateTimeCtrl m_wndTime;
4. In the OnInitDialog
(or OnInitialUpdate
or OnCreate
) function call the
AttachEdit
member function to subclass the existing edit control. You will
also probably want to call the SetParserOption
and SetDisplayFormat
functions.
The default parser options attempt both date and time parsing. The default
display format is %m%/d/%Y.
MyDialog.cpp // BOOL CMyDialog::OnInitDialog() { ... whatever else m_wndDate.SetShowPickerButton(TRUE); m_wndDate.AttachEdit(this, IDC_DATE); m_wndDate.SetParserOption(VAR_DATEVALUEONLY); m_wndDate.SetDisplayFormat("%m/%d/%Y"); m_wndTime.AttachEdit(this, IDC_TIME); m_wndTime.SetParserOption(VAR_TIMEVALUEONLY); m_wndTime.SetDisplayFormat("%I:%M %p"); }
That's it.
I have found it pretty easy to use and have not had any complaints from users yet. However, the application I use this code in is currently in "late-alpha" testing. So there may well be some problems found as time goes on. If problems are uncovered, I will correct the code and post the changes to CodeProject as quickly as I can.
The code
The code is not heavily commented, but the comments I believe are adequate to
explain the code. Basically, there are 2 key functions in the COleDateTimeEx
class which you will probably want to look at. These are the "EvaluateInputTextDate"
and "EvaluateInputTextTime" functions. These functions contain
the actual parsing algorithms for date and time situations. WARNING: These
functions are basically a hard-coded set of the situations listed above.
As I mentioned earlier and in the code I did not have time to develop a
rules-based engine for parsing date text.
Notes
This code is not perfect and not suited for all situations but at least for my project it has been very well received (especially by the end users.) I would appreciate any comments you have to make regarding my implementation or code. Thanks.
CHANGES INTRODUCED ON 01-26-2002
Added support for the date picker drop down. See screen shots above for how it looks. The control used for the calendar can be used separately from this control. I have posted an article on CP with just this control and a demo project. See it for more details.
The date picker control works in much the same way that the MS Outlook date picker control works.
I am aware of one problem with the date picker option. When the user drops down the calendar control, it is possible for the user to click on certain UI elements (frame controls) and the calendar control will not automatically be hidden. This has not been a problem in my app so I have not attempted to correct this yet. If you have a solution to this issue, please let me know.
CHANGES INTRODUCED ON 01-16-2002
Added methods for ease-of-use in code.
IsValid() | |
IsSunday() | |
IsMonday() | |
Is[XXX] where XXX is the name of day of week | |
IsWeekDay() | |
IsWeekendDay() | |
IsJanuary() | |
IsFebruary() | |
Is[XXX] where XXX is name of month | |
IsLeapYear() | |
Previously the special time parser always interpreted numeric input as hours based from 7AM and on. For example, if you entered 8, the parser considered this to be 8 AM, but if you entered 6, it recognized this as 6PM.
Daebi (see threads below) suggested and provided code for allowing options to control this functionality. I incorporated the code (with some changes to make code more uniform) to allow this to work. You can now use the SetHourOption and SetHourThreshold functions to control how the time parser handles hour numbers. See the code for additional details.
Some users have reported having problems entering times with AM/PM indicators. This was caused because instead of "AM", they entered "A.M." or some other variant. The standard parser did not understand these. I have modified the code to check for AM, PM variants and deal with them appropriately.
CHANGES INTRODUCED ON 01-09-2002
White space handling has been improved.
Now supports generic use of the "next", "this next", "last" and "this last" prefixes. This allows for data entry in the form of "next easter", "next christmas", "last christmas", "last 4th of july".
Support has been added for year suffix situations like "easter 2005", "halloween 2005", etc.
Methods added to CFPSDateTimeCtrl class.
SetDate
GetDate
Finally, support has been added for shortcuts. There are numerous built-in shortcuts, but these can be programmatically removed or modified. The shortcuts currently supported are listed below.
T | Today |
N | Now |
Y | Yesterday |
TM | Tomorrow |
NW | Next week |
NM | Next month |
LW | Last week |
LM | Last month |
DY | Day before yesterday |
DT | Day after tomorrow |
X | Christmas |
XE | Christmas eve |
E | Easter |
GF | Good friday |
AW | Ash wednesday |
NY | New years |
NYE | New years eve |
J4 | July 4th |
MD | Memorial day |
LD | Labor day |
VD | Veterans day |
CD | Columbus day |
TH | Thanksgiving |
PD | Presidents day |
Updates
01-07-2002A | Corrected demo zip file to include files from \res directory |
01-07-2002B | Thanks to Richard Jones for pointing out flaw involving dependence on ParseDateTime and regional settings. Modified code to use SetDate instead of ParseDateTime. Appears to correct problem. Will try with numerous regional settings. |
01-09-2002 | Thanks to Rick Crone for identifying need for
shortcuts. (See article for more info). Also, Rick identified the need
for better white space management and enhancements to date arithemtic
algorithms. Thanks to Pål K Tønder for identifying the need for generic "next" and "last" prefix keywords. Added support for shortcuts. Added support for various "next"/"last" situations involving holidays and pre-coded dates. Added support for situations like "easter 2005"> Added Get/SetDate members to CFPSDateTimeCtrl |
01-09-2002 | Thanks to Rick Crone for identifying need for
shortcuts. (See article for more info). Also, Rick identified the need
for better white space management and enhancements to date arithemtic
algorithms. Thanks to Pål K Tønder for identifying the need for generic "next" and "last" prefix keywords. Added support for shortcuts. Added support for various "next"/"last" situations involving holidays and pre-coded dates. Added support for situations like "easter 2005"> Added Get/SetDate members to CFPSDateTimeCtrl |
01-11-2002 | Thanks to daebi for pointing out a problem
with time parsing logic. I have corrected the time parsing logic as noted. |
01-16-2002 | Thanks to daebi for point out issues with
tome cutoffs used in parser. daebi provided code to allow for
various control options for time parsing (see notes in code) Time parsing further enhanced to deal with users entering "A.M." (and variants) instead of "AM". Added copy constructors. Added IsValid method. Added numerous other methods for ease-of-use. (Ie. IsSunday(), IsJanuary(), etc.) |
01-26-2002 | I added support for the date picker
"mode". This feature displays a button to the right of
the control similar to Outlook. When the user clicks the button a
mini-month calendar control is displayed below the control (like
standard date picker.) This mini calendar control simulates the MS
Outlook's date picker control.
The mini-calendar control can be used independent of the date time control. I have posted the code and a demo project for the mini calendar control separately from this article. See that article for details on using the mini calendar control. |