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
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. |