2011-07-12 51 views
5

由於date_parse_from_format()僅在PHP 5.3中可用,所以我需要編寫一個函數來模擬PHP 5.2中的行爲。PHP date_parse_from_format()替代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] => 
) 
+0

是否'的strtotime()'不工作? – Michael

回答

6
<?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:

Array ( [year] => 2011 [month] => 03 [天] => 23 [小時] => 12 [分鐘] => 03 [秒] => 00 )

+0

感謝您的幫助,但輸出需要與原來的php 5.3功能完全一樣。 – Acacio

+3

究竟?爲什麼?不可能。也許你應該給PHP家族寫一封非常好的信,要求他們將它支持到5.2。哦,沒有等待5.2不再積極支持= p – Rudie

2

如果你希望它是完全一樣的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 
) 

不夠完美不夠好。

5

這是我的改進版本,我認爲是完整的。只有錯誤和警告沒有被考慮在內。

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

哇。你實際上偷了我的答案。優雅。 – Rudie

+0

工程就像一個魅力! TNX。 –

1

首先我要感謝@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); 
    } 
} 
0

如果你不需要數組的最後4場,你可以簡單地使用date_parse_from_format的的strtotime而不是得到同樣的結果。 例如:

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

這適用於PHP 5.2