bug fix: Check offsets when browser doesn't canonicalize.

This is more of a workaround than a bug fix.

Some JS implementations are less "aggressive" about
canonicalizing things like America/Montreal to America/Toronto.

We instead use offset checks.
This commit is contained in:
Steve Howell
2025-01-22 13:02:21 +00:00
committed by Tim Abbott
parent 8227070087
commit d00934b0ef
2 changed files with 72 additions and 18 deletions

View File

@@ -555,18 +555,44 @@ export function should_display_profile_incomplete_alert(timestamp: number): bool
return false;
}
export function browser_canonicalize_timezone(uncanonicalized_timezone: string): string {
export function get_time_in_timezone(date: Date, timezone: string): number {
return Date.parse(date.toLocaleString("en-US", {timeZone: timezone}));
}
export function get_offset_difference_at_date(
timezone1: string,
timezone2: string,
reference_date: Date,
): number {
const date1 = get_time_in_timezone(reference_date, timezone1);
const date2 = get_time_in_timezone(reference_date, timezone2);
return date1 - date2;
}
export function are_timezones_on_same_clock_now(timezone1: string, timezone2: string): boolean {
// America/Los_Angeles is clearly the same as America/Los_Angeles:
if (timezone1 === timezone2) {
return true;
}
// We still want this function to return true if the timezones are
// on the same clock for now, even though they may eventually diverge
// during Daylight Savings. This avoids nagging the user. The only
// tradeoff is if the user stays logged on while the clocks change,
// but that should be rare.
const now = new Date();
try {
// we use the runtime's default locale, to match _browser_time_zone_.
const canonicalized_timezone = new Intl.DateTimeFormat(undefined, {
timeZone: uncanonicalized_timezone,
}).resolvedOptions().timeZone;
return canonicalized_timezone;
return get_offset_difference_at_date(timezone1, timezone2, now) === 0;
} catch {
return "";
// This should only happen during testing, but we just catch any error
// related to invalid time zones.
return false;
}
}
export function is_browser_timezone_same_as(uncanonicalized_target_timezone: string): boolean {
return browser_time_zone() === browser_canonicalize_timezone(uncanonicalized_target_timezone);
export function is_browser_timezone_same_as(zulip_time_zone: string): boolean {
// We delegate most of this check to facilitate testing.
// We don't want to mock browser_time_zone.
return are_timezones_on_same_clock_now(browser_time_zone(), zulip_time_zone);
}

View File

@@ -654,6 +654,40 @@ run_test("should_display_profile_incomplete_alert", () => {
assert.equal(timerender.should_display_profile_incomplete_alert(realm_date_created_secs), true);
});
run_test("are_timezones_on_same_clock_now", () => {
assert.ok(
timerender.are_timezones_on_same_clock_now("America/Los_Angeles", "America/Los_Angeles"),
);
assert.ok(timerender.are_timezones_on_same_clock_now("America/New_York", "America/New_York"));
// Montreal and Toronto are equivalent year-round.
assert.ok(timerender.are_timezones_on_same_clock_now("America/Montreal", "America/Toronto"));
assert.ok(
!timerender.are_timezones_on_same_clock_now("Invalid/Timezone", "America/Los_Angeles"),
);
assert.ok(
!timerender.are_timezones_on_same_clock_now("America/New_York", "America/Los_Angeles"),
);
});
run_test("are_timezones_on_same_clock_now (Phoenix)", () => {
/*
If you live in Phoenix and make the short flight to Los Angeles during
Daylight Savings time, you don't need to reset your watch.
Likewise, during the winter, if you go to Denver, you don't need to
reset your watch.
We err on the side of not nagging Phoenix folks to change their
"Zulip clock" for these short trips.
*/
assert.ok(
timerender.are_timezones_on_same_clock_now("America/Phoenix", "America/Los_Angeles") ||
timerender.are_timezones_on_same_clock_now("America/Phoenix", "America/Denver"),
);
});
run_test("is_browser_timezone_same_as", () => {
assert.equal(timerender.is_browser_timezone_same_as(timerender.browser_time_zone()), true);
@@ -661,19 +695,13 @@ run_test("is_browser_timezone_same_as", () => {
assert.equal(timerender.is_browser_timezone_same_as("Invalid/Timezone"), false);
});
run_test("test time zone math", () => {
// This test doesn't seem to test any Zulip code, but it seems
// useful to keep around anyway, since the offset logic
// here is useful to understand.
run_test("time zone helpers", () => {
function get_time_in_timezone(date, timezone) {
return Date.parse(date.toLocaleString("en-US", {timeZone: timezone}));
return timerender.get_time_in_timezone(date, timezone);
}
function get_offset_difference_at_date(tz1, tz2, reference_date) {
const date1 = get_time_in_timezone(reference_date, tz1);
const date2 = get_time_in_timezone(reference_date, tz2);
return date1 - date2;
return timerender.get_offset_difference_at_date(tz1, tz2, reference_date);
}
// The current time in America/Phoenix does equal the current time