Luxon: retrieve an array of time offsets and time zones (similar to what can be done in moment-timezone)

Currently, I am using the moment-timezone library to retrieve raw information for a specific timezone and then incorporating it directly into my downstream code.

const zone = moment.tz.zone('Europe/London');

This data contains:

{
    "name":"Europe/London",
    "abbrs":["GMT","BST","GMT","BST", ...],
    "untils":[-1691964000000,-1680472800000,-1664143200000,-1650146400000, ...],
    "offsets":[0,-60,0,-60,0, ...],
    "population":10000000
}

For my purposes, I only require the offsets and untils arrays from moment, as I manage the remaining aspects on my own in the downstream code by utilizing these arrays (or a subset of them)... all without depending on moment.

Is there a similar method to extract the offsets and untils arrays in the luxon library?

Answer №1

After reviewing @VincenzoC's comment, I have come up with a simple approach to calculate offsets and untils (as per the moment-timezone library) for a given time range. This method assumes that offsets change only on the hour, allowing us to iterate through the time range in one-hour increments.

import { IANAZone } from 'luxon';

const getZoneInfo = function (id, startTime, endTime) {
    startTime = Math.floor(startTime / 36e5) * 36e5; // adjust to the previous whole hour
    endTime = Math.ceil(endTime / 36e5) * 36e5;      // adjust to the next whole hour
    const increment = 36e5;                         // one-hour increment
    const zone = IANAZone.create(id);
    const offsets = [];
    const untils = [];
    let offset, prevOffset;
    for (let time = startTime; time <= endTime; time += increment) {
        offset = zone.offset(time);
        if (prevOffset != undefined && offset != prevOffset || time == endTime) {
            offsets.push(-prevOffset);
            untils.push(time);
        }
        prevOffset = offset;
    }
    const zoneInfo = {
        id: id,
        offsets: offsets,
        untils: untils
    };
    console.log('zoneInfo', zoneInfo);
    return zoneInfo;
};

const startTime = 1646931245000;
const endTime = 1650473645000;
getZoneInfo('Europe/London', startTime, endTime);
getZoneInfo('Europe/Stockholm', startTime, endTime);
getZoneInfo('Asia/Tokyo', startTime, endTime);
getZoneInfo('America/New_York', startTime, endTime);

Output:

zoneInfo {
  id: 'Europe/London',
  offsets: [ -0, -60 ],
  untils: [ 1648342800000, 1650474000000 ]
}
zoneInfo {
  id: 'Europe/Stockholm',
  offsets: [ -60, -120 ],
  untils: [ 1648342800000, 1650474000000 ]
}
zoneInfo {
  id: 'Asia/Tokyo',
  offsets: [ -540 ],
  untils: [ 1650474000000 ]
}
zoneInfo {
  id: 'America/New_York',
  offsets: [ 300, 240 ],
  untils: [ 1647154800000, 1650474000000 ]
}

The last until value does not indicate an actual offset change but ensures full coverage of the specified time range up to endTime.

UPDATE

Here is an alternative version of the above code, which aims to be more efficient (especially for larger time ranges) by iterating in full days and switching to hourly increments only when necessary to identify offset changes accurately:

import { IANAZone } from 'luxon';

const getZoneInfo = function (id, startTime, endTime) {
    const zone = IANAZone.create(id);
    const offsets = [];
    const untils = [];
    startTime = Math.floor(startTime / 36e5) * 36e5;   // move to the previous whole hour
    endTime = Math.ceil(endTime / 36e5) * 36e5;        // move to the next whole hour
    const stepSmall = 36e5;                     // one-hour increment
    const stepLarge = 24 * 36e5;                // one-day increment
    let step = stepLarge;                       // start with the larger increment for speed
    let offset, prevOffset, offsetChanged;
    let time = startTime;
    offset = prevOffset = zone.offset(time);
    do {
        time = Math.min(time + step, endTime);
        offset = zone.offset(time);
        offsetChanged = (offset != prevOffset);
        if (offsetChanged || time == endTime) {
            if (offsetChanged && step == stepLarge) {
                // go back and switch to the smaller increment for better transition accuracy...
                time = time - stepLarge;
                step = stepSmall;
                continue;
            } else {
                offsets.push(-prevOffset);
                untils.push(time);
                // revert to the larger increment...
                step = stepLarge;
            }
        }
        prevOffset = offset;

    } while (time < endTime);

    const zoneInfo = {
        id: id,
        offsets: offsets,
        untils: untils
    };
    console.log('zoneInfo', zoneInfo);

    return zoneInfo;
};


const startTime = 1608020493000;
const endTime = 1734250893000;
getZoneInfo('Europe/London', startTime, endTime);
getZoneInfo('Europe/Stockholm', startTime, endTime);
getZoneInfo('Asia/Tokyo', startTime, endTime);
getZoneInfo('America/New_York', startTime, endTime);

Output:

