Re: Date: additional ISO format support
| From: | Alan Knowles | Date: | Thu, 09 Oct 2003 04:48:54 +0000 |
| Subject: | Re: Date: additional ISO format support | ||
| References: | 1 | Groups: | php.pear.dev |
| Request: | Send a blank email to pear-dev+get-22507@lists.php.net to get a copy of this message | ||
Can you file a bug report - add a link to email on marc.theaimsgroup.org
That way it wont get lost in the mailing list archives.. - if nobody has added this already.
Regards
Alan
Marshall Roch wrote:
I've finally got working support for additional ISO 8601 formats for Date. It now can input and output:-- Can you help out? Need Consulting Services or Know of a Job? http://www.akbkhome.comDATE_FORMAT_ISO: YYYY-MM-DD HH:MM:SS (already exists) DATE_FORMAT_ISO_BASIC: YYYYMMDDTHHMMSS-HHMM (new)DATE_FORMAT_ISO_EXTENDED: YYYY-MM-DDTHH:MM:SS-HH:MM (new)DATE_FORMAT_TIMESTAMP: YYYYMMDDHHMMSS (already exists) DATE_FORMAT_UNIXTIME: <secs> (already exists)Several points: o All 3 ISO formats are basically the same, and as such, can be parsed with one regex (more efficient than testing for each format individually). o "Basic" and "Extended" come from the ISO spec. I couldn't find names for these formats in Perl. o Each DATE_FORMAT_* constant specifies a different output format. Even though the /parser/ doesn't differentiate between the 3 ISO formats, the builder does. o To support timezone offsets, I added a toUTCbyOffset() method. Since Date can't look up a timestamp by an offset, I had to convert the datetime to UTC. Unit Test: o To test my changes, I began a PHPUnit test for Date.php. It is not complete, but it checks enough methods to be sure that the new date formats work. Fixed bugs: o I found two bugs while writing the unit test. See the bug tracker[1] o Possible bug: the current Date's regexps do not make sure that the date string contains only a date. I've added ^ and $ to make sure of this. Attachments: o Date-ISO.diff - adds date formats and fixes 2 bugs o testunit_date.php - goes in the tests/ folder, PHPUnit test for Date o testunit.php - goes in tests/, displays Date and Date_Span unit tests -- Marshall Roch [1] http://pear.php.net/bugs/search.php?cmd=display&status=Open&bug_type[]=Date ------------------------------------------------------------------------ Index: Date.php =================================================================== RCS file: /repository/pear/Date/Date.php,v retrieving revision 1.13 diff -u -r1.13 Date.php --- Date.php 25 May 2003 18:59:38 -0000 1.13 +++ Date.php 5 Oct 2003 19:12:15 -0000 @@ -30,13 +30,21 @@ */ define('DATE_FORMAT_ISO', 1); /** + * "YYYYMMSSTHHMMSS(Z|(+/-)HHMM)?" + */ +define('DATE_FORMAT_ISO_BASIC', 2); +/** + * "YYYY-MM-SSTHH:MM:SS(Z|(+/-)HH:MM)?" + */ +define('DATE_FORMAT_ISO_EXTENDED', 3); +/** * "YYYYMMDDHHMMSS" */ -define('DATE_FORMAT_TIMESTAMP', 2); +define('DATE_FORMAT_TIMESTAMP', 4); /** * long int, seconds since the unix epoch */ -define('DATE_FORMAT_UNIXTIME', 3); +define('DATE_FORMAT_UNIXTIME', 5); /** * Generic date handling class for PEAR. @@ -95,7 +103,7 @@* Creates a new Date Object * initialized to the current date/time in the * system default time zone by default. A date optionally - * passed in may be in the ISO, TIMESTAMP or UNIXTIME format, + * passed in may be in the ISO 8601, TIMESTAMP or UNIXTIME format, * or another Date object. * * @access public@@ -109,16 +117,20 @@$this->setDate(date('Y-m-d H:i:s')); } elseif (is_object($date) && (get_class($date) == 'date')) { $this->copy($date); - } elseif (preg_match('/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/', $date)) { + // matches DATE_FORMAT_ISO, DATE_FORMAT_ISO_BASIC, and DATE_FORMAT_ISO_EXTENDED + } elseif (preg_match('/^(\d{4})-?(\d{2})-?(\d{2})[T\s](\d{2}):?(\d{2}):?(\d{2})(Z|[\+\-]\d{2}\d{2})?$/i', $date)) { $this->setDate($date); - } elseif (preg_match('/\d{14}/',$date)) { + } elseif (preg_match('/^\d{14}$/',$date)) { $this->setDate($date,DATE_FORMAT_TIMESTAMP); - } elseif (preg_match('/\d{4}-\d{2}-\d{2}/', $date)) { + } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) { $this->setDate($date.' 00:00:00'); - } elseif (preg_match('/\d{8}/',$date)) { + } elseif (preg_match('/^\d{8}$/',$date)) { $this->setDate($date.'000000',DATE_FORMAT_TIMESTAMP); - } else { + } elseif (is_numeric($date)) { $this->setDate($date,DATE_FORMAT_UNIXTIME); + } else { + include_once 'PEAR.php'; + PEAR::raiseError("Invalid/unrecognized date format!"); } } @@ -136,13 +148,25 @@ { switch ($format) { case DATE_FORMAT_ISO: - if (ereg("([0-9]{4})-([0-9]{2})-([0-9]{2})[ ]([0-9]{2}):([0-9]{2}):([0-9]{2})",$date,$regs)) { + case DATE_FORMAT_ISO_BASIC: + case DATE_FORMAT_ISO_EXTENDED: + if (preg_match('/^(\d{4})-?(\d{2})-?(\d{2})[T\s](\d{2}):?(\d{2}):?(\d{2})(Z|[\+\-]\d{2}\d{2})?$/i', $date, $regs)) {+$this->year = $regs[1]; $this->month = $regs[2]; $this->day = $regs[3]; $this->hour = $regs[4]; $this->minute = $regs[5]; $this->second = $regs[6];++ // if an offset is defined, convert time to UTC + // Date currently can't set a timezone only by offset, + // so it has to store it as UTC + if ($regs[7]) { + $this->toUTCbyOffset($regs[7]); + }++ } else { $this->year = 0; $this->month = 1;@@ -191,11 +215,25 @@case DATE_FORMAT_ISO: return $this->format("%Y-%m-%d %T"); break; + case DATE_FORMAT_ISO_BASIC: + $format = "%Y%m%dT%H%M%S"; + if ($this->tz->getID() == 'UTC') { + $format .= "Z"; + } + return $this->format($format); + break; + case DATE_FORMAT_ISO_EXTENDED: + $format = "%Y-%m-%dT%H:%M:%S"; + if ($this->tz->getID() == 'UTC') { + $format .= "Z"; + } + return $this->format($format); + break; case DATE_FORMAT_TIMESTAMP: return $this->format("%Y%m%d%H%M%S"); break; case DATE_FORMAT_UNIXTIME: - return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year, 0); + return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); break; } }@@ -293,7 +331,7 @@$output .= sprintf("%02d/%02d/%02d",$this->month,$this->day,$this->year); break; case "e": - $output .= $this->day; + $output .= $this->day * 1; // get rid of leading zero break; case "E": $output .= Date_Calc::dateToDays($this->day,$this->month,$this->year);@@ -515,6 +553,37 @@$tz = Date_TimeZone::getDefault(); } $this->convertTZ($tz); + }++ function toUTCbyOffset($offset) + {++ if ($offset == "Z" || $offset == "+00:00" || $offset == "+0000") { + $this->toUTC(); + return true; + }++ if (preg_match('/([\+\-])(\d{2}):?(\d{2})/', $offset, $regs)) { + // convert offset to seconds + $hours = (int) $regs[2]; + $mins = (int) $regs[3]; + $offset = ($hours * 3600) + ($mins * 60);++ if ($regs[1] == "-") { + $offset *= -1; + }++ if ($offset > 0) { + $this->subtractSeconds(intval($offset / 1000)); + } else { + $this->addSeconds(intval(abs($offset) / 1000)); + }++ $this->tz = new Date_TimeZone('UTC'); + return true; + }++ return false; } /**------------------------------------------------------------------------ <?php echo "<pre>"; require_once 'PHPUnit.php'; require_once 'testunit_date.php'; require_once 'testunit_date_span.php'; echo "</pre>"; ?> ------------------------------------------------------------------------ <?php // vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: // +----------------------------------------------------------------------+// | PHP Version 4 |// +----------------------------------------------------------------------+// | Copyright (c) 1997-2003 The PHP Group |// +----------------------------------------------------------------------+// | This source file is subject to version 2.02 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. |// | If you did not receive a copy of the PHP license and are unable to |// | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. |// +----------------------------------------------------------------------+// | Author: Marshall Roch <mroch@php.net> |// +----------------------------------------------------------------------+ // // $Id$ // require_once '../Date.php'; require_once 'PHPUnit.php'; class Date_Test extends PHPUnit_TestCase {var $time;function Date_Test($name) { $this->PHPUnit_TestCase($name); }function setUp() { $this->time = new Date("2003-10-04 14:03:24"); }function tearDown() { unset($this->time); }function testDateNull() { $time = new Date(); $this->assertEquals( date('Y-m-d H:i:s'), sprintf('%04d-%02d-%02d %02d:%02d:%02d', $time->year, $time->month, $time->day, $time->hour, $time->minute, $time->second) ); }function testDateCopy() { $temp = new Date($this->time); $this->assertEquals($temp, $this->time); }function testDateISO() { $temp = new Date("2003-10-04 14:03:24"); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $temp->year, $temp->month, $temp->day, $temp->hour, $temp->minute, $temp->second) ); }function testDateISOBasic() { $temp = new Date("20031004T140324", DATE_FORMAT_ISO_BASIC); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $temp->year, $temp->month, $temp->day, $temp->hour, $temp->minute, $temp->second) ); }function testDateISOExtended() { $temp = new Date("2003-10-04T14:03:24", DATE_FORMAT_ISO_EXTENDED); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $temp->year, $temp->month, $temp->day, $temp->hour, $temp->minute, $temp->second) ); }function testDateISOTimestamp() { $temp = new Date("20031004140324", DATE_FORMAT_ISO_TIMESTAMP); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $temp->year, $temp->month, $temp->day, $temp->hour, $temp->minute, $temp->second) ); }function testDateUnixtime() { $temp = new Date(strtotime("2003-10-04 14:03:24"), DATE_FORMAT_UNIXTIME); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $temp->year, $temp->month, $temp->day, $temp->hour, $temp->minute, $temp->second) ); }function testSetDateISO() { $this->time->setDate("2003-10-04 14:03:24", DATE_FORMAT_ISO); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $this->time->year, $this->time->month, $this->time->day, $this->time->hour, $this->time->minute, $this->time->second) ); }function testSetDateISOBasic() { $this->time->setDate("20031004T140324", DATE_FORMAT_ISO_BASIC); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $this->time->year, $this->time->month, $this->time->day, $this->time->hour, $this->time->minute, $this->time->second) ); }function testSetDateISOExtended() { $this->time->setDate("2003-10-04T14:03:24", DATE_FORMAT_ISO_EXTENDED); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $this->time->year, $this->time->month, $this->time->day, $this->time->hour, $this->time->minute, $this->time->second) ); }function testSetDateTimestamp() { $this->time->setDate("20031004140324", DATE_FORMAT_TIMESTAMP); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $this->time->year, $this->time->month, $this->time->day, $this->time->hour, $this->time->minute, $this->time->second) ); }function testSetDateUnixtime() { $this->time->setDate(strtotime("2003-10-04 14:03:24"), DATE_FORMAT_UNIXTIME); $this->assertEquals( '2003-10-04 14:03:24', sprintf('%04d-%02d-%02d %02d:%02d:%02d', $this->time->year, $this->time->month, $this->time->day, $this->time->hour, $this->time->minute, $this->time->second) ); }function testGetDateISO() { $date = $this->time->getDate(DATE_FORMAT_ISO); $this->assertEquals('2003-10-04 14:03:24', $date); }function testGetDateISOBasic() { $date = $this->time->getDate(DATE_FORMAT_ISO_BASIC); $this->assertEquals('20031004T140324Z', $date); }function testGetDateISOExtended() { $date = $this->time->getDate(DATE_FORMAT_ISO_EXTENDED); $this->assertEquals('2003-10-04T14:03:24Z', $date); }function testGetDateTimestamp() { $date = $this->time->getDate(DATE_FORMAT_TIMESTAMP); $this->assertEquals('20031004140324', $date); }function testGetDateUnixtime() { $date = $this->time->getDate(DATE_FORMAT_UNIXTIME); $this->assertEquals(strtotime('2003-10-04 14:03:24'), $date); }function testFormat() { $codes = array( 'a' => 'Sat', 'A' => 'Saturday', 'b' => 'Oct', 'B' => 'October', 'C' => '20', 'd' => '04', 'D' => '10/04/2003', 'e' => '4', 'H' => '14', 'I' => '02', 'j' => '277', 'm' => '10', 'M' => '03', 'n' => "\n", 'O' => '+00:00', 'o' => '+00:00', 'p' => 'pm', 'P' => 'PM', 'r' => '02:03:24 PM', 'R' => '14:03', 'S' => '24', 't' => "\t", 'T' => '14:03:24', 'w' => '6', 'U' => '40', 'y' => '03', 'Y' => '2003', '%' => '%' );foreach ($codes as $code => $expected) { $this->assertEquals( "$code: $expected", $this->time->format("$code: %$code") ); } }function testToUTCbyOffset() { $this->time->setTZbyID('EST'); $this->time->toUTC(); $temp = new Date("2003-10-04 14:03:24"); $temp->toUTCbyOffset("-05:00");$this->assertEquals($temp, $this->time); }} // runs the tests $suite = new PHPUnit_TestSuite("Date_Test"); $result = PHPUnit::run($suite); // prints the tests echo $result->toString(); ?>