Considerations for a medical date entry widget ---------------------------------------------- In medicine we deal with exact (Anasthesia for the thyroidectomy commenced November 21st 1978 10:35) and fuzzy ("Doctor, I think I had that goiter removed in late 1977. Oh, wait, that was after aunt Millie had her 80th birthday, ... must have been '78 ... but I know it was in fall") points in time. For rapid data entry we must be able to efficiently input both. Structured input fields ----------------------- Some people prefer structured input fields such as: __.__.____ for DayDay.MonthMonth.YearYearYearYear The structure, of course, should only guide the eye and not necessitate any extra keystrokes if at all possible. So, if one typed two digits (it should not accept anything other than digits in the first place) the cursor should auto-skip the dot. For the year field 2 (with a y2k window technique) or 3 digits should suffice yet 4 should be possible. Sensible defaults should be filled per sub-field in but marked for quick deletion upon typing any digit. The actual layout is subject to localization. ____/__/__ is just as valid. The S.I. date format, for example, is "YYYY MM DD". Unstructured input fields ------------------------- This widget does not impose a particular layout for entering dates. There should be a "super smart" input interpretation for this field, eg. 231074 means October 23rd 1974 for me. This applies if the entire input is composed of consecutive digits only. "Super smart" because it needs to know about local conventions (DDMMYY in Germany vs YYMMDD in, say, China), ie. the order of components. Tons of restrictions apply: 1,2,3,4,6, 7 or 8 digits required, order of month, day and year must be predefined, if 6 digits a window for y2k decision must be agreed upon. Less than 6 digits invite ambiguity, eg. 11291 can mean 1.12.1991 or 11.2.1991 Subject to known component order (eg. day month year): 1 digit - given day of current month in current year - "7" -> "August 7th 2001" 2 digits - given day of current month in current year - "29" -> "August 29th 2001" 3 digits - given day and month in current year - if ambigous the day is assumed to occupy 2 digits - thus: "112" -> "Feb 11th 2001" and NOT "Dec 1st 2001" - unless that is logically impossible - like: "412" -> "Dec 4th 2001" and NOT " Feb 41st 2001" - or: "402 -> "Feb 4th 2001" (same as "042" and "0402") - but: "212" -> "Feb 21st" (unlike "0212" -> "Dec 2nd") If we have to input a single digit day we must pad it with zero ("01" for 1st) to avoid ambiguity. This may lead to 4 digits where actually only 3 carry content (0212). We choose to require a padded day and not a padded month because the probability of having to pad a day (3 out of 10 times) is less than half of that for a month (7.5 out of 10 times). 4 digits: - given day and month in current year - "0711" -> "November 7th 2001" - "2310" -> "October 23rd" - "0202" -> "Feb 2nd" Dates as offsets ---------------- Parameter: root date relative to which offsets are calculated. At least two variants: Always relative to the root date predefined at widget invocation OR relative to the currently displayed date (that may have been manipulated already). +3d -> 3 days from "root date" +1w -> 1 week from "root date" -1m -> 1 month before "root date" -5y -> 5 years before "root date" (mostly 5 years ago) Optionally skip days on the weekend ? Or just warn (color) when we hit Saturday, Sunday or other days that we routinely don't attend the office (seeing patients at nursing homes etc.) ? Manipulation of displayed dates ------------------------------- The keys +/- and/or up/down arrow count the displayed date up and down. It needs to be decided whether this should apply to the whole date (i.e. manipulate day only with month/year wrapping) or to the field (see structured input fields above) we are in (i.e. manipulate day, month and year separately). National calendars ------------------ Yes, there are countries today that actively use calendars different (in offset, mostly) from the Gregorian one. We should keep that in mind and have a very clear policy on stored dates. If we store local dates we should tag them with a unique "calender type" flag. On the other hand we could "normalize" all dates to one agreed upon calendar and handle local variants in UI code. We won't deal with dates before 1582 Common Era or 45 Before Common Era to often so we shouldn't have to worry about skips, different leap year algorithms et al. Leap years ---------- According to the Gregorian Calender definition the following years are leap years: - any year dividable by 4 with a remainder of 0 - except multiples of 100 (centuries, that is) - but including multiples of 400 The last condition is often forgotten, although it applies to the 2000 ! Also, every so often a leap second is inserted at convenient intervals but I don't see how that needs to concern us. Context Menu ------------ Right-click opens a context menu: - invoke calendar widget - a few predefined times (now, today, tomorrow, 1 week from now ...) - set national calendar - set component order (day month year) - show help Keywords -------- - subject to localization today tomorrow yesterday now (w)eek (m)onth (y)ear names of months names of days of week ago from (eg "1 year from today") mid(-) (mid-1991) late early spring summer fall winter rainy season (??) Delimiters ---------- Accept a wide range: space dash slash dot hyphen (ambiguous !! - "-1w" == 1 week ago ? Subject to component order except: 3/4 digit components are always years spelled out months obviously aren't days :-) Fuzzy dates ----------- > Maybe this can be solved by allowing one impossible date as a > special case ? And allowing input like "957f" meaning > "1957 _f_uzzy" or "9 957f" for "September 1957". Internally > this could be handled as "0.0.1957" and "0.9.1957". However, > this needs proper processing of the exception under all > circumstances. >From an interface perspective, I think this is a very elegant solution. When last I turned my brain to this problem I had also contemplated using the "0.0.1957" and "0.9.1957" (a _very_ auspicious year, incidentally ;-) There is one challenge to be overcome, DB storage. Dates are stored in most DBs as a decimal value representing the number of days before or after a reference date, with the decimal fraction representing the fraction of a day. The advantage of this method of storage is simplified date comparisons and arithmetic - it makes it easy to write queries that before, after or between specified dates etc. We could invent our own special representation (eg the above stored as text) but we would then lose a lot of flexibility when querying the database. A couple of alternatives could be: 1. Store a "fuzzy" date as the nearest proper date value (eg 1.1.1957 or 1.9.1957 for Karsten's examples), but with a zero time value. 2. For each "fuzzy" date, have a companion field that stores a representation of the "certainty" of the date value. OR, as Horst suggests, implement a user data type "fuzzy_date" for Postgres. Here keywords are especially important (late, early, mid-, seasons). Code ---- Look at GNU date. Comments welcome ! Some of these ideas are most likely overkill. Rob, maybe you can still find the design document for your Delphi widget ?