Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

User-friendly time zone names corresponding to IANA names

I am attempting to find a user-friendly list of time zones for users to select their current time zone. It would have common names that time zones that are referred to such as Pacific Time (US and Canada).

This list should map the text to an IANA standard name such as America/Los_Angeles.

It would also remove listings that the user thinks of as the "same" time zone. For example the listing for Pacific Time (US and Canada) would correspond to America/Los_Angeles and America/Tijuana, which are the "same" time.

I've tried looking in the IANA Time Zone Database, ECMA Script internationalization API, and libraries like Moment.js. They can parse the IANA name into the common name, for example America/Los_Angeles ==> "Pacific Standard Time" but don't have a built-in function for listing all the common names and their corresponding IANA names.

like image 808
nth-child Avatar asked Oct 15 '22 04:10

nth-child


People also ask

What is the TZ name from the IANA Time Zone Database?

The tz database is also known as tzdata, the zoneinfo database or IANA time zone database, and occasionally as the Olson database, referring to the founding contributor, Arthur David Olson.

How many IANA time zones are there?

There are total of 422 time zones listed.

How do you list different time zones?

Reference to a specific time and zone would follow standard guidelines with the zone in parentheses: 4:42 p.m. (PST), 11:03 a.m. (MDT), 2:30 p.m. (CST), 10:00 P.M. (EST). AP on the other hand advises to capitalize the full name of each time zone: Pacific/Mountain/Central/Eastern Standard Time.


3 Answers

You can use Intl.DateTimeFormat.prototype.formatToParts().

new Intl.DateTimeFormat(
    'default',
    { timeZone: 'America/Los_Angeles', timeZoneName: 'long' }
).formatToParts().find(({ type }) => type === 'timeZoneName').value
// 'Pacific Daylight Time'
like image 139
kimamula Avatar answered Nov 05 '22 09:11

kimamula


The short answer is there is no simple map from IANA representative location names to common names because the IANA name doesn't reflect historic changes in offset or daylight saving, nor are common names sufficiently standardised either officially or de facto.

Long answer

IANA representative locations include historic changes in timezone offset, both for the local time and daylight saving as it has been applied over the years. The standard timezone name probably hasn't changed, however the offsets have and continue to. Also, the IANA representative location name stays the same during daylight saving, however the offset and common name don't.

E.g. Samoa has an IANA name of "Pacific/Samoa". Its standard offset prior to 29 December 2011 was UTC−11:00 but after then its offset has been UTC+13:00, and its daylight saving time went from UTC-1000 to UTC+14:00. The common names for its timezone are "Samoa Standard Time" and "Samoa Daylight Saving Time", however these names aren't standardised and in many places are ambiguous (e.g. CST might be central standard time in 2 or 3 different places, or China standard time).

So there is no simple map from IANA representative location name to common timezone name for all IANA locations. You may be able to create one for a small subset, however using the built–in Intl object is likely simpler, e.g.

let opts = {
  hour: 'numeric',
  minute:'2-digit',
  timeZone: 'Pacific/Samoa',
  timeZoneName: 'long'
};
let time = new Date().toLocaleString('en', opts);
console.log('Currently in Samoa it\'s ' + time);

Note

On the latest version of MacOS, the above gives an offset for Samoa of UTC-1100 so still using old values, and it doesn't recognise daylight saving so the Intl object can't be relied upon.

You might use the Intl object as below, noting that for some locations it may not have a name for the timezone, so just shows the offset (e.g. try Europe/Astrakhan). There are nearly 600 IANA locations.

// Detect user representative location if available
let repLoc = (typeof Intl == 'object' && 
              typeof Intl.DateTimeFormat == 'function' &&
              typeof Intl.DateTimeFormat().resolvedOptions == 'function')?
              Intl.DateTimeFormat().resolvedOptions().timeZone : 'default';

function setRepLoc(evt) {
  showSampleDates(this.value);
}

