Completed
Push — 3 ( bc9e38...39c73e )
by Robbie
05:15 queued 10s
created

Zend_Locale_Format   F

Complexity

Total Complexity 277

Size/Duplication

Total Lines 1279
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 1279
rs 0.8
c 0
b 0
f 0
wmc 277
lcom 1
cbo 4

25 Methods

Rating   Name   Duplication   Size   Complexity  
A setOptions() 0 5 1
F _checkOptions() 0 100 35
B convertNumerals() 0 31 6
B getNumber() 0 43 11
F toNumber() 0 190 37
A _seperateFormat() 0 17 4
B isNumber() 0 25 6
F _getRegexForType() 0 94 27
A getFloat() 0 4 1
A toFloat() 0 5 1
A isFloat() 0 4 1
A getInteger() 0 5 1
A toInteger() 0 6 1
A isInteger() 0 12 3
A convertPhpToIsoFormat() 0 22 4
F _parseDate() 0 318 95
A _replaceMonth() 0 18 5
A getDateFormat() 0 10 2
A getDate() 0 10 2
D checkDateFormat() 0 48 24
A getTimeFormat() 0 9 2
A getTime() 0 9 2
A getDateTimeFormat() 0 9 2
A getDateTime() 0 9 2
A _getUniCodeSupport() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like Zend_Locale_Format often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Zend_Locale_Format, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Zend Framework
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://framework.zend.com/license/new-bsd
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to [email protected] so we can send you a copy immediately.
14
 *
15
 * @category   Zend
16
 * @package    Zend_Locale
17
 * @subpackage Format
18
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
19
 * @version    $Id$
20
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
21
 */
22
23
/**
24
 * include needed classes
25
 */
26
require_once 'Zend/Locale/Data.php';
27
28
/**
29
 * @category   Zend
30
 * @package    Zend_Locale
31
 * @subpackage Format
32
 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
33
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
34
 */
