Skip to content

Commit 54278fc

Browse files
Bizleycebe
Bizley
authored andcommitted
asTime fixed for time-only values
close yiisoft#13348 fixes yiisoft#13343
1 parent 7fda48e commit 54278fc

File tree

4 files changed

+52
-17
lines changed

4 files changed

+52
-17
lines changed

framework/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Yii Framework 2 Change Log
88
- Bug #13538: Fixed `yii\db\BaseActiveRecord::deleteAll()` changes method signature declared by `yii\db\ActiveRecordInterface::deleteAll()` (klimov-paul)
99
- Enh #13278: `yii\caching\DbQueryDependency` created allowing specification of the cache dependency via `yii\db\QueryInterface` (klimov-paul)
1010
- Bug #11230: Include `defaultRoles` in `yii\rbac\DbManager->getRolesByUser()` results (developeruz)
11+
- Bug #13343: Fixed `yii\i18n\Formatter::asTime()` to process time-only values without time zone conversion (bizley)
1112
- Bug #11404: `yii\base\Model::loadMultiple()` returns true even if `yii\base\Model::load()` returns false (zvook)
1213
- Bug #13513: Fixed RBAC migration to work correctly on Oracle DBMS (silverfire)
1314
- Enh #13550: Refactored unset call order in `yii\di\ServiceLocator::set()` (Lanrik)

framework/UPGRADE.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ if you want to upgrade from version A to version C and there is
5050
version B between A and C, you need to follow the instructions
5151
for both A and B.
5252

53+
Upgrade from Yii 2.0.11
54+
-----------------------
55+
56+
* `yii\i18n\Formatter::normalizeDatetimeValue()` returns now array with additional third boolean element
57+
indicating whether the timestamp has date information or it is just time value.
58+
59+
5360
Upgrade from Yii 2.0.10
5461
-----------------------
5562

@@ -59,7 +66,7 @@ Upgrade from Yii 2.0.10
5966

