【问题标题】:PHP date_parse_from_format( ) alternative in PHP 5.2PHP 5.2 中的 PHP date_parse_from_format( ) 替代方案
【发布时间】:2011-10-03 20:05:45
【问题描述】:

由于 date_parse_from_format( ) 仅在 PHP 5.3 中可用,我需要编写一个函数来模仿它在 PHP 5.2 中的行为。

是否可以为 PHP 5.2 编写此函数并使其工作方式与在 PHP 5.3 中完全相同?

例子:

对于这个输入:

<?php
$date = "6.1.2009 13:00+01:00";
print_r(date_parse_from_format("j.n.Y H:iP", $date));
?>

我需要这个输出:

Array
(
    [year] => 2009
    [month] => 1
    [day] => 6
    [hour] => 13
    [minute] => 0
    [second] => 0
    [fraction] => 
    [warning_count] => 0
    [warnings] => Array
        (
        )

    [error_count] => 0
    [errors] => Array
        (
        )

    [is_localtime] => 1
    [zone_type] => 1
    [zone] => -60
    [is_dst] => 
)

【问题讨论】:

    标签: php datetime backwards-compatibility php-5.2


    【解决方案1】:
    <?php
    function date_parse_from_format($format, $date) {
      $dMask = array(
        'H'=>'hour',
        'i'=>'minute',
        's'=>'second',
        'y'=>'year',
        'm'=>'month',
        'd'=>'day'
      );
      $format = preg_split('//', $format, -1, PREG_SPLIT_NO_EMPTY); 
      $date = preg_split('//', $date, -1, PREG_SPLIT_NO_EMPTY); 
      foreach ($date as $k => $v) {
        if ($dMask[$format[$k]]) $dt[$dMask[$format[$k]]] .= $v;
      }
      return $dt;
    }
    ?>
    

    示例 1:

    <?php
        print_r(date_parse_from_format('mmddyyyy','03232011');
    ?>
    

    输出 1:

    数组 ( [月] => 03 [天] => 23 [年份] => 2011 )

    示例 2:

     <?php
        print_r(date_parse_from_format('yyyy.mm.dd HH:ii:ss','2011.03.23 12:03:00'));
     ?>
    

    输出 2:

    数组 ( [年份] => 2011 [月] => 03 [天] => 23 [小时] => 12 [分钟] => 03 [第二] => 00 )

    【讨论】:

    • 感谢您的帮助,但输出需要与原始 php 5.3 函数完全相同。
    • 对吗?为什么?不可能的。也许您应该给 PHP 家族写一封非常好的信,要求他们将其向后移植到 5.2。 Oh no wait 5.2 不再受到积极支持 =p
    【解决方案2】:

    这是我的改进版本,我认为完整。只考虑错误和警告。

    if( !function_exists('date_parse_from_format') ){
        function date_parse_from_format($format, $date) {
            // reverse engineer date formats
            $keys = array(
                'Y' => array('year', '\d{4}'),              //Année sur 4 chiffres
                'y' => array('year', '\d{2}'),              //Année sur 2 chiffres
                'm' => array('month', '\d{2}'),             //Mois au format numérique, avec zéros initiaux
                'n' => array('month', '\d{1,2}'),           //Mois sans les zéros initiaux
                'M' => array('month', '[A-Z][a-z]{3}'),     //Mois, en trois lettres, en anglais
                'F' => array('month', '[A-Z][a-z]{2,8}'),   //Mois, textuel, version longue; en anglais, comme January ou December
                'd' => array('day', '\d{2}'),               //Jour du mois, sur deux chiffres (avec un zéro initial)
                'j' => array('day', '\d{1,2}'),             //Jour du mois sans les zéros initiaux
                'D' => array('day', '[A-Z][a-z]{2}'),       //Jour de la semaine, en trois lettres (et en anglais)
                'l' => array('day', '[A-Z][a-z]{6,9}'),     //Jour de la semaine, textuel, version longue, en anglais
                'u' => array('hour', '\d{1,6}'),            //Microsecondes
                'h' => array('hour', '\d{2}'),              //Heure, au format 12h, avec les zéros initiaux
                'H' => array('hour', '\d{2}'),              //Heure, au format 24h, avec les zéros initiaux
                'g' => array('hour', '\d{1,2}'),            //Heure, au format 12h, sans les zéros initiaux
                'G' => array('hour', '\d{1,2}'),            //Heure, au format 24h, sans les zéros initiaux
                'i' => array('minute', '\d{2}'),            //Minutes avec les zéros initiaux
                's' => array('second', '\d{2}')             //Secondes, avec zéros initiaux
            );
    
            // convert format string to regex
            $regex = '';
            $chars = str_split($format);
            foreach ( $chars AS $n => $char ) {
                $lastChar = isset($chars[$n-1]) ? $chars[$n-1] : '';
                $skipCurrent = '\\' == $lastChar;
                if ( !$skipCurrent && isset($keys[$char]) ) {
                    $regex .= '(?P<'.$keys[$char][0].'>'.$keys[$char][1].')';
                }
                else if ( '\\' == $char ) {
                    $regex .= $char;
                }
                else {
                    $regex .= preg_quote($char);
                }
            }
    
            $dt = array();
            // now try to match it
            if( preg_match('#^'.$regex.'$#', $date, $dt) ){
                foreach ( $dt AS $k => $v ){
                    if ( is_int($k) ){
                        unset($dt[$k]);
                    }
                }
                if( !checkdate($dt['month'], $dt['day'], $dt['year']) ){
                    $dt['error_count'] = 1;
                } else {
                    $dt['error_count'] = 0;
                }
            }
            else {
                $dt['error_count'] = 1;
            }
    
            $dt['errors'] = array();
            $dt['fraction'] = '';
            $dt['warning_count'] = 0;
            $dt['warnings'] = array();
            $dt['is_localtime'] = 0;
            $dt['zone_type'] = 0;
            $dt['zone'] = 0;
            $dt['is_dst'] = '';
            return $dt;
        }
    }
    

    【讨论】:

    • 哇。你实际上偷了我的答案。优雅。
    • 像魅力一样工作! Tnx。
    【解决方案3】:

    如果您希望它与 PHP 5.3 函数完全一样,您将需要大量代码。我会从这样的事情开始:

    $format = '\Y: Y-m-d';
    var_dump($format);
    
    $date = date($format);
    var_dump($date);
    
    // reverse engineer date formats
    $keys = array(
        'Y' => array('year', '\d{4}'),
        'm' => array('month', '\d{2}'),
        'd' => array('day', '\d{2}'),
        'j' => array('day', '\d{1,2}'),
        'n' => array('month', '\d{1,2}'),
        'M' => array('month', '[A-Z][a-z]{2}'),
        'F' => array('month', '[A-Z][a-z]{2,8}'),
        'D' => array('day', '[A-Z][a-z]{2}'),
        // etc etc etc
    );
    
    // convert format string to regex
    $regex = '';
    $chars = str_split($format);
    foreach ( $chars AS $n => $char ) {
        $lastChar = isset($chars[$n-1]) ? $chars[$n-1] : '';
        $skipCurrent = '\\' == $lastChar;
        if ( !$skipCurrent && isset($keys[$char]) ) {
            $regex .= '(?P<'.$keys[$char][0].'>'.$keys[$char][1].')';
        }
        else if ( '\\' == $char ) {
            $regex .= $char;
        }
        else {
            $regex .= preg_quote($char);
        }
    }
    
    var_dump($regex);
    
    // now try to match it
    if ( preg_match('#^'.$regex.'$#', $date, $matches) ) {
        foreach ( $matches AS $k => $v ) if ( is_int($k) ) unset($matches[$k]);
        print_r($matches);
    }
    else {
        echo 'invalid date "'.$date.'" for format "'.$format.'"'."\n";
    }
    

    结果:

    string(9) "\Y: Y-m-d"
    string(13) "Y: 2011-07-12"
    string(51) "\Y\: (?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})"
    Array
    (
        [year] => 2011
        [month] => 07
        [day] => 12
    )
    

    不完整和不完美。

    【讨论】:

      【解决方案4】:

      首先我要感谢@rudie 的回答和@jeremy 对他的回答的完善。

      我需要一个更灵活的版本来处理带有 TimePicker 插件的 jQueryUI Datepicker。我还需要它与\ 反斜杠转义字符一起使用,以处理用户输入的奇怪时间格式,例如H\h i\m\i\n

      这是我的解决方案,基于我在 Connections Business Directory WordPress plugin 中实施的那些先前的答案。

      它更接近于date_parse_from_format()DateTime::createFromFormat() 的功能。

      希望这对某人有所帮助!

      <?php
      
      /**
       * Class cnDate
       */
      class cnDate {
      
          /**
           * Date format characters and their name and regex structure.
           *
           * @access public
           * @since  8.6.4
           *
           * @var array
           */
          protected static $keys = array(
              'Y' => array( 'year', '\d{4}' ),            // Year with 4 Digits
              'y' => array( 'year', '\d{2}' ),            // Year with 2 Digits
              'm' => array( 'month', '\d{2}' ),           // Month with leading 0
              'n' => array( 'month', '\d{1,2}' ),         // Month without the leading 0
              'M' => array( 'month', '[A-Z][a-z]{2}' ),   // Month ABBR 3 letters
              'F' => array( 'month', '[A-Z][a-z]{2,8}' ), // Month Name
              'd' => array( 'day', '\d{2}' ),             // Day with leading 0
              'j' => array( 'day', '\d{1,2}' ),           // Day without leading 0
              'D' => array( 'day', '[A-Z][a-z]{2}' ),     // Day ABBR 3 Letters
              'l' => array( 'day', '[A-Z][a-z]{5,8}' ),   // Day Name
              'h' => array( 'hour', '\d{2}' ),            // Hour 12h formatted, with leading 0
              'H' => array( 'hour', '\d{2}' ),            // Hour 24h formatted, with leading 0
              'g' => array( 'hour', '\d{1,2}' ),          // Hour 12h formatted, without leading 0
              'G' => array( 'hour', '\d{1,2}' ),          // Hour 24h formatted, without leading 0
              'i' => array( 'minute', '\d{2}' ),          // Minutes with leading 0
              's' => array( 'second', '\d{2}' ),          // Seconds with leading 0
              'u' => array( 'hour', '\d{1,6}' ),          // Microseconds
              'a' => array( 'meridiem', '[ap]m' ),        // Lowercase ante meridiem and Post meridiem
              'A' => array( 'meridiem', '[AP]M' ),        // Uppercase ante meridiem and Post meridiem
          );
      
          /**
           * Create a regex used to parse the supplied datetime format.
           *
           * @access public
           * @since  8.6.4
           *
           * @param string $format The datetime format.
           *
           * @return string
           */
          private static function getFormatRegex( $format ) {
      
              $keys = self::$keys;
      
              // Convert format string to regex.
              $regex = '';
              $chars = str_split( $format );
      
              foreach ( $chars as $n => $char ) {
      
                  $lastChar    = isset( $chars[ $n - 1 ] ) ? $chars[ $n - 1 ] : '';
                  $skipCurrent = '\\' == $lastChar;
      
                  if ( ! $skipCurrent && isset( $keys[ $char ] ) ) {
      
                      $regex .= '(?P<' . $keys[ $char ][0] . '>' . $keys[ $char ][1] . ')';
      
                  } elseif ( '\\' == $char || '!' == $char ) {
      
                      /*
                       * No need to add the date format escaping character to the regex since it should not exist in the
                       * supplied datetime string. Including it would cause the preg_match to fail.
                       */
                      //$regex .= $char;
      
                  } else {
      
                      $regex .= preg_quote( $char );
                  }
              }
      
              return '#^' . $regex . '$#';
          }
      
          /**
           * PHP 5.2 does not have a version of @see date_parse_from_format(), this is a mostly PHP 5.2 compatible version.
           *
           * @link http://stackoverflow.com/a/14196482/5351316
           *
           * @access public
           * @since  8.6.4
           *
           * @param string $format The datetime format.
           * @param string $date   The datetime string to parse.
           *
           * @return array
           */
          public static function parseFromFormat( $format, $date ) {
      
              /** Setup the default values to be returned, matching @see date_parse_from_format() */
              $dt = array(
                  'year'          => FALSE,
                  'month'         => FALSE,
                  'day'           => FALSE,
                  'hour'          => FALSE,
                  'minute'        => FALSE,
                  'second'        => FALSE,
                  'fraction'      => FALSE,
                  'warning_count' => 0,
                  'warnings'      => array(),
                  'error_count'   => 0,
                  'errors'        => array(),
                  'is_localtime'  => FALSE,
                  'zone_type'     => 0,
                  'zone'          => 0,
                  'is_dst'        => '',
              );
      
              // Now try to match it.
              if ( preg_match( self::getFormatRegex( $format ), $date, $matches ) ) {
      
                  foreach ( $matches as $k => $v ) {
      
                      // Remove unwanted indexes from resulting preg_match.
                      if ( is_int( $k ) ) {
      
                          unset( $matches[ $k ] );
                      }
      
                      // Year, month, day, hour, minute, second and fraction should be coerced from string to int.
                      if ( in_array( $k, array( 'year', 'month', 'day', 'hour', 'minute', 'second', 'fraction' ) )
                           && is_numeric( $v ) ) {
      
                          $matches[ $k ] = (int) $v;
      
                      } elseif ( 'month' === $k ) {
      
                          $parsed = date_parse( $v );
                          $matches[ $k ] = (int) $parsed['month'];
      
                      } elseif ( 'day' === $k ) {
      
                          $parsed = date_parse( $v );
                          $matches[ $k ] = (int) $parsed['day'];
                      }
                  }
      
              } else {
      
                  $dt['error_count'] = 1;
                  $dt['errors'][]    = 'Invalid date supplied.'; // @todo match error string from date_parse_from_format()
              }
      
              return wp_parse_args( $matches, $dt );
          }
      
          /**
           * PHP 5.2 does not have a version of @see DateTime::createFromFormat(), this is a mostly PHP 5.2 compatible version.
           *
           * @link http://bordoni.me/date_parse_from_format-php-5-2/
           *
           * @access public
           * @since  8.6.4
           *
           * @param  string $format  The datetime format.
           * @param  string $date    The datetime string to parse.
           *
           * @return false|DateTime  Instance of DateTime, false on failure.
           */
          public static function createFromFormat( $format, $date ) {
      
              $keys  = self::$keys;
              $pos   = strpos( $format, '!' );
              $chars = str_split( $format );
      
              // Setup default datetime values based on time now or Unix epoch based on if `!` if present in $format.
              if ( FALSE !== $pos ) {
      
                  $datetime = array(
                      'year'          => '1970',
                      'month'         => '01',
                      'day'           => '01',
                      'hour'          => '00',
                      'minute'        => '00',
                      'second'        => '00',
                      'fraction'      => '000000',
                  );
      
              } else {
      
                  /** @link http://stackoverflow.com/a/38334226/5351316 */
                  list( $usec, $sec ) = explode( ' ', microtime() );
      
                  $datetime = array(
                      'year'          => date( 'Y', $sec ),
                      'month'         => date( 'm', $sec ),
                      'day'           => date( 'd', $sec ),
                      'hour'          => date( 'H', $sec ),
                      'minute'        => date( 'i', $sec ),
                      'second'        => date( 's', $sec ),
                      'fraction'      => substr( $usec, 2, 6 ),
                  );
              }
      
              $parsed = self::parseFromFormat( $format, $date );
      
              foreach ( $chars as $n => $char ) {
      
                  $lastChar    = isset( $chars[ $n - 1 ] ) ? $chars[ $n - 1 ] : '';
                  $skipCurrent = '\\' == $lastChar;
      
                  if ( ! $skipCurrent && isset( $keys[ $char ] ) ) {
      
                      // Existing value exists in supplied parsed date.
                      if ( $parsed[ $keys[ $char ][0] ] ) {
      
                          /*
                           * Replace default datetime interval with the parsed datetime interval only if
                           * an `!` was found within the supplied $format and its position is
                           * greater than the current $format character position.
                           */
                          if ( ! ( FALSE !== $pos && $pos > $n ) ) {
      
                              $datetime[ $keys[ $char ][0] ] = $parsed[ $keys[ $char ][0] ];
                          }
                      }
                  }
              }
      
              // Ensure the datetime integers are correctly padded with leading zeros.
              $datetime['month']  = str_pad( $datetime['month'], 2, '0', STR_PAD_LEFT );
              $datetime['day']    = str_pad( $datetime['day'], 2, '0', STR_PAD_LEFT );
              $datetime['hour']   = str_pad( $datetime['hour'], 2, '0', STR_PAD_LEFT );
              $datetime['minute'] = str_pad( $datetime['minute'], 2, '0', STR_PAD_LEFT );
              $datetime['second'] = str_pad( $datetime['second'], 2, '0', STR_PAD_LEFT );
      
              // Parse the $datetime into a string which can be parsed by DateTime().
              $formatted = strtr( 'year-month-day hour:minute:second.fraction', $datetime );
      
              // Sanity check to make sure the datetime is valid.
              if ( ! strtotime( $formatted ) ) {
      
                  return FALSE;
              }
      
              // Return a new DateTime instance.
              return new DateTime( $formatted );
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果您不需要数组的最后 4 个字段,您可以简单地使用 strtotime 而不是 date_parse_from_format 来获得相同的结果。 例如:

        $textdate = $date;
        $datetime = strtotime($textdate);
        $datearray = date_parse($datetime);
        print_r($datearray);
        

        这适用于 PHP 5.2

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-17
          • 1970-01-01
          • 2012-04-22
          • 1970-01-01
          相关资源
          最近更新 更多