diff --git a/admin/src/Fields/Validator.php b/admin/src/Fields/Validator.php index 7ed84853..6e9c407a 100644 --- a/admin/src/Fields/Validator.php +++ b/admin/src/Fields/Validator.php @@ -2,10 +2,11 @@ namespace Formwork\Admin\Fields; -use Formwork\Core\Formwork; use Formwork\Data\Collection; use Formwork\Data\DataGetter; -use DateTime; +use Formwork\Utils\Date; +use Formwork\Utils\Str; +use InvalidArgumentException; class Validator { @@ -112,14 +113,11 @@ class Validator return null; } - $format = Formwork::instance()->option('date.format'); - $date = date_create_from_format($format, $value) ?: date_create($value); - - if ($date instanceof DateTime) { - return date_format($date, 'Y-m-d'); + try { + return date('Y-m-d', Date::toTimestamp($value, null, true)); + } catch (InvalidArgumentException $e) { + throw new ValidationException('Invalid value for field "' . $field->name() . '" of type "' . $field->type() . '":' . Str::after($e->getMessage(), ':')); } - - throw new ValidationException('Invalid date format for field "' . $field->name() . '" of type "' . $field->type() . '"'); } /** diff --git a/admin/src/Statistics.php b/admin/src/Statistics.php index 96033a01..738dc896 100644 --- a/admin/src/Statistics.php +++ b/admin/src/Statistics.php @@ -4,6 +4,7 @@ namespace Formwork\Admin; use Formwork\Admin\Utils\IPAnonymizer; use Formwork\Admin\Utils\Registry; +use Formwork\Utils\Date; use Formwork\Utils\FileSystem; use Formwork\Utils\HTTPRequest; use Formwork\Utils\Visitor; @@ -151,7 +152,7 @@ class Statistics $uniqueVisits = array_slice($uniqueVisits, -$limit, null, true); $label = static function (string $day): string { - $time = strtotime($day); + $time = Date::toTimestamp($day, self::DATE_FORMAT); $month = Admin::instance()->label('date.months.short')[date('n', $time) - 1]; $weekday = Admin::instance()->label('date.weekdays.short')[date('N', $time) % 7]; $day = date('j', $time); diff --git a/admin/src/Updater.php b/admin/src/Updater.php index f99f53c4..ba0c9ed4 100644 --- a/admin/src/Updater.php +++ b/admin/src/Updater.php @@ -5,6 +5,7 @@ namespace Formwork\Admin; use Formwork\Admin\Utils\Registry; use Formwork\Core\Formwork; use Formwork\Parsers\JSON; +use Formwork\Utils\Date; use Formwork\Utils\FileSystem; use Formwork\Utils\Str; use RuntimeException; @@ -253,7 +254,7 @@ class Updater $this->release = [ 'name' => $data['name'], 'tag' => $data['tag_name'], - 'date' => strtotime($data['published_at']), + 'date' => Date::toTimestamp($data['published_at'], DATE_ISO8601), 'archive' => $data['zipball_url'] ]; diff --git a/formwork/Core/Formwork.php b/formwork/Core/Formwork.php index 495c9f14..8c421421 100644 --- a/formwork/Core/Formwork.php +++ b/formwork/Core/Formwork.php @@ -10,6 +10,7 @@ use Formwork\Parsers\YAML; use Formwork\Router\RouteParams; use Formwork\Router\Router; use Formwork\Traits\SingletonTrait; +use Formwork\Utils\Date; use Formwork\Utils\FileSystem; use Formwork\Utils\Header; use Formwork\Utils\HTTPRequest; @@ -336,8 +337,8 @@ final class Formwork return $this->site->errorPage(); } if ($this->option('cache.enabled') && ($page->has('publish-date') || $page->has('unpublish-date'))) { - if (($page->published() && !$this->site->modifiedSince((int) strtotime($page->get('publish-date')))) - || (!$page->published() && !$this->site->modifiedSince((int) strtotime($page->get('unpublish-date'))))) { + if (($page->published() && !$this->site->modifiedSince(Date::toTimestamp($page->get('publish-date')))) + || (!$page->published() && !$this->site->modifiedSince(Date::toTimestamp($page->get('unpublish-date'))))) { // Clear cache if the site was not modified since the page has been published or unpublished $this->cache->clear(); FileSystem::touch($this->option('content.path')); diff --git a/formwork/Core/Page.php b/formwork/Core/Page.php index 463b7f8e..beb6311f 100644 --- a/formwork/Core/Page.php +++ b/formwork/Core/Page.php @@ -7,6 +7,7 @@ use Formwork\Metadata\Metadata; use Formwork\Parsers\Markdown; use Formwork\Parsers\YAML; use Formwork\Template\Template; +use Formwork\Utils\Date; use Formwork\Utils\FileSystem; use Formwork\Utils\Header; use Formwork\Utils\Str; @@ -288,7 +289,7 @@ class Page extends AbstractPage $format = Formwork::instance()->option('date.format'); } if ($this->has('publish-date')) { - return date($format, strtotime($this->data['publish-date'])); + return date($format, Date::toTimestamp($this->data['publish-date'])); } return parent::date($format); } @@ -572,11 +573,11 @@ class Page extends AbstractPage $this->published = $this->data['published']; if ($this->has('publish-date')) { - $this->published = $this->published && strtotime($this->get('publish-date')) < time(); + $this->published = $this->published && Date::toTimestamp($this->get('publish-date')) < time(); } if ($this->has('unpublish-date')) { - $this->published = $this->published && strtotime($this->get('unpublish-date')) > time(); + $this->published = $this->published && Date::toTimestamp($this->get('unpublish-date')) > time(); } $this->routable = $this->data['routable']; diff --git a/formwork/Utils/Date.php b/formwork/Utils/Date.php new file mode 100644 index 00000000..519ba557 --- /dev/null +++ b/formwork/Utils/Date.php @@ -0,0 +1,62 @@ +option('date.format'); + } + + $dateTime = DateTime::createFromFormat($format, $date); + + if ($dateTime === false) { + if ($isFormatGiven) { + throw new InvalidArgumentException('Date "' . $date . '" is not formatted according to the format "' . $format . '": ' . static::getLastDateTimeError()); + } + try { + $dateTime = new DateTime($date); + } catch (Exception $e) { + $message = 'Invalid date "' . $date . '": ' . static::getLastDateTimeError(); + // TODO: this will always be the case in 2.0 + if ($throwOnError) { + throw new InvalidArgumentException($message, $e->getCode(), $e->getPrevious()); + } + trigger_error('Using invalid dates is deprecated since Formwork 1.11.0. ' . $message, E_USER_DEPRECATED); + } + } + + return $dateTime instanceof DateTime ? $dateTime->getTimestamp() : strtotime($date); + } + + /** + * Return a human-readable string containing details about last DateTime error + */ + protected static function getLastDateTimeError(): string + { + $result = []; + $lastError = null; + if (($errors = DateTime::getLastErrors()) !== false) { + foreach ($errors['errors'] as $position => $error) { + $currentError = lcfirst(rtrim($error, '.')); + $result[] = ($currentError !== $lastError ? $currentError . ' at position ' : '') . $position; + $lastError = $currentError; + } + } + return implode(', ', $result); + } +}