Passed
Push — yearMonthFix ( 2f892e )
by no
06:34 queued 03:59
created

YearMonthTimeParser::parseYearMonth()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
rs 9.6666
cc 3
eloc 5
nc 3
nop 2
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
			if ( $this->canBeMonth( $a ) ) {
70
				return $this->getTimeFromYearMonth( $b, $a );
71
			} elseif ( $this->canBeMonth( $b ) ) {
72
				return $this->getTimeFromYearMonth( $a, $b );
73
			}
74
		} elseif ( $aIsInt ) {
75
			$month = $this->parseMonth( $b );
76
77
			if ( $month ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
78
				return $this->getTimeFromYearMonth( $a, $month );
79
			}
80
		} elseif ( $bIsInt ) {
81
			$month = $this->parseMonth( $a );
82
83
			if ( $month ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $month of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
84
				return $this->getTimeFromYearMonth( $b, $month );
85
			}
86
		}
87
88
		throw new ParseException( 'Failed to parse year and month', $value, self::FORMAT_NAME );
89
	}
90
91
	/**
92
	 * @param string $month
93
	 *
94
	 * @return int|null
95
	 */
96
	private function parseMonth( $month ) {
97
		foreach ( $this->monthNumbers as $monthName => $i ) {
98
			if ( strcasecmp( $monthName, $month ) === 0 ) {
99
				return $i;
100
			}
101
		}
102
103
		return null;
104
	}
105
106
	/**
107
	 * @param string $year
108
	 * @param string $month as a canonical month number
109
	 *
110
	 * @return TimeValue
111
	 */
112
	private function getTimeFromYearMonth( $year, $month ) {
113
		if ( $year[0] !== '-' ) {
114
			$year = '+' . $year;
115
		}
116
117
		return $this->isoTimestampParser->parse( sprintf( '%s-%02s-00T00:00:00Z', $year, $month ) );
118
	}
119
120
	/**
121
	 * @param string $value
122
	 *
123
	 * @return bool can the given value be a month?
124
	 */
125
	private function canBeMonth( $value ) {
126
		return $value >= 0 && $value <= 12;
127
	}
128
129
}
130