I'm using Carbon to work with MongoDB\BSON\UTCDateTime values, which are stored as a unix timestamp in milliseconds. I want to convert this to a Carbon object and preserve the millisecond precision. After converting it to a DateTime and trying to create a Carbon instance, it fails:
$timestamp = floor(microtime(true) * 1000); // 1467132827636.0
$mongoDate = new MongoDB\BSON\UTCDateTime($timestamp);
$carbonDate = Carbon\Carbon::instance($mongoDate->toDateTime());
Exception with message 'DateTime::__construct(): Failed to parse time string (2016-06-28 16:53:47.636000000) at position 11 (1): The timezone could not be found in the database'
This seems to be caused because of the three extra trailing zeros generated by $dt->format('Y-m-d H:i:s.u'). It's unclear to me why this is happening--it seems like it could be a bug in PHP (I'm running 7.0.8). Certainly DateTime doesn't support nanosecond precision!
The only way I've found to make this work is to manipulate the timestamps as strings (yuck):
$timestamp = (string)$mongoDate; // "1467132827636"
Carbon::parse(date('Y-m-d H:i:s', bcdiv($timestamp, 1000)) . '.' . substr($timestamp, -3));
I had similar problem with MS SQL Server with Laravel 5.2.
Try adding the following 2 methods to your model.
public function getDateFormat()
{
return 'Y-m-d H:i:s.u';
}
public function fromDateTime($value)
{
return substr(parent::fromDateTime($value), 0, -3);
}
But to avid having to copy and paste my code into bunch of model I used class inheritance and extended my model like so.
I create my BaseModel Class
<?php
namespace Vendor\MyApp;
use Illuminate\Database\Eloquent\Model as EloquentModel;
class BaseModel extends EloquentModel
{
public function getDateFormat()
{
return 'Y-m-d H:i:s.u';
}
public function fromDateTime($value)
{
return substr(parent::fromDateTime($value), 0, -3);
}
}
Then for each model I had I extended my BaseModel class something like this
class User extends BaseModel
{
...
...
}
I hope this helps
@malhayek2014 The substr method is not robust. It should at least use a regex to check the format and length of the number of characters after the decimal point before truncating it. Also, instead of overriding getDateFormat(), you can just set the property: protected $dateFormat = 'Y-m-d\TH:i:s.uP'; (from my own BaseModel)
@hackel , this is expected: http://php.net/manual/en/mongodb-bson-utcdatetime.todatetime.php#119308.
Feel free to extend \Carbon\Carbon
<?php
require __DIR__.'/vendor/autoload.php';
use Carbon\Carbon;
use MongoDB\BSON\UTCDatetime;
class MyCarbon extends Carbon
{
/**
* Create a new instance from a BSON UTCDatetime.
*
* @param \MongoDB\BSON\UTCDatetime $UTCDatetime
* @return static
*/
public static function fromBSONUTCDateTime(UTCDatetime $UTCDatetime)
{
return static::createFromFormat('Y-m-d H:i:s.u', $UTCDatetime->toDateTime());
}
}
@lucasmichot createFromFormat is supposed to take a string, not a DateTime object. That comment you linked to is not an indication that this is "expected." It's still a bug, even if it's confirmed by others.
$mongoDate->toDateTime()->format('u') returns "636000000" instead of "636000" when "u" is supposed to be _microseconds_ (according to php.net/date), not nanoseconds. It's off by a factor of 1000, and seems to be treating it as a string instead of a fraction. I don't know if the blame lies in how UTCDate::toDateTime() works, or in how DateTime stores the microseconds internally that it would allow such a large number.
Either way, this is a PHP bug and not related to Carbon.
Edit: Upstream bug report: https://jira.mongodb.org/browse/PHPC-631
Fixed in version 1.1.8 of the PHP MongoDB driver.
Thanks for this useful feeback @hackel
Most helpful comment
@lucasmichot createFromFormat is supposed to take a string, not a DateTime object. That comment you linked to is not an indication that this is "expected." It's still a bug, even if it's confirmed by others.
$mongoDate->toDateTime()->format('u')returns "636000000" instead of "636000" when "u" is supposed to be _microseconds_ (according to php.net/date), not nanoseconds. It's off by a factor of 1000, and seems to be treating it as a string instead of a fraction. I don't know if the blame lies in how UTCDate::toDateTime() works, or in how DateTime stores the microseconds internally that it would allow such a large number.Either way, this is a PHP bug and not related to Carbon.
Edit: Upstream bug report: https://jira.mongodb.org/browse/PHPC-631
Fixed in version 1.1.8 of the PHP MongoDB driver.