由于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] =>
)
答案 0 :(得分: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:
阵 ( [月] =&gt; 03 [day] =&gt; 23 [year] =&gt; 2011 )
示例2:
<?php
print_r(date_parse_from_format('yyyy.mm.dd HH:ii:ss','2011.03.23 12:03:00'));
?>
输出2:
阵 ( [year] =&gt; 2011 [月] =&gt; 03 [day] =&gt; 23 [小时] =&gt; 12 [分钟] =&gt; 03 [second] =&gt; 00 )
答案 1 :(得分: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;
}
}
答案 2 :(得分: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
)
不完整和不完美。
答案 3 :(得分: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 );
}
}
答案 4 :(得分:0)
如果您不需要数组的最后4个字段,您可以使用strtotime而不是date_parse_from_format来获得相同的结果。 例如:
$textdate = $date;
$datetime = strtotime($textdate);
$datearray = date_parse($datetime);
print_r($datearray);
这适用于PHP 5.2