6067
* `yii\validators\FileValidator::getClientOptions()` and `yii\validators\ImageValidator::getClientOptions()` are now public.
6168
If you extend from these classes and override these methods, you must make them public as well.
62-
69+
6370
* `yii\widgets\MaskedInput` inputmask dependency was updated to `~3.3.3`.
6471
[See its changelog for details](https://github.com/RobinHerbots/Inputmask/blob/3.x/CHANGELOG.md).
6572

framework/i18n/Formatter.php

+20-13
Original file line numberDiff line numberDiff line change
@@ -584,10 +584,10 @@ public function asDatetime($value, $format = null)
584584
private function formatDateTimeValue($value, $format, $type)
585585
{
586586
$timeZone = $this->timeZone;
587-
// avoid time zone conversion for date-only values
588-
if ($type === 'date') {
589-
list($timestamp, $hasTimeInfo) = $this->normalizeDatetimeValue($value, true);
590-
if (!$hasTimeInfo) {
587+
// avoid time zone conversion for date-only and time-only values
588+
if ($type === 'date' || $type === 'time') {
589+
list($timestamp, $hasTimeInfo, $hasDateInfo) = $this->normalizeDatetimeValue($value, true);
590+
if ($type === 'date' && !$hasTimeInfo || $type === 'time' && !$hasDateInfo) {
591591
$timeZone = $this->defaultTimeZone;
592592
}
593593
} else {
@@ -650,40 +650,47 @@ private function formatDateTimeValue($value, $format, $type)
650650
* The timestamp is assumed to be in [[defaultTimeZone]] unless a time zone is explicitly given.
651651
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
652652
*
653-
* @param bool $checkTimeInfo whether to also check if the date/time value has some time information attached.
653+
* @param bool $checkDateTimeInfo whether to also check if the date/time value has some time and date information attached.
654654
* Defaults to `false`. If `true`, the method will then return an array with the first element being the normalized
655-
* timestamp and the second a boolean indicating whether the timestamp has time information or it is just a date value.
655+
* timestamp, the second a boolean indicating whether the timestamp has time information and third a boolean indicating
656+
* whether the timestamp has date information.
656657
* This parameter is available since version 2.0.1.
657658
* @return DateTime|array the normalized datetime value.
658659
* Since version 2.0.1 this may also return an array if `$checkTimeInfo` is true.
659660
* The first element of the array is the normalized timestamp and the second is a boolean indicating whether
660661
* the timestamp has time information or it is just a date value.
662+
* Since version 2.0.12 the array has third boolean element indicating whether the timestamp has date information
663+
* or it is just a time value.
661664
* @throws InvalidParamException if the input value can not be evaluated as a date value.
662665
*/
663-
protected function normalizeDatetimeValue($value, $checkTimeInfo = false)
666+
protected function normalizeDatetimeValue($value, $checkDateTimeInfo = false)
664667
{
665668
// checking for DateTime and DateTimeInterface is not redundant, DateTimeInterface is only in PHP>5.5
666669
if ($value === null || $value instanceof DateTime || $value instanceof DateTimeInterface) {
667670
// skip any processing
668-
return $checkTimeInfo ? [$value, true] : $value;
671+
return $checkDateTimeInfo ? [$value, true, true] : $value;
669672
}
670673
if (empty($value)) {
671674
$value = 0;
672675
}
673676
try {
674677
if (is_numeric($value)) { // process as unix timestamp, which is always in UTC
675678
$timestamp = new DateTime('@' . (int)$value, new DateTimeZone('UTC'));
676-
return $checkTimeInfo ? [$timestamp, true] : $timestamp;
679+
return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;
677680
} 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)
678-
return $checkTimeInfo ? [$timestamp, false] : $timestamp;
681+
return $checkDateTimeInfo ? [$timestamp, false, true] : $timestamp;
679682
} 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)
680-
return $checkTimeInfo ? [$timestamp, true] : $timestamp;
683+
return $checkDateTimeInfo ? [$timestamp, true, true] : $timestamp;
681684
}
682685
// finally try to create a DateTime object with the value
683-
if ($checkTimeInfo) {
686+
if ($checkDateTimeInfo) {
684687
$timestamp = new DateTime($value, new DateTimeZone($this->defaultTimeZone));
685688
$info = date_parse($value);
686-
return [$timestamp, !($info['hour'] === false && $info['minute'] === false && $info['second'] === false)];
689+
return [
690+
$timestamp,
691+
!($info['hour'] === false && $info['minute'] === false && $info['second'] === false),
692+
!($info['year'] === false && $info['month'] === false && $info['day'] === false)
693+
];
687694
} else {
688695
return new DateTime($value, new DateTimeZone($this->defaultTimeZone));
689696
}

tests/framework/i18n/FormatterDateTest.php

+23-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace yiiunit\framework\i18n;
44

55
use yii\i18n\Formatter;
6-
use Yii;
76
use yiiunit\TestCase;
87
use DateTime;
98
use DateInterval;
@@ -437,7 +436,7 @@ public function testAsDuration()
437436
$this->assertSame('5 months', $this->formatter->asDuration($interval_5_months));
438437
$this->assertSame('1 year', $this->formatter->asDuration($interval_1_year));
439438
$this->assertSame('12 years', $this->formatter->asDuration($interval_12_years));
440-
439+
441440
// Pass a numeric value
442441
$this->assertSame('0 seconds', $this->formatter->asDuration(0));
443442
$this->assertSame('1 second', $this->formatter->asDuration(1));
@@ -674,7 +673,7 @@ public function testTimezoneInputNonDefault()
674673
public function testDateOnlyValues()
675674
{
676675
date_default_timezone_set('Pacific/Kiritimati');
677-
// timzones with exactly 24h difference, ensure this test does not fail on a certain time
676+
// timezones with exactly 24h difference, ensure this test does not fail on a certain time
678677
$this->formatter->defaultTimeZone = 'Pacific/Kiritimati'; // always UTC+14
679678
$this->formatter->timeZone = 'Pacific/Honolulu'; // always UTC-10
680679

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

690+
/**
691+
* https://github.com/yiisoft/yii2/issues/13343
692+
*
693+
* Prevent timezone conversion for time-only values.
694+
*/
695+
public function testTimeOnlyValues()
696+
{
697+
$this->formatter->defaultTimeZone = 'UTC';
698+
$this->formatter->timeZone = 'Europe/Zurich'; // UTC+1 (DST UTC+2)
699+
700+
// time-only value, do not convert
701+
$this->assertSame('12:00:00', $this->formatter->asTime('12:00:00', 'HH:mm:ss'));
702+
// full info, convert
703+
$this->assertSame('13:00:00', $this->formatter->asTime('07.01.2017 12:00:00', 'HH:mm:ss'));
704+
$this->assertSame('14:00:00', $this->formatter->asTime('29.06.2017 12:00:00', 'HH:mm:ss'));
705+
706+
// timezone conversion expected with asDatetime() and asDate() with time-only value
707+
$this->assertNotSame('12:00:00', $this->formatter->asDatetime('12:00:00', 'HH:mm:ss'));
708+
$this->assertNotSame('12:00:00', $this->formatter->asDate('12:00:00', 'HH:mm:ss'));
709+
}
710+
691711
/**
692712
* https://github.com/yiisoft/yii2/issues/6263
693713
*

0 commit comments

Comments
 (0)