Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
Converter
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
10 / 10
23
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 convert
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 convertToDateTime
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
9
 getDateExceptionMessage
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
6
 convertToDisplayDate
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 convertFromDisplayDate
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 convertToDisplayTime
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 convertToDisplayDateAndTime
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 convertToDisplayTimeAndDate
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getTimeZone
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Date/time conversion functionality.
5 *
6 * PHP version 7
7 *
8 * Copyright (C) Villanova University 2011.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Date
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @author   Luke O'Sullivan <l.osullivan@swansea.ac.uk>
27 * @author   Ere Maijala <ere.maijala@helsinki.fi>
28 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
29 * @link     https://vufind.org/wiki/development Wiki
30 */
31
32namespace VuFind\Date;
33
34use DateTime;
35use DateTimeZone;
36
37use function in_array;
38use function intval;
39use function is_array;
40use function is_float;
41
42/**
43 * Date/time conversion functionality.
44 *
45 * @category VuFind
46 * @package  Date
47 * @author   Demian Katz <demian.katz@villanova.edu>
48 * @author   Luke O'Sullivan <l.osullivan@swansea.ac.uk>
49 * @author   Ere Maijala <ere.maijala@helsinki.fi>
50 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
51 * @link     https://vufind.org/wiki/development Wiki
52 */
53class Converter
54{
55    /**
56     * Format string for dates
57     *
58     * @var string
59     */
60    protected $displayDateFormat;
61
62    /**
63     * Format string for times
64     *
65     * @var string
66     */
67    protected $displayTimeFormat;
68
69    /**
70     * Time zone to use for conversions
71     *
72     * @var DateTimeZone
73     */
74    protected $timezone;
75
76    /**
77     * Constructor
78     *
79     * @param array $config Configuration to use (omit to use defaults)
80     */
81    public function __construct(array $config = [])
82    {
83        // Set Display Date Format
84        $this->displayDateFormat = $config['displayDateFormat'] ?? 'm-d-Y';
85
86        // Set Display Date Format
87        $this->displayTimeFormat = $config['displayTimeFormat'] ?? 'H:i';
88
89        // Set time zone
90        $zone = $config['timezone'] ?? 'America/New_York';
91        $this->timezone = new DateTimeZone($zone);
92    }
93
94    /**
95     * Generic method for conversion of a time / date string
96     *
97     * @param string $inputFormat  The format of the time string to be changed
98     * @param string $outputFormat The desired output format
99     * @param string $dateString   The date string
100     *
101     * @throws DateException
102     * @return string               A re-formatted time string
103     */
104    public function convert($inputFormat, $outputFormat, $dateString)
105    {
106        $date = $this->convertToDateTime($inputFormat, $dateString);
107        return $date->format($outputFormat);
108    }
109
110    /**
111     * Generic method for conversion of a time / date string to a DateTime
112     *
113     * @param string $inputFormat The format of the time string to be changed
114     * @param string $dateString  The date string
115     *
116     * @throws DateException
117     * @return DateTime           A DateTime object
118     */
119    public function convertToDateTime($inputFormat, $dateString)
120    {
121        // These are date formats that we definitely know how to handle, and some
122        // benefit from special processing. However, items not found in this list
123        // will still be attempted in a generic fashion before giving up.
124        $validFormats = [
125            'm-d-Y', 'm-d-y', 'm/d/Y', 'm/d/y', 'U', 'm-d-y H:i', 'Y-m-d',
126            'Y-m-d H:i',
127        ];
128        $isValid = in_array($inputFormat, $validFormats);
129        if ($isValid) {
130            if ($inputFormat == 'U') {
131                // Special case for Unix timestamps (including workaround for
132                // floating point numbers):
133                $dateString = '@'
134                    . (is_float($dateString) ? intval($dateString) : $dateString);
135            } else {
136                // Strip leading zeroes from date string and normalize date separator
137                // to slashes:
138                $regEx = '/0*([0-9]+)(-|\/)0*([0-9]+)(-|\/)0*([0-9]+)/';
139                $dateString = trim(preg_replace($regEx, '$1/$3/$5', $dateString));
140            }
141            $errors = [
142                'warning_count' => 0, 'error_count' => 0, 'errors' => [],
143            ];
144            try {
145                $date = new DateTime($dateString, $this->timezone);
146            } catch (\Exception $e) {
147                $errors['error_count']++;
148                $errors['errors'][] = $e->getMessage();
149            }
150        } else {
151            $date = DateTime::createFromFormat(
152                $inputFormat,
153                $dateString,
154                $this->timezone
155            );
156            $errors = DateTime::getLastErrors();
157        }
158
159        if (
160            ($errors === false
161            || ($errors['warning_count'] == 0 && $errors['error_count'] == 0))
162            && $date
163        ) {
164            $date->setTimeZone($this->timezone);
165            return $date;
166        }
167        throw new DateException($this->getDateExceptionMessage($errors));
168    }
169
170    /**
171     * Build an exception message from a detailed error array.
172     *
173     * @param array $details Error details
174     *
175     * @return string
176     */
177    protected function getDateExceptionMessage($details)
178    {
179        $errors = 'Date/time problem: Details: ';
180        if (is_array($details['errors']) && $details['error_count'] > 0) {
181            foreach ($details['errors'] as $error) {
182                $errors .= $error . ' ';
183            }
184        } elseif (is_array($details['warnings'])) {
185            foreach ($details['warnings'] as $warning) {
186                $errors .= $warning . ' ';
187            }
188        }
189        return $errors;
190    }
191
192    /**
193     * Convert a date string to admin-defined format.
194     *
195     * @param string $createFormat The format of the date string to be changed
196     * @param string $dateString   The date string
197     *
198     * @throws DateException
199     * @return string               A re-formatted date string
200     */
201    public function convertToDisplayDate($createFormat, $dateString)
202    {
203        return $this->convert($createFormat, $this->displayDateFormat, $dateString);
204    }
205
206    /**
207     * Public method for conversion of an admin defined date string
208     * to a driver required date string
209     *
210     * @param string $outputFormat The format of the required date string
211     * @param string $displayDate  The display formatted date string
212     *
213     * @throws DateException
214     * @return string               A re-formatted date string
215     */
216    public function convertFromDisplayDate($outputFormat, $displayDate)
217    {
218        return $this->convert(
219            $this->displayDateFormat,
220            $outputFormat,
221            $displayDate
222        );
223    }
224
225    /**
226     * Public support method for conversion of a time string to admin defined
227     * time string.
228     *
229     * @param string $createFormat The format of the time string to be changed
230     * @param string $timeString   The time string
231     *
232     * @throws DateException
233     * @return string               A re-formatted time string
234     */
235    public function convertToDisplayTime($createFormat, $timeString)
236    {
237        return $this->convert($createFormat, $this->displayTimeFormat, $timeString);
238    }
239
240    /**
241     * Public method for getting a date prepended to a time.
242     *
243     * @param string $createFormat The format of the time string to be changed
244     * @param string $timeString   The time string
245     * @param string $separator    String between time/date
246     *
247     * @throws DateException
248     * @return string               A re-formatted time string
249     */
250    public function convertToDisplayDateAndTime(
251        $createFormat,
252        $timeString,
253        $separator = ' '
254    ) {
255        return $this->convertToDisplayDate($createFormat, $timeString)
256            . $separator . $this->convertToDisplayTime($createFormat, $timeString);
257    }
258
259    /**
260     * Public method for getting a time prepended to a date.
261     *
262     * @param string $createFormat The format of the time string to be changed
263     * @param string $timeString   The time string
264     * @param string $separator    String between time/date
265     *
266     * @throws DateException
267     * @return string               A re-formatted time string
268     */
269    public function convertToDisplayTimeAndDate(
270        $createFormat,
271        $timeString,
272        $separator = ' '
273    ) {
274        return $this->convertToDisplayTime($createFormat, $timeString)
275            . $separator . $this->convertToDisplayDate($createFormat, $timeString);
276    }
277
278    /**
279     * Get the active time zone
280     *
281     * @return DateTimeZone
282     */
283    public function getTimeZone(): DateTimeZone
284    {
285        return $this->timezone;
286    }
287}