PHP date_parse_from_format( ) alternative in PHP 5.2

前端 未结 5 666
你的背包
你的背包 2021-01-03 01:20

Since date_parse_from_format( ) is available only in PHP 5.3, I need to write a function that mimics its behaviour in PHP 5.2.

Is it possible to write this function

相关标签:
5条回答
  • In case you don't need the last 4 fields of the array you may simply use strtotime instead of date_parse_from_format to get the same result. For example:

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

    This works with PHP 5.2

    0 讨论(0)
  • 2021-01-03 01:54
    <?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;
    }
    ?>
    

    Example 1:

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

    Output 1:

    Array ( [month] => 03 [day] => 23 [year] => 2011 )

    Example 2:

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

    Output 2:

    Array ( [year] => 2011 [month] => 03 [day] => 23 [hour] => 12 [minute] => 03 [second] => 00 )

    0 讨论(0)
  • 2021-01-03 01:54

    Here is my improved version and I think complete. Only errors and warnings are not taken into account.

    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;
        }
    }
    
    0 讨论(0)
  • 2021-01-03 01:54

    First I'd like to thank @rudie for his answer and @jeremy refinement of his answer.

    I needed a bit more flexible version would could deal with jQueryUI Datepicker with the TimePicker plugin. I also needed it to work with the \ backslash escape character to deal with the odd time formats users enter such as H\h i\m\i\n.

    Here's my solution based on those previous answers that I implemented in my Connections Business Directory WordPress plugin.

    It more closely matches the functionality of date_parse_from_format() and DateTime::createFromFormat().

    Hope this helps someone!

    <?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 );
        }
    }
    
    0 讨论(0)
  • 2021-01-03 02:04

    If you want it to be exactly like PHP 5.3 function, you're gonna need a lot of code. I'd start with something like this:

    $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";
    }
    

    Result:

    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
    )
    

    Incomplete and imperfect.

    0 讨论(0)
提交回复
热议问题