Completed
Push — master ( 6fde37...d12889 )
by
unknown
02:24
created

YearMonthTimeParser::stringParse()   D

Complexity

Conditions 9
Paths 12

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 34
rs 4.909
cc 9
eloc 21
nc 12
nop 1
1
<?php
2
3
namespace ValueParsers;
4
5
use DataValues\TimeValue;
6
7
/**
8
 * @since 0.8.4
9
 *
10
 * @license GPL-2.0+
11
 * @author Addshore
12
 * @author Thiemo Mättig
13
 *
14
 * @todo match BCE dates in here
15
 */
16
class YearMonthTimeParser extends StringValueParser {
17
18
	const FORMAT_NAME = 'year-month';
19
20
	/**
21
	 * @var int[] Array mapping localized month names to month numbers (1 to 12).
22
	 */
23
	private $monthNumbers;
24
25
	/**
26
	 * @var ValueParser
27
	 */
28
	private $isoTimestampParser;
29
30
	/**
31
	 * @see StringValueParser::__construct
32
	 *
33
	 * @param MonthNameProvider $monthNameProvider
34
	 * @param ParserOptions|null $options
35
	 */
36
	public function __construct(
37
		MonthNameProvider $monthNameProvider,
38
		ParserOptions $options = null
39
	) {
40
		parent::__construct( $options );
41
42
		$languageCode = $this->getOption( ValueParser::OPT_LANG );
43
		$this->monthNumbers = $monthNameProvider->getMonthNumbers( $languageCode );
44
		$this->isoTimestampParser = new IsoTimestampParser(
45
			new CalendarModelParser( $this->options ),
46
			$this->options
47
		);
48
	}
49
50
	/**
51
	 * Parses the provided string and returns the result.
52
	 *
53
	 * @param string $value
54
	 *
55
	 * @throws ParseException
56
	 * @return TimeValue
57
	 */
58
	protected function stringParse( $value ) {
59
		//Matches Year and month separated by a separator, \p{L} matches letters outside the ASCII range
60
		if ( !preg_match( '/^([\d\p{L}]+)\s*[\/\-\s.,]\s*([\d\p{L}]+)$/', trim( $value ), $matches ) ) {
61
			throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
62
		}
63
		list( , $a, $b ) = $matches;
64
65
		$aIsInt = preg_match( '/^\d+$/', $a );
66
		$bIsInt = preg_match( '/^\d+$/', $b );
67
68
		if ( $aIsInt && $bIsInt ) {
69
			$parsed = $this->parseYearMonthTwoInts( $a, $b );
70
			if ( $parsed ) {
71
				return $parsed;
72
			}
73
		}
74
75
		if ( $aIsInt || $bIsInt ) {
76
			if ( $aIsInt ) {
77
				$year = $a;
78
				$month = trim( $b );
79
			} else {
80
				$year = $b;
81
				$month = trim( $a );
82
			}
83
84
			$parsed = $this->parseYearMonth( $year, $month );
85
			if ( $parsed ) {
86
				return $parsed;
87
			}
88
		}
89
90
		throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
91
	}
92
93
	/**
94
	 * If we have 2 integers parse the date assuming that the larger is the year
95
	 * unless the smaller is not a 'legal' month value
96
	 *
97
	 * @param string|int $a
98
	 * @param string|int $b
99
	 *
100
	 * @return TimeValue|bool
101
	 */
102
	private function parseYearMonthTwoInts( $a, $b ) {
103
		if ( !preg_match( '/^\d+$/', $a ) || !preg_match( '/^\d+$/', $b ) ) {
104
			return false;
105
		}
106
107
		if ( !$this->canBeMonth( $a ) && $this->canBeMonth( $b ) ) {
108
			return $this->getTimeFromYearMonth( $a, $b );
109
		} elseif ( $this->canBeMonth( $a ) ) {
110
			return $this->getTimeFromYearMonth( $b, $a );
111
		}
112
113
		return false;
114
	}
115
116
	/**
117
	 * If we have 1 int and 1 string then try to parse the int as the year and month as the string
118
	 * Check for both the full name and abbreviations
119
	 *
120
	 * @param string|int $year
121
	 * @param string $month
122
	 *
123
	 * @return TimeValue|bool
124
	 */
125
	private function parseYearMonth( $year, $month ) {
126
		foreach ( $this->monthNumbers as $monthName => $i ) {
127
			if ( strcasecmp( $monthName, $month ) === 0 ) {
128
				return $this->getTimeFromYearMonth( $year, $i );
129
			}
130
		}
131
132
		return false;
133
	}
134
135
	/**
136
	 * @param string $year
137
	 * @param string $month
138
	 *
139
	 * @return TimeValue
140
	 */
141
	private function getTimeFromYearMonth( $year, $month ) {
142
		return $this->isoTimestampParser->parse( sprintf( '+%d-%02d-00T00:00:00Z', $year, $month ) );
143
	}
144
145
	/**
146
	 * @param string|int $value
147
	 *
148
	 * @return bool can the given value be a month?
149
	 */
150
	private function canBeMonth( $value ) {
151
		return $value >= 0 && $value <= 12;
152
	}
153
154
}
155