feature #40008 [Uid] Replace getTime() with getDateTime() (fancyweb)
This PR was merged into the 5.3-dev branch.
Discussion
----------
[Uid] Replace getTime() with getDateTime()
| Q | A
| ------------- | ---
| Branch? | 5.x
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | https://github.com/symfony/symfony/pull/39507#pullrequestreview-575491827
| License | MIT
| Doc PR | -
Getting a `\DateTimeImmutable` has two benefits: easier to use and no float precision problems.
There is however one drawback for UUIDs: we *technically* loose some precision in the output because datetimes do not handle nanoseconds (only microseconds) and uuid timestamps increment every 100 nanoseconds (0.1 microseconds). However, this is theoretical since the increment is only there to generate more entropy. Also, if an end user really want the precision, he can still do the conversion him/herself from the raw data. Finally, because of some rounding problems with floats even on 64b platforms, precision is *actually* won most of the time thanks to the datetime.
The idea is to also accept `\DateTimeInterface` as input in `UuidFactory` and `UlidFactory` (see https://github.com/symfony/symfony/pull/39507) btw.
The breaking change is allowed because the component is experimental.
Commits
-------
360c900acf
[Uid] Replace getTime() with getDateTime()
This commit is contained in:
commit
1a78e057d5
@ -4,24 +4,24 @@ UPGRADE FROM 5.2 to 5.3
|
||||
Asset
|
||||
-----
|
||||
|
||||
* Deprecated `RemoteJsonManifestVersionStrategy`, use `JsonManifestVersionStrategy` instead.
|
||||
* Deprecated `RemoteJsonManifestVersionStrategy`, use `JsonManifestVersionStrategy` instead
|
||||
|
||||
DomCrawler
|
||||
----------
|
||||
|
||||
* Deprecated the `parents()` method, use `ancestors()` instead.
|
||||
* Deprecated the `parents()` method, use `ancestors()` instead
|
||||
|
||||
Form
|
||||
----
|
||||
|
||||
* Changed `$forms` parameter type of the `DataMapperInterface::mapDataToForms()` method from `iterable` to `\Traversable`.
|
||||
* Changed `$forms` parameter type of the `DataMapperInterface::mapFormsToData()` method from `iterable` to `\Traversable`.
|
||||
* Deprecated passing an array as the second argument of the `DataMapper::mapDataToForms()` method, pass `\Traversable` instead.
|
||||
* Deprecated passing an array as the first argument of the `DataMapper::mapFormsToData()` method, pass `\Traversable` instead.
|
||||
* Deprecated passing an array as the second argument of the `CheckboxListMapper::mapDataToForms()` method, pass `\Traversable` instead.
|
||||
* Deprecated passing an array as the first argument of the `CheckboxListMapper::mapFormsToData()` method, pass `\Traversable` instead.
|
||||
* Deprecated passing an array as the second argument of the `RadioListMapper::mapDataToForms()` method, pass `\Traversable` instead.
|
||||
* Deprecated passing an array as the first argument of the `RadioListMapper::mapFormsToData()` method, pass `\Traversable` instead.
|
||||
* Changed `$forms` parameter type of the `DataMapperInterface::mapDataToForms()` method from `iterable` to `\Traversable`
|
||||
* Changed `$forms` parameter type of the `DataMapperInterface::mapFormsToData()` method from `iterable` to `\Traversable`
|
||||
* Deprecated passing an array as the second argument of the `DataMapper::mapDataToForms()` method, pass `\Traversable` instead
|
||||
* Deprecated passing an array as the first argument of the `DataMapper::mapFormsToData()` method, pass `\Traversable` instead
|
||||
* Deprecated passing an array as the second argument of the `CheckboxListMapper::mapDataToForms()` method, pass `\Traversable` instead
|
||||
* Deprecated passing an array as the first argument of the `CheckboxListMapper::mapFormsToData()` method, pass `\Traversable` instead
|
||||
* Deprecated passing an array as the second argument of the `RadioListMapper::mapDataToForms()` method, pass `\Traversable` instead
|
||||
* Deprecated passing an array as the first argument of the `RadioListMapper::mapFormsToData()` method, pass `\Traversable` instead
|
||||
|
||||
HttpFoundation
|
||||
--------------
|
||||
@ -36,29 +36,37 @@ HttpKernel
|
||||
Messenger
|
||||
---------
|
||||
|
||||
* Deprecated the `prefetch_count` parameter in the AMQP bridge, it has no effect and will be removed in Symfony 6.0.
|
||||
* Deprecated the `prefetch_count` parameter in the AMQP bridge, it has no effect and will be removed in Symfony 6.0
|
||||
|
||||
Notifier
|
||||
-------
|
||||
--------
|
||||
|
||||
* Changed the return type of `AbstractTransportFactory::getEndpoint()` from `?string` to `string`
|
||||
* Changed the signature of `Dsn::__construct()` to accept a single `string $dsn` argument
|
||||
* Removed the `Dsn::fromString()` method
|
||||
|
||||
* Changed the return type of `Symfony\Component\Notifier\Transport\AbstractTransportFactory::getEndpoint()` from `?string` to `string`
|
||||
|
||||
PhpunitBridge
|
||||
-------------
|
||||
|
||||
* Deprecated the `SetUpTearDownTrait` trait, use original methods with "void" return typehint.
|
||||
* Deprecated the `SetUpTearDownTrait` trait, use original methods with "void" return typehint
|
||||
|
||||
PropertyInfo
|
||||
------------
|
||||
|
||||
* Deprecated the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead.
|
||||
* Deprecated the `Type::getCollectionKeyType()` and `Type::getCollectionValueType()` methods, use `Type::getCollectionKeyTypes()` and `Type::getCollectionValueTypes()` instead
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
* Deprecated voters that do not return a valid decision when calling the `vote` method.
|
||||
* Deprecated voters that do not return a valid decision when calling the `vote` method
|
||||
|
||||
Serializer
|
||||
----------
|
||||
|
||||
* Deprecated `ArrayDenormalizer::setSerializer()`, call `setDenormalizer()` instead.
|
||||
* Deprecated `ArrayDenormalizer::setSerializer()`, call `setDenormalizer()` instead
|
||||
|
||||
Uid
|
||||
---
|
||||
|
||||
* Replaced `UuidV1::getTime()`, `UuidV6::getTime()` and `Ulid::getTime()` by `UuidV1::getDateTime()`, `UuidV6::getDateTime()` and `Ulid::getDateTime()`
|
||||
|
@ -119,10 +119,10 @@ class BinaryUtil
|
||||
/**
|
||||
* @param string $time Count of 100-nanosecond intervals since the UUID epoch 1582-10-15 00:00:00 in hexadecimal
|
||||
*/
|
||||
public static function timeToFloat(string $time): float
|
||||
public static function timeToDateTime(string $time): \DateTimeImmutable
|
||||
{
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
$time = hexdec($time) - self::TIME_OFFSET_INT;
|
||||
$time = (string) (hexdec($time) - self::TIME_OFFSET_INT);
|
||||
} else {
|
||||
$time = str_pad(hex2bin($time), 8, "\0", \STR_PAD_LEFT);
|
||||
|
||||
@ -136,6 +136,10 @@ class BinaryUtil
|
||||
}
|
||||
}
|
||||
|
||||
return $time / 10000000;
|
||||
if (9 > \strlen($time)) {
|
||||
$time = '-' === $time[0] ? '-'.str_pad(substr($time, 1), 8, '0', \STR_PAD_LEFT) : str_pad($time, 8, '0', \STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return \DateTimeImmutable::createFromFormat('U.u?', substr_replace($time, '.', -7, 0));
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ CHANGELOG
|
||||
---
|
||||
|
||||
* Add `AbstractUid::fromBinary()`, `AbstractUid::fromBase58()`, `AbstractUid::fromBase32()` and `AbstractUid::fromRfc4122()`
|
||||
* [BC BREAK] Replace `UuidV1::getTime()`, `UuidV6::getTime()` and `Ulid::getTime()` by `UuidV1::getDateTime()`, `UuidV6::getDateTime()` and `Ulid::getDateTime()`
|
||||
|
||||
5.2.0
|
||||
-----
|
||||
|
@ -76,13 +76,18 @@ class UlidTest extends TestCase
|
||||
/**
|
||||
* @group time-sensitive
|
||||
*/
|
||||
public function testGetTime()
|
||||
public function testGetDateTime()
|
||||
{
|
||||
$time = microtime(false);
|
||||
$ulid = new Ulid();
|
||||
$time = substr($time, 11).substr($time, 1, 4);
|
||||
|
||||
$this->assertSame((float) $time, $ulid->getTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', $time), $ulid->getDateTime());
|
||||
|
||||
$this->assertEquals(new \DateTimeImmutable('@0'), (new Ulid('000000000079KA1307SR9X4MV3'))->getDateTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '0.001'), (new Ulid('000000000179KA1307SR9X4MV3'))->getDateTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '281474976710.654'), (new Ulid('7ZZZZZZZZY79KA1307SR9X4MV3'))->getDateTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '281474976710.655'), (new Ulid('7ZZZZZZZZZ79KA1307SR9X4MV3'))->getDateTime());
|
||||
}
|
||||
|
||||
public function testIsValid()
|
||||
|
@ -60,7 +60,7 @@ class UuidTest extends TestCase
|
||||
|
||||
$uuid = new UuidV1(self::A_UUID_V1);
|
||||
|
||||
$this->assertSame(1583245966.746458, $uuid->getTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '1583245966.746458'), $uuid->getDateTime());
|
||||
$this->assertSame('3499710062d0', $uuid->getNode());
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ class UuidTest extends TestCase
|
||||
|
||||
$uuid = new UuidV6(substr_replace(self::A_UUID_V1, '6', 14, 1));
|
||||
|
||||
$this->assertSame(85916308548.27832, $uuid->getTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '85916308548.278321'), $uuid->getDateTime());
|
||||
$this->assertSame('3499710062d0', $uuid->getNode());
|
||||
}
|
||||
|
||||
@ -308,12 +308,14 @@ class UuidTest extends TestCase
|
||||
$this->assertInstanceOf(CustomUuid::class, CustomUuid::fromString(self::A_UUID_V4));
|
||||
}
|
||||
|
||||
public function testGetTime()
|
||||
public function testGetDateTime()
|
||||
{
|
||||
$this->assertSame(103072857660.6847, ((new UuidV1('ffffffff-ffff-1fff-a456-426655440000'))->getTime()));
|
||||
$this->assertSame(0.0000001, ((new UuidV1('13814001-1dd2-11b2-a456-426655440000'))->getTime()));
|
||||
$this->assertSame(0.0, (new UuidV1('13814000-1dd2-11b2-a456-426655440000'))->getTime());
|
||||
$this->assertSame(-0.0000001, (new UuidV1('13813fff-1dd2-11b2-a456-426655440000'))->getTime());
|
||||
$this->assertSame(-12219292800.0, ((new UuidV1('00000000-0000-1000-a456-426655440000'))->getTime()));
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '103072857660.684697'), ((new UuidV1('ffffffff-ffff-1fff-a456-426655440000'))->getDateTime()));
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '0.000001'), ((new UuidV1('1381400a-1dd2-11b2-a456-426655440000'))->getDateTime()));
|
||||
$this->assertEquals(new \DateTimeImmutable('@0'), (new UuidV1('13814001-1dd2-11b2-a456-426655440000'))->getDateTime());
|
||||
$this->assertEquals(new \DateTimeImmutable('@0'), (new UuidV1('13814000-1dd2-11b2-a456-426655440000'))->getDateTime());
|
||||
$this->assertEquals(new \DateTimeImmutable('@0'), (new UuidV1('13813fff-1dd2-11b2-a456-426655440000'))->getDateTime());
|
||||
$this->assertEquals(\DateTimeImmutable::createFromFormat('U.u', '-0.000001'), ((new UuidV1('13813ff6-1dd2-11b2-a456-426655440000'))->getDateTime()));
|
||||
$this->assertEquals(new \DateTimeImmutable('@-12219292800'), ((new UuidV1('00000000-0000-1000-a456-426655440000'))->getDateTime()));
|
||||
}
|
||||
}
|
||||
|
@ -104,24 +104,26 @@ class Ulid extends AbstractUid
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float Seconds since the Unix epoch 1970-01-01 00:00:00
|
||||
*/
|
||||
public function getTime(): float
|
||||
public function getDateTime(): \DateTimeImmutable
|
||||
{
|
||||
$time = strtr(substr($this->uid, 0, 10), 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
|
||||
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
return hexdec(base_convert($time, 32, 16)) / 1000;
|
||||
$time = (string) hexdec(base_convert($time, 32, 16));
|
||||
} else {
|
||||
$time = sprintf('%02s%05s%05s',
|
||||
base_convert(substr($time, 0, 2), 32, 16),
|
||||
base_convert(substr($time, 2, 4), 32, 16),
|
||||
base_convert(substr($time, 6, 4), 32, 16)
|
||||
);
|
||||
$time = BinaryUtil::toBase(hex2bin($time), BinaryUtil::BASE10);
|
||||
}
|
||||
|
||||
$time = sprintf('%02s%05s%05s',
|
||||
base_convert(substr($time, 0, 2), 32, 16),
|
||||
base_convert(substr($time, 2, 4), 32, 16),
|
||||
base_convert(substr($time, 6, 4), 32, 16)
|
||||
);
|
||||
if (4 > \strlen($time)) {
|
||||
$time = str_pad($time, 4, '0', \STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return BinaryUtil::toBase(hex2bin($time), BinaryUtil::BASE10) / 1000;
|
||||
return \DateTimeImmutable::createFromFormat('U.u', substr_replace($time, '.', -3, 0));
|
||||
}
|
||||
|
||||
private static function generate(): string
|
||||
|
@ -31,14 +31,9 @@ class UuidV1 extends Uuid
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float Seconds since the Unix epoch 1970-01-01 00:00:00
|
||||
*/
|
||||
public function getTime(): float
|
||||
public function getDateTime(): \DateTimeImmutable
|
||||
{
|
||||
$time = '0'.substr($this->uid, 15, 3).substr($this->uid, 9, 4).substr($this->uid, 0, 8);
|
||||
|
||||
return BinaryUtil::timeToFloat($time);
|
||||
return BinaryUtil::timeToDateTime('0'.substr($this->uid, 15, 3).substr($this->uid, 9, 4).substr($this->uid, 0, 8));
|
||||
}
|
||||
|
||||
public function getNode(): string
|
||||
|
@ -50,14 +50,9 @@ class UuidV6 extends Uuid
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return float Seconds since the Unix epoch 1970-01-01 00:00:00
|
||||
*/
|
||||
public function getTime(): float
|
||||
public function getDateTime(): \DateTimeImmutable
|
||||
{
|
||||
$time = '0'.substr($this->uid, 0, 8).substr($this->uid, 9, 4).substr($this->uid, 15, 3);
|
||||
|
||||
return BinaryUtil::timeToFloat($time);
|
||||
return BinaryUtil::timeToDateTime('0'.substr($this->uid, 0, 8).substr($this->uid, 9, 4).substr($this->uid, 15, 3));
|
||||
}
|
||||
|
||||
public function getNode(): string
|
||||
|
Reference in New Issue
Block a user