lemmy-ui
lemmy-ui copied to clipboard
Fix leap year issue
Closes #2441
Besides fixing the bug listed in the issue, if a user creates their account on February 29th of a leap year, their cake day is March 1st for non leap years.
Date constructor docs (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date):
If any parameter overflows its defined bounds, it "carries over". For example, if a monthIndex greater than 11 is passed in, those months will cause the year to increment; if a minutes greater than 59 is passed in, hours will increment accordingly, etc. Therefore, new Date(1990, 12, 1) will return January 1st, 1991; new Date(2020, 5, 19, 25, 65) will return 2:05 A.M. June 20th, 2020.
setMonth and setFullYear have similar descriptions in their docs. This also considers leap days in non leap years as first of march:
return (
isSameDay(currentDate, setYear(createDate, getYear(currentDate))) &&
!isSameYear(currentDate, createDate)
);
There is also an issue with how Profile displays the date. The same profile can show different dates to users in different timezones. This assertion will fail unless the user is in UTC+0:
console.assert(
format(parseISO("2023-05-11T00:00:00.000Z"), "PPP") ===
format(parseISO("2023-05-11T23:59:00.000Z"), "PPP"),
);
https://github.com/LemmyNet/lemmy-ui/blob/17097ecd34fd65a900825ec488f47a9ccdae839f/src/shared/components/person/profile.tsx#L696-L699
Is the first codeblock you shared supposed to be a simpler implementation and/or fix the timezone bug you mentioned? If so, I can change the PR to use that instead.
Just simpler.
I think this should fix the timezone issue, as long as published always starts with "yyyy-MM-dd" (the optional current date is just for testing):
import {
format,
getYear,
isSameDay,
isSameYear,
parse,
setYear,
} from "date-fns";
// Returns a date in local time with the same year, month and day. Ignores the
// source timezone.
export function cakeDate(published: string): Date {
return parse(published.substring(0, 10), "yyyy-MM-dd", new Date(0));
}
export default function isCakeDay(published: string, current?: Date): boolean {
const createDate = cakeDate(published);
const currentDate = current ?? new Date();
return (
isSameDay(currentDate, setYear(createDate, getYear(currentDate))) &&
!isSameYear(currentDate, createDate)
);
}
console.assert(
isCakeDay("2023-05-11T00:00:00.000Z", new Date(2024, 4, 11)),
"basic, early",
);
console.assert(
isCakeDay("2023-05-11T23:59:00.000Z", new Date(2024, 4, 11)),
"basic, late",
);
console.assert(
!isCakeDay("2024-05-11T00:00:00.000Z", new Date(2024, 4, 11)),
"not today, early",
);
console.assert(
!isCakeDay("2024-05-11T23:59:00.000Z", new Date(2024, 4, 11)),
"not today, late",
);
console.assert(
isCakeDay("2020-02-29T00:00:00.000Z", new Date(2024, 1, 29)),
"leap day, leap year",
);
console.assert(
isCakeDay("2020-02-29T00:00:00.000Z", new Date(2025, 2, 1)),
"leap day, non leap year",
);
console.assert(
isCakeDay("2023-03-01T00:00:00.000Z", new Date(2024, 2, 1)),
"first of march, leap year",
);
console.assert(
isCakeDay("2023-03-01T00:00:00.000Z", new Date(2024, 2, 1)),
"first of march, non leap year",
);
console.assert(
isCakeDay("2020-03-01T00:00:00.000Z", new Date(2024, 2, 1)),
"first of march leap year, leap year",
);
console.assert(
isCakeDay("2020-03-01T00:00:00.000Z", new Date(2024, 2, 1)),
"first of march leap year, non leap year",
);
console.assert(
format(cakeDate("2023-05-11T00:00:00.000Z"), "PPP") ===
format(cakeDate("2023-05-11T23:59:00.000Z"), "PPP"),
);
console.assert(
format(cakeDate("2023-05-11T23:59:00.000Z"), "PPP") === "May 11th, 2023",
format(cakeDate("2023-05-11T23:59:00.000Z"), "PPP"),
);
@matc-pub could you open that one up either as a PR to this branch, or to main?