From: Andrey Kutejko Date: Sun, 11 Aug 2013 11:15:57 +0000 (+0300) Subject: time formats. improve format parsing. X-Git-Tag: 0.5~73 X-Git-Url: https://git.andy128k.dev/?a=commitdiff_plain;h=7256562461cf674106f089ebda92ce693b6268b7;p=ipf.git time formats. improve format parsing. --- diff --git a/composer.json b/composer.json index 2cbf46a..d8476fd 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "pear/archive_tar": "1.3.*", "mustangostang/spyc": "0.5.*", "andy128k/missing-tools": "dev-master", + "andy128k/pegp": "dev-master", "andy128k/routeexpression": "dev-master", "andy128k/ipf-template": "dev-master" }, diff --git a/composer.lock b/composer.lock index 274009c..7d8e7aa 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "57c4d731e20fcf4857e1a79b7884e303", + "hash": "989d177219589997f54f98aa1bfaca9c", "packages": [ { "name": "andy128k/ipf-template", @@ -77,6 +77,35 @@ "description": "Miscellaneous utilities", "time": "2013-08-10 18:31:29" }, + { + "name": "andy128k/pegp", + "version": "dev-master", + "source": { + "type": "git", + "url": "git://git.andy128k.net/pegp.git", + "reference": "115cde2f198fc306ef3ddf5fe6d4db416bf4cd51" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Kutejko", + "email": "andy128k@gmail.com" + } + ], + "description": "PEG", + "time": "2013-08-11 10:41:26" + }, { "name": "andy128k/routeexpression", "version": "dev-master", @@ -635,6 +664,7 @@ "minimum-stability": "stable", "stability-flags": { "andy128k/missing-tools": 20, + "andy128k/pegp": 20, "andy128k/routeexpression": 20, "andy128k/ipf-template": 20 }, diff --git a/ipf/form/widget/dateinput.php b/ipf/form/widget/dateinput.php index 549d1b2..2a79814 100644 --- a/ipf/form/widget/dateinput.php +++ b/ipf/form/widget/dateinput.php @@ -3,13 +3,13 @@ class IPF_Form_Widget_DateInput extends IPF_Form_Widget_Input { public $input_type = 'text'; - public $format = IPF_Format::DATE_ISO8601; + public $format = IPF_Format::DATE_DEFAULT; public function __construct($attrs=array()) { $format = ArrayTools::pop($attrs, 'format'); if ($format) - $this->format = is_string($format) ? IPF_Format::flagsFromString($format) : $format; + $this->format = is_string($format) ? IPF_Format::dateFlagsFromString($format) : $format; parent::__construct($attrs); } @@ -17,7 +17,7 @@ class IPF_Form_Widget_DateInput extends IPF_Form_Widget_Input public function render($name, $value, $extra_attrs=array()) { $extra_attrs['class'] = 'dateinput'; - $extra_attrs['data-format'] = IPF_Format::makeFormat($this->format, array('yy', 'mm', 'dd')); + $extra_attrs['data-format'] = IPF_Format::makeDateFormat($this->format, IPF_Format::$pickerControls); return parent::render($name, $value, $extra_attrs); } diff --git a/ipf/format.php b/ipf/format.php index 77c5041..de455b8 100644 --- a/ipf/format.php +++ b/ipf/format.php @@ -2,105 +2,178 @@ final class IPF_Format { - const DATE_BIG_ENDIAN = 0; - const DATE_LITTLE_ENDIAN = 4; - const DATE_MIDDLE_ENDIAN = 8; + const DATE_BIG_ENDIAN = 0; + const DATE_LITTLE_ENDIAN = 4; + const DATE_MIDDLE_ENDIAN = 8; - const DATE_SLASHES = 0; - const DATE_DOTS = 1; - const DATE_HYPHENS = 2; - const DATE_SPACES = 3; + const DATE_SLASHES = 0; + const DATE_DOTS = 1; + const DATE_HYPHENS = 2; + const DATE_SPACES = 3; - const DATE_ISO8601 = 2; // DATE_BIG_ENDIAN | DATE_HYPHENS - const DATE_US = 8; // DATE_MIDDLE_ENDIAN | DATE_SLASHES - const DATE_EUROPEAN = 5; // DATE_LITTLE_ENDIAN | DATE_DOTS + const TIME_24HOURS = 16; + const TIME_12HOURS = 32; + const TIME_12HOURS_UPPER = 32; + const TIME_12HOURS_LOWER = 48; - public static $dateSeparators; + const TIME_SECONDS = 64; + + const DATE_DEFAULT = 2; // DATE_BIG_ENDIAN | DATE_HYPHENS + const DATE_US = 8; // DATE_MIDDLE_ENDIAN | DATE_SLASHES + const DATE_EUROPEAN = 5; // DATE_LITTLE_ENDIAN | DATE_DOTS + + const DATETIME_DEFAULT = 18; // DATE_DEFAULT | TIME_24HOURS + const DATETIME_US = 40; // DATE_US | TIME_12HOURS + const DATETIME_EUROPEAN = 21; // DATE_EUROPEAN | TIME_24HOURS + + public static $strftimeControls, $dateControls, $pickerControls; + + private static $dateSeparators; public static function init() { self::$dateSeparators = array('/', '.', '-', ' '); + + self::$strftimeControls = array('%Y', '%m', '%d', '%H', '%l', '%p', '%P', '%M', '%S'); + self::$dateControls = array( 'Y', 'm', 'd', 'H', 'h', 'A', 'a', 'i', 's'); + self::$pickerControls = array('yy', 'mm', 'dd', 'HH', 'hh', 'TT', 'tt', 'mm', 'ss'); } - public static function flagsFromString($str) + public static function dateFlagsFromString($str) { - $str = strtolower($str); - switch ($str) { - case 'iso8601': - return self::DATE_ISO8601; - case 'us': - return self::DATE_US; - case 'european': - return self::DATE_EUROPEAN; - default: - $a = explode(' with ', $str, 2); - $result = 0; - switch ($a[0]) { - case 'big-endian': - $result |= self::DATE_BIG_ENDIAN; - break; - case 'little-endian': - $result |= self::DATE_LITTLE_ENDIAN; - break; - case 'middle-endian': - $result |= self::DATE_MIDDLE_ENDIAN; - break; - default: - throw new IPF_Exception('Bad date endianess'); - } - switch ($a[1]) { - case 'slashes': - $result |= self::DATE_SLASHES; - break; - case 'dots': - $result |= self::DATE_DOTS; - break; - case 'hyphens': - $result |= self::DATE_HYPHENS; - break; - case 'spaces': - $result |= self::DATE_SPACES; - break; - default: - throw new IPF_Exception('Bad date separator'); - } - return $result; - } + return + Pegp::oneOf( + Pegp::stri('default')->value(self::DATE_DEFAULT), + Pegp::stri('us')->value(self::DATE_US), + Pegp::stri('european')->value(self::DATE_EUROPEAN), + Pegp::seq( + Pegp::optional( + Pegp::seq( + Pegp::oneOf( + Pegp::stri('big-endian')->value(self::DATE_BIG_ENDIAN), + Pegp::stri('little-endian')->value(self::DATE_LITTLE_ENDIAN), + Pegp::stri('middle-endian')->value(self::DATE_MIDDLE_ENDIAN)), + Pegp::re('\s+')->drop())->bitOr(), 0), + Pegp::oneOf( + Pegp::stri('slashes')->value(self::DATE_SLASHES), + Pegp::stri('dots')->value(self::DATE_DOTS), + Pegp::stri('hyphens')->value(self::DATE_HYPHENS), + Pegp::stri('spaces')->value(self::DATE_SPACES)))->bitOr()) + ->parseString($str); } - public static function formatFor_date($flags) + public static function datetimeFlagsFromString($str) { - return self::makeFormat($flags, array('Y', 'm', 'd')); + return + Pegp::oneOf( + Pegp::stri('default')->value(self::DATETIME_DEFAULT), + Pegp::stri('us')->value(self::DATETIME_US), + Pegp::stri('european')->value(self::DATETIME_EUROPEAN), + Pegp::seq( + Pegp::optional( + Pegp::seq( + Pegp::oneOf( + Pegp::stri('big-endian')->value(self::DATE_BIG_ENDIAN), + Pegp::stri('little-endian')->value(self::DATE_LITTLE_ENDIAN), + Pegp::stri('middle-endian')->value(self::DATE_MIDDLE_ENDIAN)), + Pegp::re('\s+')->drop())->bitOr(), 0), + Pegp::oneOf( + Pegp::stri('slashes')->value(self::DATE_SLASHES), + Pegp::stri('dots')->value(self::DATE_DOTS), + Pegp::stri('hyphens')->value(self::DATE_HYPHENS), + Pegp::stri('spaces')->value(self::DATE_SPACES)), + Pegp::re('\s+')->drop(), + Pegp::oneOf( + Pegp::stri('24')->value(self::TIME_24HOURS), + Pegp::stri('12-upper')->value(self::TIME_12HOURS_UPPER), + Pegp::stri('12-lower')->value(self::TIME_12HOURS_LOWER), + Pegp::stri('12')->value(self::TIME_12HOURS)), + Pegp::optional( + Pegp::seq( + Pegp::re('\s+')->drop(), + Pegp::stri('seconds'))->value(self::TIME_SECONDS), 0))->bitOr()) + ->parseString($str); } - public static function formatFor_strftime($flags) + /** + * Builds format string corresponding to given flags and controls. + * + * @param integer Format flags + * @param array Format values. + * Examples: + * array( + * 'Y', // year + * 'm', // month + * 'd', // day + * ) + * array( + * '%Y', // year + * '%m', // month + * '%d', // day + * '%H', '%l', '%p', '%P', // hour-24, hour-12, AM/PM, am/pm + * '%M', // minute + * '%S', // second + * ) + * @return string Format string + */ + public static function makeFormat($flags, $controls) { - return self::makeFormat($flags, array('%Y', '%m', '%d')); + return self::makeDateFormat($flags, $controls) . self::makeTimeFormat($flags, $controls); } - public static function makeFormat($flags, $chars) + public static function makeDateFormat($flags, $controls) { $separator = self::$dateSeparators[$flags % 4]; switch ($flags & 12) { case self::DATE_BIG_ENDIAN: - return $chars[0].$separator.$chars[1].$separator.$chars[2]; + return $controls[0].$separator.$controls[1].$separator.$controls[2]; case self::DATE_LITTLE_ENDIAN: - return $chars[2].$separator.$chars[1].$separator.$chars[0]; + return $controls[2].$separator.$controls[1].$separator.$controls[0]; case self::DATE_MIDDLE_ENDIAN: - return $chars[1].$separator.$chars[2].$separator.$chars[0]; + return $controls[1].$separator.$controls[2].$separator.$controls[0]; default: throw new IPF_Exception('Bad date format'); } } + public static function makeTimeFormat($flags, $controls) + { + if (!($flags & 102)) // no time flags + return ''; + + $seconds = $flags & self::TIME_SECONDS; + switch ($flags & 48) { + case self::TIME_24HOURS: + $result = $controls[3].':'.$controls[7]; + if ($seconds) + $result .= ':'.$controls[8]; + break; + case self::TIME_12HOURS_UPPER: + $result = $controls[4].':'.$controls[7]; + if ($seconds) + $result .= ':'.$controls[8]; + $result .= ' '.$controls[5]; + break; + case self::TIME_12HOURS_LOWER: + $result = $controls[4].':'.$controls[7]; + if ($seconds) + $result .= ':'.$controls[8]; + $result .= ' '.$controls[6]; + break; + default: + throw new IPF_Exception('Bad time format'); + } + return $result; + } + public static function parseDate($format, $str) { - return strptime($str, self::formatFor_strftime($format)); + return strptime($str, self::makeFormat($format, self::$strftimeControls)); } public static function formatDate($format, $timestamp) { - return strftime(self::formatFor_strftime($format), $timestamp); + return strftime(self::makeFormat($format, self::$strftimeControls), $timestamp); } } diff --git a/t/FormatTest.php b/t/FormatTest.php index 6dfd746..fd01777 100644 --- a/t/FormatTest.php +++ b/t/FormatTest.php @@ -4,21 +4,33 @@ class Format_Test extends PHPUnit_Framework_TestCase { public function testFlags() { - $this->assertEquals(IPF_Format::DATE_ISO8601, IPF_Format::DATE_BIG_ENDIAN | IPF_Format::DATE_HYPHENS); + $this->assertEquals(IPF_Format::DATE_DEFAULT, IPF_Format::DATE_BIG_ENDIAN | IPF_Format::DATE_HYPHENS); $this->assertEquals(IPF_Format::DATE_US, IPF_Format::DATE_MIDDLE_ENDIAN | IPF_Format::DATE_SLASHES); $this->assertEquals(IPF_Format::DATE_EUROPEAN, IPF_Format::DATE_LITTLE_ENDIAN | IPF_Format::DATE_DOTS); } - public function testFlagsFromString() + public function testDateFlagsFromString() { - $this->assertEquals(IPF_Format::DATE_ISO8601, IPF_Format::flagsFromString('iso8601')); - $this->assertEquals(IPF_Format::DATE_ISO8601, IPF_Format::flagsFromString('Big-endian with hyphEns')); - $this->assertEquals(IPF_Format::DATE_EUROPEAN, IPF_Format::flagsFromString('European')); - $this->assertEquals(IPF_Format::DATE_EUROPEAN, IPF_Format::flagsFromString('little-endian with dots')); - $this->assertEquals(IPF_Format::DATE_US, IPF_Format::flagsFromString('US')); - $this->assertEquals(IPF_Format::DATE_US, IPF_Format::flagsFromString('Middle-Endian With Slashes')); + $this->assertEquals(IPF_Format::DATE_DEFAULT, IPF_Format::dateFlagsFromString('Default')); + $this->assertEquals(IPF_Format::DATE_DEFAULT, IPF_Format::dateFlagsFromString('Big-endian hyphEns')); + $this->assertEquals(IPF_Format::DATE_EUROPEAN, IPF_Format::dateFlagsFromString('European')); + $this->assertEquals(IPF_Format::DATE_EUROPEAN, IPF_Format::dateFlagsFromString('little-endian dots')); + $this->assertEquals(IPF_Format::DATE_US, IPF_Format::dateFlagsFromString('US')); + $this->assertEquals(IPF_Format::DATE_US, IPF_Format::dateFlagsFromString('Middle-Endian Slashes')); $this->assertEquals(IPF_Format::DATE_BIG_ENDIAN | IPF_Format::DATE_SPACES, - IPF_Format::flagsFromString('big-endian with spaces')); + IPF_Format::dateFlagsFromString('big-endian spaces')); + } + + public function testDatetimeFlagsFromString() + { + $this->assertEquals(IPF_Format::DATETIME_DEFAULT, IPF_Format::datetimeFlagsFromString('Default')); + $this->assertEquals(IPF_Format::DATETIME_DEFAULT, IPF_Format::datetimeFlagsFromString('Big-endian hyphEns 24')); + $this->assertEquals(IPF_Format::DATETIME_EUROPEAN, IPF_Format::datetimeFlagsFromString('European')); + $this->assertEquals(IPF_Format::DATETIME_EUROPEAN, IPF_Format::datetimeFlagsFromString('little-endian dots 24')); + $this->assertEquals(IPF_Format::DATETIME_US, IPF_Format::datetimeFlagsFromString('US')); + $this->assertEquals(IPF_Format::DATETIME_US, IPF_Format::datetimeFlagsFromString('Middle-Endian Slashes 12-upper')); + $this->assertEquals(IPF_Format::DATE_BIG_ENDIAN | IPF_Format::DATE_SPACES | IPF_Format::TIME_12HOURS_LOWER | IPF_Format::TIME_SECONDS, + IPF_Format::datetimeFlagsFromString("big-endian spaces \n 12-lower Seconds")); } }