Laravel5 Eloquent default date mutator fails with JSON date input - php

I've a model with date mutators, such as created_at. Let's say a generic timestamp property.
When passing data from the client in JSON format, i'll get
{
timestamp: "2016-06-07T22:00:00.000Z"
}
That property will be used to set some model like so:
$model = new Foo();
$model->timestamp = Input::get('timestamp');
Since this will trigger the date mutator, php Carbon gets called and fails with
InvalidArgumentException in Carbon.php line 425
Unexpected data found.
Trailing data
Now, that's not an issue with Carbon itself, that is able to handle JSON formats. If you'll try
new Carbon('2016-06-07T22:00:00.000Z');
you'd infact get the expected result.
From what i see, the problem origins from a weird Eloquent behaviour. See this trace from the above mentioned InvalidArgumentException in Carbon.php line 425:
in Carbon.php line 425
at Carbon::createFromFormat('Y-m-d H:i:s', '2016-06-07T22:00:00.000Z') in Model.php line 2915
at Model->asDateTime('2016-06-07T22:00:00.000Z') in Model.php line 2871
at Model->fromDateTime('2016-06-07T22:00:00.000Z') in Model.php line 2826
at Model->setAttribute('timestamp', '2016-06-07T22:00:00.000Z') in Model.php line 3351
Eloquent is trying to createFromFormat passing a bad format which doesnt mirror the actual JSON format, that is causing the Carbon exception.
On the other hand, a simple call like the above mentioned new Carbon($jsonDate) would work fine in this case.
How should i approach to solve this?
Isn't this supposed to be an Eloquent bug?
== EDIT ==
I didn't code any mutator myself. I'm using default date mutators like so:
public function getDates() {
return ['timestamp'];
}

Eloquent analyses the date you give it, when the attribute is set in the getDates function.
In it's analyses it has four steps: (see Modal.php asDateTime method for clarification, the code is pretty well commented: https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L2898)
If it is already a Carbon instance, use that
If it is an instance implementing DateTimeInterface, use that
If it is numeric, parse as a Unix timestamp
If it is a simple year month day format (tried by regex) use that
If none of the above, create a Carbon instance with the format you specify as default
In you case, 1-4 don't match, so it tries 5.
You have no custom dateFormat specified on the model, so it uses the default date format applicable to the type of database connection you are using. You can override this by adding a $dateFormat variable to the model:
protected $dateFormat = 'U';
But your normal timestamps are probably in the correct format already. So this solution would not work for you.
In your case I would just do:
$model->timestamp = Carbon::parse(Input::get('timestamp'));
Or use Carbon::createFromFormat() with the appropriate format.
I don't exactly know why they don't just parse it with Carbon directly. I guess it is more error prone and less controllable. With different international formats you might get an unexpected outcome (month and day switched etc). It might also be slower than their current approach.

Related

Why do I get error when try to convert Carbon to DateTime?

I am developing a Laravel project. I try to create a DateTime object by using Carbon. This is what I tried:
Carbon::createFromFormat('Y-m-d H:i:s', '2021-10-01T00:01:00')->toDateTime();
But my phpstan complains : Cannot call method toDateTime() on Carbon\Carbon|false.
Why is this error? What is the correct way to convert Carbon to a DateTime object?
Your format is incorrect, so Carbon cannot create the time. You're missing the T, which needs to be escaped.
Carbon::createFromFormat('Y-m-d\TH:i:s', '2021-10-01T00:01:00')->toDateTime();
If PHPStan complains, that's because the static analysis (which does not execute the code) cannot determine the types properly. As Carbon extends DateTime, the PHP documentation can help for this method call:
Returns a new DateTime instance or false on failure.
So, to ensure that the code is sound in terms of static analysis, you need to split it up:
$object = Carbon::createFromFormat('Y-m-d H:i:s', '2021-10-01T00:01:00');
if (!$object instanceof Carbon) {
throw new RuntimeException('could not parse date');
}
$object->toDateTime();
The difference: now, PHPStan can safely assume that $object is of type Carbon when toDateTime() is called
As others pointed out: running that code would also yield an error, as the date format you try to parse from and the input date do not match. But that is out of scope for PHPStan, which does not execute the code
Carbon objects are already DateTime objects.
class Carbon extends \DateTime
You may want toDateTimeString, but if you really want a DateTime object, you've already got one, just with a little Carbon syntax sugar sprinkled on top.
If you really need a DateTime object, the ->toDate() function is what you're looking for.
https://carbon.nesbot.com/docs/
Return native DateTime PHP object matching the current instance.

Laravel: Get time between two timestamps in diffForHumans format (Carbon)

I am trying to get the time between two dates in diffForHumans format, can anyone help me out to why its throwing this error?
Code:
{{ Carbon\Carbon::createFromTimestamp($clientLogin->exit_timestamp)->diffForHumans($clientLogin->enter_timestamp) }}
Error:
Type error: Argument 1 passed to Illuminate\Cookie\Middleware\EncryptCookies::encrypt() must be an instance of Symfony\Component\HttpFoundation\Response, instance of Illuminate\View\View given
Suggestion: Define your datetime fields as dates so Laravel can treat then already as a carbon instance, so you can do something like this:
$clientLogin->exit_timestamp->subDays($clientLogin->enter_timestamp)->diffForHumans();

PHP using Laravel 4 and Carbon will not print out a DateTime field from my Database

