Skip to content

Commit

Permalink
Major rewrite of TimeFormatter
Browse files Browse the repository at this point in the history
  • Loading branch information
thiemowmde committed Jun 8, 2017
1 parent 937765b commit a56faa9
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 26 deletions.
93 changes: 86 additions & 7 deletions src/ValueFormatters/TimeFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@

/**
* Basic plain text formatter for TimeValue objects that either delegates formatting to an other
* formatter given via OPT_TIME_ISO_FORMATTER or outputs the timestamp as it is.
* formatter given via self::OPT_TIME_ISO_FORMATTER or outputs the timestamp in simple YMD-ordered
* fallback formats, resembling ISO 8601.
*
* @since 0.1
*
* @license GPL-2.0+
* @author H. Snater < mediawiki@snater.com >
* @author Thiemo Mättig
*/
class TimeFormatter extends ValueFormatterBase {

Expand Down Expand Up @@ -47,7 +49,11 @@ class TimeFormatter extends ValueFormatterBase {
public function __construct( FormatterOptions $options = null ) {
parent::__construct( $options );

$this->defaultOption( self::OPT_CALENDARNAMES, array() );
// A non-localized default is still better than showing full concept URIs.
$this->defaultOption( self::OPT_CALENDARNAMES, array(
TimeValue::CALENDAR_GREGORIAN => 'Gregorian',
TimeValue::CALENDAR_JULIAN => 'Julian',
) );
$this->defaultOption( self::OPT_TIME_ISO_FORMATTER, null );
}

Expand All @@ -64,14 +70,87 @@ public function format( $value ) {
throw new InvalidArgumentException( 'Data value type mismatch. Expected a TimeValue.' );
}

$formatted = $value->getTime();
$formatted = $this->getFormattedTimestamp( $value );
// FIXME: Temporarily disabled.
// $formatted .= ' (' . $this->getFormattedCalendarModel( $value->getCalendarModel() ) . ')';
return $formatted;
}

$isoFormatter = $this->getOption( self::OPT_TIME_ISO_FORMATTER );
if ( $isoFormatter instanceof ValueFormatter ) {
$formatted = $isoFormatter->format( $value );
/**
* @param TimeValue $value
*
* @return string Plain text
*/
private function getFormattedTimestamp( TimeValue $value ) {
$formatter = $this->getOption( self::OPT_TIME_ISO_FORMATTER );

if ( $formatter instanceof ValueFormatter ) {
return $formatter->format( $value );
}

return $formatted;
if ( !preg_match(
// Loose check for ISO-like strings, as used in Gregorian and Julian time values.
'/^([-+]?)(\d+)-(\d+)-(\d+)(?:T(\d+):(\d+)(?::(\d+))?)?Z?$/i',
$value->getTime(),
$matches
) ) {
return $value->getTime();
}

list( , $sign, $year, $month, $day, $hour, $minute, $second ) = $matches;

// Actual MINUS SIGN (U+2212) instead of HYPHEN-MINUS (U+002D)
$sign = $sign === '-' ? "\xE2\x88\x92" : '';

// Warning, never cast the year to integer to not run into 32-bit integer overflows!
$year = ltrim( $year, '0' );

if ( $value->getPrecision() <= TimeValue::PRECISION_YEAR ) {
return sprintf( '%s%04s', $sign, $year );
}

switch ( $value->getPrecision() ) {
case TimeValue::PRECISION_MONTH:
return sprintf(
'%s%04s-%02s',
$sign, $year, $month
);
case TimeValue::PRECISION_DAY:
return sprintf(
'%s%04s-%02s-%02s',
$sign, $year, $month, $day
);
case TimeValue::PRECISION_HOUR:
return sprintf(
'%s%04s-%02s-%02sT%02s',
$sign, $year, $month, $day, $hour
);
case TimeValue::PRECISION_MINUTE:
return sprintf(
'%s%04s-%02s-%02sT%02s:%02s',
$sign, $year, $month, $day, $hour, $minute
);
default:
return sprintf(
'%s%04s-%02s-%02sT%02s:%02s:%02s',
$sign, $year, $month, $day, $hour, $minute, $second
);
}
}

/**
* @param string $calendarModel
*
* @return string Plain text
*/
private function getFormattedCalendarModel( $calendarModel ) {
$calendarNames = $this->getOption( self::OPT_CALENDARNAMES );

if ( array_key_exists( $calendarModel, $calendarNames ) ) {
return $calendarNames[$calendarModel];
}

return $calendarModel;
}

}
109 changes: 90 additions & 19 deletions tests/ValueFormatters/TimeFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use DataValues\TimeValue;
use ValueFormatters\FormatterOptions;
use ValueFormatters\TimeFormatter;
use ValueFormatters\ValueFormatter;

/**
* @covers ValueFormatters\TimeFormatter
Expand Down Expand Up @@ -36,6 +37,18 @@ protected function getInstance( FormatterOptions $options = null ) {
return new TimeFormatter( $options );
}

/**
* @return ValueFormatter
*/
private function getTimestampFormatter() {
$mock = $this->getMock( 'ValueFormatters\ValueFormatter' );
$mock->expects( $this->any() )
->method( 'format' )
->will( $this->returnValue( '<timestamp>' ) );

return $mock;
}

/**
* @see ValueFormatterTestBase::validProvider
*/
Expand All @@ -44,42 +57,100 @@ public function validProvider() {
$julian = 'http://www.wikidata.org/entity/Q1985786';

$baseOptions = new FormatterOptions();
$baseOptions->setOption( TimeFormatter::OPT_CALENDARNAMES, array(
$gregorian => '<Gregorian>',
$julian => '<Julian>',
) );

$timestampFormatterOptions = new FormatterOptions();
$timestampFormatterOptions->setOption(
TimeFormatter::OPT_TIME_ISO_FORMATTER,
$this->getTimestampFormatter()
);

$tests = array(
'+2013-07-16T00:00:00Z' => array(
'2013-07-16' => array(
'+2013-07-16T00:00:00Z',
),
'+0000-01-01T00:00:00Z' => array(
'+0000-01-01T00:00:00Z',

// Custom timestamp formatter
'<timestamp>' => array(
'+2013-07-16T00:00:00Z',
TimeValue::PRECISION_DAY,
$gregorian,
$timestampFormatterOptions,
),

// Different calendar models
'+0001-01-14T00:00:00Z' => array(
'+0001-01-14T00:00:00Z',
'1701-12-14' => array(
'+1701-12-14T00:00:00Z',
TimeValue::PRECISION_DAY,
$julian,
),
'1702-12-14' => array(
'+1702-12-14T00:00:00Z',
TimeValue::PRECISION_DAY,
$julian
'Stardate',
),

// Different years
'+10000-01-01T00:00:00Z' => array(
'+10000-01-01T00:00:00Z',
"\xE2\x88\x9210000-01-01" => array(
'-010000-01-01T00:00:00Z',
),
"\xE2\x88\x920001-01-01" => array(
'-1-01-01T00:00:00Z',
),
"\xE2\x88\x920100-01-01" => array(
'-100-01-01T00:00:00Z',
),
"\xE2\x88\x920000-01-01" => array(
'-0-01-01T00:00:00Z',
),
'-0001-01-01T00:00:00Z' => array(
'-0001-01-01T00:00:00Z',
'0000-01-01' => array(
'+0-01-01T00:00:00Z',
),
'0001-01-01' => array(
'+1-01-01T00:00:00Z',
),
'0100-01-01' => array(
'+100-01-01T00:00:00Z',
),
'10000-01-01' => array(
'+010000-01-01T00:00:00Z',
),

// Different precisions
'+2013-07-17T00:00:00Z' => array(
'+2013-07-17T00:00:00Z',
TimeValue::PRECISION_MONTH,
'2000' => array(
'+2000-01-01T00:00:00Z',
TimeValue::PRECISION_YEAR1G,
),
'+2013-07-18T00:00:00Z' => array(
'+2013-07-18T00:00:00Z',
'2008' => array(
'+2008-01-08T00:00:00Z',
TimeValue::PRECISION_YEAR10,
),
'2009' => array(
'+2009-01-09T00:00:00Z',
TimeValue::PRECISION_YEAR,
),
'+2013-07-19T00:00:00Z' => array(
'+2013-07-19T00:00:00Z',
TimeValue::PRECISION_YEAR10,
'2010-07' => array(
'+2010-07-10T00:00:00Z',
TimeValue::PRECISION_MONTH,
),
'2011-07-11' => array(
'+2011-07-11T00:00:00Z',
TimeValue::PRECISION_DAY,
),
'2012-07-12T00' => array(
'+2012-07-12T00:00:00Z',
TimeValue::PRECISION_HOUR,
),
'2013-07-13T00:00' => array(
'+2013-07-13T00:00:00Z',
TimeValue::PRECISION_MINUTE,
),
'2014-07-14T00:00:00' => array(
'+2014-07-14T00:00:00Z',
TimeValue::PRECISION_SECOND,
),
);

Expand All @@ -93,7 +164,7 @@ public function validProvider() {

$argLists[] = array(
new TimeValue( $timestamp, 0, 0, 0, $precision, $calendarModel ),
$expected,
(string)$expected,
$options
);
}
Expand Down

0 comments on commit a56faa9

Please sign in to comment.