Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion SparkyFitnessFrontend/src/contexts/PreferencesContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,11 @@ export const PreferencesProvider: React.FC<{ children: React.ReactNode }> = ({

if (typeof date === 'string') {
// If it's a full ISO string with time (e.g. 2026-02-16T...), keep it as is for parseISO
if (date.match(/^\d{4}-\d{2}-\d{2}$/) || date.includes('T00:00:00')) {
if (
date.match(/^\d{4}-\d{2}-\d{2}$/) ||
date.includes('T00:00:00') ||
(date.endsWith('Z') && date.includes('T00:00'))
) {
Comment on lines +348 to +352
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The condition date.includes('T00:00') is too broad and can lead to bugs. For example, a date string like '2023-01-01T00:00:30Z' would match, causing the time information to be incorrectly truncated. This can lead to subtle date-shifting issues.

The logic can be simplified and made more robust by checking if the time part of the string contains any non-zero digits. This correctly identifies true midnight timestamps while ignoring others.

        if (
          date.match(/^\d{4}-\d{2}-\d{2}$/) ||
          (date.includes('T') && !/[1-9]/.test(date.substring(date.indexOf('T') + 1)))
        ) {

// IMPORTANT: Treat YYYY-MM-DD as a literal local date to avoid UTC-to-Local shifting.
const datePart = date.split('T')[0];
if (datePart) {
Expand Down Expand Up @@ -389,6 +393,22 @@ export const PreferencesProvider: React.FC<{ children: React.ReactNode }> = ({
loggingLevel,
`PreferencesProvider: Parsing date string "${dateString}".`
);

// Handle literal date strings (YYYY-MM-DD or DB DATE format) to prevent shifting
if (
dateString.match(/^\d{4}-\d{2}-\d{2}$/) ||
dateString.includes('T00:00:00') ||
(dateString.endsWith('Z') && dateString.includes('T00:00'))
) {
const datePart = dateString.split('T')[0];
if (datePart) {
const [year, month, day] = datePart.split('-').map(Number);
if (year && month && day) {
return new Date(year, month - 1, day);
}
}
}
Comment on lines +397 to +410
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This block of logic for handling literal date strings is nearly identical to the logic in the formatDateInUserTimezone function. This duplication can make the code harder to maintain and can lead to inconsistencies if one instance is updated but the other is not.

I've left a comment on the other instance with a suggestion for a more robust implementation. It would be best to extract that improved logic into a shared helper function and call it from both formatDateInUserTimezone and parseDateInUserTimezone to avoid duplication.


const parsedDate = parseISO(dateString);
return startOfDay(parsedDate);
},
Expand Down