35
class Zend_Locale_Format
36
{
37
    const STANDARD   = 'auto';
38
39
    private static $_options = array('date_format'   => null,
40
                                     'number_format' => null,
41
                                     'format_type'   => 'iso',
42
                                     'fix_date'      => false,
43
                                     'locale'        => null,
44
                                     'cache'         => null,
45
                                     'disableCache'  => false,
46
                                     'precision'     => null);
47
48
    /**
49
     * Sets class wide options, if no option was given, the actual set options will be returned
50
     * The 'precision' option of a value is used to truncate or stretch extra digits. -1 means not to touch the extra digits.
51
     * The 'locale' option helps when parsing numbers and dates using separators and month names.
52
     * The date format 'format_type' option selects between CLDR/ISO date format specifier tokens and PHP's date() tokens.
53
     * The 'fix_date' option enables or disables heuristics that attempt to correct invalid dates.
54
     * The 'number_format' option can be used to specify a default number format string
55
     * The 'date_format' option can be used to specify a default date format string, but beware of using getDate(),
56
     * checkDateFormat() and getTime() after using setOptions() with a 'format'.  To use these four methods
57
     * with the default date format for a locale, use array('date_format' => null, 'locale' => $locale) for their options.
58
     *
59
     * @param  array  $options  Array of options, keyed by option name: format_type = 'iso' | 'php', fix_date = true | false,
60
     *                          locale = Zend_Locale | locale string, precision = whole number between -1 and 30
61
     * @throws Zend_Locale_Exception
62
     * @return Options array if no option was given
63
     */
64
    public static function setOptions(array $options = array())
65
    {
66
        self::$_options = self::_checkOptions($options) + self::$_options;
67
        return self::$_options;
68
    }
69
70
    /**
71
     * Internal function for checking the options array of proper input values
72
     * See {@link setOptions()} for details.
73
     *
74
     * @param  array  $options  Array of options, keyed by option name: format_type = 'iso' | 'php', fix_date = true | false,
75
     *                          locale = Zend_Locale | locale string, precision = whole number between -1 and 30
76
     * @throws Zend_Locale_Exception
77
     * @return Options array if no option was given
78
     */
79
    private static function _checkOptions(array $options = array())
80
    {
81
        if (count($options) == 0) {
82
            return self::$_options;
83
        }
84
        foreach ($options as $name => $value) {
85
            $name  = strtolower($name);
86
            if ($name !== 'locale') {
87
                if (gettype($value) === 'string') {
88
                    $value = strtolower($value);
89
                }
90
            }
91
92
            switch($name) {
93
                case 'number_format' :
94
                    if ($value == Zend_Locale_Format::STANDARD) {
95
                        $locale = self::$_options['locale'];
96
                        if (isset($options['locale'])) {
97
                            $locale = $options['locale'];
98
                        }
99
                        $options['number_format'] = Zend_Locale_Data::getContent($locale, 'decimalnumber');
100
                    } else if ((gettype($value) !== 'string') and ($value !== NULL)) {
101
                        require_once 'Zend/Locale/Exception.php';
102
                        $stringValue = (string)(is_array($value) ? implode(' ', $value) : $value);
103
                        throw new Zend_Locale_Exception("Unknown number format type '" . gettype($value) . "'. "
104
                            . "Format '$stringValue' must be a valid number format string.");
105
                    }
106
                    break;
107
108
                case 'date_format' :
109
                    if ($value == Zend_Locale_Format::STANDARD) {
110
                        $locale = self::$_options['locale'];
111
                        if (isset($options['locale'])) {
112
                            $locale = $options['locale'];
113
                        }
114
                        $options['date_format'] = Zend_Locale_Format::getDateFormat($locale);
115
                    } else if ((gettype($value) !== 'string') and ($value !== NULL)) {
116
                        require_once 'Zend/Locale/Exception.php';
117
                        $stringValue = (string)(is_array($value) ? implode(' ', $value) : $value);
118
                        throw new Zend_Locale_Exception("Unknown dateformat type '" . gettype($value) . "'. "
119
                            . "Format '$stringValue' must be a valid ISO or PHP date format string.");
120
                    } else {
121
                        if (((isset($options['format_type']) === true) and ($options['format_type'] == 'php')) or
122
                            ((isset($options['format_type']) === false) and (self::$_options['format_type'] == 'php'))) {
123
                            $options['date_format'] = Zend_Locale_Format::convertPhpToIsoFormat($value);
124
                        }
125
                    }
126
                    break;
127
128
                case 'format_type' :
129
                    if (($value != 'php') && ($value != 'iso')) {
130
                        require_once 'Zend/Locale/Exception.php';
131
                        throw new Zend_Locale_Exception("Unknown date format type '$value'. Only 'iso' and 'php'"
132
                           . " are supported.");
133
                    }
134
                    break;
135
136
                case 'fix_date' :
137
                    if (($value !== true) && ($value !== false)) {
138
                        require_once 'Zend/Locale/Exception.php';
139
                        throw new Zend_Locale_Exception("Enabling correction of dates must be either true or false"
140
                            . "(fix_date='$value').");
141
                    }
142
                    break;
143
144
                case 'locale' :
145
                    $options['locale'] = Zend_Locale::findLocale($value);
146
                    break;
147
148
                case 'cache' :
149
                    if ($value instanceof Zend_Cache_Core) {
150
                        Zend_Locale_Data::setCache($value);
151
                    }
152
                    break;
153
154
                case 'disablecache' :
155
                    Zend_Locale_Data::disableCache($value);
156
                    break;
157
158
                case 'precision' :
159
                    if ($value === NULL) {
160
                        $value = -1;
161
                    }
162
163
                    if (($value < -1) || ($value > 30)) {
164
                        require_once 'Zend/Locale/Exception.php';
165
                        throw new Zend_Locale_Exception("'$value' precision is not a whole number less than 30.");
166
                    }
167
                    break;
168
169
                default:
170
                    require_once 'Zend/Locale/Exception.php';
171
                    throw new Zend_Locale_Exception("Unknown option: '$name' = '$value'");
172
                    break;
173
174
            }
175
        }
176
177
        return $options;
178
    }
179
180
    /**
181
     * Changes the numbers/digits within a given string from one script to another
182
     * 'Decimal' representated the stardard numbers 0-9, if a script does not exist
183
     * an exception will be thrown.
184
     *
185
     * Examples for conversion from Arabic to Latin numerals:
186
     *   convertNumerals('١١٠ Tests', 'Arab'); -> returns '100 Tests'
187
     * Example for conversion from Latin to Arabic numerals:
188
     *   convertNumerals('100 Tests', 'Latn', 'Arab'); -> returns '١١٠ Tests'
189
     *
190
     * @param  string  $input  String to convert
191
     * @param  string  $from   Script to parse, see {@link Zend_Locale::getScriptList()} for details.
192
     * @param  string  $to     OPTIONAL Script to convert to
193
     * @return string  Returns the converted input
194
     * @throws Zend_Locale_Exception
195
     */
196
    public static function convertNumerals($input, $from, $to = null)
197
    {
198
        if (!self::_getUniCodeSupport()) {
199
            trigger_error("Sorry, your PCRE extension does not support UTF8 which is needed for the I18N core", E_USER_NOTICE);
200
        }
201
202
        $from   = strtolower($from);
203
        $source = Zend_Locale_Data::getContent('en', 'numberingsystem', $from);
204
        if (empty($source)) {
205
            require_once 'Zend/Locale/Exception.php';
206
            throw new Zend_Locale_Exception("Unknown script '$from'. Use 'Latn' for digits 0,1,2,3,4,5,6,7,8,9.");
207
        }
208
209
        if ($to !== null) {
210
            $to     = strtolower($to);
211
            $target = Zend_Locale_Data::getContent('en', 'numberingsystem', $to);
212
            if (empty($target)) {
213
                require_once 'Zend/Locale/Exception.php';
214
                throw new Zend_Locale_Exception("Unknown script '$to'. Use 'Latn' for digits 0,1,2,3,4,5,6,7,8,9.");
215
            }
216
        } else {
217
            $target = '0123456789';
218
        }
219
220
        for ($x = 0; $x < 10; ++$x) {
221
            $asource[$x] = "/" . iconv_substr($source, $x, 1, 'UTF-8') . "/u";
222
            $atarget[$x] = iconv_substr($target, $x, 1, 'UTF-8');
223
        }
224
225
        return preg_replace($asource, $atarget, $input);
226
    }
227
228
    /**
229
     * Returns the normalized number from a localized one
230
     * Parsing depends on given locale (grouping and decimal)
231
     *
232
     * Examples for input:
233
     * '2345.4356,1234' = 23455456.1234
234
     * '+23,3452.123' = 233452.123
235
     * '12343 ' = 12343
236
     * '-9456' = -9456
237
     * '0' = 0
238
     *
239
     * @param  string $input    Input string to parse for numbers
240
     * @param  array  $options  Options: locale, precision. See {@link setOptions()} for details.
241
     * @return string Returns the extracted number
242
     * @throws Zend_Locale_Exception
243
     */
244
    public static function getNumber($input, array $options = array())
245
    {
246
        $options = self::_checkOptions($options) + self::$_options;
247
        if (!is_string($input)) {
248
            return $input;
249
        }
250
251
        if (!self::isNumber($input, $options)) {
252
            require_once 'Zend/Locale/Exception.php';
253
            throw new Zend_Locale_Exception('No localized value in ' . $input . ' found, or the given number does not match the localized format');
254
        }
255
256
        // Get correct signs for this locale
257
        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
258
        // Change locale input to be default number
259
        if ((strpos($input, $symbols['minus']) !== false) ||
260
            (strpos($input, '-') !== false)) {
261
            $input = strtr($input, array($symbols['minus'] => '', '-' => ''));
262
            $input = '-' . $input;
263
        }
264
265
        $input = str_replace($symbols['group'],'', $input);
266
        if (strpos($input, $symbols['decimal']) !== false) {
267
            if ($symbols['decimal'] != '.') {
268
                $input = str_replace($symbols['decimal'], ".", $input);
269
            }
270
271
            $pre = substr($input, strpos($input, '.') + 1);
272
            if ($options['precision'] === null) {
273
                $options['precision'] = strlen($pre);
274
            }
275
276
            if (strlen($pre) >= $options['precision']) {
277
                $input = substr($input, 0, strlen($input) - strlen($pre) + $options['precision']);
278
            }
279
280
            if (($options['precision'] == 0) && ($input[strlen($input) - 1] == '.')) {
281
                $input = substr($input, 0, -1);
282
            }
283
        }
284
285
        return $input;
286
    }
287
288
    /**
289
     * Returns a locale formatted number depending on the given options.
290
     * The seperation and fraction sign is used from the set locale.
291
     * ##0.#  -> 12345.12345 -> 12345.12345
292
     * ##0.00 -> 12345.12345 -> 12345.12
293
     * ##,##0.00 -> 12345.12345 -> 12,345.12
294
     *
295
     * @param   string  $input    Localized number string
296
     * @param   array   $options  Options: number_format, locale, precision. See {@link setOptions()} for details.
297
     * @return  string  locale formatted number
298
     * @throws Zend_Locale_Exception
299
     */
300
    public static function toNumber($value, array $options = array())
301
    {
302
        // load class within method for speed
303
        require_once 'Zend/Locale/Math.php';
304
305
        $value             = Zend_Locale_Math::floatalize($value);
306
        $value             = Zend_Locale_Math::normalize($value);
307
        $options           = self::_checkOptions($options) + self::$_options;
308
        $options['locale'] = (string) $options['locale'];
309
310
        // Get correct signs for this locale
311
        $symbols = Zend_Locale_Data::getList($options['locale'], 'symbols');
312
        if (version_compare(PHP_VERSION, '5.6', '<')) {
313
            $oenc = iconv_get_encoding('internal_encoding');
314
            iconv_set_encoding('internal_encoding', 'UTF-8');
315
        } else {
316
            $oenc = ini_get('php.internal_encoding');
317
            ini_set('php.internal_encoding', 'UTF-8');
318
        }
319
320
        // Get format
321
        $format = $options['number_format'];
322
        if ($format === null) {
323
            $format  = Zend_Locale_Data::getContent($options['locale'], 'decimalnumber');
324
            $format  = self::_seperateFormat($format, $value, $options['precision']);
325
326
            if ($options['precision'] !== null) {
327
                $value   = Zend_Locale_Math::normalize(Zend_Locale_Math::round($value, $options['precision']));
328
            }
329
        } else {
330
            // seperate negative format pattern when available
331
            $format  = self::_seperateFormat($format, $value, $options['precision']);
332
            if (strpos($format, '.')) {
333
                if (is_numeric($options['precision'])) {
334
                    $value = Zend_Locale_Math::round($value, $options['precision']);
335
                } else {
336
                    if (substr($format, iconv_strpos($format, '.') + 1, 3) == '###') {
337
                        $options['precision'] = null;
338
                    } else {
339
                        $options['precision'] = iconv_strlen(iconv_substr($format, iconv_strpos($format, '.') + 1,
340
                                                             iconv_strrpos($format, '0') - iconv_strpos($format, '.')));
341
                        $format = iconv_substr($format, 0, iconv_strpos($format, '.') + 1) . '###'
342
                                . iconv_substr($format, iconv_strrpos($format, '0') + 1);
343
                    }
344
                }
345
            } else {
346
                $value = Zend_Locale_Math::round($value, 0);
347
                $options['precision'] = 0;
348
            }
349
            $value = Zend_Locale_Math::normalize($value);
350
        }
351
352
        if (iconv_strpos($format, '0') === false) {
353
            if (version_compare(PHP_VERSION, '5.6', '<')) {
354
                iconv_set_encoding('internal_encoding', $oenc);
355
            } else {
356
                ini_set('php.internal_encoding', $oenc);
357
            }
358
            require_once 'Zend/Locale/Exception.php';
359
            throw new Zend_Locale_Exception('Wrong format... missing 0');
360
        }
361
362
        // get number parts
363
        $pos = iconv_strpos($value, '.');
364
        if ($pos !== false) {
365
            if ($options['precision'] === null) {
366
                $precstr = iconv_substr($value, $pos + 1);
367
            } else {
368
                $precstr = iconv_substr($value, $pos + 1, $options['precision']);
369
                if (iconv_strlen($precstr) < $options['precision']) {
370
                    $precstr = $precstr . str_pad("0", ($options['precision'] - iconv_strlen($precstr)), "0");
371
                }
372
            }
373
        } else {
374
            if ($options['precision'] > 0) {
375
                $precstr = str_pad("0", ($options['precision']), "0");
376
            }
377
        }
378
379
        if ($options['precision'] === null) {
380
            if (isset($precstr)) {
381
                $options['precision'] = iconv_strlen($precstr);
382
            } else {
383
                $options['precision'] = 0;
384
            }
385
        }
386
387
        // get fraction and format lengths
388
        if (strpos($value, '.') !== false) {
389
            $number = substr((string) $value, 0, strpos($value, '.'));
390
        } else {
391
            $number = $value;
392
        }
393
394
        $prec = call_user_func(Zend_Locale_Math::$sub, $value, $number, $options['precision']);
395
        $prec = Zend_Locale_Math::floatalize($prec);
396
        $prec = Zend_Locale_Math::normalize($prec);
397
        if (iconv_strpos($prec, '-') !== false) {
398
            $prec = iconv_substr($prec, 1);
399
        }
400
401
        if (($prec == 0) and ($options['precision'] > 0)) {
402
            $prec = "0.0";
403
        }
404
405
        if (($options['precision'] + 2) > iconv_strlen($prec)) {
406
            $prec = str_pad((string) $prec, $options['precision'] + 2, "0", STR_PAD_RIGHT);
407
        }
408
409
        if (iconv_strpos($number, '-') !== false) {
410
            $number = iconv_substr($number, 1);
411
        }
412
        $group  = iconv_strrpos($format, ',');
413
        $group2 = iconv_strpos ($format, ',');
414
        $point  = iconv_strpos ($format, '0');
415
        // Add fraction
416
        $rest = "";
417
        if (iconv_strpos($format, '.')) {
418
            $rest   = iconv_substr($format, iconv_strpos($format, '.') + 1);
419
            $length = iconv_strlen($rest);
420
            for($x = 0; $x < $length; ++$x) {
421
                if (($rest[0] == '0') || ($rest[0] == '#')) {
422
                    $rest = iconv_substr($rest, 1);
423
                }
424
            }
425
            $format = iconv_substr($format, 0, iconv_strlen($format) - iconv_strlen($rest));
426
        }
427
428
        if ($options['precision'] == '0') {
429
            if (iconv_strrpos($format, '-') != 0) {
430
                $format = iconv_substr($format, 0, $point)
431
                        . iconv_substr($format, iconv_strrpos($format, '#') + 2);
432
            } else {
433
                $format = iconv_substr($format, 0, $point);
434
            }
435
        } else {
436
            $format = iconv_substr($format, 0, $point) . $symbols['decimal']
437
                               . iconv_substr($prec, 2);
438
        }
439
440
        $format .= $rest;
441
        // Add seperation
442
        if ($group == 0) {
443
            // no seperation
444
            $format = $number . iconv_substr($format, $point);
445
        } else if ($group == $group2) {
446
            // only 1 seperation
447
            $seperation = ($point - $group);
448
            for ($x = iconv_strlen($number); $x > $seperation; $x -= $seperation) {
449
                if (iconv_substr($number, 0, $x - $seperation) !== "") {
450
                    $number = iconv_substr($number, 0, $x - $seperation) . $symbols['group']
451
                            . iconv_substr($number, $x - $seperation);
452
                }
453
            }
454
            $format = iconv_substr($format, 0, iconv_strpos($format, '#')) . $number . iconv_substr($format, $point);
455
        } else {
456
457
            // 2 seperations
458
            if (iconv_strlen($number) > ($point - $group)) {
459
                $seperation = ($point - $group);
460
                $number = iconv_substr($number, 0, iconv_strlen($number) - $seperation) . $symbols['group']
461
                        . iconv_substr($number, iconv_strlen($number) - $seperation);
462
463
                if ((iconv_strlen($number) - 1) > ($point - $group + 1)) {
464
                    $seperation2 = ($group - $group2 - 1);
465
                    for ($x = iconv_strlen($number) - $seperation2 - 2; $x > $seperation2; $x -= $seperation2) {
466
                        $number = iconv_substr($number, 0, $x - $seperation2) . $symbols['group']
467
                                . iconv_substr($number, $x - $seperation2);
468
                    }
469
                }
470
471
            }
472
            $format = iconv_substr($format, 0, iconv_strpos($format, '#')) . $number . iconv_substr($format, $point);
473
        }
474
        // set negative sign
475
        if (call_user_func(Zend_Locale_Math::$comp, $value, 0, $options['precision']) < 0) {
476
            if (iconv_strpos($format, '-') === false) {
477
                $format = $symbols['minus'] . $format;
478
            } else {
479
                $format = str_replace('-', $symbols['minus'], $format);
480
            }
481
        }
482
483
        if (version_compare(PHP_VERSION, '5.6', '<')) {
484
            iconv_set_encoding('internal_encoding', $oenc);
485
        } else {
486
            ini_set('php.internal_encoding', $oenc);
487
        }
488
        return (string) $format;
489
    }
490
491
    private static function _seperateFormat($format, $value, $precision)
492
    {
493
        if (iconv_strpos($format, ';') !== false) {
494
            if (call_user_func(Zend_Locale_Math::$comp, $value, 0, $precision) < 0) {
495
                $tmpformat = iconv_substr($format, iconv_strpos($format, ';') + 1);
496
                if ($tmpformat[0] == '(') {
497
                    $format = iconv_substr($format, 0, iconv_strpos($format, ';'));
498
                } else {
499
                    $format = $tmpformat;
500
                }
501
            } else {
502
                $format = iconv_substr($format, 0, iconv_strpos($format, ';'));
503
            }
504
        }
505
506
        return $format;
507
    }
508
509
510
    /**
511
     * Checks if the input contains a normalized or localized number
512
     *
513
     * @param   string  $input    Localized number string
514
     * @param   array   $options  Options: locale. See {@link setOptions()} for details.
515
     * @return  boolean           Returns true if a number was found
516
     */
517
    public static function isNumber($input, array $options = array())
518
    {
519
        if (!self::_getUniCodeSupport()) {
520
            trigger_error("Sorry, your PCRE extension does not support UTF8 which is needed for the I18N core", E_USER_NOTICE);
521
        }
522
523
        $options = self::_checkOptions($options) + self::$_options;
524
525
        // Get correct signs for this locale
526
        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
527
528
        $regexs = Zend_Locale_Format::_getRegexForType('decimalnumber', $options);
529
        $regexs = array_merge($regexs, Zend_Locale_Format::_getRegexForType('scientificnumber', $options));
530
        if (!empty($input) && ($input[0] == $symbols['decimal'])) {
531
            $input = 0 . $input;
532
        }
533
        foreach ($regexs as $regex) {
534
            preg_match($regex, $input, $found);
535
            if (isset($found[0])) {
536
                return true;
537
            }
538
        }
539
540
        return false;
541
    }
542
543
    /**
544
     * Internal method to convert cldr number syntax into regex
545
     *
546
     * @param  string $type
547
     * @return string
548
     */
549
    private static function _getRegexForType($type, $options)
550
    {
551
        $decimal  = Zend_Locale_Data::getContent($options['locale'], $type);
552
        $decimal  = preg_replace('/[^#0,;\.\-Ee]/u', '',$decimal);
553
        $patterns = explode(';', $decimal);
554
555
        if (count($patterns) == 1) {
556
            $patterns[1] = '-' . $patterns[0];
557
        }
558
559
        $symbols = Zend_Locale_Data::getList($options['locale'],'symbols');
560
561
        foreach($patterns as $pkey => $pattern) {
562
            $regex[$pkey]  = '/^';
563
            $rest   = 0;
564
            $end    = null;
565
            if (strpos($pattern, '.') !== false) {
566
                $end     = substr($pattern, strpos($pattern, '.') + 1);
567
                $pattern = substr($pattern, 0, -strlen($end) - 1);
568
            }
569
570
            if (strpos($pattern, ',') !== false) {
571
                $parts = explode(',', $pattern);
572
                $count = count($parts);
573
                foreach($parts as $key => $part) {
574
                    switch ($part) {
575
                        case '#':
576
                        case '-#':
577
                            if ($part[0] == '-') {
578
                                $regex[$pkey] .= '[' . $symbols['minus'] . '-]{0,1}';
579
                            } else {
580
                                $regex[$pkey] .= '[' . $symbols['plus'] . '+]{0,1}';
581
                            }
582
583
                            if (($parts[$key + 1]) == '##0')  {
584
                                $regex[$pkey] .= '[0-9]{1,3}';
585
                            } else if (($parts[$key + 1]) == '##') {
586
                                $regex[$pkey] .= '[0-9]{1,2}';
587
                            } else {
588
                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 1):"' . $pattern . '"');
589
                            }
590
                            break;
591
                        case '##':
592
                            if ($parts[$key + 1] == '##0') {
593
                                $regex[$pkey] .=  '(\\' . $symbols['group'] . '{0,1}[0-9]{2})*';
594
                            } else {
595
                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 2):"' . $pattern . '"');
596
                            }
597
                            break;
598
                        case '##0':
599
                            if ($parts[$key - 1] == '##') {
600
                                $regex[$pkey] .= '[0-9]';
601
                            } else if (($parts[$key - 1] == '#') || ($parts[$key - 1] == '-#')) {
602
                                $regex[$pkey] .= '(\\' . $symbols['group'] . '{0,1}[0-9]{3})*';
603
                            } else {
604
                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 3):"' . $pattern . '"');
605
                            }
606
                            break;
607
                        case '#0':
608
                            if ($key == 0) {
609
                                $regex[$pkey] .= '[0-9]*';
610
                            } else {
611
                                throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 4):"' . $pattern . '"');
612
                            }
613
                            break;
614
                    }
615
                }
