Completed
Push — master ( c83be9...d2b57a )
by mw
14s
created

TimeValueFormatter::getLocalizedFormat()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.0961

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 25
ccs 9
cts 11
cp 0.8182
crap 4.0961
rs 8.5806
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 66
	public function isFormatterFor( DataValue $dataValue ) {
29 66
		return $dataValue instanceof TimeValue;
30
	}
31
32
	/**
33
	 * @since 2.4
34
	 *
35
	 * {@inheritDoc}
36
	 */
37 51
	public function format( $type, $linker = null ) {
38
39 51
		if ( !$this->dataValue instanceof TimeValue ) {
40 1
			throw new RuntimeException( "The formatter is missing a valid TimeValue object" );
41
		}
42
43
		if (
44 50
			$this->dataValue->isValid() &&
45 50
			( $type === self::WIKI_SHORT || $type === self::HTML_SHORT ) ) {
46 33
			return ( $this->dataValue->getCaption() !== false ) ? $this->dataValue->getCaption() : $this->getPreferredCaption();
47
		}
48
49
		if (
50 26
			$this->dataValue->isValid() &&
51 26
			( $type === self::WIKI_LONG || $type === self::HTML_LONG ) ) {
52 19
			return $this->getPreferredCaption();
53
		}
54
55
		// #1074
56 7
		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 50
	public function getISO8601Date( $mindefault = true ) {
81
82 50
		$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 50
		$precision = $dataItem->getPrecision();
84
85 50
		$result = $dataItem->getYear() > 0 ? '' : '-';
86 50
		$result .= str_pad( $dataItem->getYear(), 4, "0", STR_PAD_LEFT );
87
88 50
		$monthnum = $precision >= DITime::PREC_YM ? $dataItem->getMonth() : ( $mindefault ? 1 : 12 );
89 50
		$result .= '-' . str_pad( $monthnum, 2, "0", STR_PAD_LEFT );
90
91 50
		$day = $dataItem->getDay();
92
93 50
		if ( !$mindefault && $precision < DITime::PREC_YMD ) {
94 2
			$day = DITime::getDayNumberForMonth( $monthnum, $dataItem->getYear(), DITime::CM_GREGORIAN );
95
		}
96
97 50
		$result .= '-' . str_pad( $day, 2, "0", STR_PAD_LEFT );
98
99 50
		if ( $precision === DITime::PREC_YMDT ) {
100 45
			$result .= 'T' . $this->getTimeString( ( $mindefault ? '00:00:00' : '23:59:59' ) );
101
		}
102
103 50
		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 6
	public function getMediaWikiDate() {
119
120 6
		$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 6
		$precision = $dataItem->getPrecision();
122
123 6
		$language = Localizer::getInstance()->getLanguage(
124 6
			$this->dataValue->getOptionValueFor( 'user.language' )
0 ignored issues
show
Security Bug introduced by
It seems like $this->dataValue->getOpt...lueFor('user.language') targeting SMWDataValue::getOptionValueFor() can also be of type false; however, SMW\Localizer::getLanguage() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
125
		);
126
127 6
		$year = $dataItem->getYear();
128
129 6
		if ( $year < 0 || $year > 9999 ) {
130 1
			$year = '0000';
131
		}
132
133 6
		$year = str_pad( $year, 4, "0", STR_PAD_LEFT );
134
135 6
		if ( $precision <= DITime::PREC_Y ) {
136 3
			return $language->formatNum( $year, true );
137
		}
138
139 4
		$month = str_pad( $dataItem->getMonth(), 2, "0", STR_PAD_LEFT );
140 4
		$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 4
		if ( $precision <= DITime::PREC_YMD ) {
150 3
			return $language->date( "$year$month$day" . '000000', false, true, false );
151
		}
152
153 3
		$time = str_replace( ':', '', $this->getTimeString() );
154
155 3
		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 18
	public function getCaptionFromDataItem( DITime $dataItem ) {
175
176
		// If the language code is empty then the content language code is used
177 18
		$extraneousLanguage = Localizer::getInstance()->getExtraneousLanguage(
178 18
			Localizer::getInstance()->getContentLanguage()
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 18
		if ( $dataItem->getYear() > 0 ) {
187 18
			$cestring = $dataItem->getEra() > 0 ? 'AD' : '';
188 18
			$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 18
		if ( $dataItem->getPrecision() >= DITime::PREC_YM ) {
195 16
			$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 18
		if ( $dataItem->getPrecision() >= DITime::PREC_YMD ) {
199 16
			$result = $dataItem->getDay() . " " . $result;
200
		}
201
202 18
		if ( $dataItem->getPrecision() >= DITime::PREC_YMDT ) {
203 12
			$result .= " " . $this->getTimeString();
204
		}
205
206 18
		$result .= $this->hintCalendarModel( $dataItem );
207
208 18
		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 50
	public function getTimeString( $default = '00:00:00' ) {
228
229 50
		$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 50
		if ( $dataItem->getPrecision() < DITime::PREC_YMDT ) {
232
			return $default;
233
		}
234
235 50
		return sprintf( "%02d", $dataItem->getHour() ) . ':' .
236 50
		       sprintf( "%02d", $dataItem->getMinute() ) . ':' .
237 50
		       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->getOptionValueFor( 'user.language' )
0 ignored issues
show
Security Bug introduced by
It seems like $this->dataValue->getOpt...lueFor('user.language') targeting SMWDataValue::getOptionValueFor() can also be of type false; however, SMW\Localizer::getLanguage() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
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
	 * @private
276
	 *
277
	 * @since 2.4
278
	 *
279
	 * @param  DITime|null $dataItem
280
	 *
281
	 * @return string
282
	 */
283 7
	public function getLocalizedFormat( DITime $dataItem = null ) {
284
285 7
		if ( $dataItem === null ) {
286
			return '';
287
		}
288
289 7
		if ( $dataItem->getYear() < DITime::PREHISTORY ) {
290
			return $this->getISO8601Date();
291
		}
292
293 7
		$outputFormat = $this->dataValue->getOutputFormat();
294
295 7
		if ( ( $language = Localizer::getInstance()->getAnnotatedLanguageCodeFrom( $outputFormat ) ) === false ) {
296 6
			$language = $this->dataValue->getOptionValueFor( DataValue::OPT_USER_LANGUAGE );
297
		}
298
299 7
		$language = Localizer::getInstance()->getLanguage( $language );
0 ignored issues
show
Security Bug introduced by
It seems like $language can also be of type false; however, SMW\Localizer::getLanguage() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
300
301 7
		$intlTimeFormatter = new IntlTimeFormatter(
302
			$dataItem,
303
			$language
304
		);
305
306 7
		return $intlTimeFormatter->getLocalizedFormat() . $this->hintCalendarModel( $dataItem );
307
	}
308
309
	/**
310
	 * Compute a suitable string to display this date, taking into account the
311
	 * output format and the preferrable calendar models for the data.
312
	 *
313
	 * @note MediaWiki's date functions are not applicable for the range
314
	 * of historic dates we support.
315
	 *
316
	 * @return string
317
	 */
318 28
	protected function getPreferredCaption() {
319
320 28
		$dataItem = $this->dataValue->getDataItem();
321 28
		$format = strtoupper( $this->dataValue->getOutputFormat() );
322
323 28
		if ( $format == 'ISO' || $this->dataValue->getOutputFormat() == '-' ) {
324 6
			return $this->getISO8601Date();
325 24
		} elseif ( $format == 'MEDIAWIKI' ) {
326 4
			return $this->getMediaWikiDate();
327 22
		} elseif ( $format == 'SORTKEY' ) {
328
			return $dataItem->getSortKey();
329 22
		} elseif ( $format == 'JD' ) {
330 2
			return $dataItem->getJD();
331
		}
332
333
		// Does the formatting require calendar conversion?
334 22
		$model = $dataItem->getCalendarModel();
335
336
		if (
337 22
			( strpos( $format, 'JL' ) !== false ) ||
338 22
			( $dataItem->getJD() < TimeValue::J1582 && strpos( $format, 'GR' ) === false ) ) {
339 5
			$model = DITime::CM_JULIAN;
340 22
		} elseif ( strpos( $format, 'GR' ) !== false ) {
341 4
			$model = DITime::CM_GREGORIAN;
342
		}
343
344 22
		if ( strpos( $format, '-F[' ) !== false ) {
345 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 345 which is incompatible with the return type documented by SMW\DataValues\ValueForm...er::getPreferredCaption of type string.
Loading history...
346 22
		} elseif ( strpos( $format, 'LOCL' ) !== false ) {
347 6
			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...
348 17
		} elseif ( $dataItem->getYear() > TimeValue::PREHISTORY && $dataItem->getPrecision() >= DITime::PREC_YM ) {
349
			// Do not convert between Gregorian and Julian if only
350
			// year is given (years largely overlap in history, but
351
			// assuming 1 Jan as the default date, the year number
352
			// would change in conversion).
353
			// Also do not convert calendars in prehistory: not
354
			// meaningful (getDataItemForCalendarModel may return null).
355 16
			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...
356
		}
357
358 7
		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...
359
	}
360
361 24
	private function hintCalendarModel( $dataItem ) {
362
363 24
		if ( $this->dataValue->isEnabledFeature( SMW_DV_TIMEV_CM ) && $dataItem->getCalendarModel() !== DITime::CM_GREGORIAN ) {
364 5
			return ' ' . \Html::rawElement( 'sup', array(), $dataItem->getCalendarModelLiteral() );
365
		}
366
367 24
		return '';
368
	}
369
370
}
371