Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about parsing in other timezones #327

Closed
msakrejda opened this issue May 9, 2020 · 8 comments
Closed

Question about parsing in other timezones #327

msakrejda opened this issue May 9, 2020 · 8 comments

Comments

@msakrejda
Copy link

First of all, thanks for the great library!

I had a question about timezone behavior: When chrono detects a timezone specified in the input string, that shows up under timezoneOffset in knownValues, and is accounted for when the input is parsed. When no timezone is specified, chrono parses the date in the local timezone, and no timezone offset is made.

I have a use case where I would like to parse dates in another timezone that does not match the local timezone, without being specified explicitly in the input string (it's an application setting--the use case is viewing operational metrics in a specific timezone: many users prefer UTC than their local timezone, since it's common across all their systems). Is it possible to have chrono assume another timezone when parsing? If not, is there a suggested workaround?

@wanasit
Copy link
Owner

wanasit commented May 9, 2020

You can call imply('timezoneOffset', ...) in result.start and result.end

const results = chrono.parse('An appointment on Sep 12-13 at 13:00')
console.log(results.map(r => r.date().toString()))
// [ 'Sat Sep 12 2020 13:00:00 GMT+0900 (Japan Standard Time)' ]

results.forEach(r => {
	r.start.imply('timezoneOffset', 0)
	r.end && r.end.imply('timezoneOffset', 0)
})
console.log(results.map(r => r.date().toString()))
// [ 'Sat Sep 12 2020 22:00:00 GMT+0900 (Japan Standard Time)' ]

I also suggest you create a custom Chrono with a Refiner to imply the timezone. See more example in readme.

const custom = new chrono.Chrono();
custom.refiners.push(
{
	refine: (text, results, opt) => {
			results.forEach(r => {
				r.start.imply('timezoneOffset', 0);
				r.end && r.end.imply('timezoneOffset', 0);
			})
			return results;
		}
});

const results = custom.parse('An appointment on Sep 12-13 at 13:00')
console.log(results.map(r => r.date().toString()))
// [ 'Sat Sep 12 2020 22:00:00 GMT+0900 (Japan Standard Time)' ]

@wanasit wanasit closed this as completed May 9, 2020
@msakrejda
Copy link
Author

Thanks for the quick response! How do you handle Daylight Saving Time changes in the target time zone? For UTC this is not an issue, but if a user in e.g., America/Los_Angeles wants to use America/New_York, the America/New_York offset will vary depending on what date is specified because of DST changes.

@wanasit
Copy link
Owner

wanasit commented May 10, 2020

I see. That would be challenging. Chrono doesn't have the logic for looking up DST change. You probably have to check the parsed results, adjust the timezoneOffset, and check the resulted period and account for the DST manually.

@msakrejda
Copy link
Author

Thanks! I'm also using moment-timezone and this is what I ended up with:

const parseTs = (value) => {
  const parsed = chrono.parse(value);
  if (parsed.length !== 1) {
    return undefined;
  }
  const parsedDateInfo = parsed[0].start;
  const tentativeDate = parsedDateInfo.date();
  const offset = moment.defaultZone.utcOffset(tentativeDate.getTime());
  parsedDateInfo.imply('timezoneOffset', -offset);

  return moment(parsedDateInfo.date());
};

In this case, we only expect one date in expressions; you may need to adjust as necessary. Leaving this here in case someone else runs into a similar issue.

@msakrejda
Copy link
Author

@wanasit it looks like this no longer works for 2.x because imply is gone--is there a recommended approach for 2.x or a general migration guide?

@erbridge
Copy link

erbridge commented Sep 7, 2020

@uhoh-itsmaciek The custom Refiner solution worked for me.

@rijvirajib
Copy link

rijvirajib commented Apr 5, 2021

Just an update for those who are using dayjs and v2 of the Refiner:

    const localTz = 'America/New_York' // This is the ISO string
    const localTime = dayjs.tz(new Date(), localTz)
    const localOffset = localTime.utcOffset() // returns in minutes
    const custom = chrono.casual.clone()
    custom.refiners.push({
      refine: (context, results) => {
        results.forEach((result) => {
          // Returns the time with the offset in included (must use minutes)
          result.start.imply('timezoneOffset', localOffset)
          result.end && result.end.imply('timezoneOffset', localOffset)
        })
        return results
      }

@horneber
Copy link

@rijvirajib I'm late to the party, but: That solution does not take into account that the UTC offset might change over time, depending on whether the local time uses daylight-saving-time or not. The solution @msakrejda showed above, even though it is for moment and not dayjs, is more robust in that regard. Basically one needs to pick the UTC offset off of (that's a lot of offs) the date already parsed by chrono.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants