From 6e6491214b2c6fa4c7c679f7f6ca54430d67a754 Mon Sep 17 00:00:00 2001 From: Fede Isas Date: Wed, 7 Feb 2024 19:35:04 -0300 Subject: [PATCH] Avoid warnings on ComputesNanosecondTimestamps (#21) * Avoid warnings on ComputesNanosecondTimestamps * Tweaks - Compute `strlen` once. - `microtime(true)` can return different number of decimals: These are all valid: ``` 1706498205.1234 1706498205.123 1706498205.12 1706498205.1 ``` * Refactor and tests * Improve float length and more tests --- src/Traits/ComputesNanosecondTimestamps.php | 59 ++++++++++++---- tests/InfluxDBV2DriverTest.php | 77 +++++++++++++++++++-- 2 files changed, 117 insertions(+), 19 deletions(-) diff --git a/src/Traits/ComputesNanosecondTimestamps.php b/src/Traits/ComputesNanosecondTimestamps.php index 8eb0b3a..05305ea 100644 --- a/src/Traits/ComputesNanosecondTimestamps.php +++ b/src/Traits/ComputesNanosecondTimestamps.php @@ -4,6 +4,8 @@ trait ComputesNanosecondTimestamps { + protected const TO_NANO = 1000000000; + /** * A public way tog et the nanosecond precision we desire. * @@ -14,25 +16,52 @@ trait ComputesNanosecondTimestamps public function getNanoSecondTimestamp($timestamp = null) { if ($timestamp instanceof \DateTime) { - return $timestamp->getTimestamp() * 1000000000; - } + return $timestamp->getTimestamp() * self::TO_NANO; + } elseif (is_int($timestamp)) { + $length = strlen((string) $timestamp); - if (strlen($timestamp) == 19) { - // Looks like it is already nanosecond precise! - return $timestamp; - } + return match ($length) { + // Looks like it is already nanosecond precise! + 19 => $timestamp, + // This appears to be in seconds + 10 => $timestamp * self::TO_NANO, + default => $this->generateTimestamp(), + }; + } elseif (is_string($timestamp)) { + if (preg_match("/\d{10}\.\d{1,4}$/", $timestamp)) { + return (int) ($timestamp * self::TO_NANO); + } elseif (ctype_digit($timestamp)) { + $length = strlen($timestamp); - if (strlen($timestamp) == 10) { - // This appears to be in seconds - return $timestamp * 1000000000; - } + return match ($length) { + // Looks like it is already nanosecond precise! + 19 => (int) $timestamp, + // This appears to be in seconds + 10 => (int) ($timestamp * self::TO_NANO), + default => $this->generateTimestamp(), + }; + } + } elseif (is_float($timestamp)) { + $integerLength = (int) floor(log10(abs($timestamp))) + 1; - if (preg_match("/\d{10}\.\d{4}/", $timestamp)) { - // This looks like a microtime float - return (int)($timestamp * 1000000000); + return match ($integerLength) { + // Looks like it is already nanosecond precise! + 19 => (int) $timestamp, + // This appears to be in seconds + 10 => (int) ($timestamp * self::TO_NANO), + default => $this->generateTimestamp(), + }; } // We weren't given a valid timestamp, generate. - return (int)(microtime(true) * 1000000000); + return $this->generateTimestamp(); + } + + /** + * @return int + */ + protected function generateTimestamp(): int + { + return (int) (microtime(true) * self::TO_NANO); } -} \ No newline at end of file +} diff --git a/tests/InfluxDBV2DriverTest.php b/tests/InfluxDBV2DriverTest.php index 0de81e4..ae07ff5 100644 --- a/tests/InfluxDBV2DriverTest.php +++ b/tests/InfluxDBV2DriverTest.php @@ -25,15 +25,84 @@ public function testTcpWriteClient() ); } - public function testNanoSecondTimestamp() + /** + * @dataProvider nanoSecondTimestampInvalid + */ + public function testNanoSecondTimestampInvalid($input) { $this->setupInfluxDB(); $influx = app(InfluxDB::class); - $this->assertEquals(1508713728000000000, $influx->getNanoSecondTimestamp(1508713728000000000)); - $this->assertEquals(1508713728000000000, $influx->getNanoSecondTimestamp(1508713728)); - $this->assertEquals(1508713728000000000, $influx->getNanoSecondTimestamp(new \DateTime('@1508713728'))); + $now = $influx->getNanoSecondTimestamp(); + $result = $influx->getNanoSecondTimestamp($input); + + $this->assertTrue(is_int($result)); + $this->assertEquals(19, strlen((string) $result)); + $this->assertGreaterThanOrEqual($now, $result); } + /** + * @dataProvider nanoSecondTimestampValid + */ + public function testNanoSecondTimestamp($expected, $input) + { + $this->setupInfluxDB(); + + $influx = app(InfluxDB::class); + + $result = $influx->getNanoSecondTimestamp($input); + + $this->assertTrue(is_int($result)); + $this->assertEquals(19, strlen((string) $result)); + $this->assertEquals($expected, $result); + } + + public static function nanoSecondTimestampValid() + { + $expected = 1508713728000000000; + $expectedPrecise = 1508713728123400000; + + return [ + [$expected, 1508713728000000000,], + [$expected, 1508713728,], + [$expected, '1508713728000000000',], + [$expected, '1508713728',], + [$expected, new \DateTime('@1508713728'),], + [$expected, '1508713728.0000',], + [$expected, '1508713728.000',], + [$expected, '1508713728.00',], + [$expected, '1508713728.0',], + [$expected, 1508713728.0000,], + [$expected, 1508713728.000,], + [$expected, 1508713728.00,], + [$expected, 1508713728.0,], + [$expectedPrecise, 1508713728123400000,], + [$expectedPrecise, '1508713728123400000',], + // [$expectedPrecise, '1508713728.1234',], // PHP float precision breaks this + // [1508713728123000000, '1508713728.123',], // PHP float precision breaks this + [1508713728120000000, '1508713728.12',], + [1508713728100000000, '1508713728.1',], + // [1508713728123400000, 1508713728.1234,], // PHP float precision breaks this + // [1508713728123000000, 1508713728.123,], // PHP float precision breaks this + [1508713728120000000, 1508713728.12,], + [1508713728100000000, 1508713728.1,], + ]; + } + + public static function nanoSecondTimestampInvalid() + { + return [ + ['abc'], // letters + ['150871372800000000a',], // numbers with letters + [150871372800000000,], // 18 digits + [15087137281,], // 11 digits + [150871372,], // 9 digits + [15087137,], // 8 digits + [0,], + [0.0,], + ['000000000.1',], + ['000000000.0',], + ]; + } } \ No newline at end of file