function showSampleDates(repLoc) {
  let el0 = document.getElementById('sDate0');
  let el1 = document.getElementById('sOff0');
  let el2 = document.getElementById('sDate1');
  let el3 = document.getElementById('sOff1');

  let d0 = new Date(Date.UTC(2020,0,1,11));
  let d1 = new Date(Date.UTC(2020,5,1,11));
  let opts = {
    weekday: 'short',
    day: 'numeric',
    month: 'short',
    year: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
    timeZone: repLoc,
    timeZoneName: 'long'
  };
  let offOpts = {
    hour: 'numeric',
    hour12: false,
    timeZone: repLoc,
    timeZoneName: 'short'
  }
  if (repLoc == 'default') {
    el0.textContent = '';
    el1.textContent = '';
    el2.textContent = '';
    el3.textContent = '';
  } else {
    el0.textContent = d0.toLocaleString('en-NZ', opts);
    el1.textContent = d0.toLocaleString('en-NZ', offOpts).split(' ')[1];
    el2.textContent = d1.toLocaleString('en-NZ', opts);
    el3.textContent = d1.toLocaleString('en-NZ', offOpts).split(' ')[1];
  }
}

window.onload = function(){
  let sel = document.getElementById('repLoc')
  if (repLoc) {
    sel.options[0].text = repLoc;
    sel.options[0].value = repLoc;
    sel.selectedIndex = 0;
    setRepLoc.call(sel);
  }
  sel.addEventListener(
    'change',
    setRepLoc,
    false);
};
td:nth-child(1){text-align:right;color:#999;}
td:nth-child(2){font-family:monospace}
<table>
  <tr>
    <td>IANA loc:
    <td><select id="repLoc">
          <option selected value="default">Select a location
          <option>Europe/Amsterdam
          <option>Europe/Andorra
          <option>Europe/Astrakhan
          <option>Europe/Athens
          <option>Europe/Belfast
          <option>Pacific/Samoa
          <option>Pacific/Kiritimati
          <option>Antarctica/DumontDUrville
          <option>Antarctica/Davis
          <option>America/Chicago
          <option>Australia/Brisbane
        </select>
  <tr>
    <td>Sample:
    <td id="sDate0">
  <tr>
    <td>Offset:
    <td id="sOff0">
  <tr>
    <td>Sample:
    <td id="sDate1">
  <tr>
    <td>Offset:
    <td id="sOff1">

</table>
like image 42
RobG Avatar answered Nov 05 '22 08:11

RobG


Building on Kimamula's answer, I took the current list of IANA time zone names and ran it through my browser's Intl formatter to obtain long and short names for all of them. The result is a gist with a mapping of IANA names to human readable names. I've taken the 'generic' forms of these names, which omits mention of daylight saving time. Otherwise we would need to know whether DST is in effect to select the name. (e.g. Pacific Time instead of Pacific Standard Time or Pacific Daylight Time).

Not every timezone has an ideal name in this database, as with most things time related this will be an imperfect solution. But it will hopefully cover the time zones that are most popular with your users, and displaying an IANA name as a fallback is an option.

In case you want to generate these names yourself, you can run this in your browser. These names came from Firefox, I would guess that they are implementation dependent and might be a bit different in other browsers.


const iana_names = [
  "Africa/Abidjan",
  "Africa/Accra",
  "Africa/Addis_Ababa",
  // ...
  "W-SU",
  "WET",
  "Zulu"
]

const mapping = {}

iana_names.forEach( name => {

   const long = new Intl.DateTimeFormat('default', { timeZone: name, timeZoneName: 'longGeneric' }).formatToParts().find(({ type }) => type === 'timeZoneName').value
   const short = new Intl.DateTimeFormat('default', { timeZone: name, timeZoneName: 'shortGeneric' }).formatToParts().find(({ type }) => type === 'timeZoneName').value

   mapping[name] = {
     long, short
   }

})
like image 28
Pathogen Avatar answered Nov 05 '22 08:11

Pathogen