Update todo checklist#17
Conversation
Pull Request ReviewThank you for this substantial contribution! This PR adds several new features and tools to the datetime.app project. Positives
Critical Issues1. Duplicate Variable Declaration (changelog/page.tsx:245-246)
2. Missing Params Await (Multiple Files)
Important Issues3. Timer Precision - setInterval(1000) can drift over time Code Quality Notes
Test CoverageNo tests in PR - recommend adding:
SummarySubstantial and valuable PR with good code quality. Critical blockers:
Recommendation: Request Changes - Fix critical issues before merge. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d949c140b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const now = new Date(); | ||
| const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); | ||
| const targetDate = new Date(now.toLocaleString('en-US', { timeZone: cityInfo.timezone })); | ||
| const utcDate = new Date(now.toLocaleString(localeCode, { timeZone: 'UTC' })); | ||
| const targetDate = new Date(now.toLocaleString(localeCode, { timeZone: cityInfo.timezone })); |
There was a problem hiding this comment.
Avoid parsing locale-formatted dates for offsets
The GMT offset is computed by round-tripping through toLocaleString(localeCode, …) and then new Date(...). For many non-English locales (e.g., fr-FR, ar), the resulting string is not guaranteed to be parseable by Date, so utcDate/targetDate become Invalid Date and the offset label renders as NaN or incorrect values. This regression only appears on non‑en locales because the earlier en-US parsing was parseable; use Date.UTC/Intl parts instead of parsing localized strings.
Useful? React with 👍 / 👎.
| "calendar": { | ||
| "title": "{year}年日历", | ||
| "subtitle": "完整年历视图,包含周数", | ||
| "description": "查看{year}年完整日历,包含所有月份、日期和周数", | ||
| "metaTitle": "{year}年日历 - 免费可打印日历,包含周数、月度计划器和年度视图", | ||
| "metaDescription": "免费{year}年日历,包含周数、月度视图和年度概览。使用我们的交互式{year}年日历规划您的{year}年,包含节假日、闰年信息和可打印格式。", | ||
| "metaKeywords": "{year}年日历, {year}年度日历, {year}月度日历, {year}年历, 免费{year}年日历, 可打印{year}年日历, 带周数的{year}年日历", | ||
| "currentYear": "当前年份", | ||
| "leapYear": "闰年", | ||
| "regularYear": "平年", | ||
| "weekNumber": "第{number}周", | ||
| "today": "今天", | ||
| "viewYear": "查看年份", | ||
| "previousYear": "上一年", | ||
| "nextYear": "下一年", | ||
| "yearNavigation": "年份导航", | ||
| "monthNavigation": "月份导航", | ||
| "jumpToYear": "跳转到年份", | ||
| "toggleTheme": "切换主题", | ||
| "previousMonth": "上个月", | ||
| "nextMonth": "下个月", | ||
| "currentMonth": "当前月份", | ||
| "backToYear": "返回{year}年", | ||
| "daysInMonth": "共{days}天", | ||
| "selectedDate": "选中日期", | ||
| "date": "日期", | ||
| "dayOfYear": "年内第几天", | ||
| "months": { | ||
| "january": "一月", | ||
| "february": "二月", | ||
| "march": "三月", | ||
| "april": "四月", | ||
| "may": "五月", | ||
| "june": "六月", | ||
| "july": "七月", | ||
| "august": "八月", | ||
| "september": "九月", | ||
| "october": "十月", | ||
| "november": "十一月", | ||
| "december": "十二月" | ||
| }, | ||
| "monthsShort": { | ||
| "jan": "1月", | ||
| "feb": "2月", | ||
| "mar": "3月", | ||
| "apr": "4月", | ||
| "may": "5月", | ||
| "jun": "6月", | ||
| "jul": "7月", | ||
| "aug": "8月", | ||
| "sep": "9月", | ||
| "oct": "10月", | ||
| "nov": "11月", | ||
| "dec": "12月" | ||
| }, | ||
| "weekdays": { | ||
| "sunday": "星期日", | ||
| "monday": "星期一", | ||
| "tuesday": "星期二", | ||
| "wednesday": "星期三", | ||
| "thursday": "星期四", | ||
| "friday": "星期五", | ||
| "saturday": "星期六" | ||
| }, | ||
| "weekdaysShort": { | ||
| "sun": "日", | ||
| "mon": "一", | ||
| "tue": "二", | ||
| "wed": "三", | ||
| "thu": "四", | ||
| "fri": "五", | ||
| "sat": "六" | ||
| }, | ||
| "info": { | ||
| "totalDays": "{year}年总天数", | ||
| "daysPassed": "已过天数", | ||
| "daysRemaining": "剩余天数", | ||
| "weeksPassed": "已过周数", | ||
| "weeksRemaining": "剩余周数" | ||
| }, | ||
| "faq": { | ||
| "title": "常见问题", | ||
| "whatIsCalendar": { | ||
| "question": "什么是年历?", | ||
| "answer": "年历以结构化格式显示特定年份的所有12个月,显示日期、星期几和周数。它有助于日期规划、安排日程和理解年份结构。" | ||
| }, | ||
| "weekNumbers": { | ||
| "question": "什么是周数?", | ||
| "answer": "周数是对一年中的周进行编号的方式,从1到52(某些年份为53)。一年的第一周通常是包含新年至少四天的第一周。" | ||
| }, | ||
| "leapYear": { | ||
| "question": "什么是闰年?", | ||
| "answer": "闰年有366天而不是通常的365天。每4年出现一次(有一些例外),增加一天(2月29日)以保持我们的日历与地球绕太阳轨道对齐。" | ||
| }, | ||
| "monthOverview": { | ||
| "question": "这个月显示了什么信息?", | ||
| "answer": "{month} {year}有{days}天,从{startDay}开始,到{endDay}结束。此日历视图显示所有日期及其对应的工作日和周数。" | ||
| }, | ||
| "weekCount": { | ||
| "question": "这个月有多少周?", | ||
| "answer": "{month}在日历网格上跨越{weeks}周,尽管有些周可能包含上个月或下个月的天数。" | ||
| }, | ||
| "currentMonth": { | ||
| "question": "为什么这个月被突出显示?", | ||
| "answer": "这是当前月份({month} {year})。今天是本月的第{today}天,在日历上以蓝色突出显示。" | ||
| }, | ||
| "monthCharacteristics": { | ||
| "question": "这个月的特点是什么?", | ||
| "february": "{month} {year}有{days}天,使其成为{leapYear}。二月是最短的月份,也是唯一天数可变的月份。", | ||
| "thirtyDays": "{month}是四个恰好有30天的月份之一(四月、六月、九月、十一月)。", | ||
| "thirtyOneDays": "{month}有31天,是一年中七个较长月份之一。" | ||
| }, | ||
| "navigation": { | ||
| "question": "如何导航到其他月份?", | ||
| "answer": "使用箭头按钮前往{prevMonth}或{nextMonth}。您也可以点击下方导航网格中的任何月份,或使用\"查看年份\"按钮一次查看所有月份。" | ||
| } | ||
| }, | ||
| "seo": { | ||
| "title": "关于{year}年日历", | ||
| "yearOverview": { | ||
| "title": "{year}年概览", | ||
| "content": "{year}年日历提供所有12个月的全面视图,总共{totalDays}天。{year}年是{yearType}{leapYearInfo}。我们的{year}年日历包含周数,便于跟踪每周日程并规划整个{year}年。" | ||
| }, | ||
| "features": { | ||
| "title": "我们{year}年日历的特色功能", | ||
| "list": [ | ||
| "完整的{year}年度日历视图,包含所有12个月", | ||
| "{year}年每周的周数显示", | ||
| "用于{year}年规划的交互式日期选择", | ||
| "适合移动设备的{year}年日历设计", | ||
| "免费可打印的{year}年日历格式", | ||
| "{year}年闰年指示器", | ||
| "{year}年日历中当前日期高亮显示", | ||
| "{year}年内的月度导航", | ||
| "年度间日历对比功能" | ||
| ] | ||
| }, | ||
| "usage": { | ||
| "title": "如何使用这个{year}年日历", | ||
| "content": "这个交互式{year}年日历允许您详细浏览{year}年的每个月。点击任何日期查看详细信息,包括周数和年内天数。使用导航箭头浏览不同年份,或点击月份名称查看{year}年的单个月份。{year}年日历非常适合在整个{year}年进行规划、安排日程和组织活动。" | ||
| }, | ||
| "benefits": { | ||
| "title": "使用{year}年日历的好处", | ||
| "content": "全面的{year}年日历帮助您一次性看到整年,使长期规划更容易。包含周数后,您可以与使用基于周的规划的商业日程进行协调。我们的{year}年日历非常适合学术规划、项目管理、假期安排以及整个{year}年的个人组织。" | ||
| } | ||
| }, | ||
| "yearMetaTitle": "{year}年日历 | 完整年历视图 | Datetime.app", | ||
| "yearMetaDescription": "查看{year}年完整日历,包含所有月份、周数和日期。{yearType}{currentYearSuffix}。", | ||
| "yearMetaKeywords": "{year}年日历, 年历, 月历, 日期查询, 周数, {yearType}", | ||
| "monthMetaTitle": "{year}年{month}月日历 | {monthName} {year} | Datetime.app", | ||
| "monthMetaDescription": "查看{year}年{month}月({monthName})完整日历,包含所有日期和星期信息。{currentMonthSuffix}", | ||
| "monthMetaKeywords": "{year}年{month}月, {monthName} {year}, 月历, 日历, 日期查询" | ||
| "lunarDate": "Lunar date", | ||
| "holidays": { |
There was a problem hiding this comment.
Restore missing calendar translations for zh-Hans
The zh-Hans calendar namespace now only contains lunarDate and holidays. Calendar pages still call keys like calendar.toggleTheme, calendar.previousMonth, calendar.daysInMonth, etc., so for zh-Hans/zh-Hant locales next-intl will throw missing-message errors and the calendar routes will fail to render. Keep the existing calendar keys or provide fallbacks for these locales.
Useful? React with 👍 / 👎.
| const totalWorkdays = useMemo(() => { | ||
| if (!startDate || !endDate) return null | ||
| const start = new Date(startDate) | ||
| const end = new Date(endDate) |
There was a problem hiding this comment.
Parse date-only inputs as local dates
Start/end dates are parsed with new Date('YYYY-MM-DD'), which JavaScript treats as UTC. In time zones west of UTC this shifts the date to the previous day, so a user selecting 2026‑01‑01 can be counted as 2025‑12‑31 and the workday count becomes off by one (especially around DST). Use a local date parse (e.g., append T00:00 or manual Y/M/D) before iterating; the holiday list parsing above has the same issue.
Useful? React with 👍 / 👎.
| const regex = new RegExp(`\\b${escapeRegExp(keyword)}\\b`, 'i') | ||
| if (regex.test(updated)) { | ||
| updated = updated.replace( | ||
| regex, | ||
| `<a href="${href}" class="text-primary underline">${keyword}</a>` |
There was a problem hiding this comment.
Prevent nested anchors in linkified content
The linkifier runs on every text segment without tracking whether it is inside an existing <a> tag, so keyword text inside links is replaced with another anchor, producing invalid nested <a> elements. This breaks clicks and accessibility on posts that already link e.g. “UTC”. Consider skipping replacements while inside an anchor (or use an HTML parser).
Useful? React with 👍 / 👎.
| const timer = setInterval(() => { | ||
| const now = new Date() | ||
| const [hour, minute] = alarmTime.split(":").map(Number) | ||
| if (now.getHours() === hour && now.getMinutes() === minute && now.getSeconds() === 0) { | ||
| playSound() |
There was a problem hiding this comment.
Alarm sound can be blocked without user gesture
The alarm path calls playSound() from the interval callback. Because the AudioContext is created only at trigger time, browsers that require a user gesture to start audio will keep the context suspended and the alarm stays silent unless the user manually clicked “Test sound” earlier. Creating/resuming the context on the enable button (or explicitly resuming on user gesture) would avoid silent alarms.
Useful? React with 👍 / 👎.
Motivation
todo.md).Description
todo.mdto mark items completed for programmatic SEO pages and tools such astime-difference, calendar holiday/lunar support,countdown,unix-timestamp,workday-calculator,date-difference,meeting-planner,pomodoro,sunrise-sunset,alarm-stopwatch,life-progress,widgets, blog content additions, structured data (FAQ/WebApplication/Breadcrumb),hreflangchecks, localized date formatting, CLS tweaks, automatic keyword linking, and 404 improvements.Update todo checklist.Testing
todo.md).npm run build:blogand a dev server / Playwright smoke check, which previously completed successfully for the content pipeline.)Codex Task