616
            }
617
618
            if (strpos($pattern, 'E') !== false) {
619
                if (($pattern == '#E0') || ($pattern == '#E00')) {
620
                    $regex[$pkey] .= '[' . $symbols['plus']. '+]{0,1}[0-9]{1,}(\\' . $symbols['decimal'] . '[0-9]{1,})*[eE][' . $symbols['plus']. '+]{0,1}[0-9]{1,}';
621
                } else if (($pattern == '-#E0') || ($pattern == '-#E00')) {
622
                    $regex[$pkey] .= '[' . $symbols['minus']. '-]{0,1}[0-9]{1,}(\\' . $symbols['decimal'] . '[0-9]{1,})*[eE][' . $symbols['minus']. '-]{0,1}[0-9]{1,}';
623
                } else {
624
                    throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 5):"' . $pattern . '"');
625
                }
626
            }
627
628
            if (!empty($end)) {
629
                if ($end == '###') {
630
                    $regex[$pkey] .= '(\\' . $symbols['decimal'] . '{1}[0-9]{1,}){0,1}';
631
                } else if ($end == '###-') {
632
                    $regex[$pkey] .= '(\\' . $symbols['decimal'] . '{1}[0-9]{1,}){0,1}[' . $symbols['minus']. '-]';
633
                } else {
634
                    throw new Zend_Locale_Exception('Unsupported token for numberformat (Pos 6):"' . $pattern . '"');
635
                }
636
            }
