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