YearMonthDayTimeParser::parseYearMonthDay()   C
last analyzed

Complexity

Conditions 14
Paths 8

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 14

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 18
cts 18
cp 1
rs 6.2666
c 0
b 0
f 0
cc 14
nc 8
nop 1
crap 14

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace ValueParsers;
4
5
use DataValues\TimeValue;
6
7
/**
8
 * A straight time parser with a strict rule set that only accepts YMD, DMY, MDY and YDM formatted
9
 * dates if they can not be confused with an other format.
10
 *
11
 * @since 0.8.1
12
 *
13
 * @license GPL-2.0+
14
 * @author Thiemo Kreuz
15
 */
16
class YearMonthDayTimeParser extends StringValueParser {
17
18
	const FORMAT_NAME = 'year-month-day';
19
20
	/**
21
	 * @var ValueParser
22
	 */
23
	private $eraParser;
24
25
	/**
26
	 * @var ValueParser
27
	 */
28
	private $isoTimestampParser;
29
30
	/**
31
	 * @param ValueParser|null $eraParser String parser that detects signs, "BC" suffixes and such and
32
	 * returns an array with the detected sign character and the remaining value.
33
	 * @param ParserOptions|null $options
34
	 */
35 100
	public function __construct( ValueParser $eraParser = null, ParserOptions $options = null ) {
36 100
		parent::__construct( $options );
37
38 100
		$this->eraParser = $eraParser ?: new EraParser();
39 100
		$this->isoTimestampParser = new IsoTimestampParser( null, $this->options );
40 100
	}
41
42
	/**
43
	 * @param string $value
44
	 *
45
	 * @throws ParseException
46
	 * @return TimeValue
47
	 */
48 93
	protected function stringParse( $value ) {
49
		try {
50 93
			list( $sign, $preparsedValue ) = $this->eraParser->parse( $value );
51 93
			list( $signedYear, $month, $day ) = $this->parseYearMonthDay( $preparsedValue );
52
53 61
			if ( substr( $signedYear, 0, 1 ) !== '-' ) {
54 53
				$signedYear = $sign . $signedYear;
55 8
			} elseif ( $sign === '-' ) {
56 2
				throw new ParseException( 'Two eras found' );
57
			}
58
59 59
			return $this->newTimeValue( $signedYear, $month, $day );
60 51
		} catch ( ParseException $ex ) {
61 51
			throw new ParseException( $ex->getMessage(), $value, self::FORMAT_NAME );
62
		}
63
	}
64
65
	/**
66
	 * @param string $value
67
	 *
68
	 * @throws ParseException
69
	 * @return string[]
70
	 */
71 93
	private function parseYearMonthDay( $value ) {
72 93
		if ( !preg_match( '/^\D*?(-?\d+)\D+(\d+)\D+?(-?\d+)\D*$/', $value, $matches ) ) {
73 8
			throw new ParseException( 'Can not find three numbers' );
74
		}
75
76
		// A 32 in the first spot can not be confused with anything.
77 85
		if ( $matches[1] < 1 || $matches[1] > 31 ) {
78 44
			if ( $matches[3] > 12 || $matches[2] == $matches[3] ) {
79 37
				list( , $signedYear, $month, $day ) = $matches;
80 7
			} elseif ( $matches[2] > 12 ) {
81 3
				list( , $signedYear, $day, $month ) = $matches;
82
			} else {
83 44
				throw new ParseException( 'Can not distinguish YDM and YMD' );
84
			}
85 41
		} elseif ( $matches[3] < 1 || $matches[3] > 59
86
			// A 59 in the third spot may be a second, but can not if the first number is > 24.
87
			// A 31 in the last spot may be the day, but can not if it's negative.
88 41
			|| ( abs( $matches[1] ) > 24 && $matches[3] > 31 )
89
		) {
90 22
			if ( $matches[1] > 12 || $matches[1] == $matches[2] ) {
91 15
				list( , $day, $month, $signedYear ) = $matches;
92 7
			} elseif ( $matches[2] > 12 ) {
93 6
				list( , $month, $day, $signedYear ) = $matches;
94
			} else {
95 22
				throw new ParseException( 'Can not distinguish DMY and MDY' );
96
			}
97
		} else {
98
			// Formats DYM and MYD do not exist.
99 19
			throw new ParseException( 'Can not identify year' );
100
		}
101
102 61
		return array( $signedYear, $month, $day );
103
	}
104
105
	/**
106
	 * @param string $signedYear
107
	 * @param string $month
108
	 * @param string $day
109
	 *
110
	 * @throws ParseException
111
	 * @return TimeValue
112
	 */
113 59
	private function newTimeValue( $signedYear, $month, $day ) {
114 59
		if ( $month < 1 || $month > 12 ) {
115 7
			throw new ParseException( 'Month out of range' );
116 52
		} elseif ( $day < 1 || $day > 31 ) {
117 3
			throw new ParseException( 'Day out of range' );
118
		}
119
120 49
		return $this->isoTimestampParser->parse(
121 49
			sprintf( '%s-%02s-%02sT00:00:00Z', $signedYear, $month, $day )
122
		);
123
	}
124
125
}
126