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 -------------------------------------------------- 45 $Precision = NULL, $DebugLevel = 0)
48 $this->DebugLevel = $DebugLevel;
50 if ($this->DebugLevel) { print(
"Date: Date(BeginDate=\"".$BeginDate
51 .
"\" EndDate=\"".$EndDate.
"\" Precision=" 52 .$this->FormattedPrecision($Precision).
")<br>\n"); }
81 # Formats we need to parse: 101 # append end date to begin date if available 103 if ($EndDate !== NULL)
105 $Date .=
" - ".$EndDate;
108 # strip off any leading or trailing whitespace 111 # bail out if we don't have anything to parse 112 if (strlen($Date) < 1) {
return; }
114 # check for and strip out inferred indicators ("[" and "]") 116 if (preg_match(
"/\\[/", $Date))
118 $Prec |= self::PRE_INFERRED;
119 $Date = preg_replace(
"/[\\[\\]]/",
"", $Date);
122 # check for and strip off copyright indicator (leading "c") 123 if (preg_match(
"/^c/", $Date))
125 $Prec |= self::PRE_COPYRIGHT;
126 $Date = preg_replace(
"/^c/",
"", $Date);
129 # check for and strip off continuous indicator (trailing "-") 130 if (preg_match(
"/\\-$/", $Date))
132 $Prec |= self::PRE_CONTINUOUS;
133 $Date = preg_replace(
"/\\-$/",
"", $Date);
136 # strip out any times 137 $Date = preg_replace(
"/[0-9]{1,2}:[0-9]{2,2}[:]?[0-9]{0,2}/",
"", $Date);
140 $Date = strtolower($Date);
142 # a regex to match short and long month names 143 $MonthRegex =
"(?:jan(?:uary)?|feb(?:ruary)?|mar(?:ch)?|apr(?:il)?|may".
144 "|jun(?:e)?|jul(?:y)?|aug(?:ust)?|sep(?:tember)?|oct(?:ober)?".
145 "|nov(?:ember)?|dec(?:ember)?)";
147 # Here we'll construct a template regex for dates 148 # We want a single regex that covers all the different formats 149 # of date we understand, with the various components of the 150 # date pulled out using named subexpressions (eg: (?P<name>)). 151 # Annoyingly, we can't re-use the same name for subexpressions 152 # that will never both be matched at once. 153 # So, we have to number them (year1, year2, etc) and figure 154 # out which one did match. 155 # Use XX_ThingNumber in the parameterized subexpressions 157 # We'll use string substitutions later to convert the XX_ to 161 # Matched formats are separated by |, as
this isn
't used in any of the formats 162 # First alternative will match the following formats: 163 # 1999-09-19 | 19990909 | 1999-09 | 199909 | 1999 164 "(?:(?P<XX_year1>\d{4})(?:-?(?P<XX_month1>\d{1,2})" 165 ."(?:-?(?P<XX_day1>\d{1,2}))?)?)". 166 # Second alternative will match the following formats: 167 # 09-19-1999 | 19-09-1999 | 09/19/01 | 09-19-01 168 "|(?:(?P<XX_month2>\d{1,2})[\/-](?P<XX_day2>\d{1,2})" 169 ."[\/-](?P<XX_year2>(?:\d{2,4})))". 170 # Third alternative will match the following formats: 171 # 09-Sep-1999 | 09 Sep 1999 | Sep-1999 | Sep 1999 172 "|(?:(?:(?P<XX_day3>\d+)[ -])?(?P<XX_month3>".$MonthRegex 173 .")[ -](?P<XX_year3>\d{4}))". 174 # Fourth alternative will match the following formats: 175 # Sep 9 1999 | September 9th, 1999 176 "|(?:(?P<XX_month4>".$MonthRegex 177 .") (?P<XX_day4>\d{1,2})(?:(?:st|nd|rd|th|),)? (?P<XX_year4>\d{4}))". 180 # IMPORTANT: if more formats are added, bump this 181 $NumberOfDateRegexes = 4; 183 # construct the begin and end regexes for the date range 184 $BeginRegex = str_replace('XX
', 'Begin
', $DateRegex ); 185 $EndRegex = str_replace('XX
', 'End
', $DateRegex ); 187 # glue them together, making the second one optional, 188 # and do the matching 189 if ( preg_match("/".$BeginRegex. 190 "(?:(?:(?: - )|,)".$EndRegex.")?/", 193 # pull out the Begin and End data from the matches array: 194 foreach( array("Begin","End") as $Time ) 196 # extract the matching elements from the regex parse 197 ${$Time."Day"} = $this->ExtractMatchData( 198 $Matches, $Time."_day", $NumberOfDateRegexes); 199 ${$Time."Month"} = $this->ExtractMatchData( 200 $Matches,$Time."_month", $NumberOfDateRegexes); 201 ${$Time."Year"} = $this->ExtractMatchData( 202 $Matches,$Time."_year", $NumberOfDateRegexes); 204 # convert named months to month numbers: 205 if (!is_null(${$Time."Month"}) && !is_numeric(${$Time."Month"})) 207 ${$Time."Month"} = $MonthNames[${$Time."Month"}]; 210 # handle 2-digit years 211 if (!is_null(${$Time.'Year
'}) && 212 ${$Time."Year"} != 0 && ${$Time."Year"} < 100) 214 ${$Time."Year"} += (${$Time."Year"} > 50) ? 1900 : 2000; 217 # deal with D-M-Y format, when we can detect it 218 if ( !is_null(${$Time."Month"}) && ${$Time."Month"}>12) 220 $Tmp = ${$Time."Month"}; 222 ${$Time."Month"} = ${$Time."Day"}; 223 ${$Time."Day"} = $Tmp; 228 # use current month if begin day but no begin month specified 229 if (isset($BeginDay) && !isset($BeginMonth)) 231 $BeginMonth = date("m"); 234 # use current year if begin month but no begin year specified 235 if (isset($BeginMonth) && !isset($BeginYear)) 237 $BeginYear = date("Y"); 240 # use begin year if end month but no end year specified 241 if (isset($EndMonth) && !isset($EndYear)) 243 $EndYear = $BeginYear; 246 # after we've shuffled around the numbers, check the result to see
if 247 # it looks valid, dropping that which doesn't 248 foreach( array(
"Begin",
"End") as $Time)
250 if (isset(${$Time.
"Year"}) && !${$Time.
"Year"} >=1)
252 unset(${$Time.
"Year"});
255 if (isset(${$Time.
"Month"}) &&
256 !(${$Time.
"Month"}>=1 && ${$Time.
"Month"}<=12))
258 unset(${$Time.
"Month"});
261 if (isset(${$Time.
"Day"}) &&
262 !(${$Time.
"Day"} >=1 && ${$Time.
"Day"} <=31))
264 unset(${$Time.
"Day"});
268 # if no begin date found and begin date value is not illegal 269 if (!isset($BeginYear)
270 && ($BeginDate !=
"0000-00-00")
271 && ($BeginDate !=
"0000-00-00 00:00:00"))
273 # try system call to parse incoming date 274 $UDateStamp = strtotime($BeginDate);
275 if ($this->DebugLevel > 1) { print(
"Date: calling strtotime" 276 .
" to parse BeginDate \"".$BeginDate
277 .
"\" -- strtotime returned \"".$UDateStamp.
"\"<br>\n"); }
279 # if system call was able to parse date 280 if (($UDateStamp != -1) && ($UDateStamp !== FALSE))
282 # set begin date to value returned by system call 283 $BeginYear = date(
"Y", $UDateStamp);
284 $BeginMonth = date(
"n", $UDateStamp);
285 $BeginDay = date(
"j", $UDateStamp);
289 # if end date value supplied and no end date found and end date value 291 if (($EndDate != NULL) && !isset($EndYear)
292 && ($EndDate !=
"0000-00-00")
293 && ($EndDate !=
"0000-00-00 00:00:00"))
295 # try system call to parse incoming date 296 $UDateStamp = strtotime($EndDate);
298 # if system call was able to parse date 299 if (($UDateStamp != -1) && ($UDateStamp !== FALSE))
301 # set begin date to value returned by system call 302 $EndYear = date(
"Y", $UDateStamp);
303 $EndMonth = date(
"n", $UDateStamp);
304 $EndDay = date(
"j", $UDateStamp);
308 # if end date is before begin date 309 if ((isset($EndYear) && isset($BeginYear) && ($EndYear < $BeginYear))
310 || (isset($BeginYear) && isset($EndYear) && ($EndYear == $BeginYear) &&
311 isset($BeginMonth) && isset($EndMonth) && ($EndMonth < $BeginMonth))
312 || (isset($BeginYear) && isset($EndYear) && ($EndYear == $BeginYear) &&
313 isset($BeginMonth) && isset($EndMonth) && ($EndMonth == $BeginMonth) &&
314 isset($BeginDay) && isset($EndDay) && ($EndDay < $BeginDay)))
316 # swap begin and end dates 317 $TempYear = $BeginYear;
318 $BeginYear = $EndYear;
319 $EndYear = $TempYear;
321 if (isset($BeginMonth) && isset($EndMonth))
323 $TempMonth = $BeginMonth;
324 $BeginMonth = $EndMonth;
325 $EndMonth = $TempMonth;
328 if (isset($BeginDay) && isset($EndDay))
330 $TempDay = $BeginDay;
336 # if precision value supplied by caller 337 if ($Precision != NULL)
339 # use supplied precision value 344 # save new precision value 345 if (isset($BeginYear)) { $Prec |= self::PRE_BEGINYEAR; }
346 if (isset($BeginMonth)) { $Prec |= self::PRE_BEGINMONTH; }
347 if (isset($BeginDay)) { $Prec |= self::PRE_BEGINDAY; }
348 if (isset($EndYear)) { $Prec |= self::PRE_ENDYEAR; }
349 if (isset($EndMonth)) { $Prec |= self::PRE_ENDMONTH; }
350 if (isset($EndDay)) { $Prec |= self::PRE_ENDDAY; }
354 # save new date values 355 if ($this->DebugLevel > 1) { print(
"Date: BeginYear = $BeginYear<br>\n"); }
356 if ($this->DebugLevel > 1) { print(
"Date: BeginMonth = $BeginMonth<br>\n"); }
357 if ($this->DebugLevel > 1) { print(
"Date: BeginDay = $BeginDay<br>\n"); }
358 if ($this->DebugLevel > 1) { print(
"Date: EndYear = $EndYear<br>\n"); }
359 if ($this->DebugLevel > 1) { print(
"Date: EndMonth = $EndMonth<br>\n"); }
360 if ($this->DebugLevel > 1) { print(
"Date: EndDay = $EndDay<br>\n"); }
361 if ($this->DebugLevel > 1) { print(
"Date: Precision = 363 $this->BeginYear = isset($BeginYear) ? $BeginYear : NULL ;
364 $this->BeginMonth = isset($BeginMonth) ? $BeginMonth : NULL ;
365 $this->BeginDay = isset($BeginDay) ? $BeginDay : NULL ;
366 $this->EndYear = isset($EndYear) ? $EndYear : NULL ;
367 $this->EndMonth = isset($EndMonth) ? $EndMonth : NULL ;
368 $this->EndDay = isset($EndDay) ? $EndDay : NULL ;
377 # if begin year available 379 if ($this->
Precision & self::PRE_BEGINYEAR)
381 # start with begin year 382 $DateString = sprintf(
"%04d", $this->BeginYear);
384 # if begin month available 385 if ($this->
Precision & self::PRE_BEGINMONTH)
388 $DateString .=
"-".sprintf(
"%02d", $this->BeginMonth);
390 # if begin day available 391 if ($this->
Precision & self::PRE_BEGINDAY)
394 $DateString .=
"-".sprintf(
"%02d", $this->BeginDay);
398 # if end year available 399 if ($this->
Precision & self::PRE_ENDYEAR)
401 # separate dates with dash 402 $DateString .=
" - ";
405 $DateString .= sprintf(
"%04d", $this->EndYear);
407 # if end month available 408 if ($this->
Precision & self::PRE_ENDMONTH)
411 $DateString .=
"-".sprintf(
"%02d", $this->EndMonth);
413 # if end day available 417 $DateString .=
"-".sprintf(
"%02d", $this->EndDay);
423 # if date is open-ended 424 if ($this->
Precision & self::PRE_CONTINUOUS)
426 # add dash to indicate open-ended 431 # if copyright flag is set 432 if ($this->
Precision & self::PRE_COPYRIGHT)
434 # add on copyright indicator 435 $DateString =
"c".$DateString;
438 # if flag is set indicating date was inferred 439 if ($this->
Precision & self::PRE_INFERRED)
441 # add on inferred indicators 442 $DateString =
"[".$DateString.
"]";
446 # return formatted date string to caller 461 $Month = ($this->
Precision & self::PRE_ENDMONTH) ? $this->EndMonth : 1;
462 $Day = ($this->
Precision & self::PRE_ENDDAY) ? $this->EndDay : 1;
463 $Year = ($this->
Precision & self::PRE_ENDYEAR) ? $this->EndYear : 1;
467 $Month = ($this->
Precision & self::PRE_BEGINMONTH) ? $this->BeginMonth : 1;
468 $Day = ($this->
Precision & self::PRE_BEGINDAY) ? $this->BeginDay : 1;
469 $Year = ($this->
Precision & self::PRE_BEGINYEAR) ? $this->BeginYear : 1;
471 return date($Format, mktime(0, 0, 0, $Month, $Day, $Year));
482 return $this->
PFormatted(
"Y-m-d H:i:s", $ReturnEndDate);
491 # start out assuming date will be empty 494 # if begin year available 495 if ($this->
Precision & self::PRE_BEGINYEAR)
497 # start with begin year 498 $DateString = sprintf(
"%04d", $this->BeginYear);
500 # if begin month available 501 if ($this->
Precision & self::PRE_BEGINMONTH)
504 $DateString .= sprintf(
"-%02d", $this->BeginMonth);
506 # if begin day available 507 if ($this->
Precision & self::PRE_BEGINDAY)
510 $DateString .= sprintf(
"-%02d", $this->BeginDay);
515 # return ISO 8601 formatted date string to caller 525 # build date string based on current precision 526 if ($this->
Precision & self::PRE_BEGINYEAR)
528 if ($this->
Precision & self::PRE_BEGINMONTH)
530 if ($this->
Precision & self::PRE_BEGINDAY)
532 $DateFormat =
"%04d-%02d-%02d";
536 $DateFormat =
"%04d-%02d-01";
541 $DateFormat =
"%04d-01-01";
544 $DateString = sprintf($DateFormat,
545 $this->BeginYear, $this->BeginMonth, $this->BeginDay);
552 # return date string to caller 562 # build date string based on current precision 563 if ($this->
Precision & self::PRE_ENDYEAR)
565 if ($this->
Precision & self::PRE_ENDMONTH)
567 if ($this->
Precision & self::PRE_ENDMONTH)
569 $DateFormat =
"%04d-%02d-%02d";
573 $DateFormat =
"%04d-%02d-00";
578 $DateFormat =
"%04d-00-00";
581 $DateString = sprintf($DateFormat,
582 $this->EndYear, $this->EndMonth, $this->EndDay);
589 # return date string to caller 600 if ($NewPrecision != NULL) { $this->
Precision = $NewPrecision; }
601 return $this->Precision;
613 public function SqlCondition($FieldName, $EndFieldName = NULL, $Operator =
"=")
615 # if no date value is set 618 # if operator is equals 619 if ($Operator ==
"=")
621 # construct conditional that will find null dates 622 $Condition =
"(".$FieldName.
" IS NULL OR ".$FieldName
623 .
" < '0000-01-01 00:00:01')";
627 # construct conditional that will find non-null dates 628 $Condition =
"(".$FieldName.
" > '0000-01-01 00:00:00')";
633 # use begin field name as end if no end field specified 634 if ($EndFieldName == NULL) { $EndFieldName = $FieldName; }
636 # determine begin and end of range 637 $BeginYear = $this->BeginYear;
638 if ($this->
Precision & self::PRE_BEGINMONTH)
640 $BeginMonth = $this->BeginMonth;
641 if ($this->
Precision & self::PRE_BEGINDAY)
643 $BeginDay = $this->BeginDay - 1;
655 if ($this->
Precision & self::PRE_ENDYEAR)
657 $EndYear = $this->EndYear;
658 if ($this->
Precision & self::PRE_ENDMONTH)
660 $EndMonth = $this->EndMonth;
663 $EndDay = $this->EndDay;
680 $EndYear = $BeginYear;
681 if ($this->
Precision & self::PRE_BEGINMONTH)
683 $EndMonth = $BeginMonth;
684 if ($this->
Precision & self::PRE_BEGINDAY)
686 $EndDay = $BeginDay + 1;
701 $RangeBegin =
"'".date(
"Y-m-d H:i:s", mktime(23, 59, 59,
702 $BeginMonth, $BeginDay, $BeginYear)).
"'";
703 $RangeEnd =
"'".date(
"Y-m-d H:i:s", mktime(23, 59, 59,
704 $EndMonth, $EndDay, $EndYear)).
"'";
706 # construct SQL condition 710 $Condition =
" ${FieldName} > ${RangeEnd} ";
714 $Condition =
" ${FieldName} > ${RangeBegin} ";
718 $Condition =
" ${FieldName} <= ${RangeBegin} ";
722 $Condition =
" ${FieldName} <= ${RangeEnd} ";
726 $Condition =
" (${FieldName} <= ${RangeBegin}" 727 .
" OR ${FieldName} > ${RangeEnd}) ";
732 $Condition =
" (${FieldName} > ${RangeBegin}" 733 .
" AND ${FieldName} <= ${RangeEnd}) ";
738 # return condition to caller 750 if ($Precision === NULL) { $Precision = $this->Precision; }
752 if ($Precision & self::PRE_BEGINYEAR) { $String .=
"| BEGINYEAR "; }
753 if ($Precision & self::PRE_BEGINMONTH) { $String .=
"| BEGINMONTH "; }
754 if ($Precision & self::PRE_BEGINDAY) { $String .=
"| BEGINDAY "; }
755 if ($Precision & self::PRE_BEGINDECADE) { $String .=
"| BEGINDECADE "; }
756 if ($Precision & self::PRE_ENDYEAR) { $String .=
"| ENDYEAR "; }
757 if ($Precision & self::PRE_ENDMONTH) { $String .=
"| ENDMONTH "; }
758 if ($Precision & self::PRE_ENDDAY) { $String .=
"| ENDDAY "; }
759 if ($Precision & self::PRE_ENDDECADE) { $String .=
"| ENDDECADE "; }
760 if ($Precision & self::PRE_INFERRED) { $String .=
"| INFERRED "; }
761 if ($Precision & self::PRE_COPYRIGHT) { $String .=
"| COPYRIGHT "; }
762 if ($Precision & self::PRE_CONTINUOUS) { $String .=
"| CONTINUOUS "; }
763 $String = preg_replace(
"/^\\|/",
"", $String);
768 # ---- PRIVATE INTERFACE ------------------------------------------------- 787 private function ExtractMatchData($Matches, $Member, $Max)
789 for( $Index=1; $Index<=$Max; $Index++ )
791 if (isset($Matches[$Member.$Index]) && strlen($Matches[$Member.$Index])>0)
793 $Data = $Matches[$Member.$Index];
794 return is_numeric($Data) ? intval($Data) : $Data;
FormattedForSql($ReturnEndDate=FALSE)
Get begin date (or end date if requested) formatted for SQL DATETIME field.
SqlCondition($FieldName, $EndFieldName=NULL, $Operator="=")
Get SQL condition for records that match date.
__construct($BeginDate, $EndDate=NULL, $Precision=NULL, $DebugLevel=0)
Object constructor.
Precision($NewPrecision=NULL)
Get/set date precision (combination of self::PRE_ bit constants).
FormattedISO8601()
Get begin time in ISO 8601 format.
PFormatted($Format, $ReturnEndDate=FALSE)
Get date in format specified like PHP date() format parameter.
EndDate()
Get normalized end date, suitable for storing via SQL.
BeginDate()
Get normalized begin date, suitable for storing via SQL.
Formatted()
Get date value suitable for display.
FormattedPrecision($Precision=NULL)
Get string containing printable version of precision flags.