Skip to content

Commit d8bf14e

Browse files
second control iteration on structure classes. updated tests with providers.
1 parent 60b9f57 commit d8bf14e

File tree

8 files changed

+109
-51
lines changed

8 files changed

+109
-51
lines changed

src/structures/Date.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ public function days(): int
4040

4141
public function __toString(): string
4242
{
43-
return gmdate('Y-m-d', strtotime($this->days . ' days', 0));
43+
return gmdate('Y-m-d', strtotime('+' . $this->days . ' days +0000', 0));
4444
}
4545
}

src/structures/DateTime.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ public function tz_offset_seconds(): int
7676

7777
public function __toString(): string
7878
{
79-
$ts = bcsub(bcadd($this->seconds, bcdiv($this->nanoseconds, 1000000000, 6), 6), $this->tz_offset_seconds, 6);
79+
$ts = bcadd($this->seconds - $this->tz_offset_seconds, bcdiv($this->nanoseconds, 10e8, 6), 6);
8080
return \DateTime::createFromFormat('U.u', $ts, new \DateTimeZone('UTC'))
81-
->setTimezone(new \DateTimeZone(sprintf('+%04d', $this->tz_offset_seconds / 3600 * 100)))
81+
->setTimezone(new \DateTimeZone(sprintf("%+'05d", $this->tz_offset_seconds / 3600 * 100)))
8282
->format('Y-m-d\TH:i:s.uP');
8383
}
8484
}

src/structures/DateTimeZoneId.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ public function tz_id(): string
7676

7777
public function __toString(): string
7878
{
79-
return \DateTime::createFromFormat('U.u', bcadd($this->seconds, bcdiv($this->nanoseconds, 1000000000, 6), 6), new \DateTimeZone('UTC'))
80-
->setTimezone(new \DateTimeZone($this->tz_id))
81-
->format('Y-m-d\TH:i:s.uP[e]');
79+
$timestamp = bcadd($this->seconds(), bcdiv($this->nanoseconds, 10e8, 6), 6);
80+
return \DateTime::createFromFormat('U.u', $timestamp, new \DateTimeZone($this->tz_id))
81+
->format('Y-m-d\TH:i:s.u') . '[' . $this->tz_id . ']';
8282
}
8383
}

src/structures/Duration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public function __toString(): string
102102
if (!empty($minutes))
103103
$time .= $minutes . 'M';
104104

105-
$seconds = rtrim(bcadd($this->seconds % 3600 % 60, bcdiv($this->nanoseconds, 1000000000, 6), 6), '0.');
105+
$seconds = rtrim(bcadd($this->seconds % 3600 % 60, bcdiv($this->nanoseconds, 10e8, 6), 6), '0.');
106106
if (!empty($seconds))
107107
$time .= $seconds . 'S';
108108

src/structures/LocalDateTime.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function nanoseconds(): int
5656

5757
public function __toString(): string
5858
{
59-
return \DateTime::createFromFormat('U.u', bcadd($this->seconds, bcdiv($this->nanoseconds, 1000000000, 6), 6), new \DateTimeZone('UTC'))
59+
return \DateTime::createFromFormat('U.u', bcadd($this->seconds, bcdiv($this->nanoseconds, 10e8, 6), 6))
6060
->format('Y-m-d\TH:i:s.u');
6161
}
6262

src/structures/LocalTime.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function nanoseconds(): int
4040

4141
public function __toString(): string
4242
{
43-
return \DateTime::createFromFormat('U.u', bcdiv($this->nanoseconds, 1000000000, 6), new \DateTimeZone("UTC"))
43+
return \DateTime::createFromFormat('U.u', bcdiv($this->nanoseconds, 10e8, 6), new \DateTimeZone('UTC'))
4444
->format('H:i:s.u');
4545
}
4646
}

src/structures/Time.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ public function tz_offset_seconds(): int
5959

6060
public function __toString(): string
6161
{
62-
return \DateTime::createFromFormat('U.u', bcsub(bcdiv($this->nanoseconds, 1000000000, 6), $this->tz_offset_seconds, 6), new \DateTimeZone('UTC'))
63-
->setTimezone(new \DateTimeZone(sprintf('+%04d', $this->tz_offset_seconds / 3600 * 100)))
62+
$ts = bcsub(bcdiv($this->nanoseconds, 10e8, 6), $this->tz_offset_seconds, 6);
63+
return \DateTime::createFromFormat('U.u', $ts, new \DateTimeZone('UTC'))
64+
->setTimezone(new \DateTimeZone(sprintf("%+'05d", $this->tz_offset_seconds / 3600 * 100)))
6465
->format('H:i:s.uP');
6566
}
6667
}

