Yii2: DateValidator

Created on 26 Oct 2020  路  9Comments  路  Source: yiisoft/yii2

https://github.com/yiisoft/yii2/blob/9e184aaeae1b1ab6dfad02830c6de2f0d7dcf7b4/framework/validators/DateValidator.php#L436

change

    $hasTimeInfo = (strpbrk($format, 'HhGgisU') !== false);

    $date = DateTime::createFromFormat($format, $value, new \DateTimeZone($hasTimeInfo ? $this->timeZone : 'UTC'));
    $errors = DateTime::getLastErrors();
    if ($date === false || $errors['error_count'] || $errors['warning_count'] || ($this->strictDateFormat && $date->format($format) !== $value)) {
        return false;
    }

    if (!$hasTimeInfo) {
        $date->setTime(0, 0, 0);
    }

to

    $date = DateTime::createFromFormat($format, $value, new \DateTimeZone($this->timeZone));
    $errors = DateTime::getLastErrors();
    if ($date === false || $errors['error_count'] || $errors['warning_count'] || ($this->strictDateFormat && $date->format($format) !== $value)) {
        return false;
    }

    $hasTimeInfo = (strpbrk($format, 'HhGgisU') !== false);

    if (!$hasTimeInfo) {
        $date->setTime(0, 0, 0);
    }

For example: I use format 'php:d.m.Y' for date validation and pass '26.10.2020' as value. After validation timestampAttribute set as '1603670400', but I need '1603663200' value because time zone in my settings is 'Europe/Kiev'.

bug

All 9 comments

As I stated in the PR - I'm not sure what this change should do and why the current implementation is insufficient. Please elaborate.

In current implementation when I set timeZone attribute in validator and use date format without time (such as "HhGgisU", for example "d.m.Y" in PHP format) - timestampAttribute stored in UTC time zone, but my app setting is Europe/Kiev.

In my case, i need save timestamp at midnight. I set value 26.10.2020 and timestampAttribute stored in DB as 1603670400 and when i get stored timestamp and show to user it display as 26.10.2020 02:00 because saves in UTC timeZone instead Europe/Kiev.

    Yii::$app->timeZone = 'Europe/Kiev';
    Yii::$app->formatter->timeZone = 'Europe/Kiev';

    $model = new DynamicModel([
        'created_date' => '26.10.2020',
        'created_timestamp',
    ]);

    $dateValidator = new DateValidator([
        'format' => 'php:d.m.Y',
        'timeZone' => 'Europe/Kiev',
        'timestampAttribute' => 'created_timestamp',
    ]);

    $model->addRule('created_date', $dateValidator);

    $model->validate();

    var_dump($model->attributes);
    /*array(2) {
            ["created_date"]=>
      string(10) "26.10.2020"
            ["created_timestamp"]=>
      int(1603670400)
    }*/

    var_dump(Yii::$app->formatter->asDate($model->created_timestamp, 'php:d.m.Y H:i'));
    /*string(16) "26.10.2020 02:00"*/

If i use format with time attributes, that work's is fine.

    $model = new DynamicModel([
        'created_date' => '26.10.2020 00:00',
        'created_timestamp',
    ]);

    $dateValidator = new DateValidator([
        'format' => 'php:d.m.Y H:i',
        'timeZone' => 'Europe/Kiev',
        'timestampAttribute' => 'created_timestamp',
    ]);

    var_dump($model->attributes);
    /*array(2) {
            ["created_date"]=>
      string(16) "26.10.2020 00:00"
            ["created_timestamp"]=>
      int(1603663200)
    }*/

    var_dump(Yii::$app->formatter->asDate($model->created_timestamp, 'php:d.m.Y H:i'));
    /*string(16) "26.10.2020 00:00"*/

Does setting timestampAttributeTimeZone in validator help?

No, because it skipped if timestampAttributeFormat is null

https://github.com/yiisoft/yii2/blob/9e184aaeae1b1ab6dfad02830c6de2f0d7dcf7b4/framework/validators/DateValidator.php#L303

If i set 'timestampAttributeFormat' => 'php:U' and 'timestampAttributeTimeZone' => 'Europe/Kiev' result are same.

Just for test i set 'timestampAttributeFormat' => 'php:d.m.Y H:i' and my created_timestamp value is 26.10.2020 02:00, but i need 26.10.2020 00:00

All trouble in

https://github.com/yiisoft/yii2/blob/9e184aaeae1b1ab6dfad02830c6de2f0d7dcf7b4/framework/validators/DateValidator.php#L441

When using format without time - timeZone always UTC.

I'm not exactly sure about $hasTimeInfo = (strpbrk($format, 'ahHkKmsSA') !== false);

But, we shouldn't check time info in create format

    $hasTimeInfo = (strpbrk($format, 'HhGgisU') !== false);

    $date = DateTime::createFromFormat($format, $value, new \DateTimeZone($hasTimeInfo ? $this->timeZone : 'UTC'));

Set midnight after date created if time not presented in format value.

    if (!$hasTimeInfo) {
        $date->setTime(0, 0, 0);
    }

Ok, now I understand better. Let me look through the code and your PR and we will continue from there.

As I found this is a duplicate of https://github.com/yiisoft/yii2/issues/14795

There is a slight problem with this issue. Most of it was already stated in the original one but I also believe there is something wrong with how this all works.

As for the PR #18353 - it cannot be accepted in the current state of framework (not only because of lack of unit tests or, what is more important, because multiple tests are failing).

The thing is that DateValidator behaves exactly like Formatter which is just. You can check it by calling Yii::$app->formatter->asTimestamp('2020-10-30') with timezone set to something different than UTC - the result's time is 00:00:00 UTC (so it will be changed when converted to different timezone). So we should not change this behavior unless the same will happen to the Formatter.

But we could add some additional option to DateValidator. For example we could change its behavior to use timestampAttributeTimeZone as a timezone to be chosen when setting timestamp attribute value (regardless of setting timestampAttributeFormat which now must be set in order to change the stamp's timezone). If we are allowing to change "timezone of timestamp" (in quotes because it's obviously not a correct term in case of timestamps) why limit it to the formatted version only?

I would be happy to prepare solution like that. I need a green light first though, @samdark

@bizley allowing to use "timezone of timestamp" for parsing sounds OK.

Was this page helpful?
0 / 5 - 0 ratings