PageRenderTime 45ms CodeModel.GetById 10ms app.highlight 24ms RepoModel.GetById 0ms app.codeStats 1ms

/library/Zend/Locale/Format.php

https://bitbucket.org/hamidrezas/melobit
PHP | 1265 lines | 924 code | 108 blank | 233 comment | 249 complexity | 11910485c316af352ce0484bfa136b09 MD5 | raw file
Possible License(s): AGPL-1.0

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file