]> git.andy128k.dev Git - ipf.git/commitdiff
time formats. improve format parsing.
authorAndrey Kutejko <andy128k@gmail.com>
Sun, 11 Aug 2013 11:15:57 +0000 (14:15 +0300)
committerAndrey Kutejko <andy128k@gmail.com>
Sun, 11 Aug 2013 11:15:57 +0000 (14:15 +0300)
composer.json
composer.lock
ipf/form/widget/dateinput.php
ipf/format.php
t/FormatTest.php

index 2cbf46ae2322dfdce49795262870b3f6251ec7a0..d8476fd5d121438a8bb9aa66dbb40a2b75f605fe 100644 (file)
@@ -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"
   },
index 274009c85e4cb3312d86056d41c3292f87a83f10..7d8e7aa895d8de332b19d791e07b1bcc07f7f095 100644 (file)
@@ -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",
             "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",
     "minimum-stability": "stable",
     "stability-flags": {
         "andy128k/missing-tools": 20,
+        "andy128k/pegp": 20,
         "andy128k/routeexpression": 20,
         "andy128k/ipf-template": 20
     },
index 549d1b2a4948f2cc5f2e8015ebbc6fe46e0dccf8..2a7981446a9ef48b2cddb17ac634c395241a3619 100644 (file)
@@ -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);
     }
 
index 77c50412998c27352454249e9657634786bbbab4..de455b8984cb3253e07abf9129e04816c2dfe9cf 100644 (file)
 
 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);
     }
 }
 
index 6dfd7461dc817740eb394e2f13bf0cd19a51cde0..fd017771c907686db5b7dbf76e0d08421ec88be7 100644 (file)
@@ -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"));
     }
 }