We have a feature that allows importing CSV files into a database. The CSV rows can contain date fields as well, so we implemented a date parser before inserting the data into the database. The date parser looked like this:
class DateParser {
....
public function parse(mixed $value)
{
try {
return Carbon::parse($value)->format('Y-m-d');
} catch (Exception $exception) {
return $value;
}
}
}
I would say that this is very standard date parsing code any Laravel developer would write, as we are very familiar with the Carbon library, which Laravel uses internally for all date and time manipulations. However, we encountered an issue. When a user imported a date field with just the month and day (e.g., 11/11
), it was being parsed as 2025-11-11
. While this makes sense—since the current year is added when the year is not provided—it was not the expected behavior for our case. Instead, we wanted it to throw an exception and return the original value.
I started looking for solutions on StackOverflow and GitHub issues to see if anyone had already discussed this case, but I couldn’t find any solutions. I considered writing a regex to check if the given date matched certain formats, such as YYYY-MM-DD
or YYYY/MM/DD
. However, there are so many possible combinations that it felt cumbersome to support all formats. I asked ChatGPT for help with the regex (I admit, I was too lazy to figure it out myself), and it provided this regex:
$pattern = '/\b(?:(?:19|20)\d{2}[-/.](?:0?[1-9]|1[0-2])[-/.](?:0?[1-9]|[12][0-9]|3[01]) # YYYY-MM-DD, YYYY/MM/DD, YYYY.MM.DD
| (?:0?[1-9]|1[0-2])[-/.](?:0?[1-9]|[12][0-9]|3[01])[-/.](?:19|20)\d{2} # MM-DD-YYYY, MM/DD/YYYY, MM.DD.YYYY
| (?:0?[1-9]|[12][0-9]|3[01])[-/.](?:0?[1-9]|1[0-2])[-/.](?:19|20)\d{2})\b/x';
But I wasn’t sure if it would work, and I didn’t feel it was the right approach. Additionally, the Carbon::parse method also supports date strings like first day of December 2020
, which returns 2020-12-01
. The new regex solution completely missed this case.
I then decided to check if 11/11
was a valid date according to Laravel's validation. I tested it in a small tinker script:
Validator::make([
'date' => '11/11'
], [
'date' => [Rule::date()]
])
It was throwing validation errors, which indicated that Laravel is handling the validation correctly. So I dug deeper into how Laravel validates dates. I discovered that Laravel uses two methods for date parsing and validation: date_parse()
and checkdate()
. These methods are built-in PHP functions. The date_parse()
function returns an array like this:
[
"year" => false,
"month" => 11,
"day" => 11,
"hour" => false,
"minute" => false,
"second" => false,
"fraction" => false,
"warning_count" => 0,
"warnings" => [],
"error_count" => 0,
"errors" => [],
"is_localtime" => false
]
We pass the year, month, and day to the checkdate()
method. It returns true if the date is valid, and we create the date and return the valid result. This method also supports string-based dates, as shown above. The final solution we came up with was:
...
public function parse(mixed $value){
$date = date_parse($value);
if (checkdate($date['month'], $date['day'], $date['year'])) {
return Carbon::create($date['year'], $date['month'], $date['day'])->format('Y-m-d');
}
return $value;
}
Conclusion
When we encounter problems and are unable to solve them using native Carbon library, many of us instinctively jump to writing regex-based solutions or complex custom logic to fix the issue immediately. However, such solutions often come at a cost—they can be hard to maintain, difficult to extend, and prone to breaking when requirements change.
Instead of taking that approach, we decided to look into the framework itself to understand how Laravel handles date validation. By doing so, we found that Laravel relies on PHP’s built-in date_parse()
and checkdate()
functions, which provided a more reliable and framework-aligned solution.
This reinforces an essential lesson: before writing custom fixes, it’s always worth exploring how the framework or language solves similar problems. This approach not only saves time but also ensures that our solution is maintainable, scalable, and in line with best practices.
Top comments (5)
I think this is a communication problem instead of a coding problem.
Why is the user allowed to add a day/month format in the first place?
The code clearly expects a year because the output format is Y-m-d.
The user should be made aware of this requirement, and even been instructed to use the output format.
That way the code didn't need to be changed.
I think this is a weird decision. Why would you want to have an faulty format after the parsing?
Then I would suggest to skip the parsing all together, because it has no added value. Less code to maintain.
@xwero I agree with your point from a developer’s perspective, but I think you're overlooking the business side of things. Users are aware that the date should be in the 'YYYY-MM-DD' format, but we can't ignore the fact that we're importing data from CSV files, and that data can be unpredictable. Instead of rejecting the entire row due to an invalid date, we chose to import the data and show the errors to the user. This way, the user is notified that the date was imported in the wrong format, and they have the opportunity to correct their mistakes after the import.
From a users perspective it is easier to spot an empty value than a faulty date format, if the decision is to keep all rows.
What happens if the user doesn't correct the mistakes after the import? Then they had to wait twice before they get blocked, which is going to annoy the user more.
That is why I think it is a weird decision.
If you provided the CSV template or instructions, the only thing that makes the CSV data unpredictable is the user. If you want to be lenient I understand. But dates are a headache to process, so it is better to be strict.
If you want to go forward with that code, I would suggest to wrap the value in an object with a isValid property. At least that way the parsing adds some information you can use further down the line.
@xwero By default these fields are empty, we are storing the faulty date for the sake of showing validation errors,
and CSV templates and instructions are already provided, but users upload files with invalid data, and these data are not published automatically into our system, it needs to be validated first, so users will fix issues if they want to publish to our system.
and sorry I am not showing the complete code here, I am just showing a part to explain it.
Don't be sorry, it is me that has gone way off topic.
I knew it was only code as a part of your narrative.
I wanted to push back to get the full story of the feature.
I wanted to show people how development is not only code.
And you took on the conversation, for which I am very grateful!