1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\validators;
use IntlDateFormatter;
use Yii;
use DateTime;
use yii\helpers\FormatConverter;
/**
* DateValidator verifies if the attribute represents a date, time or datetime in a proper format.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class DateValidator extends Validator
{
/**
* @var string the date format that the value being validated should follow.
* This can be a date time pattern as described in the [ICU manual](http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax).
*
* Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the PHP Datetime class.
* Please refer to <http://php.net/manual/en/datetime.createfromformat.php> on supported formats.
*
* If this property is not set, the default value will be obtained from `Yii::$app->formatter->dateFormat`, see [[\yii\i18n\Formatter::dateFormat]] for details.
*
* Here are some example values:
*
* ```php
* 'MM/dd/yyyy' // date in ICU format
* 'php:m/d/Y' // the same date in PHP format
* ```
*/
public $format;
/**
* @var string the locale ID that is used to localize the date parsing.
* This is only effective when the [PHP intl extension](http://php.net/manual/en/book.intl.php) is installed.
* If not set, the locale of the [[\yii\base\Application::formatter|formatter]] will be used.
* See also [[\yii\i18n\Formatter::locale]].
*/
public $locale;
/**
* @var string the timezone to use for parsing date and time values.
* This can be any value that may be passed to [date_default_timezone_set()](http://www.php.net/manual/en/function.date-default-timezone-set.php)
* e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.
* Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
* If this property is not set, [[\yii\base\Application::timeZone]] will be used.
*/
public $timeZone;
/**
* @var string the name of the attribute to receive the parsing result.
* When this property is not null and the validation is successful, the named attribute will
* receive the parsing result.
*/
public $timestampAttribute;
/**
* @var array map of short format names to IntlDateFormatter constant values.
*/
private $_dateFormats = [
'short' => 3, // IntlDateFormatter::SHORT,
'medium' => 2, // IntlDateFormatter::MEDIUM,
'long' => 1, // IntlDateFormatter::LONG,
'full' => 0, // IntlDateFormatter::FULL,
];
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->message === null) {
$this->message = Yii::t('yii', 'The format of {attribute} is invalid.');
}
if ($this->format === null) {
$this->format = Yii::$app->formatter->dateFormat;
}
if ($this->locale === null) {
$this->locale = Yii::$app->language;
}
if ($this->timeZone === null) {
$this->timeZone = Yii::$app->timeZone;
}
}
/**
* @inheritdoc
*/
public function validateAttribute($model, $attribute)
{
$value = $model->$attribute;
$timestamp = $this->parseDateValue($value);
if ($timestamp === false) {
$this->addError($model, $attribute, $this->message, []);
} elseif ($this->timestampAttribute !== null) {
$model->{$this->timestampAttribute} = $timestamp;
}
}
/**
* @inheritdoc
*/
protected function validateValue($value)
{
return $this->parseDateValue($value) === false ? [$this->message, []] : null;
}
/**
* Parses date string into UNIX timestamp
*
* @param string $value string representing date
* @return boolean|integer UNIX timestamp or false on failure
*/
protected function parseDateValue($value)
{
if (is_array($value)) {
return false;
}
$format = $this->format;
if (strncmp($this->format, 'php:', 4) === 0) {
$format = substr($format, 4);
} else {
if (extension_loaded('intl')) {
if (isset($this->_dateFormats[$format])) {
$formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE, $this->timeZone);
} else {
$formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $this->timeZone, null, $format);
}
// enable strict parsing to avoid getting invalid date values
$formatter->setLenient(false);
// There should not be a warning thrown by parse() but this seems to be the case on windows so we suppress it here
// See https://github.com/yiisoft/yii2/issues/5962 and https://bugs.php.net/bug.php?id=68528
return @$formatter->parse($value);
} else {
// fallback to PHP if intl is not installed
$format = FormatConverter::convertDateIcuToPhp($format, 'date');
}
}
$date = DateTime::createFromFormat($format, $value, new \DateTimeZone($this->timeZone));
$errors = DateTime::getLastErrors();
if ($date === false || $errors['error_count'] || $errors['warning_count']) {
return false;
} else {
// if no time was provided in the format string set time to 0 to get a simple date timestamp
if (strpbrk($format, 'HhGgis') === false) {
$date->setTime(0, 0, 0);
}
return $date->getTimestamp();
}
}
}