Ver Fonte

[Locale] added parsing implementation for year, month and day

Eriksen Costa há 14 anos atrás
pai
commit
ed85345312

+ 2 - 2
src/Symfony/Component/Locale/Stub/DateFormat/DayTransformer.php

@@ -26,9 +26,9 @@ class DayTransformer extends Transformer
     public function getReverseMatchingRegExp($length)
     {
         if (1 == $length) {
-            return '\d{1,2}';
+            return '?P<d>\d{1,2}';
         } else {
-            return "\d{$length}";
+            return '?P<d>\d{'.$length.'}';
         }
     }
 

+ 87 - 11
src/Symfony/Component/Locale/Stub/DateFormat/FullTransformer.php

@@ -28,10 +28,12 @@ class FullTransformer
 
     private $transformers;
     private $pattern;
+    private $timezone;
 
-    public function __construct($pattern)
+    public function __construct($pattern, $timezone)
     {
         $this->pattern = $pattern;
+        $this->timezone = $timezone;
 
         $implementedCharsMatch = $this->buildCharsMatch($this->implementedChars);
         $notImplementedCharsMatch = $this->buildCharsMatch($this->notImplementedChars);
@@ -58,6 +60,11 @@ class FullTransformer
         );
     }
 
+    public function getTransformers()
+    {
+        return $this->transformers;
+    }
+
     public function format(\DateTime $dateTime)
     {
         $that = $this;
@@ -93,33 +100,44 @@ class FullTransformer
 
     public function getReverseMatchingRegExp($pattern)
     {
-        $reverseMatchingRegExp = preg_replace_callback($this->regExp, function($matches) {
-            if (isset($this->transformers[$dateChars[0]])) {
-                $transformer = $this->transformers[$dateChars[0]];
-                return $transformer->getReverseMatchingRegExp($length);
+        $that = $this;
+
+        $reverseMatchingRegExp = preg_replace_callback($this->regExp, function($matches) use ($that) {
+            $length = strlen($matches[0]);
+            $transformerIndex = $matches[0][0];
+
+            $transformers = $that->getTransformers();
+
+            if (isset($transformers[$transformerIndex])) {
+                $transformer = $transformers[$transformerIndex];
+                return '(' . $transformer->getReverseMatchingRegExp($length) . ')';
             }
-        }, $this->pattern);
+        }, $pattern);
 
         return $reverseMatchingRegExp;
     }
 
-    public function parse($pattern, $value)
+    public function parse($value)
     {
-        $reverseMatchingRegExp = $this->getReverseMatchingRegExp($pattern);
+        $reverseMatchingRegExp = $this->getReverseMatchingRegExp($this->pattern);
+        $reverseMatchingRegExp = '/'.$reverseMatchingRegExp.'/';
 
         $options = array();
 
         if (preg_match($reverseMatchingRegExp, $value, $matches)) {
+            $matches = $this->normalizeArray($matches);
+
             foreach ($this->transformers as $char => $transformer) {
                 if (isset($matches[$char])) {
-                    $options = array_merge($options, $transformer->extractDateOptions($char, strlen($matches[$char])));
+                    $length = strlen($matches[$char]['pattern']);
+                    $options = array_merge($options, $transformer->extractDateOptions($matches[$char]['value'], $length));
                 }
             }
 
             return $this->calculateUnixTimestamp($options);
-        } else {
-            throw new \InvalidArgumentException(sprintf("Failed to match value '%s' with pattern '%s'", $value, $pattern));
         }
+
+        throw new \InvalidArgumentException(sprintf("Failed to match value '%s' with pattern '%s'", $value, $this->pattern));
     }
 
     protected function buildCharsMatch($specialChars)
@@ -132,4 +150,62 @@ class FullTransformer
 
         return $specialCharsMatch;
     }
+
+    protected function normalizeArray(array $data)
+    {
+        $ret = array();
+
+        foreach ($data as $key => $value) {
+            if (!is_string($key)) {
+                continue;
+            }
+
+            $ret[$key[0]] = array(
+                'value' => $value,
+                'pattern' => $key
+            );
+        }
+
+        return $ret;
+    }
+
+    private function calculateUnixTimestamp(array $options)
+    {
+        $datetime = $this->extractDateTime($options);
+
+        $year   = $datetime['year'];
+        $month  = $datetime['month'];
+        $day    = $datetime['day'];
+        $hour   = $datetime['hour'];
+        $minute = $datetime['minute'];
+        $second = $datetime['second'];
+
+        // If month is false, return immediately
+        if (false === $month) {
+            return false;
+        }
+
+        // Set the timezone
+        $originalTimezone = date_default_timezone_get();
+        date_default_timezone_set($this->timezone);
+
+        $timestamp = mktime($hour, $minute, $second, $month, $day, $year);
+
+        // Restore timezone
+        date_default_timezone_set($originalTimezone);
+
+        return $timestamp;
+    }
+
+    private function extractDateTime(array $datetime)
+    {
+        return array(
+            'year'   => isset($datetime['year']) ? $datetime['year'] : 1970,
+            'month'  => isset($datetime['month']) ? $datetime['month'] : 1,
+            'day'    => isset($datetime['day']) ? $datetime['day'] : 1,
+            'hour'   => isset($datetime['hour']) ? $datetime['hour'] : 0,
+            'minute' => isset($datetime['minute']) ? $datetime['minute'] : 0,
+            'second' => isset($datetime['second']) ? $datetime['second'] : 0,
+        );
+    }
 }

