Problem Solution Discussion Performing Validity Checking on Date or Time Subparts

sub yy_to_ccyy { my year, transition_point = _; transition_point = 70 unless defined transition_point; year += year = transition_point ? 1900 : 2000 if year 100; return year; } The funct ion uses MySQLs t ransit ion point 70 by default . An opt ional second argum ent m ay be given t o provide a different t ransit ion point . yy_to_ccyy also m akes sure t he year act ually needs convert ing is less t han 100 before m odifying it . That w ay you can pass year values t hat do or dont include t he cent ury wit hout checking first . Som e sam ple invocat ions using t he default t ransit ion point have t he following result s: val = yy_to_ccyy 60; returns 2060 val = yy_to_ccyy 1960; returns 1960 no conversion done But suppose you want t o convert year values as follows, using a t ransit ion point of 50: 00 .. 49 - 2000 .. 2049 50 .. 99 - 1950 .. 1999 To do t his, pass an explicit t ransit ion point argum ent t o yy_to_ccyy : val = yy_to_ccyy 60, 50; returns 1960 val = yy_to_ccyy 1960, 50; returns 1960 no conversion done The yy_to_ccyy funct ion is one of t hose included in t he Cookbook_Ut ils.pm library file.

10.31 Performing Validity Checking on Date or Time Subparts

10.31.1 Problem

A st ring passes a pat t ern t est as a dat e or t im e, but you want t o perform furt her checking t o m ake sure t hat it s legal.

10.31.2 Solution

Break up t he value int o subpart s and perform t he appropriat e range checking on each part .

10.31.3 Discussion

Pat t ern m at ching m ay not be sufficient for checking dat es or t im es. For exam ple, a value like 1947-15-19 m ay m at ch a dat e pat t ern, but if you insert t he value int o a DATE colum n, MySQL will convert it t o 0000-00-00 . I f you want t o find out t hat t he value is bad before put t ing it int o your dat abase, com bine pat t ern m at ching wit h range checking. To m ake sure t hat a dat e is legal, break out t he year, m ont h, and day values, t hen check t hat t heyre wit hin t he proper ranges. Years should be less t han 9999 MySQL represent s dat es t o an upper lim it of 9999-12-31 , m ont h values should be in t he range from 1 t o 12, and days should be in t he range from 1 t o t he num ber of days in t he m ont h. That lat t er part is t he t rickiest ; it s m ont h- dependent , and for February, it s also year- dependent because it changes for leap years. Suppose youre checking input dat es in I SO form at . Earlier, in Recipe 10.26 , w e used an is_iso_date funct ion from t he Cookbook_Ut ils.pm library file t o perform a pat t ern m at ch on a dat e st ring and break it int o com ponent values: my ref = is_iso_date val; if defined ref { val matched ISO format pattern; check its subparts using ref-[0] through ref-[2] } else { val didnt match ISO format pattern } is_iso_date ret urns undef if t he value doesnt sat isfy a pat t ern t hat m at ches I SO dat e form at . Ot herwise, it ret urns a reference t o an array cont aining t he year, m ont h, and day values. [ 5] To perform addit ional checking on t he dat e part s, pass t hem t o is_valid_date , anot her library funct ion: [ 5] The Cookbook_Ut ils.pm file also cont ains is_mmddyy_date and is_ddmmyy_date rout ines t hat m at ch dat es in U.S. or Brit ish form at and ret urn undef or a reference t o an array of dat e part s. The part s are always in year, m ont h, day order, not t he order in which t he part s appear in t he dat e st ring. valid = is_valid_date ref-[0], ref-[1], ref-[2]; Or, m ore concisely: valid = is_valid_date {ref}; is_valid_date checks t he part s of a dat e like t his: sub is_valid_date { my year, month, day = _; year must be non-negative, month and day must be positive return 0 if year 0 || month 1 || day 1; check maximum limits on individual parts return 0 if year 9999; return 0 if month 12; return 0 if day days_in_month year, month; return 1; } is_valid_date requires separat e year, m ont h, and day values, not a dat e st ring. This forces you t o break apart candidat e values int o com ponent s before invoking it , but m akes it applicable in m ore cont ext s. For exam ple, you can use it t o check dat es like 12 February 2003 by m apping t he m ont h t o it s num eric value before calling is_valid_date . Wer e is_valid_date t o t ake a st ring argum ent assum ed t o be in a given dat e form at , it would be m uch less general. is_valid_date uses a subsidiary funct ion days_in_month t o det erm ine how m any days t here are in t he m ont h represent ed by t he dat e. days_in_month requires bot h t he year and t he m ont h as argum ent s, because if t he m ont h is 2 February , t he num ber of days depends on whet her t he year is a leap year. This m eans you m ust pass a four- digit year value. Two- digit years are am biguous wit h respect t o t he cent ury, and proper leap- year t est ing is im possible, as discussed in Recipe 5.28 . The days_in_month and is_leap_year funct ions are based on t echniques t aken st raight from t here: sub is_leap_year { my year = shift; return year 4 == 0 year 100 = 0 || year 400 == 0; } sub days_in_month { my year, month = _; my day_tbl = 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31; my days = day_tbl[month-1]; add a day for Feb of leap years days++ if month == 2 is_leap_year year; return days; } To perform validit y checking on t im e values, a sim ilar procedure can be used, alt hough t he ranges for t he subpart s are different : 0 t o 24 for t he hour, and 0 t o 59 for t he m inut e and second. Here is a funct ion is_24hr_time t hat checks for values in 24- hour form at : sub is_24hr_time { my s = shift; return undef unless s =~ \d{1,2}\D\d{2}\D\d{2}; return [ 1, 2, 3 ]; return hour, minute, second } The following is_ampm_time funct ion looks for t im es in 12- hour form at wit h an opt ional AM or PM suffix, convert ing PM t im es t o 24- hour values: sub is_ampm_time { my s = shift; return undef unless s =~ \d{1,2}\D\d{2}\D\d{2}?:\sAM|PM?i; my hour, min, sec = 1, 2, 3; hour += 12 if defined 4 uc 4 eq PM; return [ hour, min, sec ]; return hour, minute, second } Bot h funct ions ret urn undef for values t hat dont m at ch t he pat t ern. Ot herwise, t hey ret urn a reference t o a t hree-elem ent array cont aining t he hour, m inut e, and second values.

10.32 Writing Date-Processing Utilities