Skip to content

Commit 27f92ca

Browse files
authored
Merge pull request neo4j#366 from lutovich/1.6-negative-ns
Fix `Duration#toString()` for negative ns
2 parents 04379dd + 19b4c45 commit 27f92ca

File tree

2 files changed

+67
-6
lines changed

2 files changed

+67
-6
lines changed

src/v1/internal/temporal-util.js

+24-6
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,8 @@ export function epochDayToDate(epochDay) {
187187
export function durationToIsoString(months, days, seconds, nanoseconds) {
188188
const monthsString = formatNumber(months);
189189
const daysString = formatNumber(days);
190-
const secondsString = formatNumber(seconds);
191-
const nanosecondsString = formatNanoseconds(nanoseconds);
192-
return `P${monthsString}M${daysString}DT${secondsString}${nanosecondsString}S`;
190+
const secondsAndNanosecondsString = formatSecondsAndNanosecondsForDuration(seconds, nanoseconds);
191+
return `P${monthsString}M${daysString}DT${secondsAndNanosecondsString}S`;
193192
}
194193

195194
/**
@@ -204,7 +203,7 @@ export function timeToIsoString(hour, minute, second, nanosecond) {
204203
const hourString = formatNumber(hour, 2);
205204
const minuteString = formatNumber(minute, 2);
206205
const secondString = formatNumber(second, 2);
207-
const nanosecondString = formatNanoseconds(nanosecond);
206+
const nanosecondString = formatNanosecond(nanosecond);
208207
return `${hourString}:${minuteString}:${secondString}${nanosecondString}`;
209208
}
210209

@@ -320,11 +319,30 @@ function floorMod(x, y) {
320319
return x.subtract(floorDiv(x, y).multiply(y));
321320
}
322321

322+
/**
323+
* @param {Integer|number|string} seconds the number of seconds to format.
324+
* @param {Integer|number|string} nanoseconds the number of nanoseconds to format.
325+
* @return {string} formatted value.
326+
*/
327+
function formatSecondsAndNanosecondsForDuration(seconds, nanoseconds) {
328+
seconds = int(seconds);
329+
nanoseconds = int(nanoseconds);
330+
331+
const signString = seconds.isNegative() || nanoseconds.isNegative() ? '-' : '';
332+
seconds = seconds.isNegative() ? seconds.negate() : seconds;
333+
nanoseconds = nanoseconds.isNegative() ? nanoseconds.negate() : nanoseconds;
334+
335+
const secondsString = formatNumber(seconds);
336+
const nanosecondsString = formatNanosecond(nanoseconds);
337+
338+
return signString + secondsString + nanosecondsString;
339+
}
340+
323341
/**
324342
* @param {Integer|number|string} value the number of nanoseconds to format.
325343
* @return {string} formatted and possibly left-padded nanoseconds part as string.
326344
*/
327-
function formatNanoseconds(value) {
345+
function formatNanosecond(value) {
328346
value = int(value);
329347
return value.equals(0) ? '' : '.' + formatNumber(value, 9);
330348
}
@@ -338,7 +356,7 @@ function formatNumber(num, stringLength = undefined) {
338356
num = int(num);
339357
const isNegative = num.isNegative();
340358
if (isNegative) {
341-
num = num.multiply(-1);
359+
num = num.negate();
342360
}
343361
const numString = num.toString();
344362
const paddedNumString = stringLength ? numString.padStart(stringLength, '0') : numString;

test/v1/temporal-types.test.js

+43
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,33 @@ describe('temporal-types', () => {
524524
expect(zonedDateTime.nanosecond).toEqual(neo4j.int(9346458));
525525
});
526526

527+
it('should format duration to string', done => {
528+
if (neo4jDoesNotSupportTemporalTypes(done)) {
529+
return;
530+
}
531+
532+
testDurationToString([
533+
{duration: duration(0, 0, 1, 0), expectedString: 'P0M0DT1S'},
534+
{duration: duration(0, 0, -1, 0), expectedString: 'P0M0DT-1S'},
535+
536+
{duration: duration(0, 0, 0, 5), expectedString: 'P0M0DT0.000000005S'},
537+
{duration: duration(0, 0, 0, -5), expectedString: 'P0M0DT-0.000000005S'},
538+
{duration: duration(0, 0, 0, 999999999), expectedString: 'P0M0DT0.999999999S'},
539+
{duration: duration(0, 0, 0, -999999999), expectedString: 'P0M0DT-0.999999999S'},
540+
541+
{duration: duration(0, 0, 1, 5), expectedString: 'P0M0DT1.000000005S'},
542+
{duration: duration(0, 0, -1, -5), expectedString: 'P0M0DT-1.000000005S'},
543+
{duration: duration(0, 0, 1, -5), expectedString: 'P0M0DT0.999999995S'},
544+
{duration: duration(0, 0, -1, 5), expectedString: 'P0M0DT-0.999999995S'},
545+
{duration: duration(0, 0, 1, 999999999), expectedString: 'P0M0DT1.999999999S'},
546+
{duration: duration(0, 0, -1, -999999999), expectedString: 'P0M0DT-1.999999999S'},
547+
{duration: duration(0, 0, 1, -999999999), expectedString: 'P0M0DT0.000000001S'},
548+
{duration: duration(0, 0, -1, 999999999), expectedString: 'P0M0DT-0.000000001S'},
549+
550+
{duration: duration(0, 0, -78036, -143000000), expectedString: 'P0M0DT-78036.143000000S'}
551+
], done);
552+
});
553+
527554
function testSendAndReceiveRandomTemporalValues(valueGenerator, done) {
528555
const asyncFunction = (index, callback) => {
529556
const next = () => callback();
@@ -578,6 +605,22 @@ describe('temporal-types', () => {
578605
});
579606
}
580607

608+
function testDurationToString(values, done) {
609+
const durations = values.map(value => value.duration);
610+
const expectedDurationStrings = values.map(value => value.expectedString);
611+
612+
session.run('UNWIND $durations AS d RETURN d', {durations: durations}).then(result => {
613+
const receivedDurationStrings = result.records
614+
.map(record => record.get(0))
615+
.map(duration => duration.toString());
616+
617+
expect(expectedDurationStrings).toEqual(receivedDurationStrings);
618+
done();
619+
}).catch(error => {
620+
done.fail(error);
621+
});
622+
}
623+
581624
function neo4jDoesNotSupportTemporalTypes(done) {
582625
if (serverVersion.compareTo(VERSION_3_4_0) < 0) {
583626
done();

0 commit comments

Comments
 (0)