0

Let's suppose i have a user who submitted a date in database like this: 2023-04-11T14:50||America/Managua. How may i be able to display the date that is coming from database based on each user's timezone. Knowing that i already know its timezone. I am using Javascript and React. This is my submit function.

  const handleOnBlur = useCallback(async (e, dirty) => {
  e.preventDefault();

  if (dirty) {
   if (test?.timeZone) {
    console.log(test.timeZone);
    const dateWithTimeZone = new Date(e.target.value); // Date object with local timezone
    const dateWithUTC = dateWithTimeZone.toISOString();
  
    await formValueSubmit(`${dateWithUTC}||${test.timeZone}`);
  } else {
  console.log('Timezone not available');
  }
 }
 }, [formValueSubmit, test]);
2
  • Are you allowed to use a date library? Luxon is quite good. Certainly worth checking the docs.
    – David
    Commented Apr 11, 2023 at 14:24
  • If you are in control of what format dates are stored as, I would suggest storing them as an Unix epoch (milliseconds). That way you never need to parse anything. Commented Apr 11, 2023 at 15:13

2 Answers 2

0

You will need to calculate the offset based on the stored time zone to get the UTC date. Now, you can format the date in a user's local timezone.

The America/Managua timezone has an offset of -6:00 hours or -360 minutes. The getOffset function below correctly calculates -360 as the offset. It is the same offset as Central Standard Time (CST).

Note: Usually we add the offset to the date, but since we are parsing a stored time-zoned date, we need to subtract the offset. In this case, we are adding 360 minutes to the Zulu date.

It does not require any additional libraries. It uses built-in Intl.DateTimeFormat library.

// Source: https://stackoverflow.com/a/68593283/1762224
const getOffset = (timeZone = 'UTC', date = new Date()) => {
  const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
  const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
  return (tzDate.getTime() - utcDate.getTime()) / 6e4;
};

// Process timestamp into a date and calculate offset
const parseZonedTimestamp = (zonedTimestamp, izZulu = false) => {
  const [timestamp, timeZone] = zonedTimestamp.split('||');
  const date = new Date(timestamp + (!izZulu ? ':00Z' : ''));
  const offset = getOffset(timeZone, date);
  date.setUTCMinutes(date.getUTCMinutes() - offset); // Restore date
  return date;
};

const response = '2023-04-11T14:50||America/Managua'; // 2:50 PM (CST)
const date = parseZonedTimestamp(response);

const formatOptions = {
  weekday: 'long', year: 'numeric', month: 'long', day: '2-digit',
  hour: '2-digit', minute: '2-digit', second: '2-digit',
  timeZoneName: 'long'
};

// Tuesday, April 11, 2023 at 02:50:00 PM Central Standard Time
console.log(date.toLocaleString('en-US', {
  ...formatOptions,
  timeZone: 'America/Managua'
}));

// Tuesday, April 11, 2023 at 01:50:00 PM Pacific Daylight Time
console.log(date.toLocaleString('en-US', {
  ...formatOptions,
  timeZone: 'America/Los_Angeles'
}));

// Tuesday, April 11, 2023 at 08:50:00 PM Coordinated Universal Time
console.log(date.toLocaleString('en-US', {
  ...formatOptions,
  timeZone: 'UTC'
}));

Output

Tuesday, April 11, 2023 at 02:50:00 PM Central Standard Time
Tuesday, April 11, 2023 at 01:50:00 PM Pacific Daylight Time
Tuesday, April 11, 2023 at 08:50:00 PM Coordinated Universal Time
3
  • 1
    The algorithm is inaccurate around the daylight saving time switch. DST in the Los Angeles time zone started in the early hours of 2023-03-12, after which it is 7 hours behind UTC. But parseZonedTimestamp("2023-03-12T08:00||America/Los_Angeles").toISOString() gives 2023-03-12T16:00:00.000Z, which is 8 hours behind or 3/12/2023, 9:00:00 AM in the LA time zone. Commented Apr 11, 2023 at 17:29
  • I carried out the computation in the Europe/Berlin time zone. Commented Apr 11, 2023 at 18:41
  • new Date(date.toLocaleString('en-US', { timeZone: 'UTC' })); is a bad idea. The built–in parser is not required to correctly parse the output of toLocaleString so you should not assume that it will (e.g. the timestamps produced by your examples are treated as invalid dates by Safari). Also, historic offsets include seconds components so reducing to decimal minutes reduces the accuracy.
    – RobG
    Commented Apr 13, 2023 at 13:15
0

As pointed out here, Javascript is able to convert from UTC to date and time for a given time zone, but not vice versa. You can use a date/time library for that or a "bisection method" like the following.

The stringify function uses the Hungarian locale to produce the format YYYY. MM. DD. HH:MM:SS, which can be lexicographically compared to the given local time.

function stringify(date, timeZone) {
  return date.toLocaleString("hu", {timeZone, dateStyle: "short", timeStyle: "medium"})
    .replace(/ (\d):/, " 0$1:");
}
function parseTime(localTime, timeZone) {
  var x = Math.floor(new Date(localTime.substring(0, 10)) / 1000) - 86400;
  var y = x + 2 * 86400;
  localTime = stringify(new Date(localTime));
  for (; ;) {
    var m = Math.floor((x + y) / 2);
    if (m === x) return new Date(m * 1000);
    else if (stringify(new Date(m * 1000), timeZone) <= localTime) x = m;
    else y = m;
  }
}
console.log(parseTime("2023-04-11T14:50", "America/Managua"),
            parseTime("2023-03-12T08:00", "America/Los_Angeles"),
            parseTime("2023-03-26T02:30", "Europe/Berlin"));

Not the answer you're looking for? Browse other questions tagged or ask your own question.