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/ 21 # ---- PUBLIC INTERFACE -------------------------------------------------- 25 $Precision = NULL, $DebugLevel = 0)
28 $this->DebugLevel = $DebugLevel;
30 if ($this->DebugLevel) { print(
"Date: Date(BeginDate=\"".$BeginDate
31 .
"\" EndDate=\"".$EndDate.
"\" Precision=" 32 .$this->FormattedPrecision($Precision).
")<br>\n"); }
61 # Formats we need to parse: 81 # append end date to begin date if available 85 $Date .=
" - ".$EndDate;
88 # strip off any leading or trailing whitespace 91 # bail out if we don't have anything to parse 92 if (strlen($Date) < 1) {
return; }
94 # check for and strip out inferred indicators ("[" and "]") 96 if (preg_match(
"/\\[/", $Date))
99 $Date = preg_replace(
"/[\\[\\]]/",
"", $Date);
102 # check for and strip off copyright indicator (leading "c") 103 if (preg_match(
"/^c/", $Date))
106 $Date = preg_replace(
"/^c/",
"", $Date);
109 # check for and strip off continuous indicator (trailing "-") 110 if (preg_match(
"/\\-$/", $Date))
113 $Date = preg_replace(
"/\\-$/",
"", $Date);
116 # strip out any times 117 $Date = preg_replace(
"/[0-9]{1,2}:[0-9]{2,2}[:]?[0-9]{0,2}/",
"", $Date);
120 $Date = strtolower($Date);
122 # A regex to match short and long month names: 123 $MonthRegex =
"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may".
124 "|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:tember)?|oct(?:ober)?".
125 "|nov(?:ember)?|dec(?:ember)?)";
127 # Here we'll construct a template regex for dates 128 # We want a single regex that covers all the different formats 129 # of date we understand, with the various components of the 130 # date pulled out using named subexpressions (eg: (?P<name>)). 131 # Annoyingly, we can't re-use the same name for subexpressions 132 # that will never both be matched at once. 133 # So, we have to number them (year1, year2, etc) and figure 134 # out which one did match. 135 # Use XX_ThingNumber in the parameterized subexpressions 137 # We'll use string substitutions later to convert the XX_ to 141 # Matched formats are separated by |, as
this isn
't used in any of the formats 142 # First alternative will match the following formats: 143 # 1999-09-19 | 19990909 | 1999-09 | 199909 | 1999 144 "(?:(?P<XX_year1>\d{4})(?:-?(?P<XX_month1>\d{1,2})" 145 ."(?:-?(?P<XX_day1>\d{1,2}))?)?)". 146 # Second alternative will match the following formats: 147 # 09-19-1999 | 19-09-1999 | 09/19/01 | 09-19-01 148 "|(?:(?P<XX_month2>\d{1,2})[\/-](?P<XX_day2>\d{1,2})" 149 ."[\/-](?P<XX_year2>(?:\d{2,4})))". 150 # Third alternative will match the following formats: 151 # 09-Sep-1999 | 09 Sep 1999 | Sep-1999 | Sep 1999 152 "|(?:(?:(?P<XX_day3>\d+)[ -])?(?P<XX_month3>".$MonthRegex 153 .")[ -](?P<XX_year3>\d{4}))". 154 # Fourth alternative will match the following formats: 155 # Sep 9 1999 | September 9th, 1999 156 "|(?:(?P<XX_month4>".$MonthRegex 157 .") (?P<XX_day4>\d{1,2})(?:(?:st|nd|rd|th|),)? (?P<XX_year4>\d{4}))". 160 # If more formats are added, bump this. 161 $NumberOfDateRegexes = 4; 163 # Construct the begin and end regexes for the date range 164 $BeginRegex = str_replace('XX
', 'Begin
', $DateRegex ); 165 $EndRegex = str_replace('XX
', 'End
', $DateRegex ); 167 # Glue them together, making the second one optional, 168 # and do the matching. 169 if ( preg_match("/".$BeginRegex. 170 "(?:(?:(?: - )|,)".$EndRegex.")?/", 173 # Pull out the Begin and End data from the matches array: 174 foreach( array("Begin","End") as $Time ) 177 # Extract the matching elements from the regex parse 178 '$
'.$Time.'Day = $this->ExtractMatchData($Matches,
"'. 179 $Time.'_day", $NumberOfDateRegexes );
' . 180 '$
'.$Time.'Month = $this->ExtractMatchData($Matches,
"'. 181 $Time.'_month",$NumberOfDateRegexes );
' . 182 '$
'.$Time.'Year = $this->ExtractMatchData($Matches,
"'. 183 $Time.'_year", $NumberOfDateRegexes );
' . 184 # Convert named months to month numbers: 185 'if ( isset($
'.$Time.'Month) &&
' . 186 ' !is_numeric($
'.$Time.'Month))
' . 188 ' $
'.$Time.'Month=$MonthNames[$
'.$Time.'Month];
' . 190 # Handle 2-digit years 191 'if ( isset($
'.$Time.'Year) &&
' . 192 ' strlen($
'.$Time.'Year)==2)
' . 194 ' $
'.$Time.'Year += ($
'.$Time.'Year>50)?1900:2000;
' . 196 # Deal with D-M-Y format, where we can 197 'if ( isset($
'.$Time.'Month) && $
'.$Time.'Month>12)
' . 199 ' $Tmp = $
'.$Time.'Month;
' . 200 ' $
'.$Time.'Month = $
'.$Time.'Day;
' . 201 ' $
'.$Time.'Day = $Tmp;
' . 207 # use current month if begin day but no begin month specified 208 if (isset($BeginDay) && !isset($BeginMonth)) 210 $BeginMonth = date("m"); 213 # use current year if begin month but no begin year specified 214 if (isset($BeginMonth) && !isset($BeginYear)) 216 $BeginYear = date("Y"); 219 # use begin year if end month but no end year specified 220 if (isset($EndMonth) && !isset($EndYear)) 222 $EndYear = $BeginYear; 225 # After we've shuffled around the numbers, check the result to see
if 226 # it looks valid, dropping that which doesn't. 227 foreach( array(
"Begin",
"End") as $Time)
230 # Discard invalid looking dates
231 'if ( isset($'.$Time.
'Year) && !($'.$Time.
'Year >=1)) ' .
232 ' { unset($'.$Time.
'Year); } ' .
233 'if ( isset($'.$Time.
'Month) && ' .
234 ' !( $'.$Time.
'Month>=1 && $'.$Time.
'Month<=12)) ' .
235 ' { unset($'.$Time.
'Month); } ' .
236 'if ( isset($'.$Time.
'Day) && ' .
237 ' !( $'.$Time.
'Day >=1 && $'.$Time.
'Day <=31)) ' .
238 ' { unset($'.$Time.
'Day); } ' 242 # if no begin date found and begin date value is not illegal 243 if (!isset($BeginYear)
244 && ($BeginDate !=
"0000-00-00")
245 && ($BeginDate !=
"0000-00-00 00:00:00"))
247 # try system call to parse incoming date 248 $UDateStamp = strtotime($BeginDate);
249 if ($this->DebugLevel > 1) { print(
"Date: calling strtotime" 250 .
" to parse BeginDate \"".$BeginDate
251 .
"\" -- strtotime returned \"".$UDateStamp.
"\"<br>\n"); }
253 # if system call was able to parse date 254 if (($UDateStamp != -1) && ($UDateStamp !== FALSE))
256 # set begin date to value returned by system call 257 $BeginYear = date(
"Y", $UDateStamp);
258 $BeginMonth = date(
"n", $UDateStamp);
259 $BeginDay = date(
"j", $UDateStamp);
263 # if end date value supplied and no end date found and end date value 265 if (($EndDate != NULL) && !isset($EndYear)
266 && ($EndDate !=
"0000-00-00")
267 && ($EndDate !=
"0000-00-00 00:00:00"))
269 # try system call to parse incoming date 270 $UDateStamp = strtotime($EndDate);
272 # if system call was able to parse date 273 if (($UDateStamp != -1) && ($UDateStamp !== FALSE))
275 # set begin date to value returned by system call 276 $EndYear = date(
"Y", $UDateStamp);
277 $EndMonth = date(
"n", $UDateStamp);
278 $EndDay = date(
"j", $UDateStamp);
282 # if end date is before begin date 283 if ((isset($EndYear) && isset($BeginYear) && ($EndYear < $BeginYear))
284 || (isset($BeginYear) && isset($EndYear) && ($EndYear == $BeginYear) &&
285 isset($BeginMonth) && isset($EndMonth) && ($EndMonth < $BeginMonth))
286 || (isset($BeginYear) && isset($EndYear) && ($EndYear == $BeginYear) &&
287 isset($BeginMonth) && isset($EndMonth) && ($EndMonth == $BeginMonth) &&
288 isset($BeginDay) && isset($EndDay) && ($EndDay < $BeginDay)))
290 # swap begin and end dates 291 $TempYear = $BeginYear;
292 $TempMonth = $BeginMonth;
293 $TempDay = $BeginDay;
294 $BeginYear = $EndYear;
295 $BeginMonth = $EndMonth;
297 $EndYear = $TempYear;
298 $EndMonth = $TempMonth;
302 # if precision value supplied by caller 303 if ($Precision != NULL)
305 # use supplied precision value 310 # save new precision value 320 # save new date values 321 if ($this->DebugLevel > 1) { print(
"Date: BeginYear = $BeginYear<br>\n"); }
322 if ($this->DebugLevel > 1) { print(
"Date: BeginMonth = $BeginMonth<br>\n"); }
323 if ($this->DebugLevel > 1) { print(
"Date: BeginDay = $BeginDay<br>\n"); }
324 if ($this->DebugLevel > 1) { print(
"Date: EndYear = $EndYear<br>\n"); }
325 if ($this->DebugLevel > 1) { print(
"Date: EndMonth = $EndMonth<br>\n"); }
326 if ($this->DebugLevel > 1) { print(
"Date: EndDay = $EndDay<br>\n"); }
327 if ($this->DebugLevel > 1) { print(
"Date: Precision = 329 $this->BeginYear = isset($BeginYear) ? $BeginYear : NULL ;
330 $this->BeginMonth = isset($BeginMonth) ? $BeginMonth : NULL ;
331 $this->BeginDay = isset($BeginDay) ? $BeginDay : NULL ;
332 $this->EndYear = isset($EndYear) ? $EndYear : NULL ;
333 $this->EndMonth = isset($EndMonth) ? $EndMonth : NULL ;
334 $this->EndDay = isset($EndDay) ? $EndDay : NULL ;
337 # return value suitable for display 340 # if begin year available 344 # start with begin year 345 $DateString = $this->BeginYear;
347 # if begin month available 351 $DateString .=
"-".$this->BeginMonth;
353 # if begin day available 357 $DateString .=
"-".$this->BeginDay;
361 # if end year available 367 # separate dates with comma 372 # separate dates with dash 373 $DateString .=
" - ";
377 $DateString .= $this->EndYear;
379 # if end month available 383 $DateString .=
"-".$this->EndMonth;
385 # if end day available 389 $DateString .=
"-".$this->EndDay;
395 # if date is open-ended 398 # add dash to indicate open-ended 403 # if copyright flag is set 406 # add on copyright indicator 407 $DateString =
"c".$DateString;
410 # if flag is set indicating date was inferred 413 # add on inferred indicators 414 $DateString =
"[".$DateString.
"]";
418 # return formatted date string to caller 422 # return date in format specified like PHP date() format parameter 437 return date($Format, mktime(0, 0, 0, $Month, $Day, $Year));
440 # get begin date/time (or end if requested) formatted for SQL DATETIME field 443 return $this->
PFormatted(
"Y-m-d H:i:s", $ReturnEndDate);
446 # return begin time in ISO 8601 format 449 # start out assuming date will be empty 452 # if begin year available 455 # start with begin year 456 $DateString = sprintf(
"%04d", $this->BeginYear);
458 # if begin month available 462 $DateString .= sprintf(
"-%02d", $this->BeginMonth);
464 # if begin day available 468 $DateString .= sprintf(
"-%02d", $this->BeginDay);
473 # return ISO 8601 formatted date string to caller 477 # return values in UTC instead of local time (NOT IMPLEMENTED) 480 # if not currently in UTC 481 if ($this->InUTC != TRUE)
486 # set flag to indicate we are in UTC 491 # return values in local time instead of UTC (NOT IMPLEMENTED) 494 # if currently in UTC 497 # adjust date to local time 500 # set flag to indicate we are in local time 501 $this->InUTC = FALSE;
505 # return normalized values (suitable for storing via SQL) 508 # build date string based on current precision 513 if ($this->
Precision & DATEPRE_BEGINMONTH)
515 $DateFormat =
"%04d-%02d-%02d";
519 $DateFormat =
"%04d-%02d-01";
524 $DateFormat =
"%04d-01-01";
527 $DateString = sprintf($DateFormat,
528 $this->BeginYear, $this->BeginMonth, $this->BeginDay);
535 # return date string to caller 541 # build date string based on current precision 548 $DateFormat =
"%04d-%02d-%02d";
552 $DateFormat =
"%04d-%02d-00";
557 $DateFormat =
"%04d-00-00";
560 $DateString = sprintf($DateFormat,
561 $this->EndYear, $this->EndMonth, $this->EndDay);
568 # return date string to caller 572 # get or set precision value (combination of boolean flags) 575 if ($NewPrecision != NULL) { $this->
Precision = $NewPrecision; }
576 return $this->Precision;
579 # return text of SQL condition for records that match date 580 public function SqlCondition($FieldName, $EndFieldName = NULL, $Operator =
"=")
582 # if no date value is set 585 # if operator is equals 586 if ($Operator ==
"=")
588 # construct conditional that will find null dates 589 $Condition =
"(".$FieldName.
" IS NULL OR ".$FieldName
590 .
" < '0000-01-01 00:00:01')";
594 # construct conditional that will find non-null dates 595 $Condition =
"(".$FieldName.
" > '0000-01-01 00:00:00')";
600 # use begin field name as end if no end field specified 601 if ($EndFieldName == NULL) { $EndFieldName = $FieldName; }
603 # determine begin and end of range 604 $BeginYear = $this->BeginYear;
607 $BeginMonth = $this->BeginMonth;
610 $BeginDay = $this->BeginDay - 1;
624 $EndYear = $this->EndYear;
627 $EndMonth = $this->EndMonth;
630 $EndDay = $this->EndDay;
647 $EndYear = $BeginYear;
648 if ($this->
Precision & DATEPRE_BEGINMONTH)
650 $EndMonth = $BeginMonth;
653 $EndDay = $BeginDay + 1;
668 $RangeBegin =
"'".date(
"Y-m-d H:i:s", mktime(23, 59, 59,
669 $BeginMonth, $BeginDay, $BeginYear)).
"'";
670 $RangeEnd =
"'".date(
"Y-m-d H:i:s", mktime(23, 59, 59,
671 $EndMonth, $EndDay, $EndYear)).
"'";
673 # construct SQL condition 677 $Condition =
" ${FieldName} > ${RangeEnd} ";
681 $Condition =
" ${FieldName} > ${RangeBegin} ";
685 $Condition =
" ${FieldName} <= ${RangeBegin} ";
689 $Condition =
" ${FieldName} <= ${RangeEnd} ";
693 $Condition =
" (${FieldName} <= ${RangeBegin}" 694 .
" OR ${FieldName} > ${RangeEnd}) ";
699 $Condition =
" (${FieldName} > ${RangeBegin}" 700 .
" AND ${FieldName} <= ${RangeEnd}) ";
705 # return condition to caller 709 # return string containing printable version of precision flags 712 if ($Precision === NULL) { $Precision = $this->Precision; }
726 $String = preg_replace(
"/^\\|/",
"", $String);
731 # ---- PRIVATE INTERFACE ------------------------------------------------- 742 # Return the first non-empty parameterized subexpression match 743 # Expects a match array from preg_match() 744 # Expects a number of array elements, eg. match1, match2, match3 745 # Checks each element and returns the first non-empty one 746 # If they are all empty, NULL is returned 747 private function ExtractMatchData( $Matches, $Member, $Max )
749 for( $i=1; $i<=$Max; $i++ )
751 if (isset($Matches[$Member.$i]) && strlen($Matches[$Member.$i])>0)
753 return $Matches[$Member.$i];
760 # date precision flags 761 define(
"DATEPRE_BEGINYEAR", 1);
762 define(
"DATEPRE_BEGINMONTH", 2);
763 define(
"DATEPRE_BEGINDAY", 4);
764 define(
"DATEPRE_BEGINDECADE", 8);
765 define(
"DATEPRE_BEGINCENTURY", 16);
766 define(
"DATEPRE_ENDYEAR", 32);
767 define(
"DATEPRE_ENDMONTH", 64);
768 define(
"DATEPRE_ENDDAY", 128);
769 define(
"DATEPRE_ENDDECADE", 256);
770 define(
"DATEPRE_ENDCENTURY", 512);
771 define(
"DATEPRE_INFERRED", 1024);
772 define(
"DATEPRE_COPYRIGHT", 2048);
773 define(
"DATEPRE_CONTINUOUS", 4096);
774 define(
"DATEPRE_SEPARATE", 8192);
775 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)