Skip to content

ERA-11347: [P1] Desktop time field malfunctioning (Offset by 1 hour) #1283

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

Merged
merged 1 commit into from
Apr 23, 2025

Conversation

luixlive
Copy link
Contributor

@luixlive luixlive commented Apr 21, 2025

What does this PR do?

Fix a bug with the new schema date time field where if the user is in a place with daylight saving time and chooses a date that corresponds to the other daylight saving part of the current date, the automatic time adjustment logic we had was changing its input.

Relevant link(s)

Where / how to start reviewing (optional)

The bug is fixed in one single place for production, and another place for the new schemas that will be supported with the EFB. The other changes in the code were made to follow the same new pattern and stabilize the pickers.

Any background context you want to provide(if applicable)

The root cause was that the previous solution didn't consider daylight saving time, and it was "fixing" the date time fields to the current user time. The issue was that when the user chose a date with a different offset (in the other daylight saving part of the year), this automatic fixing was moving that extra offset. The new solution is to fix the time only once before the first render and then let the user freely change the picker. Once a valid date is established in the picker, we add the offset corresponding to the selected date, and not to the current date. This way we are not fixing the time automatically over every change and now we are applying the right offset to the value for areas with different offset in different parts of the year.

Notes:

  • To QA (@AlanCalvillo): an easy way to test the issue is mocking your browser time zone to San Francisco. They have the daylight saving time and you can just test setting past dates that fall in the other daylight saving part of the year (like in the ticket vide, March 5th) and dates that correspond to the current daylight saving part of the year (like April 16th). To mock the browser time zone in Chrome you open the Sensors tab and change the location:
    image

I recommend to do the change in a new tab before navigating to EarthRanger because if you do it once the app is loaded, it doesn't apply the location change properly.

Then you can use a report with a date time in the form, test the picker, and finally save the form and make sure that the right offset was applied in the changes. In this example we can see how it properly considers the daylight saving time for March 5th (-08:00):
image

@@ -231,14 +232,20 @@ const DatePicker = ({
};

const onDayInputKeyDown = (event) => {
// If the year and month inputs are complete, we can calculate the last valid day of the current month to set when
// the user decreases or increases the day with the arrows.
const lastValidDayOfMonth = isYearInputComplete(year) && isMonthInputComplete(month)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not part of the bug, just an extra addition to the PR to improve our date picker. If the user decreased the day input with the down arrow or increase with the up arrow, we were setting always 31 as the upper limit of the month. But not all months have 31 days, some of them vary with the leap year. So this new lastValidDayOfMonth calculation sets the last valid day instead of a fixed 31 if we have the year and month available (otherwise, it's impossible to calculate the last valid day of the month).

@@ -59,4 +59,4 @@ export const shouldCompleteFirstDayDigitWithZero = (day) => /^[1-3]$/.test(day);

export const isDayInputComplete = (day) => /^(0[1-9]|[12][0-9]|3[01])$/.test(day);

export const isValidDate = (time) => /^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/.test(time);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small word fix.

@@ -70,21 +70,21 @@ const DateRangeSelector = ({
onFilterSettingsToggle && onFilterSettingsToggle(!filterSettingsOpen);
}, [filterSettingsOpen, onFilterSettingsToggle]);

const onStartDateTimePickerChange = (dateTime) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No changes here, I just updated the variable name to match other places where we implemented the date time picker.

@@ -1,14 +1,12 @@
import React, { memo, useImperativeHandle, useRef } from 'react';

import { getMaxDateAndTime, getMinDateAndTime } from './utils';
import { EMPTY_DATE_TIME_VALUE, getMaxDateAndTime, getMinDateAndTime } from './utils';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the EMPTY_DATE_TIME_VALUE constant to the utils file to match the other pickers.


let dateISO = `${date}T`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file changes weren't really necessary, these pickers were working fine, but using the new approach is clearer, more concise, and I would even think stable since now we delegate the responsibility of calculating the offsets to date-fns.

// JSON schema date time format expects the date time with seconds and timezone offset. If the new date is valid,
// consider the offset of that date (to match the daylight saving), otherwise consider the current date offset.
const [newDateValue] = newDateTime.split('T');
const newOffset = format(isValidDate(newDateValue) ? newDateValue : new Date(), 'xxx');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual fix for the new schemas component (EFB), this was not the reported issue since this is behind a feature flag, but the bug was here too. Instead of setting the user's current offset, we now calculate the offset corresponding to the selected date (considering daylight saving and avoiding that unwanted incorrect fixing).

// JSON schema time format expects the time with the timezone offset.
const timeWithSecondsAndOffset = `${newTime}:00${getTimezoneOffsetString()}`;
// JSON schema time format expects the time with seconds and timezone offset.
const timeWithSecondsAndOffset = `${newTime}:00${format(new Date(), 'xxx')}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JSON schemas time formats need offsets too so the fix is also applied to time fields.

@@ -85,8 +87,7 @@ const DateTime = ({ autofillDefaultInput: _autofillDefaultInput, details, error,
}

if (details.inputType === DATE_TIME_ELEMENT_INPUT_TYPES.TIME) {
// We add a dummy date just to make it a valid ISO date
const parsedTimeValue = parseISO(`2000-01-01T${value}`);
const parsedTimeValue = parseISO(`${format(new Date(), 'yyyy-MM-dd')}T${value}`);
Copy link
Contributor Author

@luixlive luixlive Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used to fix the time value with a dummy date. I thought it wasn't really important, but it is, due to cities with daylight saving time. So now we use the current date to calculate the offset (in this case we do want the current date because this is the place where we fix the stored value into the current time zone offset).

@@ -106,13 +106,9 @@ const DetailsSection = ({
const onDatePickerChange = (newDate) => {
setDate(newDate);

const parsedDate = parseISO(newDate);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another place were the changes weren't really needed but the new pattern improves the code and it makes it more resilient.

if (formData) {
const parsedDateTimeValue = parseISO(formData);
if (isValid(parsedDateTimeValue)) {
timeZoneCorrectedData = format(parsedDateTimeValue, 'yyyy-MM-dd\'T\'HH:mm:ssXXX');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And finally we get to the place where the bug was actually affecting production sites. As you can see this line was executed in every render wrongly "fixing" the value to the current time zone, but since the daylight saving wasn't considered we were moving the selected time one hour if the date was in the other daylight saving time part of the year. Now we only fix the time once before the first render and store the value in a state variable. If the user sets a valid date, then we calculate the corresponding offset of that date (not the current date!) so we correctly store the daylight saving time, fixing the issue and making our time data resilient across geography and time.

@@ -141,16 +141,6 @@ export const getUserLocaleTime = (date = new Date()) => date.toLocaleTimeString(

export const isGreaterThan = (date1, date2) => date1.getTime() > date2.getTime();

export const getTimezoneOffsetString = () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not needed anymore, and instead we delegate this calculation to date-fns so it considers the user's locale and the daylight time when getting an offset.

Copy link
Collaborator

@JoshuaVulcan JoshuaVulcan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code looks good, and I fail to reproduce the reported bug in the feature environment, while it is easily reproducible in the development branch. Nice work!

Base automatically changed from ERA-11293-jest-29 to ERA-11293 April 23, 2025 17:00
Base automatically changed from ERA-11293 to develop April 23, 2025 18:30
@luixlive luixlive merged commit c33ddb8 into develop Apr 23, 2025
3 checks passed
@luixlive luixlive deleted the ERA-11347 branch April 23, 2025 22:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants