Easiest way to convert UTC to New Zealand date (account for daylight savings)?

Viewed 1778

I have the following function which I've written to convert msSinceEpoch to the New Zealand Date (IE11 Compatible)...

const MAGICNUMBER = 13;
const toNewZealand = (msSinceEpoch) => {
    const [day, month, year, time] = new Date(msSinceEpoch).toLocaleString("en-NZ", {
      hour12: false, timeZone: "UTC"
    }).split(/[/,]/); // timeZone UTC is the only format support on IE11
    const [hours, minutes, seconds] = time.trim().split(":");
    return new Date(~~year, ~~month - 1, ~~day, ~~hours + MAGICNUMBER, ~~minutes, ~~seconds)
  };

// usage....
console.log(
  toNewZealand(new Date().getTime())
)

However, this contains a magic number which is not relative to New Zealand's daylight savings time (+12 or +13).

So here it gets complicated, how do I get the right number relative to daylight savings in New Zealand (+12 or +13).

My initial attempt was just to calculate whether it was in between the last Sunday of September or first Sunday of April but then I realised that the second I use a new Date() constructor anywhere in the code it's going to create a date relative to their system time and break the math.

TL;DR Convert UTC Milliseconds since epoch to New Zealand Time that works with New Zealand's Daylight savings settings.

EDIT: Also not interested in using Moment or any other library to solve this problem due to bundle size costs.

2 Answers

TL;DR Convert UTC Milliseconds since epoch to New Zealand Time...

I think OP has a misunderstanding of what time conversion is. MS Since Epoch are always in UTC. Time conversation changes the display format of the time, and should not change msSinceEpoch

You can use toLocalString() and pass the desired timezone (Pacific/Auckland) to convert the display format of your DateTime:

const date = new Date();
console.log(
  date.toLocaleString('en-NZ', {
    timeZone: 'Pacific/Auckland',
  }),
);

Solved it, tried to keep it as human readable as possible, basically tries to add 12 or 13 depending on where the current UTC DateTime lies, if by adding 12 or 13 we fall into the next daylight savings period we add the alternate instead.... IE if by adding 12 we fall into +13 territory.... add +13 instead. IE if by adding +13 we fall into +12 territory.... add +12 instead.

New Zealand's daylight savings time changes on the last sunday of september and the first sunday of April.

This is the solution....

const UTCFromMS = (ms) => {
  return new Date(new Date(ms).toUTCString().replace(" GMT", ""))
};

const addHours = (dte, hrs) => {
        return new Date(
            dte.getFullYear(),
            dte.getMonth(),
            dte.getDate(),
            dte.getHours() + hrs,
            dte.getMinutes(),
            dte.getMilliseconds()
        );
};

const toNewZealand = (ms) => {
        return addNewZealandDaylightSavings(UTCFromMS(ms));
};

const getPreviousSunday = (dte) => {
    return new Date(
        dte.getFullYear(),
        dte.getMonth(),
        dte.getDate() - dte.getDay(),
        1,
        0,
        0
    );
};

const getNextSunday = (dte) => {
    return new Date(
        dte.getFullYear(),
        dte.getMonth(),
        dte.getDay() === 0 ? dte.getDate() : dte.getDate() + (7 - dte.getDay()),
        1,
        0,
        0
    )
};

const standardHours = 12;
const daylightHours = 13;
const addNewZealandDaylightSavings = (dte) => {
    const lastSundaySeptember = getPreviousSunday(
        new Date(dte.getFullYear(), 8, 30)
    );

    const firstSundayApril = getNextSunday(
            new Date(dte.getFullYear(), 3, 1)
    );

    // If its before firstSundayApril, add 13, if we went over 1am, add 12.
    if(dte <= firstSundayApril) {
        const daylightNz = addHours(dte, daylightHours);
        if(daylightNz >= firstSundayApril) {
            return addHours(dte, standardHours);
        }
        return daylightNz
    }

    // if its before lastSundaySeptember, add 12 if we went over 1am add 13.
    if(dte <= lastSundaySeptember) {
        const standardNz = addHours(dte, standardHours);
        if(standardNz >= lastSundaySeptember) {
            return addHours(dte, daylightHours);
        }
        return standardNz;
    }
    return addHours(dte, daylightHours);
};

console.log(toNewZealand(new Date().getTime()).toString());
// the above line should always output the current DateTime in New Zealand, replace the argument with any epoch milliseconds and it should still always give you the correct time.

===EDIT====

The above answer has since become less relevant as now there's way to do this that are part of the web standard.

Date.prototype.toLocaleString AND timeZone: "Pacific/Auckland"

Is one of the simplest way's to convert dates today, you can read about it more on MDN here:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString

Related