tests/StructuresTest.php

Lines changed: 97 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@
5050
*/
5151
class StructuresTest extends TestCase
5252
{
53+
/**
54+
* How many iterations do for each date/time test
55+
* @var int
56+
*/
57+
public static $iterations = 10;
58+
5359
public function testInit(): AProtocol
5460
{
5561
try {
@@ -71,40 +77,42 @@ public function testInit(): AProtocol
7177
}
7278

7379
/**
74-
* @depends testInit
80+
* @depends testInit
81+
* @dataProvider providerTimestamp
7582
*/
76-
public function testDate(AProtocol $protocol)
83+
public function testDate(int $timestamp, AProtocol $protocol)
7784
{
7885
try {
79-
$date = date('Y-m-d');
86+
$date = gmdate('Y-m-d', $timestamp);
8087

8188
//unpack
8289
$protocol->run('RETURN date($date)', [
8390
'date' => $date
8491
]);
8592
$rows = $protocol->pull();
8693
$this->assertInstanceOf(Date::class, $rows[0][0]);
87-
$this->assertEquals($date, (string)$rows[0][0]);
94+
$this->assertEquals($date, (string)$rows[0][0], 'unpack ' . $date . ' != ' . $rows[0][0]);
8895

8996
//pack
9097
$protocol->run('RETURN toString($date)', [
9198
'date' => $rows[0][0]
9299
]);
93100
$rows = $protocol->pull();
94-
$this->assertEquals($date, $rows[0][0]);
101+
$this->assertEquals($date, $rows[0][0], 'pack ' . $date . ' != ' . $rows[0][0]);
95102
} catch (Exception $e) {
96103
$this->markTestIncomplete($e->getMessage());
97104
}
98105
}
99106

100107
/**
101-
* @depends testInit
108+
* @depends testInit
109+
* @dataProvider providerTimestampTimezone
102110
*/
103-
public function testDateTime(AProtocol $protocol)
111+
public function testDateTime(int $timestamp, string $timezone, AProtocol $protocol)
104112
{
105113
try {
106-
$datetime = \DateTime::createFromFormat('U.u', bcadd(strtotime('2021-01-12 15:30:25+01:00'), 0.123456, 6), new \DateTimeZone('UTC'))
107-
->setTimezone(new \DateTimeZone('+0100'))
114+
$timestamp = bcadd($timestamp, fmod(microtime(true), 1), 6);
115+
$datetime = \DateTime::createFromFormat('U.u', $timestamp, new \DateTimeZone($timezone))
108116
->format('Y-m-d\TH:i:s.uP');
109117

110118
//unpack
@@ -113,41 +121,49 @@ public function testDateTime(AProtocol $protocol)
113121
]);
114122
$rows = $protocol->pull();
115123
$this->assertInstanceOf(DateTime::class, $rows[0][0]);
116-
$this->assertEquals($datetime, (string)$rows[0][0]);
124+
$this->assertEquals($datetime, (string)$rows[0][0], 'unpack ' . $datetime . ' != ' . $rows[0][0]);
117125

118126
//pack
119127
$protocol->run('RETURN toString($date)', [
120128
'date' => $rows[0][0]
121129
]);
122130
$rows = $protocol->pull();
123-
$this->assertEquals($datetime, $rows[0][0]);
131+
// neo4j returns fraction of seconds not padded with zeros ... zero timezone offset returns as Z
132+
$datetime = preg_replace(["/\.?0+(.\d{2}:\d{2})$/", "/\+00:00$/"], ['$1', 'Z'], $datetime);
133+
$this->assertEquals($datetime, $rows[0][0], 'pack ' . $datetime . ' != ' . $rows[0][0]);
124134
} catch (Exception $e) {
125135
$this->markTestIncomplete($e->getMessage());
126136
}
127137
}
128138