637
638
            $regex[$pkey] .= '$/u';
639
        }
640
641
        return $regex;
642
    }
643
644
    /**
645
     * Alias for getNumber
646
     *
647
     * @param   string  $value    Number to localize
648
     * @param   array   $options  Options: locale, precision. See {@link setOptions()} for details.
649
     * @return  float
650
     */
651
    public static function getFloat($input, array $options = array())
652
    {
653
        return floatval(self::getNumber($input, $options));
654
    }
655
656
    /**
657
     * Returns a locale formatted integer number
658
     * Alias for toNumber()
659
     *
660
     * @param   string  $value    Number to normalize
661
     * @param   array   $options  Options: locale, precision. See {@link setOptions()} for details.
662
     * @return  string  Locale formatted number
663
     */
664
    public static function toFloat($value, array $options = array())
665
    {
666
        $options['number_format'] = Zend_Locale_Format::STANDARD;
667
        return self::toNumber($value, $options);
668
    }
669
670
    /**
671
     * Returns if a float was found
672
     * Alias for isNumber()
673
     *
674
     * @param   string  $input    Localized number string
675
     * @param   array   $options  Options: locale. See {@link setOptions()} for details.
676
     * @return  boolean           Returns true if a number was found
677
     */
678
    public static function isFloat($value, array $options = array())
