From 9929ea1fc1f5239ebb930b47973415a3173a5915 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 6 Sep 2022 12:35:26 +0200 Subject: [PATCH] Fix Zoned DateTime support for dates before common era Years previous the common era were being treated as positive numbers causing the dates appear as positive numbers and breaking the auto-zone offset detection for zoned date times which were not created with the offset. --- .../bolt/bolt-protocol-v5x0.utc.transformer.js | 17 +++++++++++++---- .../test/bolt/bolt-protocol-v4x3.test.js | 12 ++++++++++++ .../test/bolt/bolt-protocol-v4x4.test.js | 12 ++++++++++++ .../test/bolt/bolt-protocol-v5x0.test.js | 12 ++++++++++++ .../neo4j-driver/test/temporal-types.test.js | 2 +- 5 files changed, 50 insertions(+), 5 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.utc.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.utc.transformer.js index 94c0a8b1d..905b77ef0 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.utc.transformer.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.utc.transformer.js @@ -30,6 +30,7 @@ import v4x4 from './bolt-protocol-v4x4.transformer' import { epochSecondAndNanoToLocalDateTime } from './temporal-factory' +import { identity } from '../lang/functional' const { temporalUtil: { @@ -89,8 +90,8 @@ function createDateTimeWithZoneIdTransformer (config, logger) { if (value.timeZoneOffsetSeconds == null) { logger.warn('DateTime objects without "timeZoneOffsetSeconds" property ' + - 'are prune to bugs related to ambiguous times. For instance, ' + - '2022-10-30T2:30:00[Europe/Berlin] could be GMT+1 or GMT+2.') + 'are prune to bugs related to ambiguous times. For instance, ' + + '2022-10-30T2:30:00[Europe/Berlin] could be GMT+1 or GMT+2.') } const utc = epochSecond.subtract(offset) @@ -167,7 +168,8 @@ function getTimeInZoneId (timeZoneId, epochSecond, nano) { hour: 'numeric', minute: 'numeric', second: 'numeric', - hour12: false + hour12: false, + era: 'narrow' }) const l = epochSecondAndNanoToLocalDateTime(epochSecond, nano) @@ -183,12 +185,19 @@ function getTimeInZoneId (timeZoneId, epochSecond, nano) { const formattedUtcParts = formatter.formatToParts(utc) const localDateTime = formattedUtcParts.reduce((obj, currentValue) => { - if (currentValue.type !== 'literal') { + if (currentValue.type === 'era') { + obj.adjustEra = + currentValue.value.toLocaleUpperCase() === 'B' + ? year => year.subtract(1).negate() // 1BC equals to year 0 in astronomical year numbering + : identity + } else if (currentValue.type !== 'literal') { obj[currentValue.type] = int(currentValue.value) } return obj }, {}) + localDateTime.year = localDateTime.adjustEra(localDateTime.year) + const epochInTimeZone = localDateTimeToEpochSecond( localDateTime.year, localDateTime.month, diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js index 6243e1b01..9ca0e71ac 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js @@ -801,6 +801,18 @@ describe('#unit BoltProtocolV4x3', () => { [ 'DateTimeWithZoneId / Sao Paulo just 1 after turn winter time', new DateTime(2019, 2, 18, 1, 0, 0, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateWithWithZoneId / Berlin before common era', + new DateTime(-2020, 6, 15, 4, 30, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateWithWithZoneId / Max Date', + new DateTime(99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Kiritimati') + ], + [ + 'DateWithWithZoneId / Min Date', + new DateTime(-99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Samoa') ] ])('should pack and unpack DateTimeWithZoneId and without offset (%s)', (_, object) => { const packable = protocol.packable(object) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js index 945ded5da..0a0bcc27a 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js @@ -834,6 +834,18 @@ describe('#unit BoltProtocolV4x4', () => { [ 'DateTimeWithZoneId / Sao Paulo just 1 after turn winter time', new DateTime(2019, 2, 18, 1, 0, 0, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateWithWithZoneId / Berlin before common era', + new DateTime(-2020, 6, 15, 4, 30, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateWithWithZoneId / Max Date', + new DateTime(99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Kiritimati') + ], + [ + 'DateWithWithZoneId / Min Date', + new DateTime(-99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Samoa') ] ])('should pack and unpack DateTimeWithZoneId and without offset (%s)', (_, object) => { const packable = protocol.packable(object) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js index a2bcd6c99..9d61d7c77 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js @@ -527,6 +527,18 @@ describe('#unit BoltProtocolV5x0', () => { [ 'DateTimeWithZoneId / Istanbul', new DateTime(2020, 6, 15, 4, 30, 0, 183_000_000, undefined, 'Pacific/Honolulu') + ], + [ + 'DateWithWithZoneId / Berlin before common era', + new DateTime(-2020, 6, 15, 4, 30, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateWithWithZoneId / Max Date', + new DateTime(99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Kiritimati') + ], + [ + 'DateWithWithZoneId / Min Date', + new DateTime(-99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Samoa') ] ])('should pack and unpack DateTimeWithZoneId and without offset (%s)', (_, object) => { const buffer = alloc(256) diff --git a/packages/neo4j-driver/test/temporal-types.test.js b/packages/neo4j-driver/test/temporal-types.test.js index b23a45066..9ee7d2653 100644 --- a/packages/neo4j-driver/test/temporal-types.test.js +++ b/packages/neo4j-driver/test/temporal-types.test.js @@ -38,7 +38,7 @@ const MAX_TEMPORAL_ARRAY_LENGTH = 1000 */ const MAX_DURATION_COMPONENT = 3000000000000 const MAX_NANO_OF_SECOND = 999999999 -const MAX_YEAR = 999999999 +const MAX_YEAR = 99_999 const MIN_YEAR = -MAX_YEAR const MAX_TIME_ZONE_OFFSET = 64800 const MIN_TIME_ZONE_OFFSET = -MAX_TIME_ZONE_OFFSET