1<?php
2/**
3 * Copyright 2004-2017 Horde LLC (http://www.horde.org/)
4 *
5 * See the enclosed file LICENSE for license information (LGPL). If you
6 * did not receive this file, see http://www.horde.org/licenses/lgpl21.
7 *
8 * @author   Chuck Hagenbuch <chuck@horde.org>
9 * @category Horde
10 * @license  http://www.horde.org/licenses/lgpl21 LGPL
11 * @package  Date
12 */
13
14/**
15 * Horde Date wrapper/logic class, including some calculation
16 * functions.
17 *
18 * @author    Chuck Hagenbuch <chuck@horde.org>
19 * @category  Horde
20 * @copyright 2004-2017 Horde LLC
21 * @license   http://www.horde.org/licenses/lgpl21 LGPL
22 * @package   Date
23 */
24class Horde_Date_Utils
25{
26    /**
27     * Returns whether a year is a leap year.
28     *
29     * @param integer $year  The year.
30     *
31     * @return boolean  True if the year is a leap year.
32     */
33    public static function isLeapYear($year)
34    {
35        return ($year % 4 == 0 && $year % 100 != 0) || $year % 400 == 0;
36    }
37
38    /**
39     * Returns the date of the year that corresponds to the first day of the
40     * given week.
41     *
42     * @param integer $week  The week of the year to find the first day of.
43     * @param integer $year  The year to calculate for.
44     *
45     * @return Horde_Date  The date of the first day of the given week.
46     */
47    public static function firstDayOfWeek($week, $year)
48    {
49        return new Horde_Date(sprintf('%04dW%02d', $year, $week));
50    }
51
52    /**
53     * Returns the number of days in the specified month.
54     *
55     * @param integer $month  The month
56     * @param integer $year   The year.
57     *
58     * @return integer  The number of days in the month.
59     */
60    public static function daysInMonth($month, $year)
61    {
62        static $cache = array();
63        if (!isset($cache[$year][$month])) {
64            try {
65                $date = new DateTime(sprintf($year < 0 ? '%05d-%02d-01' : '%04d-%02d-01', $year, $month));
66            } catch (Exception $e) {
67                throw new Horde_Date_Exception($e);
68            }
69            $cache[$year][$month] = $date->format('t');
70        }
71        return $cache[$year][$month];
72    }
73
74    /**
75     * Returns a relative, natural language representation of a timestamp
76     *
77     * @todo Wider range of values ... maybe future time as well?
78     * @todo Support minimum resolution parameter.
79     *
80     * @param mixed $time          The time. Any format accepted by Horde_Date.
81     * @param string $date_format  Format to display date if timestamp is
82     *                             more then 1 day old.
83     * @param string $time_format  Format to display time if timestamp is 1
84     *                             day old.
85     *
86     * @return string  The relative time (i.e. 2 minutes ago)
87     */
88    public static function relativeDateTime($time, $date_format = '%x',
89                                            $time_format = '%X')
90    {
91        $date = new Horde_Date($time);
92
93        $delta = time() - $date->timestamp();
94        if ($delta < 60) {
95            return sprintf(Horde_Date_Translation::ngettext("%d second ago", "%d seconds ago", $delta), $delta);
96        }
97
98        $delta = round($delta / 60);
99        if ($delta < 60) {
100            return sprintf(Horde_Date_Translation::ngettext("%d minute ago", "%d minutes ago", $delta), $delta);
101        }
102
103        $delta = round($delta / 60);
104        if ($delta < 24) {
105            return sprintf(Horde_Date_Translation::ngettext("%d hour ago", "%d hours ago", $delta), $delta);
106        }
107
108        if ($delta > 24 && $delta < 48) {
109            $date = new Horde_Date($time);
110            return sprintf(Horde_Date_Translation::t("yesterday at %s"), $date->strftime($time_format));
111        }
112
113        $delta = round($delta / 24);
114        if ($delta < 7) {
115            return sprintf(Horde_Date_Translation::t("%d days ago"), $delta);
116        }
117
118        if (round($delta / 7) < 5) {
119            $delta = round($delta / 7);
120            return sprintf(Horde_Date_Translation::ngettext("%d week ago", "%d weeks ago", $delta), $delta);
121        }
122
123        // Default to the user specified date format.
124        return $date->strftime($date_format);
125    }
126
127    /**
128     * Tries to convert strftime() formatters to date() formatters.
129     *
130     * Unsupported formatters will be removed.
131     *
132     * @param string $format  A strftime() formatting string.
133     *
134     * @return string  A date() formatting string.
135     */
136    public static function strftime2date($format)
137    {
138        $replace = array(
139            '/%a/'  => 'D',
140            '/%A/'  => 'l',
141            '/%d/'  => 'd',
142            '/%e/'  => 'j',
143            '/%j/'  => 'z',
144            '/%u/'  => 'N',
145            '/%w/'  => 'w',
146            '/%U/'  => '',
147            '/%V/'  => 'W',
148            '/%W/'  => '',
149            '/%b/'  => 'M',
150            '/%B/'  => 'F',
151            '/%h/'  => 'M',
152            '/%m/'  => 'm',
153            '/%C/'  => '',
154            '/%g/'  => '',
155            '/%G/'  => 'o',
156            '/%y/'  => 'y',
157            '/%Y/'  => 'Y',
158            '/%H/'  => 'H',
159            '/%I/'  => 'h',
160            '/%i/'  => 'g',
161            '/%M/'  => 'i',
162            '/%p/'  => 'A',
163            '/%P/'  => 'a',
164            '/%r/'  => 'h:i:s A',
165            '/%R/'  => 'H:i',
166            '/%S/'  => 's',
167            '/%T/'  => 'H:i:s',
168            '/%X/e' => 'Horde_Date_Utils::strftime2date(Horde_Nls::getLangInfo(T_FMT))',
169            '/%z/'  => 'O',
170            '/%Z/'  => '',
171            '/%c/'  => '',
172            '/%D/'  => 'm/d/y',
173            '/%F/'  => 'Y-m-d',
174            '/%s/'  => 'U',
175            '/%x/e' => 'Horde_Date_Utils::strftime2date(Horde_Nls::getLangInfo(D_FMT))',
176            '/%n/'  => "\n",
177            '/%t/'  => "\t",
178            '/%%/'  => '%'
179        );
180
181        return preg_replace(array_keys($replace), array_values($replace), $format);
182    }
183
184}
185