I am building a PHP application with Laravel 4.
I am getting errors when I try to print out a DateTime record from the Database though.
{{ $user->created_at }}
Gives me this error
InvalidArgumentException
Trailing data
open: E:\Server\htdocs\projects\timeclock\www\vendor\nesbot\carbon\src\Carbon\Carbon.php
Very frustrating!
An example value from that Database field is: 2013-08-31 20:50:25.
You are missing the milisecond data on the time stamp, you need to use:
Carbon::createFromFormat('Y-m-d H:i:s.u', $value)->format('d/m/Y H:i:s');
You have to format it:
{{ $user->created_at->format('h:i:s') }}
The PHP docs has a list of all the codes available to use as a format.
I have the same issue.
And I found that this is caused by my timestamp data in database.
2013-12-13 22:40:50.561709 <- this one will cause the issue.
2013-12-13 22:40:50 <- this one will not.
Timestamp value with millisecond causes this issue.
Column which is converted to Carbon object can not have millisecond timestamp.(default: created_at, updated_at).
http://readouble.com/laravel/4/2/0/en/eloquent.html#date-mutators
If Carbon Object is not necessary, you can disallow auto-converting.
class SomeModel extends Eloquent {
public function getDates()
{
return array();
}
}
But it also make Carbon methods(ex:->format()) unavailable. You have to format timestamps in other way.

How to create DateTime object from string in symfony2/php

In a DB table I have several fields with datetime as field type. So I need to persist data only as date time object.
From a form I get date time as string like
2012-10-05 17:45:54
Now when ever I persist my entity I get following error:
Fatal error: Call to a member function format() on a non-object in
..\DateTimeType.php on line 44
I tried with
$protocol->setStartedAt(strtotime($post['started_at']));
or
$from = \DateTime::createFromFormat('yy-mm-dd hh:mm:ss', $post['started_at']);
$protocol->setStartedAt($from);
or just
$from = new \DateTime($post['started_at']);
$protocol->setStartedAt($from);
The last code works but it does not uses the timestamp passed as arguement but just gets the current time.
Any ideas?
I always create a DateTime object with its constructor, in your case it would be:
$protocol->setStartedAt(new \DateTime($post['started_at']));
if this works but does not use the timestamp posted you probably do not have the value in $post['started_at']. Try debugging it or just do the dirty trick:
die($post['started_at']);
For the sake of future readers who surely will someday encounter this problem (this is the first post if you google "symfony 2 datetime from string"), keep in mind that in Symfony 2 the DateTime object does NOT accept a string with that format : "d/m/Y H:i:s", and probably doesn't support many others either.
For the sake of not becoming mad at that, I've actually found out that the easiest and safest solution to avoid such errors is this one:
First, get your date string from whatever kind of request you are doing (In my case a generic AJAX request) and convert it to a DateTime Object, this example assumes that we need to create a dateTime object for 25/04/2015 15:00, which is the format of the jQuery UI italian DateTimePicker (that's just an example):
$literalTime = \DateTime::createFromFormat("d/m/Y H:i","25/04/2015 15:00");
(note: use \ to use php's DateTime object, else you will be using Symfony's datetime object that will throw you an exception)
Then, once you did it, create a date string using the comfort format function, by giving to the first parameter the output format expected (Y-m-d H:i:s):
$expire_date = $literalTime->format("Y-m-d H:i:s");
In this way you are 100% sure that whatever kind of format you are passing or receiving this will properly be converted and you won't get any kind of exception from the DateTime symfony object, as long as you provide what you are expecting as an input.
Knowing that this post is actually quite old, I've just decided to post that because I didn't find any other valuable source but this one to understand where the problem could have been.
Please note that the best solution is still to send the datetime string in the correct format already, but if you literally have no ways to do that the safest way to convert such a string is the one above.
How about createFromFormat?
http://uk.php.net/manual/en/datetime.createfromformat.php
$from = DateTime::createFromFormat($post['started_at'], 'Y-m-d H:i:s');

How to format a getUpdatedAt() kind of date in Symfony?

I'd like to change the formatting of a date in Symfony 1.4
The default one being:
<?php echo $question->getUpdatedAt();
// Returns 2010-01-26 16:23:53
?>
I'd like my date to be formatted like so: 26/01/2010 - 16h23
I tried using the format_date helper DateHelper class.
Unfortunately the API is rather empty (something really needs to be done about it.)
Browsing the helper's source code, I found that a second argument, format, can be passed.
I assumed it was using the same syntax as PHP's date function.
But here's what it outputs (same example as above):
<?php sfContext::getInstance()->getConfiguration()->loadHelpers('Date');
// [...]
echo format_date($question->getUpdatedAt(),'d/m/y - H\hi')
// Returns 26/23/2010 - 16\4i
I'm sure I'm not the first one having trouble doing this but I've been Googling around and nothing accurate showed up.
Do you guys have any idea how to format a date in Symfony 1.4?
Have a look at the new functions in 1.4.
You can do:
$question->getDateTimeObject('updated_at')->format('d.m.Y');
// I assume the field's name is 'updated_at'
From the docs:
Date Setters and Getters
We've added two new methods for retrieving Doctrine date or timestamp values as PHP DateTime object instances.
echo $article->getDateTimeObject('created_at')->format('m/d/Y');
You can also set a dates value by simply calling the setDateTimeObject method and passing a valid DateTime instance.
$article->setDateTimeObject('created_at', new DateTime('09/01/1985'));
But it seems only to work for Doctrine.
How bout going with the default PHP date function? date('d/m/Y', strtotime($question->getUpdatedAt())
You can use also sfDateFormat class to work with dates.
link text
do you try :
echo $question->getUpdatedAt('d/m/y - H\hi')
I think it's the easiest way

Categories