We have a complicated calendar. Blame the catholics who created this mess to keep Easter a Spring holiday. 7 of the months have 31 days, 4 have 30 days, and 1 of the months has 28 days in most years but on years divisible by 4 (unless divisible by 100 (unless divisible by 400)), this month has 29 days. That's difficult to keep track of. Considering most of us still don't know our right from our left w/o thinking visually which hand can make an "L" w/ the index and thumb fingers, it's no surprise that users invariably choose the last day of a month incorrectly.
I'm taking a stand w/ you now to tell you that this is an area of improvement for our kind. Too many times I've caught myself selecting April 31 or or November 31 in a set of date drop-dpwns and received an error message about my date parameters being invalid! Really? Really? Would it be that difficult for you ease the burden and simply roll back my selected day to the final day of the month?
Having expressed my regrest towards those programmers that do not gracefully choose a more appropriate date when the one selected is invalid, it's not always valid to perform this operation. For example, the selection of a Birth Date is not a valid time to change the selected value. If someone selects June 31 for their birthdate though, you should probably ask them to leave your website (or application) for good. Politely ask them to either destroy their license key or auction off their user credentials on eBay.
In most cases it is absolutely appropriate to make the choice to correct the day by rolling back up to 3 days to find the next valid date, closest to their date. Invariably, you'll always want to go backwards as well b/c no one selects the June 31 when they mean July 1. Taking these assumptions into account, let's envision the typical scenario:
You have a report screen that enables a user to select a range of dates (Start and End) that will reload the reported data for the selected parameters (Date and possibly other filters). Many of your users will have real business needs to select the first and last days of the month to produce a report for the previous month or some specific month in the past. A good real world example is the amount of posts on TypePad.com in April. Some bigwig comes into the TypePad office on May 1, sits at his desk, and wants to pull the April report for comparison against March 2008 and April 2007 to look for improvement trends. While in an enterprise-class application, you would imagine these reports get auto-emailed to the interested parties at midnight on the first of every month, let's pretend that's not set up yet.
In our example, we have an executive-level user that works half as much and makes twice as much as you. You choose. Are you going to scream back at your user in large red blinking text that they've selected an invalid date or are you going to gracefully hide their mistake and accept their erroneous input? Make your choice.
The code below takes care of invalid dates in the manner I've described:
FUNCTION fix_date(byval old_date)
'--DIM
dim a'--DEFAULT return value to old date
fix_date = old_date'--NOT a valid date?
IF (NOT isdate(fix_date)) THEN'--SPLIT old date on / to create array of values
old_date = split(old_date, "/")'--HAS the right number of ordinals? - indicates value is a somewhat acceptable date format?
IF (ubound(old_date) = 2) THEN'--GOOD day value?
IF (isnumeric(old_date(1))) THEN'--CONTINUE until date is valid for at most 3 loops
FOR a = 1 TO 3 STEP 1'--CREATE a new possible date value by combining the lesser day w/ the existing year and month
fix_date = (old_date(0) & "/" & (old_date(1) - a) & "/" & old_date(2))'--EXIT if and when date becomes valid
IF (isdate(fix_date)) THEN EXIT FOR'--NEXT revision
NEXT'--END check for day value
END IF'--END has somewhat acceptable format
END IF'--END check for valid date
END IFEND FUNCTION
To illustrate what I've described, the following simple write statements:
response.write (fix_date("1/31/2008") & vbcrlf)
response.write (fix_date("2/31/2008") & vbcrlf)
response.write (fix_date("3/31/2008") & vbcrlf)
response.write (fix_date("4/31/2008") & vbcrlf)
response.write (fix_date("5/31/2008") & vbcrlf)
response.write (fix_date("6/31/2008") & vbcrlf)
response.write (fix_date("7/31/2008") & vbcrlf)
response.write (fix_date("8/31/2008") & vbcrlf)
response.write (fix_date("9/31/2008") & vbcrlf)
response.write (fix_date("10/31/2008") & vbcrlf)
response.write (fix_date("11/31/2008") & vbcrlf)
response.write (fix_date("12/31/2008") & vbcrlf)
Will produce the following output:
1/31/2008
2/29/2008
3/31/2008
4/30/2008
5/31/2008
6/30/2008
7/31/2008
8/31/2008
9/30/2008
10/31/2008
11/30/2008
12/31/2008
The code is written assuming that the date is being created by the aggregation of 3 values from 3 select drop-downs on a screen, i.e. it assumes a few things including the largest day value that can be selected is 31. The implementation of this routine in actual run-time code would look like:
'--RETRIEVE, CONCAT, and FIX user-selected date
end_date = fix_date(request.form("end_day") & "/" & request.form("end_day") & "/" & request.form("end_year"))
I have perhaps 3 different types of readers and you probably fit like a glove into one of the following labels:
1.) [COMMON SENSE PROGRAMMER] > I can't believe you're even writing this since I assumed that all programmers knew this and practiced it from day one.
[ME] > Uh, you're obviously living in a perfect world or have never worked on anyone else's software. Revel in the joy that you have enough sense to take care of your users and not remind them of their errors when they are so easily corrected but don't turn a blind eye to what is happening. I continue to see this.
2.) [STUPID IDEA PROGRAMMER] > I've got enough things to worry about, the least of which is trying to auto-correct user input. Give me some real advice and stop worrying about how I treat my users - it's really none of your business.
[ME] > You make is my business when you call yourself a programmer, in the same ranks as myself and thousands like myself that strive to create a positive and appreciative perception in our users. I do appreciate you however, you are the reason I make as much money as I do.
3.) [GREAT THINKING PROGRAMMER] > What marvelous ideas you have, Nick. This is a great standard function I will add to my arsenal. I've been thinking about writing the same for so long - I'm glad you took some initiative.
[ME] > You are sincerely welcome. It makes me feel special to know that at least a few of my "backwards" ideas are appreciated.
If you have some additions or enhancements to the code, please let other readers know. I invite you to comment on anything else in this post, including my visible frustration at a large faction of our profession.

thank you
Posted by: sinan | Friday, April 18, 2008 at 04:42 AM