This is a PHP Date/Time class implementation that allows you to use dates beyond or before the UNIX date limitations (UNIX epoch.) It uses the doomsday algorithm to calculate days of the week and there is a class included specifically for that function. Note this is still officially beta. However at this point it looks pretty good.
Please feel free to contact me if you’re planning on trying it out. Use the contact form on the left. If you find any bugs or recommend any advancements, please drop me a line. However, please note the todo list at the start of the class for items that are still coming.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
<? /* * * Copyright (C) 2005 James Bly * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Version 0.9 * * Todo (by priority): * Validation of input (invalid dates, leap year mistakes, etc) * Definition of missing parameters in date() (not all from PHP are mirrored) * Implement timezone and GMT offset handling functions * * WARNING: This code is beta and shouldn't be used for critical * production purposes. It also might have holes and security * issues as only a cursory review has been done prior to its * release. Use it at your own risk. * */ /* * * Construction: * new DateTime($datestr, $datefmt); * * $datestr - This is the string that identifies the date * $datefmt - This helps identify locality. Is it m-d-y, or d-m-y. * This variable is in the form "mdy" or "dmy" etc. It * will default to mdy, unless the first parameter is * 4 digits long, in which case it will go to ymd. * * */ class DateTime { public $datestr; public $datefmt; // $n vals = numeric public $ndayofweek; public $nmonth; public $nday; public $nyear; // $a vals = abbreviated text public $adayofweek; public $amonth; public $aday; public $ayear; // $l vals = long text public $ldayofweek; public $lmonth; public $lday; public $lyear; // $t vals = time defaults 24 hour public $thour = 00; public $tmin = 00; public $tsec = 00; public $tprec = 00; public $tz; public $toffset; // GMT offset // Other generals private $ldays = array( "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"); private $adays = array( "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"); private $lmonths = array( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); private $amonths = array( "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); public function show_settings() { printf("\n---------------------------------------------------------------\n"); printf("n: month=%-12s day=%-7s year=%-4s dayofweek=%s\n", $this->nmonth, $this->nday, $this->nyear, $this->ndayofweek); printf("l: month=%-12s day=%-7s year=%-4s dayofweek=%s\n", $this->lmonth, $this->lday, $this->lyear, $this->ldayofweek); printf("a: month=%-12s day=%-7s year=%-4s dayofweek=%s\n", $this->amonth, $this->aday, $this->ayear, $this->adayofweek); printf("t: hour=%-13s min=%-7s sec=%-5s prec=%s\n", $this->thour, $this->tmin, $this->tsec, $this->tprec); printf(" tz=%-15s offset=%-6s\n", $this->tz, $this->toffset); printf("---------------------------------------------------------------\n"); } public function __construct($datestr, $datefmt = "mdy") { $this->datestr = $datestr; $this->datefmt = $datefmt; self::tokens(); //important post parser functions to build remaining variables if(self::goodtogo('date')) self::builddayvalues(); if(self::goodtogo('time')) self::buildtimevalues(); } public function date($fmt) { return preg_replace_callback("/%./", array('self', 'datesub'), $fmt); } private function datesub($values) { $value = array_shift($values); switch($value[1]) { // Not implemented yet - // z - day of the year from 0 - 364 // W - week of the year // t - last day of the month // L - whether it's leap year // B - swatch internet time // all TZ and Full Date/Time settings case 'd': $rep = $this->nday; break; case 'D': $rep = $this->adays[$this->ndayofweek - 1]; break; case 'j': $rep = sprintf("%d", $this->nday); break; case 'l': $rep = $this->ldays[$this->ndayofweek-1]; break; case 'N': $rep = $this->ndayofweek; break; case 'S': $rep = self::ordinal($this->nday); break; // This just changes Sunday to 0 case 'w': $rep = ($this->ndayofweek==7)?0:$this->ndayofweek; break; case 'F': $rep = $this->lmonths[$this->nmonth-1]; break; case 'm': $rep = $this->nmonth; break; case 'M': $rep = $this->amonths[$this->nmonth-1]; break; case 'n': $rep = sprintf("%d", $this->nmonth); break; case 'Y': $rep = $this->nyear; break; case 'y': $rep = self::padnum(substr($this->nyear, -1, 2), 2); break; /*** Time **/ case 'a': $rep = ($this->thour > 11)?'pm':'am'; break; case 'A': $rep = ($this->thour > 11)?'PM':'AM'; break; case 'g': $rep = ($this->thour > 12)?$this->thour - 12:$this->thour; break; case 'G': $rep = sprintf("%d", $this->thour); break; case 'h': $rep = self::padnum(($this->thour > 12)?$this->thour - 12:$this->thour, 2); break; case 'H': $rep = $this->thour; break; case 'i': $rep = $this->tmin; break; case 's': $rep = $this->tsec; break; case '%': $rep = "%"; break; default: self::error(sprintf("Unknown datesub %s!\n", $value)); break; } $repstr = sprintf("/%s/", $value); return preg_replace($repstr, $rep, $value); } protected function goodtogo($for) { if($for == "date" && $this->nmonth && $this->nday && $this->nyear) return 1; if($for == "time" && $this->thour && $this->tmin) return 1; return 0; } private function builddayvalues() { require_once 'doomsday.inc'; // The doomsday calculation in a PHP class $dd = new DoomsDay($this->nday, $this->nmonth, $this->nyear); $this->ndayofweek = $dd->dayofweek; if($this->ndayofweek !== null) { $this->ldayofweek = $this->ldays[$dd->dayofweek-1]; $this->adayofweek = $this->adays[$dd->dayofweek-1]; } $this->aday = sprintf("%d%s", $this->nday, self::ordinal($this->nday)); $this->lday = sprintf("%d%s", $this->nday, self::ordinal($this->nday)); } public function ordinal($date) { $last = substr($date, -1, 1); switch($last) { case 1: return "st"; case 2: return "nd"; case 3: return "rd"; default: return "th"; } } private function buildtimevalues() {} private function tokens() { $fulltokens = ' ,'; // Tokens that break major date components $fdate = false; $ftime = false; $tok = strtok($this->datestr, $fulltokens); while($tok !== false) { // Handle short dates, i.e. 10/21/44 or 21/10/1944 or 1994-10-21 if(preg_match("/^(\d+)[\/-](\d+)[\/-](\d+)$/", $tok, $f)) { // Try to guess if they put the year up front if(strlen($f[1]) == 4) $this->datefmt = "ymd"; self::parse_shortdate($f); } // Handle times as 14:40, 2:40:23, or 02:40:23.152422 elseif(preg_match("/^(\d+):(\d+)(?::(\d+)(?:\.(\d+))*)*$/", $tok, $f)) self::parse_time($f); // Handle meridiems elseif(strtolower($tok) == "pm" || strtolower($tok) == "am") self::parse_meridiem(strtolower($tok)); // Handle long and short days - trashed in favor of computation elseif(in_array($tok, array_merge($this->ldays, $this->adays))) $dayholder = $tok; // Trash it for now // Handle positive short digit years elseif(strlen($tok) == 4 && is_numeric($tok)) { $this->nyear = self::longyear($tok); $this->lyear = self::longyear($tok); $this->ayear = self::longyear($tok); } // Handle formal days elseif(preg_match("/^(\d+)(st|nd|rd|th)$/", $tok, $d)) { $this->nday = self::padnum($d[1], 2); $this->aday = $tok; } // Handle long month names elseif(in_array($tok, $this->lmonths)) self::parse_val($tok, "F"); // Handle short month names elseif(in_array($tok, $this->amonths)) self::parse_val($tok, "M"); // Default to error out else self::error(sprintf("tokens() can't handle %s in %s\n", $tok, $this->datestr)); // Get next token $tok = strtok($fulltokens); } } private function parse_time($time) { if(count($time) < 3 || count($time) > 5) self::error("Bad time passed to time()!\n"); if($time[1] > 24) self::error("Bad hour ${time[1]} passed to parse_time()!\n"); else $this->thour = self::padnum($time[1], 2); if($time[2] > 59) self::error("Bad min ${time[2]} passed to parse_time()!\n"); else $this->tmin = self::padnum($time[2], 2); if(count($time) > 3) if($time[3] > 60) self::error("Bad second ${time[3]} passed to parse_time()!\n"); else $this->tsec = self::padnum($time[3], 2); if(count($time) > 4) $this->tprec = self::padnum($time[4], 2); } private function parse_meridiem($m) { if(!$this->thour || !$this->tmin) self::error("parse_meridiem() called with no time set!\n"); if($this->thour === 0 || $this->thour > 12) error("Attempt to set meridiem on 24 hour clock!\n"); if($m == "pm") if($this->thour < 12) $this->thour += 12; else $this->thour = 0; // Handles converting pm thour into 24 hour } private function parse_shortdate($date) { if(strlen($this->datefmt) != 3) self::error("Invalid datefmt in parse_shortdate()!\n"); // This little bit of weirdness builds the variables $m, $d, $y dynamically for($i = 0; $i < 3; $i++) ${$this->datefmt[$i]} = $date[$i+1]; self::parse_val($m, "m"); self::parse_val($d, "d"); $this->nyear = self::longyear($y); $this->lyear = self::longyear($y); $this->ayear = self::longyear($y); } // Note we don't distinguish like date() between m/n and d/j // m and d are treated the same private function parse_val($val, $type) { switch($type) { case "m": $this->nmonth = self::padnum($val, 2); $this->lmonth = $this->lmonths[$val - 1]; $this->amonth = $this->amonths[$val - 1]; break; case "d": $this->nday = self::padnum($val, 2); break; case "F": for($i=0; $i<count($this->lmonths); $i++) if($val == $this->lmonths[$i]) break; $this->nmonth = self::padnum($i+1, 2); $this->lmonth = $this->lmonths[$i]; $this->amonth = $this->amonths[$i]; break; case "M": for($i=0; $i<count($this->amonths); $i++) if($val == $this->amonths[$i]) $ct = $i; $this->nmonth = self::padnum($i+1, 2); $this->lmonth = $this->lmonths[$i]; $this->amonth = $this->amonths[$i]; break; default: self::error("Unknown type ${type} passed to parse_val!\n"); break; } } private function longyear($year) { if($year > 999) return $year; if($year > 100) return sprintf("0%03d", $year); // If the year is greater than this year, predate it // Otherwise assume it's a future date if($year > date("y", time())) return sprintf("19%02d", $year); else return sprintf("20%02d", $year); } private function padnum($num, $size) { $fmt = sprintf("%%0%dd", $size); return sprintf($fmt, $num); } public function error($str) { print $str; exit; } } ?> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
<? /* * * Based on a PHP implementation by Andreas Tørå Hagli * http://www.stud.ntnu.no/~andrhag/software/doomsday.php * Now converted into a handy dandy PHP class. (With permission.) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Constructor: * $val = new DoomsDay(day, month, year); * $val->dayofweek; * * Can't get much simpler than that. * */ class DoomsDay { public $dayofweek = null; // Null just avoids Monday being a "0" day // Even though we all hate Mondays public $long_weekdays = array(null, "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"); public $short_weekdays = array(null, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"); private function leap_year_add ($year) { if ($year/4 == (int)($year/4) && ($year/100 != (int)($year/100) || $year/400 == (int)($year/400))) return 1; else return 0; } private function doomsday_in_month ($month, $year) { if ($month == 1 && !self::leap_year_add ($year)) return 31; if ($month == 1 && self::leap_year_add ($year)) return 25; elseif ($month == 2) return (28 + self::leap_year_add ($year)); elseif ($month == 3) return 7; elseif ($month/2 == (int)($month/2)) return $month; elseif ($month > 8) return ($month - 4); else return ($month + 4); } private function days_in_month ($month, $year) { switch ($month) { case 12: return 31; case 2: return 28+self::leap_year_add ($year); default: return 30; } } public function __construct($day, $month, $year) { if ($day != NULL || $month != NULL || $year != NULL) { if ($day == NULL || $month == NULL || $year == NULL) { print "DoomsDay error: Invalid date.\n"; exit; } elseif (abs ($year) > 10000) { print "DoomsDay error: Year must be before 10,000\n"; exit; } elseif ($month <= 0 || $month > 12 || $day <= 0 || $day > self::days_in_month ($month, $year)) { $year = (int)$year; /* Too "clean" number. */ printf ("DoomsDay error: Invalid date. %s\n", ($month == 2 && $day == 29) ? " $year is not a leap year." : ""); } else { /* Finding doomsday difference. */ $doomdiff = ($day - self::doomsday_in_month($month, $year))%7; if ($doomdiff >= 4) $doomdiff -= 7; elseif ($doomdiff <= -4) $doomdiff += 7; /* Finding centuryday. */ $similarcentury = $year - $year%100; while ($similarcentury < 2000) $similarcentury += 400; while ($similarcentury > 2000) $similarcentury -= 400; if ($similarcentury == 2000) $centuryday = 2; elseif ($similarcentury == 1900) $centuryday = 3; elseif ($similarcentury == 1800) $centuryday = 5; elseif ($similarcentury == 1700) $centuryday = 7; /* Finding dozens. */ $dozens = 0; while($year - $year%100 + ($dozens+1)*12 <= $year) $dozens++; /* Finding remainder. */ $remainder = $year%100 - ($dozens)*12; /* Finding 4s of remainder. */ $fourremainder = (int)($remainder/4); /* And now, the weekday. */ $weekday = ($doomdiff + $centuryday + $dozens + $remainder + $fourremainder)%7; if ($weekday <= 0) $weekday += 7; $this->dayofweek = $weekday; } } } } |