Skip to content

Commit

Permalink
asTime fixed for time-only values
Browse files Browse the repository at this point in the history
  • Loading branch information
Bizley authored and cebe committed Feb 16, 2017
1 parent 7fda48e commit 54278fc
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 17 deletions.
1 change: 1 addition & 0 deletions framework/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Yii Framework 2 Change Log
- Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul)
- Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul)
- Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz)
- Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley)
- Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook)
- Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire)
- Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik)
Expand Down
9 changes: 8 additions & 1 deletion framework/UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ if you want to upgrade from version A to version C and there is
version B between A and C, you need to follow the instructions
for both A and B.

Upgrade from Yii 2.0.11
-----------------------

* `yii\i18n\Formatter::normalizeDatetimeValue()` returns now array with additional third boolean element
indicating whether the timestamp has date information or it is just time value.


Upgrade from Yii 2.0.10
-----------------------

Expand All @@ -59,7 +66,7 @@ Upgrade from Yii 2.0.10

* `yii\validators\FileValidator::getClientOptions()` and `yii\validators\ImageValidator::getClientOptions()` are now public.
If you extend from these classes and override these methods, you must make them public as well.

* `yii\widgets\MaskedInput` inputmask dependency was updated to `~3.3.3`.
[See its changelog for details](https://github.com/RobinHerbots/Inputmask/blob/3.x/CHANGELOG.md).

Expand Down
33 changes: 20 additions & 13 deletions framework/i18n/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -584,10 +584,10 @@ public function asDatetime($value, $format = null)
private function formatDateTimeValue($value, $format, $type)
{
$timeZone = $this->timeZone;
// avoid time zone conversion for date-only values
if ($type === 'date') {
list($timestamp, $hasTimeInfo) = $this->normalizeDatetimeValue($value, true);
if (!$hasTimeInfo) {
// avoid time zone conversion for date-only and time-only values
if ($type === 'date' || $type === 'time') {
list($timestamp, $hasTimeInfo, $hasDateInfo) = $this->normalizeDatetimeValue($value, true);
if ($type === 'date' && !$hasTimeInfo || $type === 'time' && !$hasDateInfo) {
$timeZone = $this->defaultTimeZone;
}
} else {
Expand Down Expand Up @@ -650,40 +650,47 @@ private function formatDateTimeValue($value, $format, $type)
* The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
*
* @param bool $checkTimeInfo whether to also check if the date/time value has some time information attached.
* @param bool $checkDateTimeInfo whether to also check if the date/time value has some time and date information attached.
* Defaults to `false`. If `true`, the method will then return an array with the first element being the normalized
* timestamp and the second a boolean indicating whether the timestamp has time information or it is just a date value.
* timestamp, the second a boolean indicating whether the timestamp has time information and third a boolean indicating
* whether the timestamp has date information.
* This parameter is available since version 2.0.1.
* @return DateTime|array the normalized datetime value.
* Since version 2.0.1 this may also return an array if `$checkTimeInfo` is true.
* The first element of the array is the normalized timestamp and the second is a boolean indicating whether
* the timestamp has time information or it is just a date value.
* Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information
* or it is just a time value.
* @throws InvalidParamException if the input value can not be evaluated as a date value.
*/
protected function normalizeDatetimeValue($value, $checkTimeInfo = false)
protected function normalizeDatetimeValue($value, $checkDateTimeInfo = false)
{
// checking for DateTime and DateTimeInterface is not redundant, DateTimeInterface is only in PHP>5.5
if ($value === null || $value instanceof DateTime || $value instanceof DateTimeInterface) {
// skip any processing
return $checkTimeInfo ? [$value, true] : $value;
return $checkDateTimeInfo ? [$value, true, true] : $value;
}
if (empty($value)) {
$value = 0;
}
try {
if (is_numeric($value)) { // process as unix timestamp, which is always in UTC
$timestamp = new DateTime('@' . (int)$value, new DateTimeZone('UTC'));
return $checkTimeInfo ? [$timestamp, true] : $timestamp;
return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;
} elseif (($timestamp = DateTime::createFromFormat('Y-m-d', $value, new DateTimeZone($this->defaultTimeZone))) !== false) { // try Y-m-d format (support invalid dates like 2012-13-01)
return $checkTimeInfo ? [$timestamp, false] : $timestamp;
return $checkDateTimeInfo ? [$timestamp, false, true] : $timestamp;
} elseif (($timestamp = DateTime::createFromFormat('Y-m-d H:i:s', $value, new DateTimeZone($this->defaultTimeZone))) !== false) { // try Y-m-d H:i:s format (support invalid dates like 2012-13-01 12:63:12)
return $checkTimeInfo ? [$timestamp, true] : $timestamp;
return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;
}
// finally try to create a DateTime object with the value
if ($checkTimeInfo) {
if ($checkDateTimeInfo) {
$timestamp = new DateTime($value, new DateTimeZone($this->defaultTimeZone));
$info = date_parse($value);
return [$timestamp, !($info['hour'] === false && $info['minute'] === false && $info['second'] === false)];
return [
$timestamp,
!($info['hour'] === false && $info['minute'] === false && $info['second'] === false),
!($info['year'] === false && $info['month'] === false && $info['day'] === false)
];
} else {
return new DateTime($value, new DateTimeZone($this->defaultTimeZone));
}
Expand Down
26 changes: 23 additions & 3 deletions tests/framework/i18n/FormatterDateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace yiiunit\framework\i18n;

use yii\i18n\Formatter;
use Yii;
use yiiunit\TestCase;
use DateTime;
use DateInterval;
Expand Down Expand Up @@ -437,7 +436,7 @@ public function testAsDuration()
$this->assertSame('5 months', $this->formatter->asDuration($interval_5_months));
$this->assertSame('1 year', $this->formatter->asDuration($interval_1_year));
$this->assertSame('12 years', $this->formatter->asDuration($interval_12_years));

// Pass a numeric value
$this->assertSame('0 seconds', $this->formatter->asDuration(0));
$this->assertSame('1 second', $this->formatter->asDuration(1));
Expand Down Expand Up @@ -674,7 +673,7 @@ public function testTimezoneInputNonDefault()
public function testDateOnlyValues()
{
date_default_timezone_set('Pacific/Kiritimati');
// timzones with exactly 24h difference, ensure this test does not fail on a certain time
// timezones with exactly 24h difference, ensure this test does not fail on a certain time
$this->formatter->defaultTimeZone = 'Pacific/Kiritimati'; // always UTC+14
$this->formatter->timeZone = 'Pacific/Honolulu'; // always UTC-10

Expand All @@ -688,6 +687,27 @@ public function testDateOnlyValues()
$this->assertSame('2014-08-01', $this->formatter->asDate('2014-08-01', 'yyyy-MM-dd'));
}

/**
* https://github.com/yiisoft/yii2/issues/13343
*
* Prevent timezone conversion for time-only values.
*/
public function testTimeOnlyValues()
{
$this->formatter->defaultTimeZone = 'UTC';
$this->formatter->timeZone = 'Europe/Zurich'; // UTC+1 (DST UTC+2)

// time-only value, do not convert
$this->assertSame('12:00:00', $this->formatter->asTime('12:00:00', 'HH:mm:ss'));
// full info, convert
$this->assertSame('13:00:00', $this->formatter->asTime('07.01.2017 12:00:00', 'HH:mm:ss'));
$this->assertSame('14:00:00', $this->formatter->asTime('29.06.2017 12:00:00', 'HH:mm:ss'));

// timezone conversion expected with asDatetime() and asDate() with time-only value
$this->assertNotSame('12:00:00', $this->formatter->asDatetime('12:00:00', 'HH:mm:ss'));
$this->assertNotSame('12:00:00', $this->formatter->asDate('12:00:00', 'HH:mm:ss'));
}

/**
* https://github.com/yiisoft/yii2/issues/6263
*
Expand Down

0 comments on commit 54278fc

Please sign in to comment.