/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ const TIMEZONES = { IDLW: -12, NT: -11, HST: -10, AKST: -9, PST: -8, AKDT: -8, MST: -7, PDT: -7, CST: -6, MDT: -6, EST: -5, CDT: -5, EDT: -4, AST: -4, GUY: -3, ADT: -3, AT: -2, UTC: 0, GMT: 0, Z: 0, WET: 0, WEST: 1, CET: 1, BST: 1, IST: 1, CEST: 2, EET: 2, EEST: 3, MSK: 3, MSD: 4, ZP4: 4, ZP5: 5, ZP6: 6, WAST: 7, AWST: 8, WST: 8, JST: 9, ACST: 9.5, ACDT: 10.5, AEST: 10, AEDT: 11, NZST: 12, IDLE: 12, NZD: 13, }; const TIME_REGEX = "([0-2]?[0-9])(:([0-5][0-9]))?\\s*([ap]m)?"; const TIMEZONE_REGEX = "\\w+"; // NOTE: This regex need to be localized upon supporting multi locales // since it supports en-US input format only. const QUERY_REGEX = new RegExp( `^(${TIME_REGEX}|now)\\s*(${TIMEZONE_REGEX})?(?:\\s+in\\s+|\\s+to\\s+|\\s*=\\s*)(${TIMEZONE_REGEX}|here)`, "i" ); const KEYWORD_HERE = "HERE"; const KEYWORD_NOW = "NOW"; /** * This module converts timezone. */ export class UnitConverterTimezone { /** * Convert the given search string. * * @param {string} searchString * The string to be converted * @returns {string} conversion result. */ convert(searchString) { const regexResult = QUERY_REGEX.exec(searchString); if (!regexResult) { return null; } const inputTime = regexResult[1].toUpperCase(); const inputTimezone = regexResult[6]?.toUpperCase(); let outputTimezone = regexResult[7].toUpperCase(); if ( (inputTimezone && inputTimezone !== KEYWORD_NOW && !(inputTimezone in TIMEZONES)) || (outputTimezone !== KEYWORD_HERE && !(outputTimezone in TIMEZONES)) ) { return null; } const inputDate = new Date(); let isMeridiemNeeded = false; if (inputTime === KEYWORD_NOW) { inputDate.setUTCHours(inputDate.getHours()); inputDate.setUTCMinutes(inputDate.getMinutes()); } else { // If the input was given as AM/PM, we need to convert it to 24h. // 12AM is converted to 00, and for PM times we add 12 to the hour value except for 12PM. // If the input is for example 23PM, we use 23 as the hour - we don't add 12 as this would result in a date increment. const inputAMPM = regexResult[5]?.toLowerCase() || ""; const inputHours = regexResult[2] === "12" && inputAMPM === "am" ? 0 : Number(regexResult[2]); const inputMinutes = regexResult[4] ? Number(regexResult[4]) : 0; const inputMeridianHourShift = inputAMPM === "pm" && inputHours < 12 ? 12 : 0; inputDate.setUTCHours(inputHours + inputMeridianHourShift); inputDate.setUTCMinutes(inputMinutes); isMeridiemNeeded = !!inputAMPM; } const inputOffset = inputTimezone ? TIMEZONES[inputTimezone] * 60 : -inputDate.getTimezoneOffset(); let outputOffset; if (outputTimezone === KEYWORD_HERE) { outputOffset = -inputDate.getTimezoneOffset(); const sign = -inputDate.getTimezoneOffset() > 0 ? "+" : "-"; const hours = parseInt(Math.abs(outputOffset) / 60); const minutes = formatMinutes((outputOffset % 60) * 60); outputTimezone = `UTC${sign}${hours}${minutes}`; } else { outputOffset = TIMEZONES[outputTimezone] * 60; } const outputDate = new Date(inputDate.getTime()); outputDate.setUTCMinutes( outputDate.getUTCMinutes() - inputOffset + outputOffset ); const time = new Intl.DateTimeFormat("en-US", { timeStyle: "short", hour12: isMeridiemNeeded, timeZone: "UTC", }).format(outputDate); return `${time} ${outputTimezone}`; } } function formatMinutes(minutes) { return minutes.toString().padStart(2, "0"); }