679
    {
680
        return self::isNumber($value, $options);
681
    }
682
683
    /**
684
     * Returns the first found integer from an string
685
     * Parsing depends on given locale (grouping and decimal)
686
     *
687
     * Examples for input:
688
     * '  2345.4356,1234' = 23455456
689
     * '+23,3452.123' = 233452
690
     * ' 12343 ' = 12343
691
     * '-9456km' = -9456
692
     * '0' = 0
693
     * '(-){0,1}(\d+(\.){0,1})*(\,){0,1})\d+'
694
     *
695
     * @param   string   $input    Input string to parse for numbers
696
     * @param   array    $options  Options: locale. See {@link setOptions()} for details.
697
     * @return  integer            Returns the extracted number
698
     */
699
    public static function getInteger($input, array $options = array())
700
    {
701
        $options['precision'] = 0;
702
        return intval(self::getFloat($input, $options));
703
    }
704
705
    /**
706
     * Returns a localized number
707
     *
708
     * @param   string  $value    Number to normalize
709
     * @param   array   $options  Options: locale. See {@link setOptions()} for details.
710
     * @return  string            Locale formatted number
711
     */
712
    public static function toInteger($value, array $options = array())
713
    {
714
        $options['precision'] = 0;
715
        $options['number_format'] = Zend_Locale_Format::STANDARD;
716
        return self::toNumber($value, $options);
717
    }
718
719
    /**
720
     * Returns if a integer was found
721
     *
722
     * @param   string  $input    Localized number string
723
     * @param   array   $options  Options: locale. See {@link setOptions()} for details.
724
     * @return  boolean           Returns true if a integer was found
725
     */
726
    public static function isInteger($value, array $options = array())
727
    {
728
        if (!self::isNumber($value, $options)) {
729
            return false;
730
        }
731
732
        if (self::getInteger($value, $options) == self::getFloat($value, $options)) {
733
            return true;
734
        }
735
736
        return false;
737
    }
738
739
    /**
740
     * Converts a format string from PHP's date format to ISO format
741
     * Remember that Zend Date always returns localized string, so a month name which returns the english
742
     * month in php's date() will return the translated month name with this function... use 'en' as locale
743
     * if you are in need of the original english names
744
     *
745
     * The conversion has the following restrictions:
746
     * 'a', 'A' - Meridiem is not explicit upper/lowercase, you have to upper/lowercase the translated value yourself
747
     *
748
     * @param  string  $format  Format string in PHP's date format
749
     * @return string           Format string in ISO format
750
     */
751
    public static function convertPhpToIsoFormat($format)
752
    {
753
        if ($format === null) {
754
            return null;
755
        }
756
757
        $convert = array('d' => 'dd'  , 'D' => 'EE'  , 'j' => 'd'   , 'l' => 'EEEE', 'N' => 'eee' , 'S' => 'SS'  ,
758
                         'w' => 'e'   , 'z' => 'D'   , 'W' => 'ww'  , 'F' => 'MMMM', 'm' => 'MM'  , 'M' => 'MMM' ,
759
                         'n' => 'M'   , 't' => 'ddd' , 'L' => 'l'   , 'o' => 'YYYY', 'Y' => 'yyyy', 'y' => 'yy'  ,
760
                         'a' => 'a'   , 'A' => 'a'   , 'B' => 'B'   , 'g' => 'h'   , 'G' => 'H'   , 'h' => 'hh'  ,
761
                         'H' => 'HH'  , 'i' => 'mm'  , 's' => 'ss'  , 'e' => 'zzzz', 'I' => 'I'   , 'O' => 'Z'   ,
762
                         'P' => 'ZZZZ', 'T' => 'z'   , 'Z' => 'X'   , 'c' => 'yyyy-MM-ddTHH:mm:ssZZZZ',
763
                         'r' => 'r'   , 'U' => 'U');
764
        $values = str_split($format);
765
        foreach ($values as $key => $value) {
766
            if (isset($convert[$value]) === true) {
767
                $values[$key] = $convert[$value];
768
            }
769
        }
770
771
        return join($values);
772
    }
773
774
    /**
775
     * Parse date and split in named array fields
776
     *
777
     * @param   string  $date     Date string to parse
778
     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
779
     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
780
     */
781
    private static function _parseDate($date, $options)
