Laravel uses a simplified format for storing dates in the database. By convention these values are only ever UTC so it is internally consistent and works well. However by serializing the date format to this format without timezone information Laravel leaks the underlying storage implementation logic into the data structures and causes problems for integrating systems by not using an inter-operable format.
Problems:
{{::f.updated_date | dateString | date : 'short'}} only works out of the box if you live in Greenwich for example.The laravel docs recommend overriding the serializeDate method on your model, which allows you to customize how you wish.
/**
* Prepare a date for array / JSON serialization.
*
* @param \DateTimeInterface $date
* @return string
*/
protected function serializeDate(DateTimeInterface $date)
{
return $date->format($this->getDateFormat());
}
_From HasAttributes.php_
I guess the Laravel docs agree its wrong then too?
There's also a use case for having different formats for different columns. For example, in the case when there are a combination of dates and datetimes on the table, we might want to serialize dates with the format 'Y-m-d' and datetimes with 'Y-m-d H:i:s'.
I m agree with @neclimdul . Something is wrong with the dates in laravel.
As input too. By default, laravel can't parse iso8601. It try to parse it as a database date. I think Model should be able to parse most common date format or at least we need a way to set a second date format for output.
I'm also having issues with this. I've implemented serializeDate to get ISO8601 dates serialized into JSON. But I need a function (unserializeDate?) that makes Carbon accept different date formats. Right now I send a ISO8601 formatted date back and I get this error:
InvalidArgumentException
Unexpected data found.
Trailing data
in Carbon.php (line 582)
Carbon::createFromFormat('Y-m-d H:i:s', '2017-06-12T17:10:02Z')
...
Another solution would be if I could set the default format for Carbon to use. If I could have Carbon store dates in the database as ISO8601 and it always parses dates as ISO8601 I would be set.
I would actually prefer setting a default format over adding serializeDate, accessors, and mutators to all of my models that have dates in them (practically all of them).
Use date serialization from the documentation.
@AustP I have a work around for unserialization. It's not the best, but it works for me.
Define this trait in your custom request namespace (or anywhere else)
use Carbon\Carbon;
use Illuminate\Validation\Validator;
/**
* Trait NormalizesISO8601Dates
* @package App\Models
*
* This traits allows to convert momentjs toISOString() into a Carbon UTC date Laravel
* is able to use. This trait should be use on custom form requests. This trait will alter the
* state of the incoming request.
*/
trait NormalizesISO8601Dates
{
public function withValidator(Validator $validator)
{
if (! isset(static::$normalizedDates)) {
throw new \Exception('Requests that uses ' . __TRAIT__ . ' trait need to have a $normalizedDates attribute');
}
// Grab the input
$data = $this->getInputSource()->all();
foreach (static::$normalizedDates as $field) {
if (isset($data[$field]) && strtotime($data[$field])) {
$data[$field] = (new Carbon($data[$field]))->toDateTimeString();
}
}
// Replace modified data back into input.
$this->getInputSource()->replace($data);
}
}
Then after, in a custom form request
class MyCustomFormRequest extends FormRequest
{
use NormalizesISO8601Dates;
// This property is mendatory with the trait. List all parameters you wish to convert from ISO 8601 to Laravel parsable format.
protected static $normalizedDates = ['starts_at', 'ends_at'];
...
}
Unfortunately, the date is converted twice, once to convert it into Laravel's readable format and again by Laravel when casted to date. Honestly, I think this should be native into the framework.
@lunfel See this... i think that is related 19920
Edited:
removed mark as related.
@ptsilva I don't see why it's related. But maybe my last comment was confusing.
I was meant that it is converted twice because in my workaround I have to parse the date $data[$field] = (new Carbon($data[$field]))->toDateTimeString(); to fix the format. I put it right back into the request and then let Laravel handle it. Which then, it will cast again to Carbon object assuming we are using the $dates property on the model.
Really... this is not related. I thought that converted twice it was because of $this->getInputSource(), i think that have something wrong with this method. Anyway, sorry.
Agree completely. I'll reiterate what I just posted in #20404:
Nowadays, API consumers expect ISO 8601 format dates. It would be great if the serialized/return date format could be configured instead of having to override the serializeDate method on all models, preferably also configured just once.
Having dateFormat property on models define both the database and the return format is not very useful, when MySQL for instance supports only three types of date/time column types, while output of date/time has so many more facets.
Alternatively, if Laravel would respect the Carbon setting for dateTime format, or keep some global default for datetime formatting, that would be cool too, so you could just define this once, for instance:
Carbon::setToStringFormat(DateTime::ATOM);
The serializeDate function actually calls the getDateFormat function on the model. So actually the only thing you need to do is overriding this function:
<?php
namespace App\Entities;
use Illuminate\Database\Eloquent\Model;
class Person extends Model
{
...
protected $casts = [
'born' => 'date',
'died' => 'date'
];
// don't include time in json-string
protected function getDateFormat() {
return "d-m-Y";
}
}
Actually, this has been addressed in 5.5. See documentation .
I think we can close this issue now.
That does seem to address the issue but are they still connected by default with just a different "easier" method for replacing the serialization format? Seems like we should get things correct out of the box.
Most helpful comment
That does seem to address the issue but are they still connected by default with just a different "easier" method for replacing the serialization format? Seems like we should get things correct out of the box.