TZ => UTC passes tests

This commit is contained in:
AJ ONeal 2021-05-27 16:25:55 -06:00
parent c30ecbd127
commit af6d1bdb5f
2 changed files with 237 additions and 21 deletions

View File

@ -1,6 +1,6 @@
"use strict"; "use strict";
function fromUTCToTimeZone(date, timeZone) { function toTimeZone(date, timeZone) {
// ISO string or existing date object // ISO string or existing date object
date = new Date(date); date = new Date(date);
var options = { var options = {
@ -15,12 +15,7 @@ function fromUTCToTimeZone(date, timeZone) {
fractionalSecondDigits: 3, fractionalSecondDigits: 3,
}; };
var tzOptions = Object.assign( var tzOptions = Object.assign({ timeZoneName: "long" }, options);
{
timeZoneName: "long",
},
options
);
// Every country uses the same year and months, right? // Every country uses the same year and months, right?
var formater = new Intl.DateTimeFormat("default", tzOptions); var formater = new Intl.DateTimeFormat("default", tzOptions);
@ -61,6 +56,11 @@ function fromUTCToTimeZone(date, timeZone) {
return whole; return whole;
} }
function toTimeZoneISOString(date, timeZone) {
var whole = toTimeZone(date, timeZone);
return toOffsetISOString(whole);
}
function _toOffsetISOString() { function _toOffsetISOString() {
return toOffsetISOString(this); return toOffsetISOString(this);
} }
@ -109,18 +109,63 @@ function toOffsetISOString(d) {
); );
} }
function fromZonedToUTC(dt, tz) {} function toUTC(dt, tz) {
if ("string" === typeof dt) {
// Either of these formats should work:
// 2021-03-14 01:15:59
// 2021-03-14T01:15:59Z
dt = dt
.replace("T", " ")
.replace("Z", "")
.replace(" ", "T")
.replace(/$/, "Z");
}
var utcDate = new Date(dt);
var tzD2 = toTimeZone(utcDate, tz);
var offset = tzD2.offset;
tzD2.offset = "";
var deltaDate = new Date(utcDate);
deltaDate.setUTCMinutes(deltaDate.getUTCMinutes() - offset);
var tzD3 = toTimeZone(deltaDate, tz);
if (
tzD3.hour === utcDate.getUTCHours() &&
tzD3.minute === utcDate.getUTCMinutes()
) {
return tzD3;
}
var diff = tzD3.offset - offset;
var h = Math.floor(Math.abs(diff) / 60);
var m = Math.abs(diff) % 60;
var sign = Math.abs(diff) / diff;
tzD3.hour -= h * sign;
tzD3.minute -= m * sign;
return tzD3;
}
function toUTCISOString(date, timeZone) {
var whole = toUTC(date, timeZone);
return toOffsetISOString(whole);
}
module.exports = { module.exports = {
// bespoke date => // bespoke date =>
// 2021-11-07T3:15:59-0500 // 2021-11-07T3:15:59-0500
toOffsetISOString: toOffsetISOString, toOffsetISOString: toOffsetISOString,
// -240 => -0400 // -240 => -0400
formatOffset: formatOffset, formatOffset: formatOffset,
// [ "2021-11-07T08:15:59Z", "America/New_York" ] // [ "2021-11-07T08:15:59Z", "America/New_York" ]
// => "2021-11-07T03:15:59-0500" // 2021-11-07 03:15:59 // => "2021-11-07T03:15:59-0500" // 2021-11-07 03:15:59
fromUTCToTimeZone: fromUTCToTimeZone, toTimeZone: toTimeZone,
toTimeZoneISOString: toTimeZoneISOString,
// [ "2021-11-07 03:15:59", "America/New_York" ] // [ "2021-11-07 03:15:59", "America/New_York" ]
// => "2021-11-07T03:15:59-0500" // 2021-11-07T08:15:59Z // => "2021-11-07T03:15:59-0500" // 2021-11-07T08:15:59Z
fromZonedToUTC: fromZonedToUTC, toUTC: toUTC,
toUTCISOString: toUTCISOString,
}; };