782
    {
783
        if (!self::_getUniCodeSupport()) {
784
            trigger_error("Sorry, your PCRE extension does not support UTF8 which is needed for the I18N core", E_USER_NOTICE);
785
        }
786
787
        $options = self::_checkOptions($options) + self::$_options;
788
        $test = array('h', 'H', 'm', 's', 'y', 'Y', 'M', 'd', 'D', 'E', 'S', 'l', 'B', 'I',
789
                       'X', 'r', 'U', 'G', 'w', 'e', 'a', 'A', 'Z', 'z', 'v');
790
791
        $format = $options['date_format'];
792
        $number = $date; // working copy
793
        $result['date_format'] = $format; // save the format used to normalize $number (convenience)
794
        $result['locale'] = $options['locale']; // save the locale used to normalize $number (convenience)
795
796
        if (version_compare(PHP_VERSION, '5.6', '<')) {
797
            $oenc = iconv_get_encoding('internal_encoding');
798
            iconv_set_encoding('internal_encoding', 'UTF-8');
799
        } else {
800
            $oenc = ini_get('php.internal_encoding');
801
            ini_set('php.internal_encoding', 'UTF-8');
802
        }
803
        $day   = iconv_strpos($format, 'd');
804
        $month = iconv_strpos($format, 'M');
805
        $year  = iconv_strpos($format, 'y');
806
        $hour  = iconv_strpos($format, 'H');
807
        $min   = iconv_strpos($format, 'm');
808
        $sec   = iconv_strpos($format, 's');
809
        $am    = null;
810
        if ($hour === false) {
811
            $hour = iconv_strpos($format, 'h');
812
        }
813
        if ($year === false) {
814
            $year = iconv_strpos($format, 'Y');
815
        }
816
        if ($day === false) {
817
            $day = iconv_strpos($format, 'E');
818
            if ($day === false) {
819
                $day = iconv_strpos($format, 'D');
820
            }
821
        }
822
823
        if ($day !== false) {
824
            $parse[$day]   = 'd';
825
            if (!empty($options['locale']) && ($options['locale'] !== 'root') &&
826
                (!is_object($options['locale']) || ((string) $options['locale'] !== 'root'))) {
827
                // erase day string
828
                    $daylist = Zend_Locale_Data::getList($options['locale'], 'day');
829
                foreach($daylist as $key => $name) {
830
                    if (iconv_strpos($number, $name) !== false) {
831
                        $number = str_replace($name, "EEEE", $number);
832
                        break;
833
                    }
834
                }
835
            }
836
        }
837
        $position = false;
838
839
        if ($month !== false) {
840
            $parse[$month] = 'M';
841
            if (!empty($options['locale']) && ($options['locale'] !== 'root') &&
842
                (!is_object($options['locale']) || ((string) $options['locale'] !== 'root'))) {
843
                    // prepare to convert month name to their numeric equivalents, if requested,
844
                    // and we have a $options['locale']
845
                    $position = self::_replaceMonth($number, Zend_Locale_Data::getList($options['locale'],
846
                        'month'));
847
                if ($position === false) {
848
                    $position = self::_replaceMonth($number, Zend_Locale_Data::getList($options['locale'],
849
                        'month', array('gregorian', 'format', 'abbreviated')));
850
                }
851
            }
852
        }
853
        if ($year !== false) {
854
            $parse[$year]  = 'y';
855
        }
856
        if ($hour !== false) {
857
            $parse[$hour] = 'H';
858
        }
859
        if ($min !== false) {
860
            $parse[$min] = 'm';
861
        }
862
        if ($sec !== false) {
863
            $parse[$sec] = 's';
864
        }
865
866
        if (empty($parse)) {
867
            if (version_compare(PHP_VERSION, '5.6', '<')) {
868
                iconv_set_encoding('internal_encoding', $oenc);
869
            } else {
870
                ini_set('php.internal_encoding', $oenc);
871
            }
872
            require_once 'Zend/Locale/Exception.php';
873
            throw new Zend_Locale_Exception("Unknown date format, neither date nor time in '" . $format . "' found");
874
        }
875
        ksort($parse);
876
877
        // get daytime
878
        if (iconv_strpos($format, 'a') !== false) {
879
            if (iconv_strpos(strtoupper($number), strtoupper(Zend_Locale_Data::getContent($options['locale'], 'am'))) !== false) {
880
                $am = true;
881
            } else if (iconv_strpos(strtoupper($number), strtoupper(Zend_Locale_Data::getContent($options['locale'], 'pm'))) !== false) {
882
                $am = false;
883
            }
884
        }
885
886
        // split number parts
887
        $split = false;
888
        preg_match_all('/\d+/u', $number, $splitted);
889
890
        if (count($splitted[0]) == 0) {
891
            if (version_compare(PHP_VERSION, '5.6', '<')) {
892
                iconv_set_encoding('internal_encoding', $oenc);
893
            } else {
894
                ini_set('php.internal_encoding', $oenc);
895
            }
896
            require_once 'Zend/Locale/Exception.php';
897
            throw new Zend_Locale_Exception("No date part in '$date' found.");
898
        }
899
        if (count($splitted[0]) == 1) {
900
            $split = 0;
901
        }
902
        $cnt = 0;
903
        foreach($parse as $key => $value) {
904
905
            switch($value) {
906
                case 'd':
907
                    if ($split === false) {
908
                        if (count($splitted[0]) > $cnt) {
909
                            $result['day']    = $splitted[0][$cnt];
910
                        }
911
                    } else {
912
                        $result['day'] = iconv_substr($splitted[0][0], $split, 2);
913
                        $split += 2;
914
                    }
915
                    ++$cnt;
916
                    break;
917
                case 'M':
918
                    if ($split === false) {
919
                        if (count($splitted[0]) > $cnt) {
920
                            $result['month']  = $splitted[0][$cnt];
921
                        }
922
                    } else {
923
                        $result['month'] = iconv_substr($splitted[0][0], $split, 2);
924
                        $split += 2;
925
                    }
926
                    ++$cnt;
927
                    break;
928
                case 'y':
929
                    $length = 2;
930
                    if ((iconv_substr($format, $year, 4) == 'yyyy')
931
                     || (iconv_substr($format, $year, 4) == 'YYYY')) {
932
                        $length = 4;
933
                    }
934
935
                    if ($split === false) {
936
                        if (count($splitted[0]) > $cnt) {
937
                            $result['year']   = $splitted[0][$cnt];
938
                        }
939
                    } else {
940
                        $result['year']   = iconv_substr($splitted[0][0], $split, $length);
941
                        $split += $length;
942
                    }
943
944
                    ++$cnt;
945
                    break;
946
                case 'H':
947
                    if ($split === false) {
948
                        if (count($splitted[0]) > $cnt) {
949
                            $result['hour']   = $splitted[0][$cnt];
950
                        }
951
                    } else {
952
                        $result['hour']   = iconv_substr($splitted[0][0], $split, 2);
953
                        $split += 2;
954
                    }
955
                    ++$cnt;
956
                    break;
957
                case 'm':
958
                    if ($split === false) {
959
                        if (count($splitted[0]) > $cnt) {
960
                            $result['minute'] = $splitted[0][$cnt];
961
                        }
962
                    } else {
963
                        $result['minute'] = iconv_substr($splitted[0][0], $split, 2);
964
                        $split += 2;
965
                    }
966
                    ++$cnt;
967
                    break;
968
                case 's':
969
                    if ($split === false) {
970
                        if (count($splitted[0]) > $cnt) {
971
                            $result['second'] = $splitted[0][$cnt];
972
                        }
973
                    } else {
974
                        $result['second'] = iconv_substr($splitted[0][0], $split, 2);
975
                        $split += 2;
976
                    }
977
                    ++$cnt;
978
                    break;
979
            }
980
        }
981
982
        // AM/PM correction
983
        if ($hour !== false) {
984
            if (($am === true) and ($result['hour'] == 12)){
985
                $result['hour'] = 0;
986
            } else if (($am === false) and ($result['hour'] != 12)) {
987
                $result['hour'] += 12;
988
            }
989
        }
990
991
        if ($options['fix_date'] === true) {
992
            $result['fixed'] = 0; // nothing has been "fixed" by swapping date parts around (yet)
993
        }
994
995
        if ($day !== false) {
996
            // fix false month
997
            if (isset($result['day']) and isset($result['month'])) {
998
                if (($position !== false) and ((iconv_strpos($date, $result['day']) === false) or
999
                                               (isset($result['year']) and (iconv_strpos($date, $result['year']) === false)))) {
1000
                    if ($options['fix_date'] !== true) {
1001
                        if (version_compare(PHP_VERSION, '5.6', '<')) {
1002
                            iconv_set_encoding('internal_encoding', $oenc);
1003
                        } else {
1004
                            ini_set('php.internal_encoding', $oenc);
1005
                        }
1006
                        require_once 'Zend/Locale/Exception.php';
1007
                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '" . $format
1008
                            . "' (false month, $position, $month)");
1009
                    }
1010
                    $temp = $result['day'];
1011
                    $result['day']   = $result['month'];
1012
                    $result['month'] = $temp;
1013
                    $result['fixed'] = 1;
1014
                }
1015
            }
1016
1017
            // fix switched values d <> y
1018
            if (isset($result['day']) and isset($result['year'])) {
1019
                if ($result['day'] > 31) {
1020
                    if ($options['fix_date'] !== true) {
1021
                        if (version_compare(PHP_VERSION, '5.6', '<')) {
1022
                            iconv_set_encoding('internal_encoding', $oenc);
1023
                        } else {
1024
                            ini_set('php.internal_encoding', $oenc);
1025
                        }
1026
                        require_once 'Zend/Locale/Exception.php';
1027
                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '"
1028
                                                      . $format . "' (d <> y)");
1029
                    }
1030
                    $temp = $result['year'];
1031
                    $result['year'] = $result['day'];
1032
                    $result['day']  = $temp;
1033
                    $result['fixed'] = 2;
1034
                }
1035
            }
