Get Timezone/Offset via City State and convert ISO-8601 UTC Date

1k Views Asked by At

I am trying to update a quoting system to show time left until a quote expires.

Admins enter a quote for a user and within all the data there is a date, time, city and state. Admins are located around the USA and the quotes are entered as if they are the user (different timezones are possible so we format as UTC).

It is formatted as such: 2022-05-30T05:15:00.000Z . This is a due date of 5/30/22 5:15am.

The problem is this specific expiration is for a user in Las Vegas, NV. There have been instances where communication was confusing because time zones were forgot about and quotes were either pre-maturely or post-maturely closed.

What I think I need to do is use the city/state combo to identify the timezone offset and save a separate date time variable in the account.

My problem is getting the two dates formatted/converted into the proper timezones.

This is what I am trying now:

Using https://www.npmjs.com/package/city-timezones

//use cityTimeZones to get the timezone for the quoted city.
const cityLookup = cityTimeZones.lookupViaCity("Las Vegas")


//convert the UTC time to the offsetted time that matches the city/state
const dateLocal = formatDate('2022-05-30T05:15:00.000Z', 'yyyy-MM-dd HH:mm:ss zzzz', 'en-US', cityLookup[0].timezone ) //cityLookup[0].timezone = America/Los_Angeles
//result ---- dateLocal = '2022-05-30 02:45:00 GMT-04:00'


//convert the city/state time into the device location time
const dateHere = formatDate(dateLocal, 'yyyy-MM-dd HH:mm:ss zzzz', 'en-US', Intl.DateTimeFormat().resolvedOptions().timeZone ) // Intl.DateTimeFormat().resolvedOptions().timeZone = America/New_York
//result ---- dateHere = '2022-05-30 02:45:00 GMT-04:00'  (same date is coming back)

//use the device location time to display a time until expiration countdown
let timeUntil = moment(dateHere).fromNow();

I have also tried:

var d1 = new Date('2022-05-30T05:15:00.000Z');
d1.toLocaleString('en-US', { timeZone: cityLookup[0].timezone }); //get leg date/time in the quoted timezone -- America/Los_Angeles
//result --- d1 = Sun May 29 2022 23:15:00 GMT-0400 (Eastern Daylight Time)

var d2 = new Date(d1);
d2.toLocaleString('en-US', { timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone }); //get leg date/time in the device timezone -- America/New_York
//result --- d2 = Sun May 29 2022 23:15:00 GMT-0400 (Eastern Daylight Time)

//use the device location time to display a time until expiration countdown
let timeUntil = moment(d2).fromNow();
1

There are 1 best solutions below

0
On

A few things:

  • You're seeing the date reflected in your own time zone, because Angular's formatDate function does not work with IANA time zone identifiers. Instead, try Intl.toLocaleString with the timeZone option, or a modern library like Luxon.

  • You don't need to convert to other time zones for your timeUntil value. Just use the UTC time as it is given in your input. Also, be aware of Moment's project status. Both Luxon and date-fns have functions you could use instead, or there's Intl.RelativeTimeFormat.

  • Be careful with resolving time zones from a city alone. Many cities names are reused in many places around the world. (Try passing "Springfield" to city-timezones and you'll see.) Additionally, city-timezones only has major cities. It won't resolve smaller cities and suburbs. You'll also likely run into localization issues.

    A better approach would be to use a geolocation API, such as those available from Google, Microsoft and others. With such APIs, you can look up a complete address (not just the city) to its approximate latitude and longitude. Then you can use one of these methods to get the IANA time zone identifier. Even better, some geolocation APIs will already have the time zone identifier in their result.

    Alternatively, you could gather the time zone from a list when you first create your data, saving the time zone identifier with the quote. Then you wouldn't need to look it up at all.