How to transform date/time from input timezone to output timezone in JavaScript / Dayjs?

41 Views Asked by At

I have this so far:

import * as chrono from 'chrono-node'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'

dayjs.extend(utc)
dayjs.extend(timezone)

let selectedInput = 'pst'
let selectedOutput = 'est'
let inputDateString = '10pm'

// set timezone reference point to PST
dayjs.tz.setDefault(selectedInput)
const inputDate = chrono.parseDate(inputDateString)
const utc = inputDate?.toISOString().replace(/Z$/, '')
const computedInputDate = utc
  ? dayjs(utc).tz(selectedInput)
  : undefined
const outputDateString = computedInputDate
  ? dayjs(computedInputDate)
      .tz(selectedOutput)
      .format('h:mma on MMM D, YYYY')
  : undefined
dayjs.tz.setDefault() // reset default timezone
console.log('output:', outputDateString)

I am using dayjs timezone and chrono-node (for the natural language time parsing feature). So in year 2024 on day Jan 31, you type 10pm (that is, you are saying "10pm Jan 31 2024 PST"), I would expect to see 1:00am on Feb 1, 2024 in the logged output, but instead I am seeing 9:00am on Feb 1, 2024, what am I doing wrong?

How do you start the time reference point from a particular timezone, and then how do you convert it to the other different timezone? Either using Dayjs (what I'm using), or some other library / native function would be fine.

If I instead remove the UTC conversion stuff, and go with this:

import * as chrono from 'chrono-node'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'

dayjs.extend(utc)
dayjs.extend(timezone)

let selectedInput = 'pst'
let selectedOutput = 'est'
let inputDateString = '10pm'

// set timezone reference point to PST
dayjs.tz.setDefault(selectedInput)
const inputDate = chrono.parseDate(inputDateString)
const computedInputDate = inputDate
  ? dayjs(inputDate).tz(selectedInput)
  : undefined;
const outputDateString = computedInputDate
  ? dayjs(computedInputDate).tz(selectedOutput).format("h:mma on MMM D, YYYY")
  : undefined;
dayjs.tz.setDefault() // reset default timezone
console.log('output:', outputDateString)

I get the correct 1:00am on Feb 1, 2024.

But if I reverse the timezones (EST -> PST), and plug in 10pm, I should see 7:00pm on Jan 31, 2024 (PST), but I see 10:00pm on Jan 31, 2024, so my calculations aren't quite right in either direction.

import * as chrono from 'chrono-node'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'

dayjs.extend(utc)
dayjs.extend(timezone)

let selectedInput = 'est'
let selectedOutput = 'pst'
let inputDateString = '10pm'

// set timezone reference point to PST
dayjs.tz.setDefault(selectedInput)
const inputDate = chrono.parseDate(inputDateString)
const computedInputDate = inputDate
  ? dayjs(inputDate).tz(selectedInput)
  : undefined;
const outputDateString = computedInputDate
  ? dayjs(computedInputDate).tz(selectedOutput).format("h:mma on MMM D, YYYY")
  : undefined;
dayjs.tz.setDefault() // reset default timezone
console.log('output:', outputDateString)

How do I get this working going forward or backward in timezones given an input timezone, output timezone, and input time (natural language)?

Update: Tried Luxon

Tried luxon but same results pretty much.

const time = '10pm'
const selectedInput = 'EST'
const selectedOutput = 'PST'

const inputDate = chrono.parseDate(time, { timezone: selectedInput })
const outputDate = inputDate
  ? DateTime.fromJSDate(inputDate, { zone: selectedInput }).setZone(
      selectedOutput,
    )
  : undefined
const outputDateString = outputDate
  ? `${outputDate.toFormat('h:mm')}${outputDate
      .toFormat('a')
      .toLowerCase()} on ${outputDate.toFormat('MMM d, yyyy')}`
  : undefined
// says 10pm instead of 7pm

Even with IANA location-based timezones, same issue:

const inputDate = chrono.parseDate(time, {
  timezone: 'America/Cancun',
})
const outputDate = inputDate
  ? DateTime.fromJSDate(inputDate, {
      zone: 'America/Cancun',
    }).setZone('America/Los_Angeles')
  : undefined
const outputDateString = outputDate
  ? `${outputDate.toFormat('h:mm')}${outputDate
      .toFormat('a')
      .toLowerCase()} on ${outputDate.toFormat('MMM d, yyyy')}`
  : undefined
// says 10pm, when it should say 7pm.

1

There are 1 best solutions below

0
Lance On

Using luxon, this seems to work:

import * as chrono from 'chrono-node'
import { DateTime } from 'luxon'

const selectedInput = 'est'
const selectedOutput = 'pst'
const time = '10pm'

const inputDate = chrono.parseDate(time, {
  timezone: selectedInput.toUpperCase(),
  instant: new Date(),
})
const outputDate = inputDate
  ? DateTime.fromJSDate(inputDate).setZone(selectedOutput)
  : undefined
const outputDateString = outputDate
  ? `${outputDate.toFormat('h:mm')}${outputDate
      .toFormat('a')
      .toLowerCase()} on ${outputDate.toFormat('MMM d, yyyy')}`
  : undefined

I am still open to seeing a better more robust solution though.