Skip to content

Error when try to validate date format when date_default_timezone_set is set for some specific timezones. #56760

@alustau

Description

@alustau

Laravel Version

12.*

PHP Version

8.4.*

Database Driver & Version

No response

Description

date_format:Y-m-d H:i:s fails for datetimes that fall into DST transitions when the app timezone (or date_default_timezone_set) is set to a DST-observing zone (e.g. Europe/Amsterdam, Europe/London, Europe/Athens).

During DST start, certain local times are non-existent (e.g. in Amsterdam on 2025-03-30, 02:00:00 → 02:59:59 does not exist). The validator appears to parse and normalize the input using the current timezone, so DateTime::createFromFormat('!Y-m-d H:i:s', '2025-03-30 02:00:00') becomes 2025-03-30 03:00:00 (normalized), which then fails the strict equality check in the validation rule—even though the input string matches the declared format exactly.

In short: DST normalization changes the parsed time and the rule reports the format as invalid.

Steps To Reproduce

1- Set the PHP default timezone to a DST zone (Amsterdam shown here).
2- Validate a string that matches Y-m-d H:i:s and falls in the DST “gap”.

<?php

use Illuminate\Support\Facades\Validator;

date_default_timezone_set('Europe/Amsterdam');

$payload = ['ts' => '2025-03-30 02:00:00']; // Local time that doesn't exist due to DST shift (+1h)
$rules   = ['ts' => 'date_format:Y-m-d H:i:s'];

$validator = Validator::make($payload, $rules);

var_dump($validator->passes()); // false
var_dump($validator->errors()->all());
// ["The ts does not match the format Y-m-d H:i:s."]

Script to find which date will fail:

<?php
date_default_timezone_set('Europe/Amsterdam');

$year = 2025;

for ($month = 1; $month <= 12; $month++) {
    $daysInMonth = match ($month) {
        1, 3, 5, 7, 8, 10, 12 => 31,
        4, 6, 9, 11 => 30,
        2 => 28,
        default => throw new Exception("Invalid month: $month"),
    };

    for ($day = 1; $day <= $daysInMonth; $day++) {
        for ($hour = 0; $hour < 24; $hour++) {
            
            $date = sprintf("$year-%02d-%02d %02d:00:00", $month, $day, $hour);

            $dateFormated = DateTime::createFromFormat('!Y-m-d H:i:s', $date)->format('Y-m-d H:i:s');

            if ($dateFormated !== $date) {
                echo "Date that will thrown exception: $dateFormated !== $date\n";
            }
        }
    }
}

Additional Context

Timezones which breaks is when it has a summer time and it's +x

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions