March 08, 2005

cultural bias, leaping leap years batman!

pretty much everybody knows what a leap year is and when one occurs. and in case you don't, coldfusion has a function isLeapYear() that will tell you if a given year is a leap year in the gregorian calendar. in fact most calendars have the concept of a leap "something". the chinese and hebrew calendars have a "leap month" but apparently no concept of a leap year (though the icu4j HebrewCalendar class API are full of references to leap years). the civil version of the islamic calendar has a "leap day" which is added to the last month of 11 out of every 30 years but again no leap year. the persian calendar does have the concept of a leap year, handled via the PersianCalendarHelper class isLeapYear method.

which brings us to the point of this blog entry, this method expects the year argument to be a persian calendar "year" (right now its 1383 in the persian calendar). which i didn't quite grasp at first, as the other calendars (gregorian, buddhist and japanese) with leap years have an isLeapYear method that expects a gregorian year (yes, even the buddhist and japanese calendar classes expect a gregorian year, i imagine this is because these calendars extend the gregorian calendar class). and that's the way i expected the new persian calendar to behave (my own cultural bias--i use the buddhist and gregorian calendars on a daily basis). but it doesn't and why the heck would it? it is a persian calendar after all. so that got me to thinking about the other calendars and the way these "should" work and what other cultural biases have leaked into our code and test harnesses--especially the tests.

first thing i did was to rewrite the i18nIsLeapYear functions across all the calendars to expect a year argument in that calendar's system (it converts to gregorian year as needed and now automatically returns false for calendars lacking the concept of a "leap year").

then i went a hunting for any other places where my cultural bias might have leaked thru....and promptly found it in the getYear function. the getYear function takes a gregorian year value and returns the year in that calendar's system. i was doing that by creating a date:


(and just in case you were wondering, the 2 for the day value is to make sure the date value created fell into that year, given that we're using UTC as the time zone standard for all the calendars). and then setting the calendar object to that date and returning the value for that calendar object's YEAR field:

return tCalendar.get(tCalendar.YEAR);

this worked swell for the gregorian, buddhist and japanese calendars because these calendars' year started at the same time. but after looking at the year values of formatted dates from the other calendars i realized that the getYear function was returning horrible nonsense for the other 4 calendars. without realizing it, i'd let my calendar bias creep in and assumed the calendar's were all the same as far as years were concerned. gregorian 2-jan actually falls into different calendar years depending on the calendar (of course, they're different freaking calendars). and the tests were only reporting whether the getYear function "worked" by checking if the year was a positive integer, no eyeball comparisons against the year bits of the formatted date strings. there's a lesson here some where.

so better grab the new code and maybe give that calendars a good poking at to make sure no other cultural bias is left in it.


At 3/08/2005 6:13 PM, Blogger Reb Yudel said...

The Hebrew leap month of Adar II starts on Saturday, so happy leap year!

At 3/09/2005 5:10 AM, Blogger Ghasem said...

Your calendar test bed has a section (the table at the bottom) which shows some calendar calculations. I think these must be shown in the selected calendar. For example consider this for the Persian calendar:

08-Mar-05 --> year +2 --> 09-Mar-07

At first glance this seems to be incorrect (adding to the year field must not make unnecessary changes in the smaller field of day). But the calculation is in fact correct, only it must be shown in the Persian calendar:

18-Esf-83 --> year +2 --> 18-Esf-85

The part related to ColdFusion (CF dateAdd) gives 08-Mar-07 (possibly adding 2 years to the Gregorian calendar which may not necessarily coincide with another calendar calculations). I don't know anything about ColdFusion classes, though.

Also, I propose that you add the possibility of selecting different time zones.

At 3/09/2005 1:26 PM, Blogger PaulH said...

thanks for the comments.

believe it or not i actually put some thought into the tesbed ;-)

the date calculations are shown (on purpose) in gregorian calendar to illustrate to coldfusion developers the trouble they can get into if they do calendar math using coldfusion's built in functions (based on gregorian calendar) rather than the icu4j/persian calendar ones.

as far as timezone's go, we've standardized these components on UTC, that is it expects dates to be UTC. these components are mainly used in multiple timezone applications (server in one tz, users in many others), so this seemed to make some sense.


Post a Comment

<< Home