129139
/**
130-
* @depends testInit
140+
* @depends testInit
141+
* @dataProvider providerTimestampTimezone
131142
*/
132-
public function testDateTimeZoneId(AProtocol $protocol)
143+
public function testDateTimeZoneId(int $timestamp, string $timezone, AProtocol $protocol)
133144
{
134145
try {
146+
$timestamp = bcadd($timestamp, fmod(microtime(true), 1), 6);
147+
$datetime = \DateTime::createFromFormat('U.u', $timestamp, new \DateTimeZone($timezone))
148+
->format('Y-m-d\TH:i:s.u') . '[' . $timezone . ']';
149+
135150
//unpack
136-
$protocol->run('RETURN datetime({
137-
year: 1984, month: 11, day: 11,
138-
hour: 12, minute: 31, second: 14, nanosecond: 645876123,
139-
timezone: \'Europe/Stockholm\'
140-
})');
151+
$protocol->run('RETURN datetime($dt)', [
152+
'dt' => $datetime
153+
]);
141154
$rows = $protocol->pull();
142155
$this->assertInstanceOf(DateTimeZoneId::class, $rows[0][0]);
143-
$this->assertEquals('1984-11-11T13:31:14.645876+01:00[Europe/Stockholm]', (string)$rows[0][0]);
156+
$this->assertEquals($datetime, (string)$rows[0][0], 'unpack ' . $datetime . ' != ' . $rows[0][0]);
144157

145158
//pack
146159
$protocol->run('RETURN toString($dt)', [
147160
'dt' => $rows[0][0]
148161
]);
149162
$rows = $protocol->pull();
150-
$this->assertEquals('1984-11-11T12:31:14.645876123+01:00[Europe/Stockholm]', $rows[0][0]);
163+
// neo4j returns fraction of seconds not padded with zeros ... also contains timezone offset before timezone id
164+
$datetime = preg_replace("/\.?0+\[/", '[', $datetime);
165+
$rows[0][0] = preg_replace("/([+\-]\d{2}:\d{2}|Z)\[/", '[', $rows[0][0]);
166+
$this->assertEquals($datetime, $rows[0][0], 'pack ' . $datetime . ' != ' . $rows[0][0]);
151167
} catch (Exception $e) {
152168
$this->markTestIncomplete($e->getMessage());
153169
}
@@ -172,27 +188,28 @@ public function testDuration(AProtocol $protocol)
172188
$protocol->run('RETURN duration($d)', ['d' => $duration]);
173189
$rows = $protocol->pull();
174190
$this->assertInstanceOf(Duration::class, $rows[0][0]);
175-
$this->assertEquals($duration, (string)$rows[0][0]);
191+
$this->assertEquals($duration, (string)$rows[0][0], 'unpack ' . $duration . ' != ' . $rows[0][0]);
176192

177193
//pack
178194
$protocol->run('RETURN toString($d)', [
179195
'd' => $rows[0][0]
180196
]);
181197
$rows = $protocol->pull();
182-
$this->assertEquals($duration, $rows[0][0]);
198+
$this->assertEquals($duration, $rows[0][0], 'pack ' . $duration . ' != ' . $rows[0][0]);
183199
}
184200
} catch (Exception $e) {
185201
$this->markTestIncomplete($e->getMessage());
186202
}
187203
}
188204

189205
/**
190-
* @depends testInit
206+
* @depends testInit
207+
* @dataProvider providerTimestamp
191208
*/
192-
public function testLocalDateTime(AProtocol $protocol)
209+
public function testLocalDateTime(int $timestamp, AProtocol $protocol)
193210
{
194211
try {
195-
$datetime = \DateTime::createFromFormat('U.u', bcadd(strtotime('2021-01-12 15:30:25+01:00'), 0.123456, 6))
212+
$datetime = \DateTime::createFromFormat('U.u', $timestamp)
196213
->format('Y-m-d\TH:i:s.u');
197214

198215
//unpack
@@ -201,26 +218,29 @@ public function testLocalDateTime(AProtocol $protocol)
201218
]);
202219
$rows = $protocol->pull();
203220
$this->assertInstanceOf(LocalDateTime::class, $rows[0][0]);
204-
$this->assertEquals($datetime, (string)$rows[0][0]);
221+
$this->assertEquals($datetime, (string)$rows[0][0], 'unpack ' . $datetime . ' != ' . $rows[0][0]);
205222

206223
//pack
207224
$protocol->run('RETURN toString($dt)', [
208225
'dt' => $rows[0][0]
209226
]);
210227
$rows = $protocol->pull();
211-
$this->assertEquals($datetime, $rows[0][0]);
228+
$datetime = rtrim($datetime, '.0');
229+
$this->assertEquals($datetime, $rows[0][0], 'pack ' . $datetime . ' != ' . $rows[0][0]);
212230
} catch (Exception $e) {
213231
$this->markTestIncomplete($e->getMessage());
214232
}
215233
}
216234