1036
1037
            // fix switched values M <> y
1038
            if (isset($result['month']) and isset($result['year'])) {
1039
                if ($result['month'] > 31) {
1040
                    if ($options['fix_date'] !== true) {
1041
                        if (version_compare(PHP_VERSION, '5.6', '<')) {
1042
                            iconv_set_encoding('internal_encoding', $oenc);
1043
                        } else {
1044
                            ini_set('php.internal_encoding', $oenc);
1045
                        }
1046
                        require_once 'Zend/Locale/Exception.php';
1047
                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '"
1048
                                                      . $format . "' (M <> y)");
1049
                    }
1050
                    $temp = $result['year'];
1051
                    $result['year']  = $result['month'];
1052
                    $result['month'] = $temp;
1053
                    $result['fixed'] = 3;
1054
                }
1055
            }
1056
1057
            // fix switched values M <> d
1058
            if (isset($result['month']) and isset($result['day'])) {
1059
                if ($result['month'] > 12) {
1060
                    if ($options['fix_date'] !== true || $result['month'] > 31) {
1061
                        if (version_compare(PHP_VERSION, '5.6', '<')) {
1062
                            iconv_set_encoding('internal_encoding', $oenc);
1063
                        } else {
1064
                            ini_set('php.internal_encoding', $oenc);
1065
                        }
1066
                        require_once 'Zend/Locale/Exception.php';
1067
                        throw new Zend_Locale_Exception("Unable to parse date '$date' using '"
1068
                                                      . $format . "' (M <> d)");
1069
                    }
1070
                    $temp = $result['day'];
1071
                    $result['day']   = $result['month'];
1072
                    $result['month'] = $temp;
1073
                    $result['fixed'] = 4;
1074
                }
1075
            }
1076
        }
1077
1078
        if (isset($result['year'])) {
1079
            if (((iconv_strlen($result['year']) == 2) && ($result['year'] < 10)) ||
1080
                (((iconv_strpos($format, 'yy') !== false) && (iconv_strpos($format, 'yyyy') === false)) ||
1081
                ((iconv_strpos($format, 'YY') !== false) && (iconv_strpos($format, 'YYYY') === false)))) {
1082
                if (($result['year'] >= 0) && ($result['year'] < 100)) {
1083
                    if ($result['year'] < 70) {
1084
                        $result['year'] = (int) $result['year'] + 100;
1085
                    }
1086
1087
                    $result['year'] = (int) $result['year'] + 1900;
1088
                }
1089
            }
1090
        }
1091
1092
        if (version_compare(PHP_VERSION, '5.6', '<')) {
1093
            iconv_set_encoding('internal_encoding', $oenc);
1094
        } else {
1095
            ini_set('php.internal_encoding', $oenc);
1096
        }
1097
        return $result;
1098
    }
1099
1100
    /**
1101
     * Search $number for a month name found in $monthlist, and replace if found.
1102
     *
1103
     * @param  string  $number     Date string (modified)
1104
     * @param  array   $monthlist  List of month names
1105
     *
1106
     * @return int|false           Position of replaced string (false if nothing replaced)
1107
     */
1108
    protected static function _replaceMonth(&$number, $monthlist)
1109
    {
1110
        // If $locale was invalid, $monthlist will default to a "root" identity
1111
        // mapping for each month number from 1 to 12.
1112
        // If no $locale was given, or $locale was invalid, do not use this identity mapping to normalize.
1113
        // Otherwise, translate locale aware month names in $number to their numeric equivalents.
1114
        $position = false;
1115
        if ($monthlist && $monthlist[1] != 1) {
1116
            foreach($monthlist as $key => $name) {
1117
                if (($position = iconv_strpos($number, $name, 0, 'UTF-8')) !== false) {
1118
                    $number   = str_ireplace($name, $key, $number);
1119
                    return $position;
1120
                }
1121
            }
1122
        }
1123
1124
        return false;
1125
    }
1126
1127
    /**
1128
     * Returns the default date format for $locale.
1129
     *
1130
     * @param  string|Zend_Locale  $locale  OPTIONAL Locale of $number, possibly in string form (e.g. 'de_AT')
1131
     * @return string  format
1132
     * @throws Zend_Locale_Exception  throws an exception when locale data is broken
1133
     */
1134
    public static function getDateFormat($locale = null)
1135
    {
1136
        $format = Zend_Locale_Data::getContent($locale, 'date');
1137
        if (empty($format)) {
1138
            require_once 'Zend/Locale/Exception.php';
1139
            throw new Zend_Locale_Exception("failed to receive data from locale $locale");
1140
        }
1141
1142
        return $format;
1143
    }
1144
1145
    /**
1146
     * Returns an array with the normalized date from an locale date
1147
     * a input of 10.01.2006 without a $locale would return:
1148
     * array ('day' => 10, 'month' => 1, 'year' => 2006)
1149
     * The 'locale' option is only used to convert human readable day
1150
     * and month names to their numeric equivalents.
1151
     * The 'format' option allows specification of self-defined date formats,
1152
     * when not using the default format for the 'locale'.
1153
     *
1154
     * @param   string  $date     Date string
1155
     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1156
     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
1157
     */
