Completed
Push — master ( f26bc0...123a08 )
by mw
13s
created

TimeValueFormatter::hintCalendarModel()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
cc 3
eloc 4
nc 2
nop 1
crap 3
1
<?php
2
3
namespace SMW\DataValues\ValueFormatters;
4
5
use RuntimeException;
6
use SMW\IntlTimeFormatter;
7
use SMW\Localizer;
8
use SMWDataValue as DataValue;
9
use SMWDITime as DITime;
10
use SMWTimeValue as TimeValue;
11
12
/**
13
 * @license GNU GPL v2+
14
 * @since 2.4
15
 *
16
 * @author mwjames
17
 * @author Markus Krötzsch
18
 * @author Fabian Howahl
19
 * @author Terry A. Hurlbut
20
 */
21
class TimeValueFormatter extends DataValueFormatter {
22
23
	/**
24
	 * @since 2.4
25
	 *
26
	 * {@inheritDoc}
27
	 */
28 55
	public function isFormatterFor( DataValue $dataValue ) {
29 55
		return $dataValue instanceOf TimeValue;
30
	}
31
32
	/**
33
	 * @since 2.4
34
	 *
35
	 * {@inheritDoc}
36
	 */
37 42
	public function format( $type, $linker = null ) {
38
39 42
		if ( !$this->dataValue instanceOf TimeValue ) {
40 1
			throw new RuntimeException( "The formatter is missing a valid TimeValue object" );
41
		}
42
43
		if (
44 41
			$this->dataValue->isValid() &&
45 41
			( $type === self::WIKI_SHORT || $type === self::HTML_SHORT ) ) {
46 26
			return ( $this->dataValue->getCaption() !== false ) ? $this->dataValue->getCaption() : $this->getPreferredCaption();
47
		}
48
49
		if (
50 18
			$this->dataValue->isValid() &&
51 18
			( $type === self::WIKI_LONG || $type === self::HTML_LONG ) ) {
52 12
			return $this->getPreferredCaption();
53
		}
54
55
		// #1074
56 6
		return $this->dataValue->getCaption() !== false ? $this->dataValue->getCaption() : '';
57
	}
58
59
	/**
60
	 * @private
61
	 *
62
	 * Compute a string representation that largely follows the ISO8601 standard
63
	 * of representing dates. Large year numbers may have more than 4 digits,
64
	 * which is not strictly conforming to the standard. The date includes year,
65
	 * month, and day regardless of the input precision, but will only include
66
	 * time when specified.
67
	 *
68
	 * Conforming to the 2000 version of ISO8601, year 1 BC(E) is represented
69
	 * as "0000", year 2 BC(E) as "-0001" and so on.
70
	 *
71
	 * @since 2.4
72
	 *
73
	 * @param DITime $dataItem
0 ignored issues
show
Bug introduced by
There is no parameter named $dataItem. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
74
	 * @param boolean $mindefault determining whether values below the
75
	 * precision of our input should be completed with minimal or maximal
76
	 * conceivable values
77
	 *
78
	 * @return string
79
	 */
80 44
	public function getISO8601Date( $mindefault = true ) {
81
82 44
		$dataItem = $this->dataValue->getDataItemForCalendarModel( DITime::CM_GREGORIAN );
0 ignored issues
show
Bug introduced by
The method getDataItemForCalendarModel() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
83 44
		$precision = $dataItem->getPrecision();
84
85 44
		$result = $dataItem->getYear() > 0 ? '' : '-';
86 44
		$result .= str_pad( $dataItem->getYear(), 4, "0", STR_PAD_LEFT );
87
88 44
		$monthnum = $precision >= DITime::PREC_YM ? $dataItem->getMonth() : ( $mindefault ? 1 : 12 );
89 44
		$result .= '-' . str_pad( $monthnum, 2, "0", STR_PAD_LEFT );
90
91 44
		$day = $dataItem->getDay();
92
93 44
		if ( !$mindefault && $precision < DITime::PREC_YMD ) {
94 2
			$day = DITime::getDayNumberForMonth( $monthnum, $dataItem->getYear(), DITime::CM_GREGORIAN );
95
		}
96
97 44
		$result .= '-' . str_pad( $day, 2, "0", STR_PAD_LEFT );
98
99 44
		if ( $precision === DITime::PREC_YMDT ) {
100 39
			$result .= 'T' . $this->getTimeString( ( $mindefault ? '00:00:00' : '23:59:59' ) );
101
		}
102
103 44
		return $result;
104
	}
105
106
	/**
107
	 * @private
108
	 *
109
	 * Use MediaWiki's date and time formatting. It can't handle all inputs
110
	 * properly, but has superior i18n support.
111
	 *
112
	 * @since 2.4
113
	 *
114
	 * @param DITime $dataItem
0 ignored issues
show
Bug introduced by
There is no parameter named $dataItem. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
115
	 *
116
	 * @return string
117
	 */
118 4
	public function getMediaWikiDate() {
119
120 4
		$dataItem = $this->dataValue->getDataItemForCalendarModel( DITime::CM_GREGORIAN );
0 ignored issues
show
Bug introduced by
The method getDataItemForCalendarModel() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
121 4
		$precision = $dataItem->getPrecision();
122
123 4
		$language = Localizer::getInstance()->getLanguage(
124 4
			$this->dataValue->getLanguageCode()
125
		);
126
127 4
		$year = $dataItem->getYear();
128
129 4
		if ( $year < 0 || $year > 9999 ) {
130 1
			$year = '0000';
131
		}
132
133 4
		$year = str_pad( $year, 4, "0", STR_PAD_LEFT );
134
135 4
		if ( $precision <= DITime::PREC_Y ) {
136 3
			return $language->formatNum( $year, true );
137
		}
138
139 2
		$month = str_pad( $dataItem->getMonth(), 2, "0", STR_PAD_LEFT );
140 2
		$day = str_pad( $dataItem->getDay(), 2, "0", STR_PAD_LEFT );
141
142
		// date/timeanddate options:
143
		// 1. Whether to adjust the time output according to the user
144
		// configured offset ($timecorrection)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
145
		// 2. format, if it's false output the default one (default true)
146
		// 3. timecorrection, the time offset as returned from
147
		// Special:Preferences
148
149 2
		if ( $precision <= DITime::PREC_YMD ) {
150 2
			return $language->date( "$year$month$day" . '000000', false, true, false );
151
		}
152
153 2
		$time = str_replace( ':', '', $this->getTimeString() );
154
155 2
		return $language->timeanddate( "$year$month$day$time", false, true, false );
156
	}
157
158
	/**
159
	 * @private
160
	 *
161
	 * @todo Internationalize the CE and BCE strings.
162
	 *
163
	 * Compute a suitable string to display the given date item.
164
	 *
165
	 * @note MediaWiki's date functions are not applicable for the range of
166
	 * historic dates we support.
167
	 *
168
	 * @since 2.4
169
	 *
170
	 * @param DITime $dataitem
0 ignored issues
show
Documentation introduced by
There is no parameter named $dataitem. Did you maybe mean $dataItem?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
171
	 *
172
	 * @return string
173
	 */
174 14
	public function getCaptionFromDataItem( DITime $dataItem ) {
175
176
		// If the language code is empty then the content language code is used
177 14
		$extraneousLanguage = Localizer::getInstance()->getExtraneousLanguage(
178 14
			Localizer::getInstance()->getContentLanguage( $this->dataValue->getContextPage() )
179
		);
180
181
		// https://en.wikipedia.org/wiki/Anno_Domini
182
		// "...placing the "AD" abbreviation before the year number ... BC is
183
		// placed after the year number (for example: AD 2016, but 68 BC)..."
184
		// Chicago Manual of Style 2010, pp. 476–7; Goldstein 2007, p. 6.
185
186 14
		if ( $dataItem->getYear() > 0 ) {
187 14
			$cestring = $dataItem->getEra() > 0 ? 'AD' : '';
188 14
			$result = ( $cestring ? ( $cestring . ' ' ) : '' ) . number_format( $dataItem->getYear(), 0, '.', '' );
189
		} else {
190 2
			$bcestring = 'BC';
191 2
			$result = number_format( -( $dataItem->getYear() ), 0, '.', '' ) . ( $bcestring ? ( ' ' . $bcestring ) : '' );
192
		}
193
194 14
		if ( $dataItem->getPrecision() >= DITime::PREC_YM ) {
195 13
			$result = $extraneousLanguage->getMonthLabel( $dataItem->getMonth() ) . " " . $result;
0 ignored issues
show
Deprecated Code introduced by
The method SMW\ExtraneousLanguage::getMonthLabel() has been deprecated with message: use getMonthLabelByNumber

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
196
		}
197
198 14
		if ( $dataItem->getPrecision() >= DITime::PREC_YMD ) {
199 13
			$result = $dataItem->getDay() . " " . $result;
200
		}
201
202 14
		if ( $dataItem->getPrecision() >= DITime::PREC_YMDT ) {
203 10
			$result .= " " . $this->getTimeString();
204
		}
205
206 14
		$result .= $this->hintCalendarModel( $dataItem );
207
208 14
		return $result;
209
	}
210
211
	/**
212
	 * @private
213
	 *
214
	 * Return the time as a string. The time string has the format HH:MM:SS,
215
	 * without any timezone information (see class documentation for details
216
	 * on current timezone handling).
217
	 * The parameter $default optionally specifies the value returned
218
	 * if the date is valid but has no explicitly specified time. It can
219
	 * also be set to false to detect this situation.
220
	 *
221
	 * @since  2.4
222
	 *
223
	 * @param string $default
224
	 *
225
	 * @return string
226
	 */
227 44
	public function getTimeString( $default = '00:00:00' ) {
228
229 44
		$dataItem = $this->dataValue->getDataItemForCalendarModel( DITime::CM_GREGORIAN );
0 ignored issues
show
Bug introduced by
The method getDataItemForCalendarModel() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
230
231 44
		if ( $dataItem->getPrecision() < DITime::PREC_YMDT ) {
232
			return $default;
233
		}
234
235 44
		return sprintf( "%02d", $dataItem->getHour() ) . ':' .
236 44
		       sprintf( "%02d", $dataItem->getMinute() ) . ':' .
237 44
		       sprintf( "%02d", $dataItem->getSecond() );
238
	}
239
240
	/**
241
	 * @since 2.4
242
	 *
243
	 * @param  DITime|null $dataItem
244
	 *
245
	 * @return string
246
	 */
247 1
	public function getCaptionFromFreeFormat( DITime $dataItem = null ) {
248
249 1
		$language = Localizer::getInstance()->getLanguage(
250 1
			$this->dataValue->getLanguageCode()
251
		);
252
253
		// Prehistory dates are not supported when using this output format
254
		// Only match options encapsulated by [ ... ]
255
		if (
256 1
			$dataItem !== null &&
257 1
			$dataItem->getYear() > DITime::PREHISTORY &&
258 1
			preg_match("/\[([^\]]*)\]/", $this->dataValue->getOutputFormat(), $matches ) ) {
259 1
			$intlTimeFormatter = new IntlTimeFormatter( $dataItem, $language );
260
261 1
			if ( ( $caption = $intlTimeFormatter->format( $matches[1] ) ) !== false ) {
262
263 1
				if ( $intlTimeFormatter->containsValidDateFormatRule( $matches[1] ) ) {
264 1
					$caption .= $this->hintCalendarModel( $dataItem );
265
				}
266
267 1
				return $caption;
268
			}
269
		}
270
271 1
		return $this->getISO8601Date();
272
	}
273
274
	/**
275
	 * @since 2.4
276
	 *
277
	 * @param  DITime|null $dataItem
278
	 *
279
	 * @return string
280
	 */
281
	public function getLocalizedFormat( DITime $dataItem = null ) {
282
283
		$language = Localizer::getInstance()->getLanguage(
284
			$this->dataValue->getLanguageCode()
285
		);
286
287
		if ( $dataItem !== null && $dataItem->getYear() > DITime::PREHISTORY ) {
288
289
			$intlTimeFormatter = new IntlTimeFormatter(
290
				$dataItem,
291
				$language
292
			);
293
294
			return $intlTimeFormatter->getLocalizedFormat() . $this->hintCalendarModel( $dataItem );
295
		}
296
297
		return $this->getISO8601Date();
298
	}
299
300
301
	/**
302
	 * Compute a suitable string to display this date, taking into account the
303
	 * output format and the preferrable calendar models for the data.
304
	 *
305
	 * @note MediaWiki's date functions are not applicable for the range
306
	 * of historic dates we support.
307
	 *
308
	 * @return string
309
	 */
310 19
	protected function getPreferredCaption() {
311
312 19
		$dataItem = $this->dataValue->getDataItem();
313 19
		$format = strtoupper( $this->dataValue->getOutputFormat() );
314
315 19
		if ( $format == 'ISO' || $this->dataValue->getOutputFormat() == '-' ) {
316 6
			return $this->getISO8601Date();
317 15
		} elseif ( $format == 'MEDIAWIKI' ) {
318 3
			return $this->getMediaWikiDate();
319 13
		} elseif ( $format == 'SORTKEY' ) {
320
			return $dataItem->getSortKey();
321 13
		} elseif ( $format == 'JD' ) {
322 2
			return $dataItem->getJD();
323
		}
324
325
		// Does the formatting require calendar conversion?
326 13
		$model = $dataItem->getCalendarModel();
327
328
		if (
329 13
			( strpos( $format, 'JL' ) !== false ) ||
330 13
			( $dataItem->getJD() < TimeValue::J1582 && strpos( $format, 'GR' ) === false ) ) {
331 4
			$model = DITime::CM_JULIAN;
332 13
		} elseif ( strpos( $format, 'GR' ) !== false ) {
333 4
			$model = DITime::CM_GREGORIAN;
334
		}
335
336 13
		if ( strpos( $format, '-F[' ) !== false ) {
337 1
			return $this->getCaptionFromFreeFormat( $this->dataValue->getDataItemForCalendarModel( $model ) );
0 ignored issues
show
Bug introduced by
The method getDataItemForCalendarModel() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
Bug Compatibility introduced by
The expression $this->getCaptionFromFre...CalendarModel($model)); of type string|boolean adds the type boolean to the return on line 337 which is incompatible with the return type documented by SMW\DataValues\ValueForm...er::getPreferredCaption of type string.
Loading history...
338 13
		} elseif ( $format == 'LOCL' ) {
339
			return $this->getLocalizedFormat( $this->dataValue->getDataItemForCalendarModel( $model ) );
0 ignored issues
show
Bug introduced by
The method getDataItemForCalendarModel() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
340 13
		} elseif ( $dataItem->getYear() > TimeValue::PREHISTORY && $dataItem->getPrecision() >= DITime::PREC_YM ) {
341
			// Do not convert between Gregorian and Julian if only
342
			// year is given (years largely overlap in history, but
343
			// assuming 1 Jan as the default date, the year number
344
			// would change in conversion).
345
			// Also do not convert calendars in prehistory: not
346
			// meaningful (getDataItemForCalendarModel may return null).
347 13
			return $this->getCaptionFromDataItem( $this->dataValue->getDataItemForCalendarModel( $model ) );
0 ignored issues
show
Bug introduced by
The method getDataItemForCalendarModel() does not exist on SMWDataValue. Did you maybe mean getDataItem()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
348
		}
349
350 6
		return $this->getCaptionFromDataItem( $dataItem );
0 ignored issues
show
Compatibility introduced by
$dataItem of type object<SMWDataItem> is not a sub-type of object<SMWDITime>. It seems like you assume a child class of the class SMWDataItem to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
351
	}
352
353 14
	private function hintCalendarModel( $dataItem ) {
354
355 14
		if ( $this->dataValue->isEnabledFeature( SMW_DV_TIMEV_CM ) && $dataItem->getCalendarModel() !== DITime::CM_GREGORIAN ) {
356 4
			return ' ' . \Html::rawElement( 'sup', array(), $dataItem->getCalendarModelLiteral() );
357
		}
358
359 14
		return '';
360
	}
361
362
}
363