diff --git a/web/src/components/ActivityCalendar.tsx b/web/src/components/ActivityCalendar.tsx
index e22f0a1a..feb03040 100644
--- a/web/src/components/ActivityCalendar.tsx
+++ b/web/src/components/ActivityCalendar.tsx
@@ -1,6 +1,6 @@
import { Tooltip } from "@mui/joy";
import classNames from "classnames";
-import { getNormalizedDateString } from "@/helpers/datetime";
+import { getNormalizedDateString, getDateWithOffset } from "@/helpers/datetime";
interface Props {
// Format: 2021-1
@@ -26,8 +26,8 @@ const getCellAdditionalStyles = (count: number, maxCount: number) => {
const ActivityCalendar = (props: Props) => {
const { month: monthStr, data, onClick } = props;
- const year = new Date(monthStr).getFullYear();
- const month = new Date(monthStr).getMonth() + 1;
+ const year = new Date(monthStr).getUTCFullYear();
+ const month = new Date(monthStr).getUTCMonth() + 1;
const dayInMonth = new Date(year, month, 0).getDate();
const firstDay = new Date(year, month - 1, 1).getDay();
const lastDay = new Date(year, month - 1, dayInMonth).getDay();
@@ -47,7 +47,9 @@ const ActivityCalendar = (props: Props) => {
return (
{days.map((day, index) => {
- const date = getNormalizedDateString(`${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`);
+ const date = getNormalizedDateString(
+ getDateWithOffset(`${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`),
+ );
const count = data[date] || 0;
const isToday = new Date().toDateString() === new Date(date).toDateString();
const tooltipText = count ? `${count} memos in ${date}` : date;
diff --git a/web/src/helpers/datetime.ts b/web/src/helpers/datetime.ts
index 576d08c9..54d57158 100644
--- a/web/src/helpers/datetime.ts
+++ b/web/src/helpers/datetime.ts
@@ -169,3 +169,15 @@ export function isFutureDate(t?: Date | number | string): boolean {
const timestamp = getTimeStampByDate(t ? t : Date.now());
return timestamp > Date.now();
}
+
+/**
+ * Calculates a new Date object by adjusting the provided date, timestamp, or date string
+ * based on the current timezone offset.
+ *
+ * @param t - The input date, timestamp, or date string (optional). If not provided,
+ * the current date and time will be used.
+ * @returns A new Date object adjusted by the current timezone offset.
+ */
+export function getDateWithOffset(t?: Date | number | string): Date {
+ return new Date(getTimeStampByDate(t) + new Date().getTimezoneOffset() * 60 * 1000);
+}
diff --git a/web/src/pages/Timeline.tsx b/web/src/pages/Timeline.tsx
index 2f344905..d5868456 100644
--- a/web/src/pages/Timeline.tsx
+++ b/web/src/pages/Timeline.tsx
@@ -148,9 +148,9 @@ const Timeline = () => {
- {new Date(group.month).toLocaleString(i18n.language, { month: "short" })}
+ {new Date(group.month).toLocaleString(i18n.language, { month: "short", timeZone: "UTC" })}
- {new Date(group.month).getFullYear()}
+ {new Date(group.month).getUTCFullYear()}
Total: {sum(Object.values(group.data))}
setSelectedDay(date)} />