193
test.js
View File

@ -2,6 +2,29 @@
var TZ = require("./"); var TZ = require("./");
function testUtcToTz(t) {
var result = TZ.toTimeZone.apply(TZ, t.inputs).toISOString();
if (t.result !== result) {
throw new Error(
`Invalid UTC to TZ conversion for ${t.desc}:\n` +
`\tExpected: ${t.result}\n` +
`\tActual: ${result}\n`
);
}
}
function testTzToUtc(t) {
var result = TZ.toUTC.apply(TZ, t.inputs);
if (t.result !== result.toISOString()) {
console.log(result);
throw new Error(
`Invalid TZ to UTC conversion for ${t.desc}:\n` +
`\tExpected: ${t.result}\n` +
`\tActual: ${result.toISOString()}\n`
);
}
}
// At this real UTC time, what does the timezone translate it to? // At this real UTC time, what does the timezone translate it to?
[ [
// //
@ -21,19 +44,23 @@ var TZ = require("./");
// 12:15am NY -0500 => -0400 // 12:15am NY -0500 => -0400
{ {
desc: "UTC to 12:15am NY EST",
inputs: ["2021-03-14T05:15:59.000Z", "America/New_York"], inputs: ["2021-03-14T05:15:59.000Z", "America/New_York"],
result: "2021-03-14T00:15:59.000-0500", result: "2021-03-14T00:15:59.000-0500",
}, },
{ {
desc: "UTC to 12:15am NY EST (2)",
inputs: ["2021-03-14T00:15:59.000-0500", "America/New_York"], inputs: ["2021-03-14T00:15:59.000-0500", "America/New_York"],
result: "2021-03-14T00:15:59.000-0500", result: "2021-03-14T00:15:59.000-0500",
}, },
// 1:15am NY (non-DST) // 1:15am NY (non-DST)
{ {
desc: "UTC to 1:15am NY EST",
inputs: ["2021-03-14T06:15:59.000Z", "America/New_York"], inputs: ["2021-03-14T06:15:59.000Z", "America/New_York"],
result: "2021-03-14T01:15:59.000-0500", result: "2021-03-14T01:15:59.000-0500",
}, },
{ {
desc: "UTC to 1:15am NY EST (2)",
inputs: ["2021-03-14T01:15:59.000-0500", "America/New_York"], inputs: ["2021-03-14T01:15:59.000-0500", "America/New_York"],
result: "2021-03-14T01:15:59.000-0500", result: "2021-03-14T01:15:59.000-0500",
}, },
@ -42,19 +69,23 @@ var TZ = require("./");
// 3:15am NY (DST) // 3:15am NY (DST)
{ {
desc: "UTC to 3:15am NY EDT",
inputs: ["2021-03-14T07:15:59.000Z", "America/New_York"], inputs: ["2021-03-14T07:15:59.000Z", "America/New_York"],
result: "2021-03-14T03:15:59.000-0400", result: "2021-03-14T03:15:59.000-0400",
}, },
{ {
desc: "UTC to 3:15am NY EDT (2)",
inputs: ["2021-03-14T03:15:59.000-0400", "America/New_York"], inputs: ["2021-03-14T03:15:59.000-0400", "America/New_York"],
result: "2021-03-14T03:15:59.000-0400", result: "2021-03-14T03:15:59.000-0400",
}, },
// 4:15am NY // 4:15am NY
{ {
desc: "UTC to 4:15am NY EDT",
inputs: ["2021-03-14T08:15:59.000Z", "America/New_York"], inputs: ["2021-03-14T08:15:59.000Z", "America/New_York"],
result: "2021-03-14T04:15:59.000-0400", result: "2021-03-14T04:15:59.000-0400",
}, },
{ {
desc: "UTC to 4:15am NY EDT (2)",
inputs: ["2021-03-14T04:15:59.000-0400", "America/New_York"], inputs: ["2021-03-14T04:15:59.000-0400", "America/New_York"],
result: "2021-03-14T04:15:59.000-0400", result: "2021-03-14T04:15:59.000-0400",
}, },
@ -75,47 +106,57 @@ var TZ = require("./");
// 12:15am NY -0400 => -0500 // 12:15am NY -0400 => -0500
{ {
desc: "UTC to 2021 Nov 7, 12:15am NY EDT",
inputs: ["2021-11-07T04:15:59.000Z", "America/New_York"], inputs: ["2021-11-07T04:15:59.000Z", "America/New_York"],
result: "2021-11-07T00:15:59.000-0400", result: "2021-11-07T00:15:59.000-0400",
}, },
{ {
desc: "UTC to 2021 Nov 7, 12:15am NY EDT (2)",
inputs: ["2021-11-07T00:15:59.000-0400", "America/New_York"], inputs: ["2021-11-07T00:15:59.000-0400", "America/New_York"],
result: "2021-11-07T00:15:59.000-0400", result: "2021-11-07T00:15:59.000-0400",
}, },
// 1:15am NY (DST) -0400 // 1:15am NY (DST) -0400
// NOTE: 1:15am happens TWICE (with different offsets) // NOTE: 1:15am happens TWICE (with different offsets)
{ {
desc: "UTC to 2021 Nov 7, 1:15am NY EDT",
inputs: ["2021-11-07T05:15:59.000Z", "America/New_York"], inputs: ["2021-11-07T05:15:59.000Z", "America/New_York"],
result: "2021-11-07T01:15:59.000-0400", result: "2021-11-07T01:15:59.000-0400",
}, },
{ {
desc: "UTC to 2021 Nov 7, 1:15am NY EDT (2)",
inputs: ["2021-11-07T01:15:59.000-0400", "America/New_York"], inputs: ["2021-11-07T01:15:59.000-0400", "America/New_York"],
result: "2021-11-07T01:15:59.000-0400", result: "2021-11-07T01:15:59.000-0400",
}, },
// 1:15am NY (non-DST) -0500 // 1:15am NY (non-DST) -0500
{ {
desc: "UTC to 2021 Nov 7, 1:15am NY EST",
inputs: ["2021-11-07T06:15:59.000Z", "America/New_York"], inputs: ["2021-11-07T06:15:59.000Z", "America/New_York"],
result: "2021-11-07T01:15:59.000-0500", result: "2021-11-07T01:15:59.000-0500",
}, },
{ {
desc: "UTC to 2021 Nov 7, 1:15am NY EST (2)",
inputs: ["2021-11-07T01:15:59.000-0500", "America/New_York"], inputs: ["2021-11-07T01:15:59.000-0500", "America/New_York"],
result: "2021-11-07T01:15:59.000-0500", result: "2021-11-07T01:15:59.000-0500",
}, },
// 2:15am NY -0500 // 2:15am NY -0500
{ {
desc: "UTC to 2021 Nov 7, 2:15am NY EST",
inputs: ["2021-11-07T07:15:59.000Z", "America/New_York"], inputs: ["2021-11-07T07:15:59.000Z", "America/New_York"],
result: "2021-11-07T02:15:59.000-0500", result: "2021-11-07T02:15:59.000-0500",
}, },
{ {
desc: "UTC to 2021 Nov 7, 2:15am NY EST (2)",
inputs: ["2021-11-07T02:15:59.000-0500", "America/New_York"], inputs: ["2021-11-07T02:15:59.000-0500", "America/New_York"],
result: "2021-11-07T02:15:59.000-0500", result: "2021-11-07T02:15:59.000-0500",
}, },
// 3:15am NY // 3:15am NY
{ {
desc: "UTC to 2021 Nov 7, 3:15am NY EST",
inputs: ["2021-11-07T08:15:59.000Z", "America/New_York"], inputs: ["2021-11-07T08:15:59.000Z", "America/New_York"],
result: "2021-11-07T03:15:59.000-0500", result: "2021-11-07T03:15:59.000-0500",
}, },
{ {
desc: "UTC to 2021 Nov 7, 3:15am NY EST (2)",
inputs: ["2021-11-07T03:15:59.000-0500", "America/New_York"], inputs: ["2021-11-07T03:15:59.000-0500", "America/New_York"],
result: "2021-11-07T03:15:59.000-0500", result: "2021-11-07T03:15:59.000-0500",
}, },
@ -124,22 +165,152 @@ var TZ = require("./");
// Positive Offset Test // Positive Offset Test
// //
// 4:15am Colombo +0530 (not DST) // Colombo +0530 (not DST)
{ {
desc: "UTC to Asia/Colombo (1)",
inputs: ["2021-03-14T08:15:59.000Z", "Asia/Colombo"], inputs: ["2021-03-14T08:15:59.000Z", "Asia/Colombo"],
result: "2021-03-14T13:45:59.000+0530", result: "2021-03-14T13:45:59.000+0530",
}, },
{ {
desc: "UTC to Asia/Colombo (2)",
inputs: ["2021-03-14T13:45:59.000+0530", "Asia/Colombo"], inputs: ["2021-03-14T13:45:59.000+0530", "Asia/Colombo"],
result: "2021-03-14T13:45:59.000+0530", result: "2021-03-14T13:45:59.000+0530",
}, },
].forEach(function (t) { {
var result = TZ.fromUTCToTimeZone.apply(TZ, t.inputs).toISOString(); desc: "UTC to Asia/Colombo (3)",
if (t.result !== result) { inputs: ["2021-11-07T08:15:59.000Z", "Asia/Colombo"],
throw new Error( result: "2021-11-07T13:45:59.000+0530",
"Invalid Conversion:\n" + },
`\tExpected: ${t.result}\n` + {
`\tActual: ${result}\n` desc: "UTC to Asia/Colombo (4)",
); inputs: ["2021-11-07T13:45:59.000+0530", "Asia/Colombo"],
} result: "2021-11-07T13:45:59.000+0530",
}); },
].forEach(testUtcToTz);
console.info("Pass: UTC to TZ for America/New_York and Asia/Colombo");
[
//
// Start-of-DST Tests
//
// [Start]
// What time is '2021-03-14 01:15:59.000 in New York' in UTC? // 2021-03-14 06:15:59.000
// // 2021-03-14T01:15:59.000-0500
// What time is '2021-03-14 02:15:59.000 in New York' in UTC? // 2021-03-14 07:15:59.000
// // 2021-03-14T03:15:59.000-0400
// What time is '2021-03-14 03:15:59.000 in New York' in UTC? // 2021-03-14 07:15:59.000
// // 2021-03-14T03:15:59.000-0400
// What time is '2021-03-14 04:15:59.000 in New York' in UTC? // 2021-03-14 08:15:59.000
// // 2021-03-14T04:15:59.000-0400
// [End]
// 12:15am NY -0500 => -0400
{
// 2021-03-14T05:15:59.000Z
desc: "2021 Mar 14, 12:15am NY EST to UTC",
inputs: ["2021-03-14 00:15:59.000", "America/New_York"],
result: "2021-03-14T00:15:59.000-0500",
},
// 1:15am NY (non-DST)
{
// 2021-03-14T06:15:59.000Z
desc: "2021 Mar 14, 1:15am NY EST to UTC",
inputs: ["2021-03-14 01:15:59.000", "America/New_York"],
result: "2021-03-14T01:15:59.000-0500",
},
// NOTE: Can't 2:15am NY, because it does not exist (skipped by DST)
// This test is here to document the "undefined" behavior
{
// Both 2021-03-14T06:15:59.000Z and 2021-03-14T07:15:59.000Z
// would be reasonable substitutions, I think
desc: "2021 Mar 14, 2:15am NY ExT to UTC",
inputs: ["2021-03-14 02:15:59.000", "America/New_York"],
//result: "2021-03-14T01:15:59.000-0500", // 2021-03-14T06:15:59.000Z
result: "2021-03-14T02:15:59.000-0400", // 2021-03-14T06:15:59.000Z
//result: "2021-03-14T02:15:59.000-0500", // 2021-03-14T07:15:59.000Z
},
// 3:15am NY (DST)
{
// 2021-03-14T07:15:59.000Z
desc: "2021 Mar 14, 3:15am NY EDT to UTC",
inputs: ["2021-03-14 03:15:59.000", "America/New_York"],
result: "2021-03-14T03:15:59.000-0400",
},
// 4:15am NY
{
// 2021-03-14T08:15:59.000Z
desc: "2021 Mar 14, 4:15am NY EDT to UTC",
inputs: ["2021-03-14 04:15:59.000", "America/New_York"],
result: "2021-03-14T04:15:59.000-0400",
},
//
// End-of-DST Tests
//
// [Start]
// What time is '2021-11-07 01:15:59.000 in New York' in UTC? // 2021-11-07 05:15:59.000
// // 2021-11-07T01:15:59.000-0400
// // 2021-11-07 06:15:59.000
// // 2021-11-07T01:15:59.000-0500
// What time is '2021-11-07 02:15:59.000 in New York' in UTC? // 2021-11-07 07:15:59.000
// // 2021-11-07T02:15:59.000-0500
// What time is '2021-11-07 03:15:59.000 in New York' in UTC? // 2021-11-07 08:15:59.000
// [End]
// 12:15am NY -0400 => -0500
{
// 2021-11-07T04:15:59.000Z
desc: "2021 Nov 7, 12:15am NY EDT to UTC",
inputs: ["2021-11-07 00:15:59.000", "America/New_York"],
result: "2021-11-07T00:15:59.000-0400",
},
// 1:15am NY (DST) -0400
// NOTE: 1:15am happens TWICE (with different offsets), so we skip one
{
// ==> 2021-11-07T05:15:59.000Z
// [Skip] 2021-11-07T06:15:59.000Z
desc: "2021 Nov 7, 1:15am NY ExT to UTC",
inputs: ["2021-11-07 01:15:59.000", "America/New_York"],
result: "2021-11-07T01:15:59.000-0400", // 2021-11-07T05:15:59.000Z
//result: "2021-11-07T01:15:59.000-0500", // 2021-11-07T06:15:59.000Z
},
// 2:15am NY -0500
{
// 2021-11-07T07:15:59.000Z
desc: "2021 Nov 7, 2:15am NY EST to UTC",
inputs: ["2021-11-07 02:15:59.000", "America/New_York"],
result: "2021-11-07T02:15:59.000-0500",
},
// 3:15am NY
{
// 2021-11-07T08:15:59.000Z
desc: "2021 Nov 7, 3:15am NY EST to UTC",
inputs: ["2021-11-07T03:15:59.000", "America/New_York"],
result: "2021-11-07T03:15:59.000-0500",
},
//
// Positive Offset Test
//
// Colombo +0530 (not DST)
{
// 2021-03-14T08:15:59.000Z
desc: "Asia/Colombo to UTC (1)",
inputs: ["2021-03-14 13:45:59.000", "Asia/Colombo"],
result: "2021-03-14T13:45:59.000+0530",
},
{
// 2021-03-14T08:15:59.000Z
desc: "Asia/Colombo to UTC (2)",
inputs: ["2021-11-07 13:45:59.000", "Asia/Colombo"],
result: "2021-11-07T13:45:59.000+0530",
},
].forEach(testTzToUtc);
console.info("Pass: TZ to UTC for America/New_York and Asia/Colombo");