217235
/**
218-
* @depends testInit
236+
* @depends testInit
237+
* @dataProvider providerTimestamp
219238
*/
220-
public function testLocalTime(AProtocol $protocol)
239+
public function testLocalTime(int $timestamp, AProtocol $protocol)
221240
{
222241
try {
223-
$time = \DateTime::createFromFormat('U.u', bcadd(time(), 0.123456, 6))
242+
$timestamp = bcadd($timestamp, fmod(microtime(true), 1), 6);
243+
$time = \DateTime::createFromFormat('U.u', $timestamp)
224244
->format('H:i:s.u');
225245

226246
//unpack
@@ -229,14 +249,15 @@ public function testLocalTime(AProtocol $protocol)
229249
]);
230250
$rows = $protocol->pull();
231251
$this->assertInstanceOf(LocalTime::class, $rows[0][0]);
232-
$this->assertEquals($time, (string)$rows[0][0]);
252+
$this->assertEquals($time, (string)$rows[0][0], 'unpack ' . $time . ' != ' . $rows[0][0]);
233253

234254
//pack
235255
$protocol->run('RETURN toString($t)', [
236256
't' => $rows[0][0]
237257
]);
238258
$rows = $protocol->pull();
239-
$this->assertEquals($time, $rows[0][0]);
259+
$time = rtrim($time, '.0');
260+
$this->assertEquals($time, $rows[0][0], 'pack ' . $time . ' != ' . $rows[0][0]);
240261
} catch (Exception $e) {
241262
$this->markTestIncomplete($e->getMessage());
242263
}
@@ -353,32 +374,68 @@ public function testRelationship(AProtocol $protocol)
353374
}
354375

355376
/**
356-
* @depends testInit
377+
* @depends testInit
378+
* @dataProvider providerTimestampTimezone
357379
*/
358-
public function testTime(AProtocol $protocol)
380+
public function testTime(int $timestamp, string $timezone, AProtocol $protocol)
359381
{
360-
$time = \DateTime::createFromFormat('U.u', bcadd(strtotime('2021-01-12 15:30:25+01:00'), 0.123456, 6), new \DateTimeZone('UTC'))
361-
->setTimezone(new \DateTimeZone('+0100'))
362-
->format('H:i:s.uP');
363-
364382
try {
383+
$timestamp = bcadd($timestamp, fmod(microtime(true), 1), 6);
384+
$time = \DateTime::createFromFormat('U.u', $timestamp, new \DateTimeZone($timezone))
385+
->format('H:i:s.uP');
386+
365387
//unpack
366388
$protocol->run('RETURN time($t)', [
367389
't' => $time
368390
]);
369391
$rows = $protocol->pull();
370392
$this->assertInstanceOf(Time::class, $rows[0][0]);
371-
$this->assertEquals($time, (string)$rows[0][0]);
393+
$this->assertEquals($time, (string)$rows[0][0], 'unpack ' . $time . ' != ' . $rows[0][0]);
372394

373395
//pack
374396
$protocol->run('RETURN toString($t)', [
375397
't' => $rows[0][0]
376398
]);
377399
$rows = $protocol->pull();
378-
$this->assertEquals($time, $rows[0][0]);
400+
// neo4j returns fraction of seconds not padded with zeros ... zero timezone offset returns as Z
401+
$time = preg_replace(["/\.?0+(.\d{2}:\d{2})$/", "/\+00:00$/"], ['$1', 'Z'], $time);
402+
$this->assertEquals($time, $rows[0][0], 'pack ' . $time . ' != ' . $rows[0][0]);
379403
} catch (Exception $e) {
380404
$this->markTestIncomplete($e->getMessage());
381405
}
382406
}
383407

408+
public function providerTimestamp(): \Generator
409+
{
410+
for ($i = 0; $i < self::$iterations; $i++) {
411+
$ts = $this->randomTimestamp();
412+
yield 'ts: ' . $ts => [$ts];
413+
}
414+
}
415+
416+
public function providerTimestampTimezone(): \Generator
417+
{
418+
for ($i = 0; $i < self::$iterations; $i++) {
419+
$tz = \DateTimeZone::listIdentifiers()[array_rand(\DateTimeZone::listIdentifiers())];
420+
$ts = $this->randomTimestamp($tz);
421+
yield 'ts: ' . $ts . ' tz: ' . $tz => [$ts, $tz];
422+
}
423+
}
424+
425+
/**
426+
* @param string $timezone
427+
* @return int
428+
*/
429+
private function randomTimestamp(string $timezone = '+0000'): int
430+
{
431+
try {
432+
$zone = new \DateTimeZone($timezone);
433+
$start = new \DateTime('-3 years', $zone);
434+
$end = new \DateTime('+3 years', $zone);
435+
return rand($start->getTimestamp(), $end->getTimestamp());
436+
} catch (Exception $e) {
437+
return time();
438+
}
439+
}
440+
384441
}

0 commit comments

Comments
 (0)