1158
    public static function getDate($date, array $options = array())
1159
    {
1160
        $options = self::_checkOptions($options) + self::$_options;
1161
        if (empty($options['date_format'])) {
1162
            $options['format_type'] = 'iso';
1163
            $options['date_format'] = self::getDateFormat($options['locale']);
1164
        }
1165
1166
        return self::_parseDate($date, $options);
1167
    }
1168
1169
    /**
1170
     * Returns if the given datestring contains all date parts from the given format.
1171
     * If no format is given, the default date format from the locale is used
1172
     * If you want to check if the date is a proper date you should use Zend_Date::isDate()
1173
     *
1174
     * @param   string  $date     Date string
1175
     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1176
     * @return  boolean
1177
     */
1178
    public static function checkDateFormat($date, array $options = array())
1179
    {
1180
        try {
1181
            $date = self::getDate($date, $options);
1182
        } catch (Exception $e) {
1183
            return false;
1184
        }
1185
1186
        if (empty($options['date_format'])) {
1187
            $options['format_type'] = 'iso';
1188
            $options['date_format'] = self::getDateFormat(isset($options['locale']) ? $options['locale'] : null);
1189
        }
1190
        $options = self::_checkOptions($options) + self::$_options;
1191
1192
        // day expected but not parsed
1193
        if ((iconv_strpos($options['date_format'], 'd', 0, 'UTF-8') !== false) and (!isset($date['day']) or ($date['day'] === ""))) {
1194
            return false;
1195
        }
1196
1197
        // month expected but not parsed
1198
        if ((iconv_strpos($options['date_format'], 'M', 0, 'UTF-8') !== false) and (!isset($date['month']) or ($date['month'] === ""))) {
1199
            return false;
1200
        }
1201
1202
        // year expected but not parsed
1203
        if (((iconv_strpos($options['date_format'], 'Y', 0, 'UTF-8') !== false) or
1204
             (iconv_strpos($options['date_format'], 'y', 0, 'UTF-8') !== false)) and (!isset($date['year']) or ($date['year'] === ""))) {
1205
            return false;
1206
        }
1207
1208
        // second expected but not parsed
1209
        if ((iconv_strpos($options['date_format'], 's', 0, 'UTF-8') !== false) and (!isset($date['second']) or ($date['second'] === ""))) {
1210
            return false;
1211
        }
1212
1213
        // minute expected but not parsed
1214
        if ((iconv_strpos($options['date_format'], 'm', 0, 'UTF-8') !== false) and (!isset($date['minute']) or ($date['minute'] === ""))) {
1215
            return false;
1216
        }
1217
1218
        // hour expected but not parsed
1219
        if (((iconv_strpos($options['date_format'], 'H', 0, 'UTF-8') !== false) or
1220
             (iconv_strpos($options['date_format'], 'h', 0, 'UTF-8') !== false)) and (!isset($date['hour']) or ($date['hour'] === ""))) {
1221
            return false;
1222
        }
1223
1224
        return true;
1225
    }
1226
1227
    /**
1228
     * Returns the default time format for $locale.
1229
     *
1230
     * @param  string|Zend_Locale  $locale  OPTIONAL Locale of $number, possibly in string form (e.g. 'de_AT')
1231
     * @return string  format
1232
     */
1233
    public static function getTimeFormat($locale = null)
1234
    {
1235
        $format = Zend_Locale_Data::getContent($locale, 'time');
1236
        if (empty($format)) {
1237
            require_once 'Zend/Locale/Exception.php';
1238
            throw new Zend_Locale_Exception("failed to receive data from locale $locale");
1239
        }
1240
        return $format;
1241
    }
1242
1243
    /**
1244
     * Returns an array with 'hour', 'minute', and 'second' elements extracted from $time
1245
     * according to the order described in $format.  For a format of 'H:m:s', and
1246
     * an input of 11:20:55, getTime() would return:
1247
     * array ('hour' => 11, 'minute' => 20, 'second' => 55)
1248
     * The optional $locale parameter may be used to help extract times from strings
1249
     * containing both a time and a day or month name.
1250
     *
1251
     * @param   string  $time     Time string
1252
     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1253
     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
1254
     */
1255
    public static function getTime($time, array $options = array())
1256
    {
1257
        $options = self::_checkOptions($options) + self::$_options;
1258
        if (empty($options['date_format'])) {
1259
            $options['format_type'] = 'iso';
1260
            $options['date_format'] = self::getTimeFormat($options['locale']);
1261
        }
1262
        return self::_parseDate($time, $options);
1263
    }
1264
1265
    /**
1266
     * Returns the default datetime format for $locale.
1267
     *
1268
     * @param  string|Zend_Locale  $locale  OPTIONAL Locale of $number, possibly in string form (e.g. 'de_AT')
1269
     * @return string  format
1270
     */
1271
    public static function getDateTimeFormat($locale = null)
1272
    {
1273
        $format = Zend_Locale_Data::getContent($locale, 'datetime');
1274
        if (empty($format)) {
1275
            require_once 'Zend/Locale/Exception.php';
1276
            throw new Zend_Locale_Exception("failed to receive data from locale $locale");
1277
        }
1278
        return $format;
1279
    }
1280
1281
    /**
1282
     * Returns an array with 'year', 'month', 'day', 'hour', 'minute', and 'second' elements
1283
     * extracted from $datetime according to the order described in $format.  For a format of 'd.M.y H:m:s',
1284
     * and an input of 10.05.1985 11:20:55, getDateTime() would return:
1285
     * array ('year' => 1985, 'month' => 5, 'day' => 10, 'hour' => 11, 'minute' => 20, 'second' => 55)
1286
     * The optional $locale parameter may be used to help extract times from strings
1287
     * containing both a time and a day or month name.
1288
     *
1289
     * @param   string  $datetime DateTime string
1290
     * @param   array   $options  Options: format_type, fix_date, locale, date_format. See {@link setOptions()} for details.
1291
     * @return  array             Possible array members: day, month, year, hour, minute, second, fixed, format
1292
     */
1293
    public static function getDateTime($datetime, array $options = array())
1294
    {
1295
        $options = self::_checkOptions($options) + self::$_options;
1296
        if (empty($options['date_format'])) {
1297
            $options['format_type'] = 'iso';
1298
            $options['date_format'] = self::getDateTimeFormat($options['locale']);
1299
        }
1300
        return self::_parseDate($datetime, $options);
1301
    }
1302
1303
    /**
1304
     * Internal method to detect of Unicode supports UTF8
1305
     * which should be enabled within vanilla php installations
1306
     *
1307
     * @return boolean
1308
     */
1309
    protected static function _getUniCodeSupport()
1310
    {
1311
        return (@preg_match('/\pL/u', 'a')) ? true : false;
1312
    }
1313
}
1314