Skip to content

Commit

Permalink
Recompute timezone adjustments for a given date.
Browse files Browse the repository at this point in the history
Different dates can have different timezone adjustments in the case that
the client timezone and server timezone are different and one of those
time zones adheres to daylight savings time and the other does not. For
example if the client timezone is America/Phoenix and the server
timezone is America/Chicago.  The America/Phoenix timezone does not
adhere to daylight savings time and is always UTC-7 while
America/Chicago is UTC-5 during daylight savings time, but is UTC-6
during standard time. So if an instructor is in the America/Phoenix
timezone, but working on a server set for the America/Chicago timezone
and selects a date that is during daylight savings time, then the
current code use an incorrect differential of 1 hour because it uses the
same differential for all dates.

So this computes the timezone adjustment for a given date so that the
correct timezone differential for that time is used.

This fixes issue #2654.
  • Loading branch information
drgrice1 committed Jan 10, 2025
1 parent f36d1c5 commit ca030b4
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 21 deletions.
28 changes: 17 additions & 11 deletions htdocs/js/DatePicker/datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,25 @@
const reduced_rule = document.getElementById(`${name}.reduced_scoring_date_id`);
if (reduced_rule) groupRules.splice(1, 0, [reduced_rule]);

// Compute the time difference between the current browser timezone and the course timezone.
// Compute the time difference between a time in the browser timezone and the same time in the course timezone.
// flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone.
// Note that this is in seconds.
const timezoneAdjustment =
new Date(new Date().toLocaleString('en-US')).getTime() -
new Date(
new Date().toLocaleString('en-US', { timeZone: open_rule.dataset.timezone ?? 'America/New_York' })
).getTime();
// Note that the input time is in seconds and output times is in milliseconds.
const timezoneAdjustment = (time) => {
const dateTime = new Date(0);
dateTime.setUTCSeconds(time);
return (
new Date(dateTime.toLocaleString('en-US')).getTime() -
new Date(
dateTime.toLocaleString('en-US', { timeZone: open_rule.dataset.timezone ?? 'America/New_York' })
).getTime()
);
};

for (const rule of groupRules) {
const classValue = document.getElementsByName(`${rule[0].name}.class_value`)[0]?.dataset.classValue;
const value = rule[0].value || classValue;
rule.push(value ? parseInt(value) * 1000 - timezoneAdjustment : 0);
if (classValue) rule.push(parseInt(classValue) * 1000 - timezoneAdjustment);
rule.push(value ? parseInt(value) * 1000 - timezoneAdjustment(parseInt(value)) : 0);
if (classValue) rule.push(parseInt(classValue) * 1000 - timezoneAdjustment(parseInt(classValue)));
}

const update = (input) => {
Expand Down Expand Up @@ -156,7 +161,8 @@
parseDate(datestr, format) {
// Deal with the case of a unix timestamp. The timezone needs to be adjusted back as this is for
// the unix timestamp stored in the hidden input whose value will be sent to the server.
if (format === 'U') return new Date(parseInt(datestr) * 1000 - timezoneAdjustment);
if (format === 'U')
return new Date(parseInt(datestr) * 1000 - timezoneAdjustment(parseInt(datestr)));

// Next attempt to parse the datestr with the current format. This should not be adjusted. It is
// for display only.
Expand All @@ -171,7 +177,7 @@
formatDate(date, format) {
// In this case the date provided is in the browser's time zone. So it needs to be adjusted to the
// timezone of the course.
if (format === 'U') return (date.getTime() + timezoneAdjustment) / 1000;
if (format === 'U') return (date.getTime() + timezoneAdjustment(date.getTime() / 1000)) / 1000;

return luxon.DateTime.fromMillis(date.getTime()).toFormat(
datetimeFormats[luxon.Settings.defaultLocale]
Expand Down
25 changes: 15 additions & 10 deletions htdocs/js/ProblemSetList/problemsetlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,22 @@
if (importDateShift) {
luxon.Settings.defaultLocale = importDateShift.dataset.locale ?? 'en';

// Compute the time difference between the current browser timezone and the course timezone.
// Compute the time difference between a time in the browser timezone and the same time in the course timezone.
// flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone.
// Note that this is in seconds.
const timezoneAdjustment =
new Date(new Date().toLocaleString('en-US')).getTime() -
new Date(
new Date().toLocaleString('en-US', { timeZone: importDateShift.dataset.timezone ?? 'America/New_York' })
).getTime();
// Note that the input time is in seconds and output times is in milliseconds.
const timezoneAdjustment = (time) => {
const dateTime = new Date(0);
dateTime.setUTCSeconds(time);
return (
new Date(dateTime.toLocaleString('en-US')).getTime() -
new Date(
dateTime.toLocaleString('en-US', { timeZone: open_rule.dataset.timezone ?? 'America/New_York' })
).getTime()
);
};

let fallbackDate = importDateShift.value
? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment)
? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment(parseInt(importDateShift.value)))
: new Date();

const fp = flatpickr(importDateShift.parentNode, {
Expand Down Expand Up @@ -248,7 +253,7 @@
parseDate(datestr, format) {
// Deal with the case of a unix timestamp. The timezone needs to be adjusted back as this is for
// the unix timestamp stored in the hidden input whose value will be sent to the server.
if (format === 'U') return new Date(parseInt(datestr) * 1000 - timezoneAdjustment);
if (format === 'U') return new Date(parseInt(datestr) * 1000 - timezoneAdjustment(parseInt(datestr)));

// Next attempt to parse the datestr with the current format. This should not be adjusted. It is
// for display only.
Expand All @@ -263,7 +268,7 @@
formatDate(date, format) {
// In this case the date provided is in the browser's time zone. So it needs to be adjusted to the
// timezone of the course.
if (format === 'U') return (date.getTime() + timezoneAdjustment) / 1000;
if (format === 'U') return (date.getTime() + timezoneAdjustment(date.getTime() / 1000)) / 1000;

return luxon.DateTime.fromMillis(date.getTime()).toFormat(
datetimeFormats[luxon.Settings.defaultLocale]
Expand Down

0 comments on commit ca030b4

Please sign in to comment.