+ 56 - 10
src/Symfony/Component/Locale/Stub/DateFormat/MonthTransformer.php

@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\Locale\Stub\DateFormat;
 
+use Symfony\Component\Locale\Exception\MethodArgumentValueNotImplementedException;
+
 /**
  * Parser and formatter for date formats
  *
@@ -18,6 +20,39 @@ namespace Symfony\Component\Locale\Stub\DateFormat;
  */
 class MonthTransformer extends Transformer
 {
+    private static $months = array(
+        'January',
+        'February',
+        'March',
+        'April',
+        'May',
+        'June',
+        'July',
+        'August',
+        'September',
+        'October',
+        'November',
+        'December'
+    );
+
+    private static $shortMonths = array();
+
+    private static $flippedMonths = array();
+
+    private static $flippedShortMonths = array();
+
+    public function __construct()
+    {
+        if (0 == count(self::$shortMonths)) {
+            self::$shortMonths = array_map(function($month) {
+                return substr($month, 0, 3);
+            }, self::$months);
+
+            self::$flippedMonths = array_flip(self::$months);
+            self::$flippedShortMonths = array_flip(self::$shortMonths);
+        }
+    }
+
     public function format(\DateTime $dateTime, $length)
     {
         $matchLengthMap = array(
@@ -38,23 +73,18 @@ class MonthTransformer extends Transformer
 
     public function getReverseMatchingRegExp($length)
     {
-        $months = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
-
         switch ($length) {
             case 1:
-                return '\d{1,2}';
+                return '?P<M>\d{1,2}';
                 break;
             case 3:
-                $shortMonths = array_map(function($month) {
-                    return substr($month, 0, 2);
-                }, $months);
-                return implode('|', $shortMonths);
+                return '?P<MMM>' . implode('|', self::$shortMonths);
                 break;
             case 4:
-                return implode('|', $months);
+                return '?P<MMMM>' . implode('|', self::$months);
                 break;
             case 5:
-                return '[JFMASOND]';
+                return '?P<MMMMM>' . '[JFMASOND]';
                 break;
             default:
                 return "\d{$length}";
@@ -64,8 +94,24 @@ class MonthTransformer extends Transformer
 
     public function extractDateOptions($matched, $length)
     {
+        if (!is_numeric($matched)) {
+            if (3 == $length) {
+                $matched = self::$flippedShortMonths[$matched] + 1;
+            }
+            elseif (4 == $length) {
+                $matched = self::$flippedMonths[$matched] + 1;
+            }
+            elseif (5 == $length) {
+                // IntlDateFormatter::parse() always returns false for MMMMM or LLLLL
+                $matched = false;
+            }
+        }
+        else {
+            $matched = (int) $matched;
+        }
+
         return array(
-            'month' => (int) $matched,
+            'month' => $matched,
         );
     }
 }

+ 3 - 2
src/Symfony/Component/Locale/Stub/DateFormat/YearTransformer.php

@@ -30,9 +30,10 @@ class YearTransformer extends Transformer
     public function getReverseMatchingRegExp($length)
     {
         if (2 == $length) {
-            return '\d{2}';
+            return '?P<yy>\d{2}';
         } else {
-            return "\d{1,$length}";
+            $length = $length < 4 ? 4 : $length;
+            return "?P<y>\d{1,$length}";
         }
     }
 

+ 3 - 4
src/Symfony/Component/Locale/Stub/StubIntlDateFormatter.php

@@ -120,8 +120,7 @@ class StubIntlDateFormatter
         $dateTime->setTimestamp($timestamp);
         $dateTime->setTimezone($this->dateTimeZone);
 
-        $pattern = $this->getPattern();
-        $transformer = new DateFormat\FullTransformer($pattern);
+        $transformer = new DateFormat\FullTransformer($this->getPattern(), $this->getTimeZoneId());
         $formatted = $transformer->format($dateTime);
 
         return $formatted;
@@ -244,11 +243,11 @@ class StubIntlDateFormatter
      *                              contain -1 otherwise it will contain the position at which parsing
      *                              ended. If $parse_pos > strlen($value), the parse fails immediately.
      * @return string               Parsed value as a timestamp
-     * @throws MethodNotImplementedException
      */
     public function parse($value, &$position = 0)
     {
-        throw new MethodNotImplementedException(__METHOD__);
+        $transformer = new DateFormat\FullTransformer($this->getPattern(), $this->getTimeZoneId());
+        return $transformer->parse($value);
     }
 
     /**

+ 45 - 4
tests/Symfony/Tests/Component/Locale/Stub/StubIntlDateFormatterTest.php

@@ -424,12 +424,53 @@ class StubIntlDateFormatterTest extends LocaleTestCase
     }
 
     /**
-     * @expectedException Symfony\Component\Locale\Exception\MethodNotImplementedException
+     * @dataProvider parseProvider
      */
-    public function testParse()
+    public function testParseIntl($pattern, $value, $expected)
     {
-        $formatter = $this->createStubFormatter();
-        $formatter->parse('Wednesday, December 31, 1969 4:00:00 PM PT');
+        $formatter = $this->createIntlFormatter($pattern);
+        $this->assertEquals($expected, $formatter->parse($value));
+    }
+
+    /**
+     * @dataProvider parseProvider
+     */
+    public function testParseStub($pattern, $value, $expected)
+    {
+        $formatter = $this->createStubFormatter($pattern);
+        $this->assertEquals($expected, $formatter->parse($value));
+    }
+
+    public function parseProvider()
+    {
+        return array(
+            // years
+            array('y-M-d', '1970-1-1', 0),
+            array('yy-M-d', '70-1-1', 0),
+
+            // months
+            array('y-M-d', '1970-1-1', 0),
+            array('y-MMM-d', '1970-Jan-1', 0),
+            array('y-MMMM-d', '1970-January-1', 0),
+
+            // 1 char month
+            array('y-MMMMM-d', '1970-J-1', false),
+            array('y-MMMMM-d', '1970-S-1', false),
+
+            // standalone months
+            array('y-L-d', '1970-1-1', 0),
+            array('y-LLL-d', '1970-Jan-1', 0),
+            array('y-LLLL-d', '1970-January-1', 0),
+
+            // standalone 1 char month
+            array('y-LLLLL-d', '1970-J-1', false),
+            array('y-LLLLL-d', '1970-S-1', false),
+
+            // days
+            array('y-M-d', '1970-1-1', 0),
+            array('y-M-dd', '1970-1-01', 0),
+            array('y-M-ddd', '1970-1-001', 0),
+        );
     }
 
     /**