mirror of
https://github.com/e107inc/e107.git
synced 2025-01-17 12:48:24 +01:00
Replaced e_date::strptime() with eShims::strptime()
- NEW: Added \e107\Shims\Internal\StrptimeTrait, which implements PHP internal function strptime(). On not-Windows, the built-in function is called. If that function fails or if the operating system is Windows, the alternative pure PHP implementation is attempted. The first successful call is returned, or false if none are successful. - MOD: Deprecated e_date::strptime() in favor of eShims::strptime() - FIX: License misatributed for e_date::strptime() (now eShims::strptime()). The library used was public domain, not CC BY-NC-SA 2.0 FR by Lionel Sauron. - MOD: Removed STRPTIME_COMPAT constant now that eShims::strptime() exists - MOD: Removed support for calling e_date::strptime() with: - a localized full month name - a localized abbreviated month name - AM or PM - am or pm because these features were only implemented in Windows mode (STRPTIME_COMPAT). - MOD: php_compatibility_handler.php now defines global strptime() using the eShims::strptime() implementation - NEW: Test all(?) the possibilities of eShims::strptime()
This commit is contained in:
parent
f93ab61372
commit
f23aec7395
163
e107_handlers/Shims/Internal/StrptimeTrait.php
Normal file
163
e107_handlers/Shims/Internal/StrptimeTrait.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
/**
|
||||
* e107 website system
|
||||
*
|
||||
* Copyright (C) 2008-2020 e107 Inc (e107.org)
|
||||
* Released under the terms and conditions of the
|
||||
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
|
||||
*
|
||||
* Shims for PHP internal functions
|
||||
* strptime()
|
||||
*/
|
||||
|
||||
namespace e107\Shims\Internal;
|
||||
|
||||
trait StrptimeTrait
|
||||
{
|
||||
/**
|
||||
* Parse a time/date generated with strftime()
|
||||
*
|
||||
* Resilient replacement for PHP internal strptime()
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.strptime.php what this function approximates
|
||||
* @param string $date The string to parse (e.g. returned from strftime()).
|
||||
* @param string $format The format used in date (e.g. the same as used in strftime()).
|
||||
* @return array|bool Returns FALSE on failure.
|
||||
* The following parameters are returned in the array:
|
||||
* "tm_sec" Seconds after the minute (0-61)
|
||||
* "tm_min" Minutes after the hour (0-59)
|
||||
* "tm_hour" Hour since midnight (0-23)
|
||||
* "tm_mday" Day of the month (1-31)
|
||||
* "tm_mon" Months since January (0-11)
|
||||
* "tm_year" Years since 1900
|
||||
* "tm_wday" Days since Sunday (0-6)
|
||||
* "tm_yday" Days since January 1 (0-365)
|
||||
* "unparsed" the date part which was not recognized using the specified format
|
||||
*/
|
||||
public static function strptime($date, $format)
|
||||
{
|
||||
$result = false;
|
||||
if (function_exists('strptime'))
|
||||
$result = strptime($date, $format);
|
||||
if (!is_array($result))
|
||||
$result = self::strptime_alt($date, $format);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a time/date generated with strftime()
|
||||
*
|
||||
* Alternative implementation based on public domain library:
|
||||
* https://github.com/Polycademy/upgradephp/
|
||||
*
|
||||
* @param string $date
|
||||
* @param string $format
|
||||
* @return array|bool
|
||||
*/
|
||||
public static function strptime_alt($date, $format)
|
||||
{
|
||||
static $expand = array('%D' => '%m/%d/%y', '%T' => '%H:%M:%S',);
|
||||
static $map_r = array(
|
||||
'%S' => 'tm_sec',
|
||||
'%M' => 'tm_min',
|
||||
'%H' => 'tm_hour',
|
||||
'%I' => 'tm_hour',
|
||||
'%d' => 'tm_mday',
|
||||
'%m' => 'tm_mon',
|
||||
'%Y' => 'tm_year',
|
||||
'%y' => 'tm_year',
|
||||
'%W' => 'tm_wday',
|
||||
'%D' => 'tm_yday',
|
||||
'%u' => 'unparsed',
|
||||
);
|
||||
|
||||
$fullmonth = array();
|
||||
$abrevmonth = array();
|
||||
|
||||
for ($i = 1; $i <= 12; $i++)
|
||||
{
|
||||
$k = strftime('%B', mktime(0, 0, 0, $i));
|
||||
$fullmonth[$k] = $i;
|
||||
|
||||
$j = strftime('%b', mktime(0, 0, 0, $i));
|
||||
$abrevmonth[$j] = $i;
|
||||
}
|
||||
|
||||
|
||||
#-- transform $format into extraction regex
|
||||
$format = str_replace(array_keys($expand), array_values($expand), $format);
|
||||
$preg = preg_replace('/(%\w)/', '(\w+)', preg_quote($format));
|
||||
|
||||
#-- record the positions of all STRFCMD-placeholders
|
||||
preg_match_all('/(%\w)/', $format, $positions);
|
||||
$positions = $positions[1];
|
||||
|
||||
$vals = array();
|
||||
|
||||
#-- get individual values
|
||||
if (preg_match("#$preg#", $date, $extracted))
|
||||
{
|
||||
#-- get values
|
||||
foreach ($positions as $pos => $strfc)
|
||||
{
|
||||
$v = $extracted[$pos + 1];
|
||||
#-- add
|
||||
if (isset($map_r[$strfc]))
|
||||
{
|
||||
$n = $map_r[$strfc];
|
||||
$vals[$n] = ($v > 0) ? (int)$v : $v;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isset($vals['unparsed'])) $vals['unparsed'] = '';
|
||||
$vals['unparsed'] .= $v . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
#-- fixup some entries
|
||||
//$vals["tm_wday"] = $names[ substr($vals["tm_wday"], 0, 3) ];
|
||||
if ($vals['tm_year'] >= 1900)
|
||||
{
|
||||
$vals['tm_year'] -= 1900;
|
||||
}
|
||||
elseif ($vals['tm_year'] > 0)
|
||||
{
|
||||
$vals['tm_year'] += 100;
|
||||
}
|
||||
|
||||
if ($vals['tm_mon'])
|
||||
{
|
||||
$vals['tm_mon'] -= 1;
|
||||
}
|
||||
|
||||
if (!isset($vals['tm_sec']))
|
||||
{
|
||||
$vals['tm_sec'] = 0;
|
||||
}
|
||||
|
||||
if (!isset($vals['tm_min']))
|
||||
{
|
||||
$vals['tm_min'] = 0;
|
||||
}
|
||||
|
||||
if (!isset($vals['tm_hour']))
|
||||
{
|
||||
$vals['tm_hour'] = 0;
|
||||
}
|
||||
|
||||
|
||||
if (!isset($vals['unparsed']))
|
||||
{
|
||||
$vals['unparsed'] = '';
|
||||
}
|
||||
|
||||
$unxTimestamp = mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], ($vals['tm_mon'] + 1), $vals['tm_mday'], ($vals['tm_year'] + 1900));
|
||||
|
||||
$vals['tm_wday'] = (int)strftime('%w', $unxTimestamp); // Days since Sunday (0-6)
|
||||
$vals['tm_yday'] = (strftime('%j', $unxTimestamp) - 1); // Days since January 1 (0-365)
|
||||
}
|
||||
|
||||
return !empty($vals) ? $vals : false;
|
||||
|
||||
}
|
||||
}
|
@ -14,4 +14,5 @@ namespace e107\Shims;
|
||||
trait InternalShimsTrait
|
||||
{
|
||||
use Internal\ReadfileTrait;
|
||||
use Internal\StrptimeTrait;
|
||||
}
|
@ -770,184 +770,22 @@ class e_date
|
||||
|
||||
|
||||
/**
|
||||
* This work of Lionel SAURON (http://sauron.lionel.free.fr:80) is licensed under the
|
||||
* Creative Commons Attribution-Noncommercial-Share Alike 2.0 France License.
|
||||
* To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.0/fr/
|
||||
* or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
|
||||
*
|
||||
* http://snipplr.com/view/4964/emulate-php-5-for-backwards-compatibility/
|
||||
*
|
||||
* Parse a date generated with strftime().
|
||||
*
|
||||
* @author Lionel SAURON and reworked by e107 Inc. for month names.
|
||||
* @version 1.0
|
||||
* @public
|
||||
* Parse a time/date generated with strftime()
|
||||
* With extra output keys for localized month
|
||||
*
|
||||
* @deprecated Use eShims::strptime() instead
|
||||
* @see eShims::strptime()
|
||||
* @param string $str date string to parse (e.g. returned from strftime()).
|
||||
* @param $format
|
||||
* @return array|bool Returns an array with the <code>$str</code> parsed, or <code>false</code> on error.
|
||||
*/
|
||||
public function strptime($str, $format)
|
||||
{
|
||||
if(STRPTIME_COMPAT !== TRUE && function_exists('strptime')) // Unix Only.
|
||||
{
|
||||
$vals = strptime($str,$format); // PHP5 is more accurate than below.
|
||||
$vals['tm_amon'] = strftime('%b', mktime(0,0,0, $vals['tm_mon'] +1) );
|
||||
$vals['tm_fmon'] = strftime('%B', mktime(0,0,0, $vals['tm_mon'] +1) );
|
||||
return $vals;
|
||||
}
|
||||
|
||||
// Below is for Windows machines. (XXX TODO - Currently not as accurate as Linux PHP5 strptime() function above. )
|
||||
|
||||
static $expand = array('%D'=>'%m/%d/%y', '%T'=>'%H:%M:%S', );
|
||||
|
||||
$ampm = (preg_match("/%l|%I|%p|%P/",$format)) ? 'true' : 'false';
|
||||
|
||||
static $map_r = array(
|
||||
'%S' =>'tm_sec',
|
||||
'%M' =>'tm_min',
|
||||
'%H' =>'tm_hour',
|
||||
'%I' =>'tm_hour',
|
||||
'%d' =>'tm_mday',
|
||||
'%m' =>'tm_mon',
|
||||
'%Y' =>'tm_year',
|
||||
'%y' =>'tm_year',
|
||||
'%W' =>'tm_wday',
|
||||
'%D' =>'tm_yday',
|
||||
'%B' =>'tm_fmon', // full month-name
|
||||
'%b' =>'tm_amon', // abrev. month-name
|
||||
'%p' =>'tm_AMPM', // AM/PM
|
||||
'%P' =>'tm_ampm', // am/pm
|
||||
'%u' =>'unparsed',
|
||||
|
||||
);
|
||||
|
||||
$fullmonth = array();
|
||||
$abrevmonth = array();
|
||||
|
||||
for ($i = 1; $i <= 12; $i++)
|
||||
{
|
||||
$k = strftime('%B',mktime(0,0,0,$i));
|
||||
$fullmonth[$k] = $i;
|
||||
|
||||
$j = strftime('%b',mktime(0,0,0,$i));
|
||||
$abrevmonth[$j] = $i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#-- transform $format into extraction regex
|
||||
$format = str_replace(array_keys($expand), array_values($expand), $format);
|
||||
$preg = preg_replace('/(%\w)/', '(\w+)', preg_quote($format));
|
||||
|
||||
#-- record the positions of all STRFCMD-placeholders
|
||||
preg_match_all('/(%\w)/', $format, $positions);
|
||||
$positions = $positions[1];
|
||||
|
||||
$vals = array();
|
||||
|
||||
#-- get individual values
|
||||
if (preg_match("#$preg#", $str, $extracted))
|
||||
{
|
||||
#-- get values
|
||||
foreach ($positions as $pos => $strfc)
|
||||
{
|
||||
$v = $extracted[$pos + 1];
|
||||
#-- add
|
||||
if (isset($map_r[$strfc]))
|
||||
{
|
||||
$n = $map_r[$strfc];
|
||||
$vals[$n] = ($v > 0) ? (int) $v : $v;
|
||||
}
|
||||
else
|
||||
{
|
||||
$vals['unparsed'] .= $v.' ';
|
||||
}
|
||||
}
|
||||
|
||||
#-- fixup some entries
|
||||
//$vals["tm_wday"] = $names[ substr($vals["tm_wday"], 0, 3) ];
|
||||
if ($vals['tm_year'] >= 1900)
|
||||
{
|
||||
$vals['tm_year'] -= 1900;
|
||||
}
|
||||
elseif ($vals['tm_year'] > 0)
|
||||
{
|
||||
$vals['tm_year'] += 100;
|
||||
}
|
||||
|
||||
if ($vals['tm_mon'])
|
||||
{
|
||||
$vals['tm_mon'] -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(isset($fullmonth[$vals['tm_fmon']]))
|
||||
{
|
||||
$vals['tm_mon'] = $fullmonth[$vals['tm_fmon']];
|
||||
}
|
||||
elseif(isset($abrevmonth[$vals['tm_amon']]))
|
||||
{
|
||||
$vals['tm_mon'] = $abrevmonth[$vals['tm_amon']];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($ampm && isset($vals['tm_hour']))
|
||||
{
|
||||
if($vals['tm_hour'] == 12 && ($vals['tm_AMPM'] == 'AM' || $vals['tm_ampm'] == 'am'))
|
||||
{
|
||||
$vals['tm_hour'] = 0;
|
||||
}
|
||||
|
||||
if($vals['tm_hour'] < 12 && ($vals['tm_AMPM'] == 'PM' || $vals['tm_ampm'] == 'pm'))
|
||||
{
|
||||
$vals['tm_hour'] = intval($vals['tm_hour']) + 12;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//$vals['tm_sec'] -= 1; always increasing tm_sec + 1 ??????
|
||||
|
||||
#-- calculate wday/yday
|
||||
//$vals['tm_mon'] = $vals['tm_mon'] + 1; // returns months from 0 - 11 so we need to +1
|
||||
|
||||
if (!isset($vals['tm_sec']))
|
||||
{
|
||||
$vals['tm_sec'] = 0;
|
||||
}
|
||||
|
||||
if (!isset($vals['tm_min']))
|
||||
{
|
||||
$vals['tm_min'] = 0;
|
||||
}
|
||||
|
||||
if (!isset($vals['tm_hour']))
|
||||
{
|
||||
$vals['tm_hour'] = 0;
|
||||
}
|
||||
|
||||
|
||||
if (!isset($vals['unparsed']))
|
||||
{
|
||||
$vals['unparsed'] = '';
|
||||
}
|
||||
|
||||
$unxTimestamp = mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], ($vals['tm_mon'] + 1), $vals['tm_mday'], ($vals['tm_year'] + 1900));
|
||||
|
||||
$vals['tm_amon'] = strftime('%b', mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], $vals['tm_mon'] + 1));
|
||||
$vals['tm_fmon'] = strftime('%B', mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], $vals['tm_mon'] + 1));
|
||||
$vals['tm_wday'] = (int) strftime('%w', $unxTimestamp); // Days since Sunday (0-6)
|
||||
$vals['tm_yday'] = (strftime('%j', $unxTimestamp) - 1); // Days since January 1 (0-365)
|
||||
|
||||
|
||||
//var_dump($vals, $str, strftime($format, $unxTimestamp), $unxTimestamp);
|
||||
}
|
||||
|
||||
return !empty($vals) ? $vals : false;
|
||||
|
||||
}
|
||||
$vals = eShims::strptime($str, $format); // PHP5 is more accurate than below.
|
||||
$vals['tm_amon'] = strftime('%b', mktime(0, 0, 0, $vals['tm_mon'] + 1));
|
||||
$vals['tm_fmon'] = strftime('%B', mktime(0, 0, 0, $vals['tm_mon'] + 1));
|
||||
return $vals;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -26,17 +26,12 @@ if (!defined('e107_INIT'))
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
if (!function_exists('strptime'))
|
||||
{
|
||||
|
||||
define('STRPTIME_COMPAT', true);
|
||||
function strptime($str, $format)
|
||||
function strptime($date, $format)
|
||||
{
|
||||
return e107::getDate()->strptime($str,$format);
|
||||
}
|
||||
|
||||
return eShims::strptime($date, $format);
|
||||
}
|
||||
}
|
||||
|
||||
//PHP < 5.2 compatibility
|
||||
|
94
e107_tests/tests/unit/e107/Shims/StrptimeTest.php
Normal file
94
e107_tests/tests/unit/e107/Shims/StrptimeTest.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/**
|
||||
* e107 website system
|
||||
*
|
||||
* Copyright (C) 2008-2020 e107 Inc (e107.org)
|
||||
* Released under the terms and conditions of the
|
||||
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
|
||||
*
|
||||
*/
|
||||
|
||||
namespace e107\Shims;
|
||||
|
||||
class StrptimeTest extends \Codeception\Test\Unit
|
||||
{
|
||||
public function testStrptimeDefault()
|
||||
{
|
||||
$this->testStrptimeImplementation([\e107\Shims\InternalShims::class, 'strptime']);
|
||||
}
|
||||
|
||||
public function testStrptimeDefaultLegacy()
|
||||
{
|
||||
$this->testStrptimeImplementation([\eShims::class, 'strptime']);
|
||||
}
|
||||
|
||||
public function testStrptimeAlt()
|
||||
{
|
||||
$this->testStrptimeImplementation([\e107\Shims\InternalShims::class, 'strptime_alt']);
|
||||
}
|
||||
|
||||
protected function testStrptimeImplementation($implementation)
|
||||
{
|
||||
$this->testStrptimeDateOnly($implementation);
|
||||
$this->testStrptimeDateTime($implementation);
|
||||
$this->testStrptimeUnparsed($implementation);
|
||||
$this->testStrptimeInvalid($implementation);
|
||||
}
|
||||
|
||||
protected function testStrptimeDateOnly($implementation)
|
||||
{
|
||||
$actual = call_user_func($implementation, '2018/05/13', '%Y/%m/%d');
|
||||
$expected = array(
|
||||
'tm_year' => 118,
|
||||
'tm_mon' => 4,
|
||||
'tm_mday' => 13,
|
||||
'tm_sec' => 0,
|
||||
'tm_min' => 0,
|
||||
'tm_hour' => 0,
|
||||
'unparsed' => '',
|
||||
'tm_wday' => 0,
|
||||
'tm_yday' => 132,
|
||||
);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
protected function testStrptimeDateTime($implementation)
|
||||
{
|
||||
$actual = call_user_func($implementation, '2018/05/13 20:10', '%Y/%m/%d %H:%M');
|
||||
$expected = array(
|
||||
'tm_year' => 118,
|
||||
'tm_mon' => 4,
|
||||
'tm_mday' => 13,
|
||||
'tm_hour' => 20,
|
||||
'tm_min' => 10,
|
||||
'tm_sec' => 0,
|
||||
'unparsed' => '',
|
||||
'tm_wday' => 0,
|
||||
'tm_yday' => 132,
|
||||
);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
protected function testStrptimeUnparsed($implementation)
|
||||
{
|
||||
$actual = call_user_func($implementation, '1607-09-04 08:10 PM', '%Y-%m-%d %l:%M %P');
|
||||
$expected = array(
|
||||
'tm_year' => 1707,
|
||||
'tm_mon' => 8,
|
||||
'tm_mday' => 4,
|
||||
'tm_hour' => 0,
|
||||
'tm_min' => 10,
|
||||
'tm_sec' => 0,
|
||||
'unparsed' => '08 PM ',
|
||||
'tm_wday' => 2,
|
||||
'tm_yday' => 246,
|
||||
);
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
protected function testStrptimeInvalid($implementation)
|
||||
{
|
||||
$actual = call_user_func($implementation, 'garbage', '%Y-%m-%d');
|
||||
$this->assertFalse($actual);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user