zoneInfo {
  id: 'Europe/London',
  offsets: [
    -0, -60, -0, -60,
    -0, -60, -0, -60,
    -0
  ],
  untils: [
    1616893200000,
    1635642000000,
    1648342800000,
    1667091600000,
    1679792400000,
    1698541200000,
    1711846800000,
    1729990800000,
    1734253200000
  ]
}
zoneInfo {
  id: 'Europe/Stockholm',
  offsets: [
     -60, -120,  -60,
    -120,  -60, -120,
     -60, -120,  -60
  ],
  untils: [
    1616893200000,
    1635642000000,
    1648342800000,
    1667091600000,
    1679792400000,
    1698541200000,
    1711846800000,
    1729990800000,
    1734253200000
  ]
}
zoneInfo {
  id: 'Asia/Tokyo',
  offsets: [ -540 ],
  untils: [ 1734253200000 ]
}
zoneInfo {
  id: 'America/New_York',
  offsets: [
    300, 240, 300,
    240, 300, 240,
    300, 240, 300
  ],
  untils: [
    1615705200000,
    1636264800000,
    1647154800000,
    1667714400000,
    1678604400000,
    1699164000000,
    1710054000000,
    1730613600000,
    1734253200000
  ]
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

issue with JavaScript canvas

My task is to develop a Blackberry application, but I have limited knowledge in Java. Since the application requires drawing capabilities, I decided to use HTML5 and JavaScript instead. I started reading some JavaScript tutorials to prepare for this proj ...

send multiple textbox values to controller in CodeIgniter

I am new to Codeigniter and I'm facing some difficulties in understanding how to accomplish a task. In my view page, there are five rows generated using a for loop. Each row consists of two select boxes and two input boxes. I don't know how to re ...

Disregard Cloudflare's Automatic RocketLoader feature for certain JavaScript scripts

After extensive research and failed attempts, I am seeking help to disable Cloudflare Rocketloader for a specific JavaScript file on my WordPress website. Specifically, I need to exclude the Automatic Rocket Loader for a particular .js file. I attempted t ...

What is the process for inserting a key value pair into a JSON object?

I am looking to enhance my JSON data by including a key-value pair in each object within the array. My goal is to insert a key-value pair into every object in the students array. ...

Regular expression patterns for authenticating and verifying passwords

Currently, I am working on a JavaScript program to validate passwords using regex. Here are the requirements: The password must consist of at least seven characters. It should include at least one of the following: an uppercase letter (A-Z) a lowercas ...

JSON - The challenge of incorporating single quotes within double quotes

In my current scenario, I am using the following code to populate form fields. The code is designed to handle a JSON dataset that has been encoded with PHP's json_encode function. Everything works smoothly when dealing with either single or double qu ...

Experimenting with a function that initiates the downloading of a file using jest

I'm currently trying to test a function using the JEST library (I also have enzyme in my project), but I've hit a wall. To summarize, this function is used to export data that has been prepared beforehand. I manipulate some data and then pass it ...

Clearing selections from a multiple-choice input field

I am seeking to implement a delete feature in a multi-select element similar to the functionality seen here on stackoverflow when selecting multiple tags for a question. Once an item is selected, I would like to display a close icon next to it so that user ...

react-router is unable to navigate to the desired page

I am currently using react-router-dom in my project, but I am still relatively new to it. I have encountered an issue regarding page navigation. Below is the code for my App.js: class App extends Component { render() { return ( <div classN ...

Two unnamed objects cannot be combined using the AsyncPipe

Currently, I am looking to implement an autocomplete feature using Angular Material in Angular 8. Below is a snippet of the code used in the TypeScript file: @Input() admins: User[]; userGroupOptions: Observable<User[]>; filterFormFG: FormGrou ...

Changing the Value of an Input Element Dynamically in React: A Step-by-Step Guide

In a scenario where I have a component that takes an element, such as <input />, and I need to update its value programmatically after 15 seconds. Initially, I had the following approach in mind: const MyComponent = (myInput: JSX.Element) => { ...

ngRepeat does not completely refresh the DOM when dealing with a basic array

I have a simple array of numbers shown below using ng-repeat: n = [1,2,3,4,5,6] The problem arises when I modify this array, for example: n=[1,2,3] Instead of fully reloading the DOM, only the last 3 div elements corresponding to array 4, 5, 6 are remo ...

How can you append an object with a defined key to an array in Vue?

Currently developing a vue-application that includes a component for managing driving licenses. Here is an overview of my data setup: data() { return { custom_licenses: [], basic_licenses: [] } } Within my methods, I have the following l ...

The condition in a Typescript function that checks for strings will never evaluate to true

I encountered a strange issue with a TypeScript condition in a function. Here is my current code, where the parameters are passed from outside: getLevel(validation: string, status: string): string { let card = ""; if (validation == &qu ...

Extract a property from a JSON object

Is there a way to access the href properties and use them to create multiple img elements with their sources set as the extracted href properties? I'm looking for a solution in either javascript or jQuery. I attempted the following code, but it didn& ...

Is there a way for me to store the retrieved information from an API into a global variable using Node.js?

function request2API(option){ const XMLHttpRequest = require('xhr2');//Cargar módulo para solicitudes xhr2 const request = new XMLHttpRequest(); request.open('GET', urlStart + ChList[option].videosList + keyPrefix + key); request. ...

The error message "TypeError: undefined in JavaScript Vue's V-for loop

I created a function that generates an array of objects in this format: getMonthDaysGrid() { const totalLastMonthDays = this.getFirstDayOfMonth().dayNumber; const totalDays = this.month.numberOfDays + totalLastMonthDays; const monthList = Array ...

The reactivity of VUE 3 arrays is not being updated, but individual array elements accessed using array

Currently facing an issue while trying to connect a dynamically updating array of objects to a Konva circle. The circles are appearing as expected, but the problem arises within a for loop where I update player locations based on a "tick". While setting th ...

Experiencing an issue in Test Cafe when attempting to click on an invisible link using the Client Function

I need to find a way to click on an invisible button in HTML. I attempted to use ClientFunction, however I encountered an error related to the element. import { Selector,ClientFunction } from 'testcafe'; fixture('Clicking Invisible link&apo ...

Using jQuery's toggle function with a double click event to change the display to none

A div I created has the ability to expand to full screen upon double click, and I now wish to implement a toggle function so it can return to its original size when double clicked again. Initially, the code successfully increased the size of the div. Howe ...