5 # A Date Manipulation Object 7 # Copyright 1999-2004 Axis Data 8 # This code is free software that can be used or redistributed under the 9 # terms of Version 2 of the GNU General Public License, as published by the 10 # Free Software Foundation (http://www.fsf.org). 12 # Author: Edward Almasy (almasy@axisdata.com) 14 # Part of the AxisPHP library v1.2.5 15 # For more information see http://www.axisdata.com/AxisPHP/ 20 # ---- PUBLIC INTERFACE -------------------------------------------------- 28 if ($this->DebugLevel) { print(
"Date: Date(BeginDate=\"".$BeginDate.
"\" EndDate=\"".$EndDate.
"\" Precision=".$this->FormattedPrecision(
$Precision).
")<br>\n"); }
57 # Formats we need to parse: 77 # append end date to begin date if available 81 $Date .=
" - ".$EndDate;
84 # strip off any leading or trailing whitespace 87 # bail out if we don't have anything to parse 88 if (strlen($Date) < 1) {
return; }
90 # check for and strip out inferred indicators ("[" and "]") 92 if (preg_match(
"/\\[/", $Date))
95 $Date = preg_replace(
"/[\\[\\]]/",
"", $Date);
98 # check for and strip off copyright indicator (leading "c") 99 if (preg_match(
"/^c/", $Date))
102 $Date = preg_replace(
"/^c/",
"", $Date);
105 # check for and strip off continuous indicator (trailing "-") 106 if (preg_match(
"/\\-$/", $Date))
109 $Date = preg_replace(
"/\\-$/",
"", $Date);
112 # strip out any times 113 $Date = preg_replace(
"/[0-9]{1,2}:[0-9]{2,2}[:]?[0-9]{0,2}/",
"", $Date);
116 $Date = strtolower($Date);
118 # A regex to match short and long month names: 119 $MonthRegex =
"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may".
120 "|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:tember)?|oct(?:ober)?".
121 "|nov(?:ember)?|dec(?:ember)?)";
123 # Here we'll construct a template regex for dates 124 # We want a single regex that covers all the different formats 125 # of date we understand, with the various components of the 126 # date pulled out using named subexpressions (eg: (?P<name>)). 127 # Annoyingly, we can't re-use the same name for subexpressions 128 # that will never both be matched at once. 129 # So, we have to number them (year1, year2, etc) and figure 130 # out which one did match. 131 # Use XX_ThingNumber in the parameterized subexpressions 133 # We'll use string substitutions later to convert the XX_ to 137 # Matched formats are separated by |, as
this isn
't used in any of the formats 138 # First alternative will match the following formats: 139 # 1999-09-19 | 19990909 | 1999-09 | 199909 | 1999 140 "(?:(?P<XX_year1>\d{4})(?:-?(?P<XX_month1>\d{1,2})(?:-?(?P<XX_day1>\d{1,2}))?)?)". 141 # Second alternative will match the following formats: 142 # 09-19-1999 | 19-09-1999 | 09/19/01 | 09-19-01 143 "|(?:(?P<XX_month2>\d{1,2})[\/-](?P<XX_day2>\d{1,2})[\/-](?P<XX_year2>(?:\d{2,4})))". 144 # Third alternative will match the following formats: 145 # 09-Sep-1999 | 09 Sep 1999 | Sep-1999 | Sep 1999 146 "|(?:(?:(?P<XX_day3>\d+)[ -])?(?P<XX_month3>".$MonthRegex.")[ -](?P<XX_year3>\d{4}))". 147 # Fourth alternative will match the following formats: 148 # Sep 9 1999 | September 9th, 1999 149 "|(?:(?P<XX_month4>".$MonthRegex.") (?P<XX_day4>\d{1,2})(?:(?:st|nd|rd|th|),)? (?P<XX_year4>\d{4}))". 152 # If more formats are added, bump this. 153 $NumberOfDateRegexes = 4; 155 # Construct the begin and end regexes for the date range 156 $BeginRegex = str_replace('XX
','Begin
', $DateRegex ); 157 $EndRegex = str_replace('XX
','End
', $DateRegex ); 159 # Glue them together, making the second one optional, 160 # and do the matching. 161 if ( preg_match("/".$BeginRegex. 162 "(?:(?:(?: - )|,)".$EndRegex.")?/", 165 # Pull out the Begin and End data from the matches array: 166 foreach( array("Begin","End") as $Time ) 169 # Extract the matching elements from the regex parse 170 '$
'.$Time.'Day = $this->ExtractMatchData($Matches,
"'. 171 $Time.'_day", $NumberOfDateRegexes );
' . 172 '$
'.$Time.'Month = $this->ExtractMatchData($Matches,
"'. 173 $Time.'_month",$NumberOfDateRegexes );
' . 174 '$
'.$Time.'Year = $this->ExtractMatchData($Matches,
"'. 175 $Time.'_year", $NumberOfDateRegexes );
' . 176 # Convert named months to month numbers: 177 'if ( isset($
'.$Time.'Month) &&
' . 178 ' !is_numeric($
'.$Time.'Month))
' . 180 ' $
'.$Time.'Month=$MonthNames[$
'.$Time.'Month];
' . 182 # Handle 2-digit years 183 'if ( isset($
'.$Time.'Year) &&
' . 184 ' strlen($
'.$Time.'Year)==2)
' . 186 ' $
'.$Time.'Year += ($
'.$Time.'Year>50)?1900:2000;
' . 188 # Deal with D-M-Y format, where we can 189 'if ( isset($
'.$Time.'Month) && $
'.$Time.'Month>12)
' . 191 ' $Tmp = $
'.$Time.'Month;
' . 192 ' $
'.$Time.'Month = $
'.$Time.'Day;
' . 193 ' $
'.$Time.'Day = $Tmp;
' . 199 # use current month if begin day but no begin month specified 200 if (isset($BeginDay) && !isset($BeginMonth)) 202 $BeginMonth = date("m"); 205 # use current year if begin month but no begin year specified 206 if (isset($BeginMonth) && !isset($BeginYear)) 208 $BeginYear = date("Y"); 211 # use begin year if end month but no end year specified 212 if (isset($EndMonth) && !isset($EndYear)) 214 $EndYear = $BeginYear; 217 # After we've shuffled around the numbers, check the result to see
if 218 # it looks valid, dropping that which doesn't. 219 foreach( array(
"Begin",
"End") as $Time)
222 # Discard invalid looking dates
223 'if ( isset($'.$Time.
'Year) && !($'.$Time.
'Year >=1)) ' .
224 ' { unset($'.$Time.
'Year); } ' .
225 'if ( isset($'.$Time.
'Month) && ' .
226 ' !( $'.$Time.
'Month>=1 && $'.$Time.
'Month<=12)) ' .
227 ' { unset($'.$Time.
'Month); } ' .
228 'if ( isset($'.$Time.
'Day) && ' .
229 ' !( $'.$Time.
'Day >=1 && $'.$Time.
'Day <=31)) ' .
230 ' { unset($'.$Time.
'Day); } ' 234 # if no begin date found and begin date value is not illegal 236 && ($BeginDate !=
"0000-00-00")
237 && ($BeginDate !=
"0000-00-00 00:00:00"))
239 # try system call to parse incoming date 240 $UDateStamp = strtotime($BeginDate);
241 if ($this->DebugLevel > 1) { print(
"Date: calling strtotime to parse BeginDate \"".$BeginDate.
"\" -- strtotime returned \"".$UDateStamp.
"\"<br>\n"); }
243 # if system call was able to parse date 244 if (($UDateStamp != -1) && ($UDateStamp !== FALSE))
246 # set begin date to value returned by system call 253 # if end date value supplied and no end date found and end date value is not illegal 254 if (($EndDate != NULL) && !isset(
$EndYear)
255 && ($EndDate !=
"0000-00-00")
256 && ($EndDate !=
"0000-00-00 00:00:00"))
258 # try system call to parse incoming date 259 $UDateStamp = strtotime($EndDate);
261 # if system call was able to parse date 262 if (($UDateStamp != -1) && ($UDateStamp !== FALSE))
264 # set begin date to value returned by system call 267 $EndDay = date(
"j", $UDateStamp);
271 # if end date is before begin date 279 # swap begin and end dates 291 # if precision value supplied by caller 294 # use supplied precision value 299 # save new precision value 309 # save new date values 310 if ($this->DebugLevel > 1) { print(
"Date: BeginYear = $BeginYear<br>\n"); }
311 if ($this->DebugLevel > 1) { print(
"Date: BeginMonth = $BeginMonth<br>\n"); }
312 if ($this->DebugLevel > 1) { print(
"Date: BeginDay = $BeginDay<br>\n"); }
313 if ($this->DebugLevel > 1) { print(
"Date: EndYear = $EndYear<br>\n"); }
314 if ($this->DebugLevel > 1) { print(
"Date: EndMonth = $EndMonth<br>\n"); }
315 if ($this->DebugLevel > 1) { print(
"Date: EndDay = $EndDay<br>\n"); }
316 if ($this->DebugLevel > 1) { print(
"Date: Precision = ".$this->
FormattedPrecision().
"<br>\n"); }
325 # return value suitable for display 328 # if begin year available 332 # start with begin year 335 # if begin month available 339 $DateString .=
"-".$this->BeginMonth;
341 # if begin day available 345 $DateString .=
"-".$this->BeginDay;
349 # if end year available 355 # separate dates with comma 360 # separate dates with dash 361 $DateString .=
" - ";
367 # if end month available 371 $DateString .=
"-".$this->EndMonth;
373 # if end day available 377 $DateString .=
"-".$this->EndDay;
383 # if date is open-ended 386 # add dash to indicate open-ended 391 # if copyright flag is set 394 # add on copyright indicator 395 $DateString =
"c".$DateString;
398 # if flag is set indicating date was inferred 401 # add on inferred indicators 402 $DateString =
"[".$DateString.
"]";
406 # return formatted date string to caller 410 # return date in format specified like PHP date() format parameter 425 return date($Format, mktime(0, 0, 0, $Month, $Day, $Year));
428 # get begin date/time (or end if requested) formatted for SQL DATETIME field 431 return $this->
PFormatted(
"Y-m-d H:i:s", $ReturnEndDate);
434 # return begin time in ISO 8601 format 437 # start out assuming date will be empty 440 # if begin year available 443 # start with begin year 444 $DateString = sprintf(
"%04d", $this->BeginYear);
446 # if begin month available 450 $DateString .= sprintf(
"-%02d", $this->BeginMonth);
452 # if begin day available 456 $DateString .= sprintf(
"-%02d", $this->BeginDay);
461 # return ISO 8601 formatted date string to caller 465 # return values in UTC instead of local time (NOT IMPLEMENTED) 468 # if not currently in UTC 469 if ($this->InUTC != TRUE)
474 # set flag to indicate we are in UTC 479 # return values in local time instead of UTC (NOT IMPLEMENTED) 482 # if currently in UTC 485 # adjust date to local time 488 # set flag to indicate we are in local time 489 $this->InUTC = FALSE;
493 # return normalized values (suitable for storing via SQL) 496 # build date string based on current precision 501 if ($this->
Precision & DATEPRE_BEGINMONTH)
503 $DateFormat =
"%04d-%02d-%02d";
507 $DateFormat =
"%04d-%02d-01";
512 $DateFormat =
"%04d-01-01";
515 $DateString = sprintf($DateFormat,
516 $this->BeginYear, $this->BeginMonth, $this->BeginDay);
523 # return date string to caller 528 # build date string based on current precision 535 $DateFormat =
"%04d-%02d-%02d";
539 $DateFormat =
"%04d-%02d-00";
544 $DateFormat =
"%04d-00-00";
547 $DateString = sprintf($DateFormat,
548 $this->EndYear, $this->EndMonth, $this->EndDay);
555 # return date string to caller 559 # get or set precision value (combination of boolean flags) 562 if ($NewPrecision != NULL) { $this->
Precision = $NewPrecision; }
566 # return text of SQL condition for records that match date 567 function SqlCondition($FieldName, $EndFieldName = NULL, $Operator =
"=")
569 # if no date value is set 572 # if operator is equals 573 if ($Operator ==
"=")
575 # construct conditional that will find null dates 576 $Condition =
"(".$FieldName.
" IS NULL OR ".$FieldName.
" < '0000-01-01 00:00:01')";
580 # construct conditional that will find non-null dates 581 $Condition =
"(".$FieldName.
" > '0000-01-01 00:00:00')";
586 # use begin field name as end if no end field specified 587 if ($EndFieldName == NULL) { $EndFieldName = $FieldName; }
589 # determine begin and end of range 634 if ($this->
Precision & DATEPRE_BEGINMONTH)
657 # construct SQL condition 661 $Condition =
" ${FieldName} > ${RangeEnd} ";
665 $Condition =
" ${FieldName} > ${RangeBegin} ";
669 $Condition =
" ${FieldName} <= ${RangeBegin} ";
673 $Condition =
" ${FieldName} <= ${RangeEnd} ";
677 $Condition =
" (${FieldName} <= ${RangeBegin}" 678 .
" OR ${FieldName} > ${RangeEnd}) ";
683 $Condition =
" (${FieldName} > ${RangeBegin}" 684 .
" AND ${FieldName} <= ${RangeEnd}) ";
689 # return condition to caller 693 # return string containing printable version of precision flags 710 $String = preg_replace(
"/^\\|/",
"", $String);
715 # ---- PRIVATE INTERFACE ------------------------------------------------- 726 # Return the first non-empty parameterized subexpression match 727 # Expects a match array from preg_match() 728 # Expects a number of array elements, eg. match1, match2, match3 729 # Checks each element and returns the first non-empty one 730 # If they are all empty, NULL is returned 731 private function ExtractMatchData( $Matches, $Member, $Max )
733 for( $i=1; $i<=$Max; $i++ )
735 if (isset($Matches[$Member.$i]) && strlen($Matches[$Member.$i])>0)
737 return $Matches[$Member.$i];
744 # date precision flags 745 define(
"DATEPRE_BEGINYEAR", 1);
746 define(
"DATEPRE_BEGINMONTH", 2);
747 define(
"DATEPRE_BEGINDAY", 4);
748 define(
"DATEPRE_BEGINDECADE", 8);
749 define(
"DATEPRE_BEGINCENTURY",16);
750 define(
"DATEPRE_ENDYEAR", 32);
751 define(
"DATEPRE_ENDMONTH", 64);
752 define(
"DATEPRE_ENDDAY", 128);
753 define(
"DATEPRE_ENDDECADE", 256);
754 define(
"DATEPRE_ENDCENTURY", 512);
755 define(
"DATEPRE_INFERRED", 1024);
756 define(
"DATEPRE_COPYRIGHT", 2048);
757 define(
"DATEPRE_CONTINUOUS", 4096);
758 define(
"DATEPRE_SEPARATE", 8192);
759 define(
"DATEPRE_UNSURE", 16384);
FormattedForSql($ReturnEndDate=FALSE)
const DATEPRE_BEGINDECADE
SqlCondition($FieldName, $EndFieldName=NULL, $Operator="=")
__construct($BeginDate, $EndDate=NULL, $Precision=NULL, $DebugLevel=0)
Precision($NewPrecision=NULL)
PFormatted($Format, $ReturnEndDate=FALSE)
FormattedPrecision($Precision=NULL)