Have time.js use UTC-related getters/setters (#30857)

Before this patch, we were using `Date` getter/setter methods that
worked with local time to get a list of Sundays that are in the range of
some start date and end date. The problem with this was that the Sundays
are in Unix epoch time and when we changed the "startDate" argument that
was passed to make sure it is on a Sunday, this change would be
reflected when we convert it to Unix epoch time. More specifically, I
observed that we may get different Unix epochs depending on your
timezone when the returned list should rather be timezone-agnostic.

This led to issues in US timezones that caused the contributor, code
frequency, and recent commit charts to not show any chart data. This fix
resolves this by using getter/setter methods that work with UTC since it
isn't dependent on timezones.

Fixes #30851.

---------

Co-authored-by: Sam Fisher <fisher@3echelon.local>
(cherry picked from commit 22c7b3a74459833b86783e84d4708c8934d34e58)
This commit is contained in:
Kemal Zebari 2024-05-05 18:36:53 -07:00 committed by Earl Warren
parent 8672ad12b1
commit 16eb85adfb
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
4 changed files with 24 additions and 19 deletions

View file

@ -67,7 +67,7 @@ export default {
const weekValues = Object.values(this.data); const weekValues = Object.values(this.data);
const start = weekValues[0].week; const start = weekValues[0].week;
const end = firstStartDateAfterDate(new Date()); const end = firstStartDateAfterDate(new Date());
const startDays = startDaysBetween(new Date(start), new Date(end)); const startDays = startDaysBetween(start, end);
this.data = fillEmptyStartDaysWithZeroes(startDays, this.data); this.data = fillEmptyStartDaysWithZeroes(startDays, this.data);
this.errorText = ''; this.errorText = '';
} else { } else {

View file

@ -114,7 +114,7 @@ export default {
const weekValues = Object.values(total.weeks); const weekValues = Object.values(total.weeks);
this.xAxisStart = weekValues[0].week; this.xAxisStart = weekValues[0].week;
this.xAxisEnd = firstStartDateAfterDate(new Date()); this.xAxisEnd = firstStartDateAfterDate(new Date());
const startDays = startDaysBetween(new Date(this.xAxisStart), new Date(this.xAxisEnd)); const startDays = startDaysBetween(this.xAxisStart, this.xAxisEnd);
total.weeks = fillEmptyStartDaysWithZeroes(startDays, total.weeks); total.weeks = fillEmptyStartDaysWithZeroes(startDays, total.weeks);
this.xAxisMin = this.xAxisStart; this.xAxisMin = this.xAxisStart;
this.xAxisMax = this.xAxisEnd; this.xAxisMax = this.xAxisEnd;

View file

@ -62,7 +62,7 @@ export default {
const data = await response.json(); const data = await response.json();
const start = Object.values(data)[0].week; const start = Object.values(data)[0].week;
const end = firstStartDateAfterDate(new Date()); const end = firstStartDateAfterDate(new Date());
const startDays = startDaysBetween(new Date(start), new Date(end)); const startDays = startDaysBetween(start, end);
this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52); this.data = fillEmptyStartDaysWithZeroes(startDays, data).slice(-52);
this.errorText = ''; this.errorText = '';
} else { } else {

View file

@ -1,25 +1,30 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import {getCurrentLocale} from '../utils.js'; import {getCurrentLocale} from '../utils.js';
// Returns an array of millisecond-timestamps of start-of-week days (Sundays) dayjs.extend(utc);
export function startDaysBetween(startDate, endDate) {
// Ensure the start date is a Sunday
while (startDate.getDay() !== 0) {
startDate.setDate(startDate.getDate() + 1);
}
const start = dayjs(startDate); /**
const end = dayjs(endDate); * Returns an array of millisecond-timestamps of start-of-week days (Sundays)
const startDays = []; *
* @param startConfig The start date. Can take any type that `Date` accepts.
* @param endConfig The end date. Can take any type that `Date` accepts.
*/
export function startDaysBetween(startDate, endDate) {
const start = dayjs.utc(startDate);
const end = dayjs.utc(endDate);
let current = start; let current = start;
// Ensure the start date is a Sunday
while (current.day() !== 0) {
current = current.add(1, 'day');
}
const startDays = [];
while (current.isBefore(end)) { while (current.isBefore(end)) {
startDays.push(current.valueOf()); startDays.push(current.valueOf());
// we are adding 7 * 24 hours instead of 1 week because we don't want current = current.add(1, 'week');
// date library to use local time zone to calculate 1 week from now.
// local time zone is problematic because of daylight saving time (dst)
// used on some countries
current = current.add(7 * 24, 'hour');
} }
return startDays; return startDays;
@ -29,10 +34,10 @@ export function firstStartDateAfterDate(inputDate) {
if (!(inputDate instanceof Date)) { if (!(inputDate instanceof Date)) {
throw new Error('Invalid date'); throw new Error('Invalid date');
} }
const dayOfWeek = inputDate.getDay(); const dayOfWeek = inputDate.getUTCDay();
const daysUntilSunday = 7 - dayOfWeek; const daysUntilSunday = 7 - dayOfWeek;
const resultDate = new Date(inputDate.getTime()); const resultDate = new Date(inputDate.getTime());
resultDate.setDate(resultDate.getDate() + daysUntilSunday); resultDate.setUTCDate(resultDate.getUTCDate() + daysUntilSunday);
return resultDate.valueOf(); return resultDate.valueOf();
} }