Skip to content

Commit

Permalink
Fix SQL Server grammar to correctly handle DATETIME columns (#22052)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulofreitas authored and taylorotwell committed Nov 15, 2017
1 parent 3788cbd commit 3af1858
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,7 @@ protected function asDateTime($value)
// the database connection and use that format to create the Carbon object
// that is returned back out to the developers after we convert it here.
return Carbon::createFromFormat(
$this->getDateFormat(), $value
str_replace('.v', '.u', $this->getDateFormat()), $value
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ public function compileSavepointRollBack($name)
*/
public function getDateFormat()
{
return 'Y-m-d H:i:s.000';
return 'Y-m-d H:i:s.v';
}

/**
Expand Down
76 changes: 76 additions & 0 deletions tests/Database/DatabaseEloquentIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Tests\Database;

use Exception;
use ReflectionObject;
use PHPUnit\Framework\TestCase;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\SoftDeletes;
Expand Down Expand Up @@ -1138,6 +1139,76 @@ public function testFreshMethodOnCollection()
$this->assertEquals($users->map->fresh(), $users->fresh());
}

public function testTimestampsUsingDefaultDateFormat()
{
$model = new EloquentTestUser;
$model->setDateFormat('Y-m-d H:i:s'); // Default MySQL/PostgreSQL/SQLite date format
$model->setRawAttributes([
'created_at' => '2017-11-14 08:23:19',
]);

$this->assertEquals('2017-11-14 08:23:19.000000', $this->getRawDateTimeString($model->getAttribute('created_at')));
$this->assertEquals('2017-11-14 08:23:19', $model->fromDateTime($model->getAttribute('created_at')));
}

public function testTimestampsUsingDefaultSqlServerDateFormat()
{
$model = new EloquentTestUser;
$model->setDateFormat('Y-m-d H:i:s.v'); // Default SQL Server date format
$model->setRawAttributes([
'created_at' => '2017-11-14 08:23:19.000',
'updated_at' => '2017-11-14 08:23:19.734',
]);

$this->assertEquals('2017-11-14 08:23:19.000000', $this->getRawDateTimeString($model->getAttribute('created_at')));
$this->assertEquals('2017-11-14 08:23:19.734000', $this->getRawDateTimeString($model->getAttribute('updated_at')));
$this->assertEquals('2017-11-14 08:23:19.000', $model->fromDateTime($model->getAttribute('created_at')));
$this->assertEquals('2017-11-14 08:23:19.734', $model->fromDateTime($model->getAttribute('updated_at')));
}

public function testTimestampsUsingCustomDateFormat()
{
// Simulating using custom precisions with timestamps(4)
$model = new EloquentTestUser;
$model->setDateFormat('Y-m-d H:i:s.u'); // Custom date format
$model->setRawAttributes([
'created_at' => '2017-11-14 08:23:19.0000',
'updated_at' => '2017-11-14 08:23:19.7348',
]);

$this->assertEquals('2017-11-14 08:23:19.000000', $this->getRawDateTimeString($model->getAttribute('created_at')));
$this->assertEquals('2017-11-14 08:23:19.734800', $this->getRawDateTimeString($model->getAttribute('updated_at')));
// Note: when storing databases would truncate the value to the given precision
$this->assertEquals('2017-11-14 08:23:19.000000', $model->fromDateTime($model->getAttribute('created_at')));
$this->assertEquals('2017-11-14 08:23:19.734800', $model->fromDateTime($model->getAttribute('updated_at')));
}

public function testTimestampsUsingOldSqlServerDateFormat()
{
$model = new EloquentTestUser;
$model->setDateFormat('Y-m-d H:i:s.000'); // Old SQL Server date format
$model->setRawAttributes([
'created_at' => '2017-11-14 08:23:19.000',
]);

$this->assertEquals('2017-11-14 08:23:19.000000', $this->getRawDateTimeString($model->getAttribute('created_at')));
$this->assertEquals('2017-11-14 08:23:19.000', $model->fromDateTime($model->getAttribute('created_at')));
}

/**
* @expectedException \InvalidArgumentException
*/
public function testTimestampsUsingOldSqlServerDateFormatFailInEdgeCases()
{
$model = new EloquentTestUser;
$model->setDateFormat('Y-m-d H:i:s.000'); // Old SQL Server date format
$model->setRawAttributes([
'updated_at' => '2017-11-14 08:23:19.734',
]);

$attribute = $model->fromDateTime($model->getAttribute('updated_at'));
}

/**
* Helpers...
*/
Expand All @@ -1161,6 +1232,11 @@ protected function schema($connection = 'default')
{
return $this->connection($connection)->getSchemaBuilder();
}

protected function getRawDateTimeString($object)
{
return (new ReflectionObject($object))->getProperty('date')->getValue($object);
}
}

/**
Expand Down

1 comment on commit 3af1858

@BertCly
Copy link

@BertCly BertCly commented on 3af1858 Mar 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code, in combination with PHP bug 74753 causes problems when saving 999+1 milliseconds: https://bugs.php.net/bug.php?id=74753
Maybe add a fix for PHP < 7.2?

Please sign in to comment.