Skip to content

Commit

Permalink
PHPORM-234 Convert dates in DB Query results (#3119)
Browse files Browse the repository at this point in the history
Use the current timezone when reading an UTCDateTime
  • Loading branch information
GromNaN authored Sep 3, 2024
1 parent 5b7ca02 commit c710097
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
* Remove support for Laravel 10 by @GromNaN in [#3123](https://github.com/mongodb/laravel-mongodb/pull/3123)
* **BREAKING CHANGE** Use `id` as an alias for `_id` in commands and queries for compatibility with Eloquent packages by @GromNaN in [#3040](https://github.com/mongodb/laravel-mongodb/pull/3040)
* **BREAKING CHANGE** Make Query\Builder return objects instead of array to match Laravel behavior by @GromNaN in [#3107](https://github.com/mongodb/laravel-mongodb/pull/3107)
* **BREAKING CHANGE** In DB query results, convert BSON `UTCDateTime` objects into `Carbon` date with the default timezone by @GromNaN in [#3119](https://github.com/mongodb/laravel-mongodb/pull/3119)
* Remove `MongoFailedJobProvider`, replaced by Laravel `DatabaseFailedJobProvider` by @GromNaN in [#3122](https://github.com/mongodb/laravel-mongodb/pull/3122)

## [4.8.0] - 2024-08-27
Expand Down
22 changes: 18 additions & 4 deletions src/Eloquent/DocumentModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
namespace MongoDB\Laravel\Eloquent;

use BackedEnum;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Contracts\Queue\QueueableCollection;
use Illuminate\Contracts\Queue\QueueableEntity;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Concerns\HasAttributes;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Arr;
Expand Down Expand Up @@ -97,8 +99,14 @@ public function getQualifiedKeyName()
return $this->getKeyName();
}

/** @inheritdoc */
public function fromDateTime($value)
/**
* Convert a DateTimeInterface (including Carbon) to a storable UTCDateTime.
*
* @see HasAttributes::fromDateTime()
*
* @param mixed $value
*/
public function fromDateTime($value): UTCDateTime
{
// If the value is already a UTCDateTime instance, we don't need to parse it.
if ($value instanceof UTCDateTime) {
Expand All @@ -113,8 +121,14 @@ public function fromDateTime($value)
return new UTCDateTime($value);
}

/** @inheritdoc */
protected function asDateTime($value)
/**
* Return a timestamp as Carbon object.
*
* @see HasAttributes::asDateTime()
*
* @param mixed $value
*/
protected function asDateTime($value): Carbon
{
// Convert UTCDateTime instances to Carbon.
if ($value instanceof UTCDateTime) {
Expand Down
13 changes: 11 additions & 2 deletions src/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
use Carbon\CarbonPeriod;
use Closure;
use DateTimeInterface;
use DateTimeZone;
use Illuminate\Database\Query\Builder as BaseBuilder;
use Illuminate\Database\Query\Expression;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\LazyCollection;
use InvalidArgumentException;
use LogicException;
Expand All @@ -39,6 +41,7 @@
use function call_user_func_array;
use function count;
use function ctype_xdigit;
use function date_default_timezone_get;
use function dd;
use function dump;
use function end;
Expand Down Expand Up @@ -1660,7 +1663,10 @@ private function aliasIdForResult(array|object $values): array|object
}

foreach ($values as $key => $value) {
if (is_array($value) || is_object($value)) {
if ($value instanceof UTCDateTime) {
$values[$key] = Date::instance($value->toDateTime())
->setTimezone(new DateTimeZone(date_default_timezone_get()));
} elseif (is_array($value) || is_object($value)) {
$values[$key] = $this->aliasIdForResult($value);
}
}
Expand All @@ -1673,7 +1679,10 @@ private function aliasIdForResult(array|object $values): array|object
}

foreach (get_object_vars($values) as $key => $value) {
if (is_array($value) || is_object($value)) {
if ($value instanceof UTCDateTime) {
$values->{$key} = Date::instance($value->toDateTime())
->setTimezone(new DateTimeZone(date_default_timezone_get()));
} elseif (is_array($value) || is_object($value)) {
$values->{$key} = $this->aliasIdForResult($value);
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace MongoDB\Laravel\Tests;

use Carbon\Carbon;
use Illuminate\Auth\Passwords\PasswordBroker;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use MongoDB\BSON\UTCDateTime;
use MongoDB\Laravel\Tests\Models\User;

use function bcrypt;
Expand Down Expand Up @@ -63,7 +63,7 @@ function ($actualUser, $actualToken) use ($user, &$token) {
$reminder = DB::table('password_reset_tokens')->first();
$this->assertEquals('john.doe@example.com', $reminder->email);
$this->assertNotNull($reminder->token);
$this->assertInstanceOf(UTCDateTime::class, $reminder->created_at);
$this->assertInstanceOf(Carbon::class, $reminder->created_at);

$credentials = [
'email' => 'john.doe@example.com',
Expand Down
23 changes: 14 additions & 9 deletions tests/QueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace MongoDB\Laravel\Tests;

use Carbon\Carbon;
use DateTime;
use DateTimeImmutable;
use Illuminate\Support\Facades\Date;
Expand Down Expand Up @@ -33,7 +34,6 @@
use function md5;
use function sort;
use function strlen;
use function strtotime;

class QueryBuilderTest extends TestCase
{
Expand Down Expand Up @@ -676,27 +676,32 @@ public function testUpdateSubdocument()
public function testDates()
{
DB::table('users')->insert([
['name' => 'John Doe', 'birthday' => new UTCDateTime(Date::parse('1980-01-01 00:00:00'))],
['name' => 'Robert Roe', 'birthday' => new UTCDateTime(Date::parse('1982-01-01 00:00:00'))],
['name' => 'Mark Moe', 'birthday' => new UTCDateTime(Date::parse('1983-01-01 00:00:00.1'))],
['name' => 'Frank White', 'birthday' => new UTCDateTime(Date::parse('1960-01-01 12:12:12.1'))],
['name' => 'John Doe', 'birthday' => Date::parse('1980-01-01 00:00:00')],
['name' => 'Robert Roe', 'birthday' => Date::parse('1982-01-01 00:00:00')],
['name' => 'Mark Moe', 'birthday' => Date::parse('1983-01-01 00:00:00.1')],
['name' => 'Frank White', 'birthday' => Date::parse('1975-01-01 12:12:12.1')],
]);

$user = DB::table('users')
->where('birthday', new UTCDateTime(Date::parse('1980-01-01 00:00:00')))
->where('birthday', Date::parse('1980-01-01 00:00:00'))
->first();
$this->assertEquals('John Doe', $user->name);

$user = DB::table('users')
->where('birthday', new UTCDateTime(Date::parse('1960-01-01 12:12:12.1')))
->where('birthday', Date::parse('1975-01-01 12:12:12.1'))
->first();

$this->assertEquals('Frank White', $user->name);
$this->assertInstanceOf(Carbon::class, $user->birthday);
$this->assertSame('1975-01-01 12:12:12.100000', $user->birthday->format('Y-m-d H:i:s.u'));

$user = DB::table('users')->where('birthday', '=', new DateTime('1980-01-01 00:00:00'))->first();
$this->assertEquals('John Doe', $user->name);
$this->assertInstanceOf(Carbon::class, $user->birthday);
$this->assertSame('1980-01-01 00:00:00.000000', $user->birthday->format('Y-m-d H:i:s.u'));

$start = new UTCDateTime(1000 * strtotime('1950-01-01 00:00:00'));
$stop = new UTCDateTime(1000 * strtotime('1981-01-01 00:00:00'));
$start = new UTCDateTime(new DateTime('1950-01-01 00:00:00'));
$stop = new UTCDateTime(new DateTime('1981-01-01 00:00:00'));

$users = DB::table('users')->whereBetween('birthday', [$start, $stop])->get();
$this->assertCount(2, $users);
Expand Down
3 changes: 1 addition & 2 deletions tests/QueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Str;
use Mockery;
use MongoDB\BSON\UTCDateTime;
use MongoDB\Laravel\Queue\MongoJob;
use MongoDB\Laravel\Queue\MongoQueue;

Expand Down Expand Up @@ -197,7 +196,7 @@ public function testFailedJobLogging()
$this->assertSame('test_connection', $failedJob->connection);
$this->assertSame('test_queue', $failedJob->queue);
$this->assertSame('test_payload', $failedJob->payload);
$this->assertEquals(new UTCDateTime(Carbon::now()), $failedJob->failed_at);
$this->assertEquals(Carbon::now(), $failedJob->failed_at);
$this->assertStringStartsWith('Exception: test_exception in ', $failedJob->exception);
}
}

0 comments on commit c710097

Please sign in to comment.