1 | <?php |
||
30 | class PhpDateTimeParser extends StringValueParser { |
||
31 | |||
32 | const FORMAT_NAME = 'php-date-time'; |
||
33 | |||
34 | /** |
||
35 | * @var MonthNameUnlocalizer |
||
36 | */ |
||
37 | private $monthNameUnlocalizer; |
||
38 | |||
39 | /** |
||
40 | * @var ValueParser |
||
41 | */ |
||
42 | private $eraParser; |
||
43 | |||
44 | /** |
||
45 | * @var ValueParser |
||
46 | */ |
||
47 | private $isoTimestampParser; |
||
48 | |||
49 | /** |
||
50 | * @param MonthNameUnlocalizer $monthNameUnlocalizer Used to translate month names to English, |
||
51 | * the language PHP's DateTime parser understands. |
||
52 | * @param ValueParser $eraParser String parser that detects signs, "BC" suffixes and such and |
||
53 | * returns an array with the detected sign character and the remaining value. |
||
54 | * @param ValueParser $isoTimestampParser String parser that gets a language independent |
||
55 | * YMD-ordered timestamp and returns a TimeValue object. Used for precision detection. |
||
56 | */ |
||
57 | 113 | public function __construct( |
|
68 | |||
69 | /** |
||
70 | * @param string $value in a format as specified by the PHP DateTime object |
||
71 | * there are exceptions as we can handel 5+ digit dates |
||
72 | * |
||
73 | * @throws ParseException |
||
74 | * @return TimeValue |
||
75 | */ |
||
76 | 106 | protected function stringParse( $value ) { |
|
77 | 106 | $rawValue = $value; |
|
78 | |||
79 | try { |
||
80 | 106 | list( $sign, $value ) = $this->eraParser->parse( $value ); |
|
81 | |||
82 | 106 | $value = trim( $value ); |
|
83 | 106 | $value = $this->monthNameUnlocalizer->unlocalize( $value ); |
|
84 | 106 | $year = $this->fetchAndNormalizeYear( $value ); |
|
85 | |||
86 | 106 | $value = $this->getValueWithFixedSeparators( $value, $year ); |
|
87 | |||
88 | 106 | $this->validateDateTimeInput( $value ); |
|
89 | |||
90 | // Parse using the DateTime object (this will allow us to format the date in a nicer way) |
||
91 | 91 | $dateTime = new DateTime( $value ); |
|
92 | |||
93 | // Fail if the DateTime object does calculations like changing 2015-00-00 to 2014-12-30. |
||
94 | 84 | if ( $year !== null && $dateTime->format( 'Y' ) !== substr( $year, -4 ) ) { |
|
95 | 7 | throw new ParseException( $value . ' is not a valid date.' ); |
|
96 | } |
||
97 | |||
98 | // Input was one, two, or three numbers? Where the heck does a time come from? |
||
99 | 77 | if ( $dateTime->format( 'H:i:s' ) !== '00:00:00' |
|
100 | 77 | && preg_match( '/^\D*\d+(?:\D+\d+){0,2}\D*$/', $value ) |
|
101 | ) { |
||
102 | 1 | throw new ParseException( $value . ' is not a valid date.' ); |
|
103 | } |
||
104 | |||
105 | 76 | if ( $year !== null && strlen( $year ) > 4 ) { |
|
106 | 12 | $timestamp = $sign . $year . $dateTime->format( '-m-d\TH:i:s\Z' ); |
|
107 | } else { |
||
108 | 64 | $timestamp = $sign . $dateTime->format( 'Y-m-d\TH:i:s\Z' ); |
|
109 | } |
||
110 | |||
111 | // Use a common base parser for precision detection and option handling. |
||
112 | 76 | return $this->isoTimestampParser->parse( $timestamp ); |
|
113 | 30 | } catch ( Exception $exception ) { |
|
114 | 30 | throw new ParseException( $exception->getMessage(), $rawValue, self::FORMAT_NAME ); |
|
115 | } |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * @param string $value |
||
120 | * |
||
121 | * @throws ParseException |
||
122 | */ |
||
123 | 106 | private function validateDateTimeInput( $value ) { |
|
135 | |||
136 | /** |
||
137 | * PHP's DateTime object does not accept spaces as separators between year, month and day, |
||
138 | * e.g. dates like 20 12 2012, but we want to support them. |
||
139 | * See http://de1.php.net/manual/en/datetime.formats.date.php |
||
140 | * |
||
141 | * @param string $value |
||
142 | * @param string|null $year |
||
143 | * |
||
144 | * @return string |
||
145 | */ |
||
146 | 106 | private function getValueWithFixedSeparators( $value, $year = null ) { |
|
153 | |||
154 | /** |
||
155 | * Tries to find and pad the sequence of digits in the input that represents the year. |
||
156 | * Refer to the class level documentation for a description of the heuristics used. |
||
157 | * |
||
158 | * @param string &$value A time value string, possibly containing a year. If found, the year in |
||
159 | * the string will be cut and padded to exactly 4 digits. |
||
160 | * |
||
161 | * @return string|null The full year, if found, not cut but padded to at least 4 digits. |
||
162 | */ |
||
163 | 106 | private function fetchAndNormalizeYear( &$value ) { |
|
211 | |||
212 | } |
||
213 |