1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
19:
20: App::uses('Multibyte', 'I18n');
21:
22: 23: 24: 25: 26: 27: 28: 29:
30: class CakeTime {
31:
32: 33: 34: 35: 36: 37: 38: 39: 40:
41: public static $niceFormat = '%a, %b %eS %Y, %H:%M';
42:
43: 44: 45:
46: protected static $_time = null;
47:
48: 49: 50: 51: 52:
53: public function __set($name, $value) {
54: switch ($name) {
55: case 'niceFormat':
56: self::${$name} = $value;
57: break;
58: default:
59: break;
60: }
61: }
62:
63: 64: 65: 66: 67:
68: public function __get($name) {
69: switch ($name) {
70: case 'niceFormat':
71: return self::${$name};
72: break;
73: default:
74: return null;
75: break;
76: }
77: }
78:
79: 80: 81: 82: 83: 84: 85: 86: 87: 88:
89: public static function convertSpecifiers($format, $time = null) {
90: if (!$time) {
91: $time = time();
92: }
93: self::$_time = $time;
94: return preg_replace_callback('/\%(\w+)/', array('CakeTime', '_translateSpecifier'), $format);
95: }
96:
97: 98: 99: 100: 101: 102: 103:
104: protected static function _translateSpecifier($specifier) {
105: switch ($specifier[1]) {
106: case 'a':
107: $abday = __dc('cake', 'abday', 5);
108: if (is_array($abday)) {
109: return $abday[date('w', self::$_time)];
110: }
111: break;
112: case 'A':
113: $day = __dc('cake', 'day', 5);
114: if (is_array($day)) {
115: return $day[date('w', self::$_time)];
116: }
117: break;
118: case 'c':
119: $format = __dc('cake', 'd_t_fmt', 5);
120: if ($format != 'd_t_fmt') {
121: return self::convertSpecifiers($format, self::$_time);
122: }
123: break;
124: case 'C':
125: return sprintf("%02d", date('Y', self::$_time) / 100);
126: case 'D':
127: return '%m/%d/%y';
128: case 'e':
129: if (DS === '/') {
130: return '%e';
131: }
132: $day = date('j', self::$_time);
133: if ($day < 10) {
134: $day = ' ' . $day;
135: }
136: return $day;
137: case 'eS' :
138: return date('jS', self::$_time);
139: case 'b':
140: case 'h':
141: $months = __dc('cake', 'abmon', 5);
142: if (is_array($months)) {
143: return $months[date('n', self::$_time) - 1];
144: }
145: return '%b';
146: case 'B':
147: $months = __dc('cake', 'mon', 5);
148: if (is_array($months)) {
149: return $months[date('n', self::$_time) - 1];
150: }
151: break;
152: case 'n':
153: return "\n";
154: case 'p':
155: case 'P':
156: $default = array('am' => 0, 'pm' => 1);
157: $meridiem = $default[date('a', self::$_time)];
158: $format = __dc('cake', 'am_pm', 5);
159: if (is_array($format)) {
160: $meridiem = $format[$meridiem];
161: return ($specifier[1] == 'P') ? strtolower($meridiem) : strtoupper($meridiem);
162: }
163: break;
164: case 'r':
165: $complete = __dc('cake', 't_fmt_ampm', 5);
166: if ($complete != 't_fmt_ampm') {
167: return str_replace('%p', self::_translateSpecifier(array('%p', 'p')), $complete);
168: }
169: break;
170: case 'R':
171: return date('H:i', self::$_time);
172: case 't':
173: return "\t";
174: case 'T':
175: return '%H:%M:%S';
176: case 'u':
177: return ($weekDay = date('w', self::$_time)) ? $weekDay : 7;
178: case 'x':
179: $format = __dc('cake', 'd_fmt', 5);
180: if ($format != 'd_fmt') {
181: return self::convertSpecifiers($format, self::$_time);
182: }
183: break;
184: case 'X':
185: $format = __dc('cake', 't_fmt', 5);
186: if ($format != 't_fmt') {
187: return self::convertSpecifiers($format, self::$_time);
188: }
189: break;
190: }
191: return $specifier[0];
192: }
193:
194: 195: 196: 197: 198: 199: 200: 201:
202: public static function convert($serverTime, $userOffset) {
203: $serverOffset = self::serverOffset();
204: $gmtTime = $serverTime - $serverOffset;
205: $userTime = $gmtTime + $userOffset * (60 * 60);
206: return $userTime;
207: }
208:
209: 210: 211: 212: 213: 214:
215: public static function serverOffset() {
216: return date('Z', time());
217: }
218:
219: 220: 221: 222: 223: 224: 225: 226:
227: public static function fromString($dateString, $userOffset = null) {
228: if (empty($dateString)) {
229: return false;
230: }
231: if (is_integer($dateString) || is_numeric($dateString)) {
232: $date = intval($dateString);
233: } else {
234: $date = strtotime($dateString);
235: }
236: if ($userOffset !== null) {
237: return self::convert($date, $userOffset);
238: }
239: if ($date === -1) {
240: return false;
241: }
242: return $date;
243: }
244:
245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256:
257: public static function nice($dateString = null, $userOffset = null, $format = null) {
258: if ($dateString != null) {
259: $date = self::fromString($dateString, $userOffset);
260: } else {
261: $date = time();
262: }
263: if (!$format) {
264: $format = self::$niceFormat;
265: }
266: $format = self::convertSpecifiers($format, $date);
267: return self::_strftime($format, $date);
268: }
269:
270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282:
283: public static function niceShort($dateString = null, $userOffset = null) {
284: $date = $dateString ? self::fromString($dateString, $userOffset) : time();
285:
286: $y = self::isThisYear($date) ? '' : ' %Y';
287:
288: if (self::isToday($dateString, $userOffset)) {
289: $ret = __d('cake', 'Today, %s', self::_strftime("%H:%M", $date));
290: } elseif (self::wasYesterday($dateString, $userOffset)) {
291: $ret = __d('cake', 'Yesterday, %s', self::_strftime("%H:%M", $date));
292: } else {
293: $format = self::convertSpecifiers("%b %eS{$y}, %H:%M", $date);
294: $ret = self::_strftime($format, $date);
295: }
296:
297: return $ret;
298: }
299:
300: 301: 302: 303: 304: 305: 306: 307: 308: 309:
310: public static function daysAsSql($begin, $end, $fieldName, $userOffset = null) {
311: $begin = self::fromString($begin, $userOffset);
312: $end = self::fromString($end, $userOffset);
313: $begin = date('Y-m-d', $begin) . ' 00:00:00';
314: $end = date('Y-m-d', $end) . ' 23:59:59';
315:
316: return "($fieldName >= '$begin') AND ($fieldName <= '$end')";
317: }
318:
319: 320: 321: 322: 323: 324: 325: 326: 327: 328:
329: public static function dayAsSql($dateString, $fieldName, $userOffset = null) {
330: return self::daysAsSql($dateString, $dateString, $fieldName);
331: }
332:
333: 334: 335: 336: 337: 338: 339: 340:
341: public static function isToday($dateString, $userOffset = null) {
342: $date = self::fromString($dateString, $userOffset);
343: return date('Y-m-d', $date) == date('Y-m-d', time());
344: }
345:
346: 347: 348: 349: 350: 351: 352: 353:
354: public static function isThisWeek($dateString, $userOffset = null) {
355: $date = self::fromString($dateString, $userOffset);
356: return date('W o', $date) == date('W o', time());
357: }
358:
359: 360: 361: 362: 363: 364: 365:
366: public static function isThisMonth($dateString, $userOffset = null) {
367: $date = self::fromString($dateString);
368: return date('m Y', $date) == date('m Y', time());
369: }
370:
371: 372: 373: 374: 375: 376: 377: 378:
379: public static function isThisYear($dateString, $userOffset = null) {
380: $date = self::fromString($dateString, $userOffset);
381: return date('Y', $date) == date('Y', time());
382: }
383:
384: 385: 386: 387: 388: 389: 390: 391: 392:
393: public static function wasYesterday($dateString, $userOffset = null) {
394: $date = self::fromString($dateString, $userOffset);
395: return date('Y-m-d', $date) == date('Y-m-d', strtotime('yesterday'));
396: }
397:
398: 399: 400: 401: 402: 403: 404: 405:
406: public static function isTomorrow($dateString, $userOffset = null) {
407: $date = self::fromString($dateString, $userOffset);
408: return date('Y-m-d', $date) == date('Y-m-d', strtotime('tomorrow'));
409: }
410:
411: 412: 413: 414: 415: 416: 417: 418:
419: public static function toQuarter($dateString, $range = false) {
420: $time = self::fromString($dateString);
421: $date = ceil(date('m', $time) / 3);
422:
423: if ($range === true) {
424: $range = 'Y-m-d';
425: }
426:
427: if ($range !== false) {
428: $year = date('Y', $time);
429:
430: switch ($date) {
431: case 1:
432: $date = array($year . '-01-01', $year . '-03-31');
433: break;
434: case 2:
435: $date = array($year . '-04-01', $year . '-06-30');
436: break;
437: case 3:
438: $date = array($year . '-07-01', $year . '-09-30');
439: break;
440: case 4:
441: $date = array($year . '-10-01', $year . '-12-31');
442: break;
443: }
444: }
445: return $date;
446: }
447:
448: 449: 450: 451: 452: 453: 454: 455:
456: public static function toUnix($dateString, $userOffset = null) {
457: return self::fromString($dateString, $userOffset);
458: }
459:
460: 461: 462: 463: 464: 465: 466: 467:
468: public static function toAtom($dateString, $userOffset = null) {
469: $date = self::fromString($dateString, $userOffset);
470: return date('Y-m-d\TH:i:s\Z', $date);
471: }
472:
473: 474: 475: 476: 477: 478: 479: 480:
481: public static function toRSS($dateString, $userOffset = null) {
482: $date = self::fromString($dateString, $userOffset);
483:
484: if (!is_null($userOffset)) {
485: if ($userOffset == 0) {
486: $timezone = '+0000';
487: } else {
488: $hours = (int)floor(abs($userOffset));
489: $minutes = (int)(fmod(abs($userOffset), $hours) * 60);
490: $timezone = ($userOffset < 0 ? '-' : '+') . str_pad($hours, 2, '0', STR_PAD_LEFT) . str_pad($minutes, 2, '0', STR_PAD_LEFT);
491: }
492: return date('D, d M Y H:i:s', $date) . ' ' . $timezone;
493: }
494: return date("r", $date);
495: }
496:
497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521:
522: public static function timeAgoInWords($dateTime, $options = array()) {
523: $userOffset = null;
524: if (is_array($options) && isset($options['userOffset'])) {
525: $userOffset = $options['userOffset'];
526: }
527: $now = time();
528: if (!is_null($userOffset)) {
529: $now = self::convert(time(), $userOffset);
530: }
531: $inSeconds = self::fromString($dateTime, $userOffset);
532: $backwards = ($inSeconds > $now);
533:
534: $format = 'j/n/y';
535: $end = '+1 month';
536:
537: if (is_array($options)) {
538: if (isset($options['format'])) {
539: $format = $options['format'];
540: unset($options['format']);
541: }
542: if (isset($options['end'])) {
543: $end = $options['end'];
544: unset($options['end']);
545: }
546: } else {
547: $format = $options;
548: }
549:
550: if ($backwards) {
551: $futureTime = $inSeconds;
552: $pastTime = $now;
553: } else {
554: $futureTime = $now;
555: $pastTime = $inSeconds;
556: }
557: $diff = $futureTime - $pastTime;
558:
559:
560: if ($diff >= 604800) {
561: list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime));
562:
563: list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
564: $years = $months = $weeks = $days = $hours = $minutes = $seconds = 0;
565:
566: if ($future['Y'] == $past['Y'] && $future['m'] == $past['m']) {
567: $months = 0;
568: $years = 0;
569: } else {
570: if ($future['Y'] == $past['Y']) {
571: $months = $future['m'] - $past['m'];
572: } else {
573: $years = $future['Y'] - $past['Y'];
574: $months = $future['m'] + ((12 * $years) - $past['m']);
575:
576: if ($months >= 12) {
577: $years = floor($months / 12);
578: $months = $months - ($years * 12);
579: }
580:
581: if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] == 1) {
582: $years --;
583: }
584: }
585: }
586:
587: if ($future['d'] >= $past['d']) {
588: $days = $future['d'] - $past['d'];
589: } else {
590: $daysInPastMonth = date('t', $pastTime);
591: $daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
592:
593: if (!$backwards) {
594: $days = ($daysInPastMonth - $past['d']) + $future['d'];
595: } else {
596: $days = ($daysInFutureMonth - $past['d']) + $future['d'];
597: }
598:
599: if ($future['m'] != $past['m']) {
600: $months --;
601: }
602: }
603:
604: if ($months == 0 && $years >= 1 && $diff < ($years * 31536000)) {
605: $months = 11;
606: $years --;
607: }
608:
609: if ($months >= 12) {
610: $years = $years + 1;
611: $months = $months - 12;
612: }
613:
614: if ($days >= 7) {
615: $weeks = floor($days / 7);
616: $days = $days - ($weeks * 7);
617: }
618: } else {
619: $years = $months = $weeks = 0;
620: $days = floor($diff / 86400);
621:
622: $diff = $diff - ($days * 86400);
623:
624: $hours = floor($diff / 3600);
625: $diff = $diff - ($hours * 3600);
626:
627: $minutes = floor($diff / 60);
628: $diff = $diff - ($minutes * 60);
629: $seconds = $diff;
630: }
631: $relativeDate = '';
632: $diff = $futureTime - $pastTime;
633:
634: if ($diff > abs($now - self::fromString($end))) {
635: $relativeDate = __d('cake', 'on %s', date($format, $inSeconds));
636: } else {
637: if ($years > 0) {
638:
639: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d year', '%d years', $years, $years);
640: $relativeDate .= $months > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d month', '%d months', $months, $months) : '';
641: $relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks) : '';
642: $relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
643: } elseif (abs($months) > 0) {
644:
645: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d month', '%d months', $months, $months);
646: $relativeDate .= $weeks > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks) : '';
647: $relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
648: } elseif (abs($weeks) > 0) {
649:
650: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d week', '%d weeks', $weeks, $weeks);
651: $relativeDate .= $days > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days) : '';
652: } elseif (abs($days) > 0) {
653:
654: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d day', '%d days', $days, $days);
655: $relativeDate .= $hours > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d hour', '%d hours', $hours, $hours) : '';
656: } elseif (abs($hours) > 0) {
657:
658: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d hour', '%d hours', $hours, $hours);
659: $relativeDate .= $minutes > 0 ? ($relativeDate ? ', ' : '') . __dn('cake', '%d minute', '%d minutes', $minutes, $minutes) : '';
660: } elseif (abs($minutes) > 0) {
661:
662: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d minute', '%d minutes', $minutes, $minutes);
663: } else {
664:
665: $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '%d second', '%d seconds', $seconds, $seconds);
666: }
667:
668: if (!$backwards) {
669: $relativeDate = __d('cake', '%s ago', $relativeDate);
670: }
671: }
672: return $relativeDate;
673: }
674:
675: 676: 677: 678: 679: 680: 681: 682: 683: 684:
685: public static function wasWithinLast($timeInterval, $dateString, $userOffset = null) {
686: $tmp = str_replace(' ', '', $timeInterval);
687: if (is_numeric($tmp)) {
688: $timeInterval = $tmp . ' ' . __d('cake', 'days');
689: }
690:
691: $date = self::fromString($dateString, $userOffset);
692: $interval = self::fromString('-' . $timeInterval);
693:
694: if ($date >= $interval && $date <= time()) {
695: return true;
696: }
697:
698: return false;
699: }
700:
701: 702: 703: 704: 705: 706: 707:
708: public static function gmt($string = null) {
709: if ($string != null) {
710: $string = self::fromString($string);
711: } else {
712: $string = time();
713: }
714: $hour = intval(date("G", $string));
715: $minute = intval(date("i", $string));
716: $second = intval(date("s", $string));
717: $month = intval(date("n", $string));
718: $day = intval(date("j", $string));
719: $year = intval(date("Y", $string));
720:
721: return gmmktime($hour, $minute, $second, $month, $day, $year);
722: }
723:
724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735:
736: public static function format($format, $date = null, $invalid = false, $userOffset = null) {
737: $time = self::fromString($date, $userOffset);
738: $_time = self::fromString($format, $userOffset);
739:
740: if (is_numeric($_time) && $time === false) {
741: $format = $date;
742: return self::i18nFormat($_time, $format, $invalid, $userOffset);
743: }
744: if ($time === false && $invalid !== false) {
745: return $invalid;
746: }
747: return date($format, $time);
748: }
749:
750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760:
761: public static function i18nFormat($date, $format = null, $invalid = false, $userOffset = null) {
762: $date = self::fromString($date, $userOffset);
763: if ($date === false && $invalid !== false) {
764: return $invalid;
765: }
766: if (empty($format)) {
767: $format = '%x';
768: }
769: $format = self::convertSpecifiers($format, $date);
770: return self::_strftime($format, $date);
771: }
772:
773: 774: 775: 776: 777: 778: 779: 780: 781:
782: protected static function _strftime($format, $date) {
783: $format = strftime($format, $date);
784: $encoding = Configure::read('App.encoding');
785:
786: if (!empty($encoding) && $encoding === 'UTF-8') {
787: if (function_exists('mb_check_encoding')) {
788: $valid = mb_check_encoding($format, $encoding);
789: } else {
790: $valid = !Multibyte::checkMultibyte($format);
791: }
792: if (!$valid) {
793: $format = utf8_encode($format);
794: }
795: }
796: return $format;
797: }
798:
799: }
800: