diff --git a/CHANGELOG.md b/CHANGELOG.md index a0a9025dbd..db90bb74b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Fix: ドライブのソートが「登録日(昇順)」の場合に正しく動作しない問題を修正 - Fix: 管理画面でアーカイブ済のお知らせを表示した際にアクティブなお知らせが多い旨の警告が出る問題を修正 - Fix: ファイルタブのセンシティブメディアを開く際に確認ダイアログを出す設定が適用されない問題を修正 +- Fix: 2月29日を誕生日に設定している場合、閏年以外は3月1日を誕生日として扱うように修正 ### Server - Enhance: OAuthのクライアント情報取得(Client Information Discovery)において、IndieWeb Living Standard 11 July 2024で定義されているJSONドキュメント形式に対応しました diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 10b1199bbb..3d4ecbf75b 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -29,6 +29,7 @@ import { prefer } from '@/preferences.js'; import { updateCurrentAccountPartial } from '@/accounts.js'; import { migrateOldSettings } from '@/pref-migrate.js'; import { unisonReload } from '@/utility/unison-reload.js'; +import { isBirthday } from '@/utility/is-birthday.js'; export async function mainBoot() { const { isClientUpdated, lastVersion } = await common(async () => { @@ -144,12 +145,8 @@ export async function mainBoot() { const m = now.getMonth() + 1; const d = now.getDate(); - if ($i.birthday) { - const bm = parseInt($i.birthday.split('-')[1]); - const bd = parseInt($i.birthday.split('-')[2]); - if (m === bm && d === bd) { - claimAchievement('loggedInOnBirthday'); - } + if (isBirthday($i, now)) { + claimAchievement('loggedInOnBirthday'); } if (m === 1 && d === 1) { diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index b61c84cbbc..ee04ca9e4a 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -186,6 +186,7 @@ import { getStaticImageUrl } from '@/utility/media-proxy.js'; import MkSparkle from '@/components/MkSparkle.vue'; import { prefer } from '@/preferences.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; +import { isBirthday } from '@/utility/is-birthday.js'; function calcAge(birthdate: string): number { const date = new Date(birthdate); @@ -319,16 +320,10 @@ function disposeBannerParallaxResizeObserver() { onMounted(() => { narrow.value = rootEl.value!.clientWidth < 1000; - if (props.user.birthday) { - const m = new Date().getMonth() + 1; - const d = new Date().getDate(); - const bm = parseInt(props.user.birthday.split('-')[1]); - const bd = parseInt(props.user.birthday.split('-')[2]); - if (m === bm && d === bd) { - confetti({ - duration: 1000 * 4, - }); - } + if (isBirthday(user.value)) { + confetti({ + duration: 1000 * 4, + }); } nextTick(() => { diff --git a/packages/frontend/src/utility/is-birthday.ts b/packages/frontend/src/utility/is-birthday.ts new file mode 100644 index 0000000000..ff875281a2 --- /dev/null +++ b/packages/frontend/src/utility/is-birthday.ts @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as Misskey from 'misskey-js'; + +export function isBirthday(user: Misskey.entities.UserDetailed, now = new Date()): boolean { + if (user.birthday == null) return false; + + const [_, bm, bd] = user.birthday.split('-').map((v) => parseInt(v, 10)); + if (isNaN(bm) || isNaN(bd)) return false; + + const y = now.getFullYear(); + const m = now.getMonth() + 1; + const d = now.getDate(); + + // 閏日生まれで平年の場合は3月1日を誕生日として扱う + if (bm === 2 && bd === 29 && m === 3 && d === 1 && !isLeapYear(y)) { + return true; + } + + return m === bm && d === bd; +} + +function isLeapYear(year: number): boolean { + return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); +} diff --git a/packages/frontend/test/is-birthday.test.ts b/packages/frontend/test/is-birthday.test.ts new file mode 100644 index 0000000000..a072db4416 --- /dev/null +++ b/packages/frontend/test/is-birthday.test.ts @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import * as Misskey from 'misskey-js'; +import { describe, test, expect } from 'vitest'; +import { isBirthday } from '@/utility/is-birthday.js'; + +describe('isBirthday', () => { + test('通常の誕生日', () => { + const currentDate = new Date('2024-05-15'); + const result = isBirthday({ + birthday: '2000-05-15', + } as Misskey.entities.UserDetailed, currentDate); + + expect(result).toBe(true); + }); + + test('誕生日ではない場合', () => { + const currentDate = new Date('2024-05-15'); + const result = isBirthday({ + birthday: '2000-06-20', + } as Misskey.entities.UserDetailed, currentDate); + + expect(result).toBe(false); + }); + + test('平年に閏日生まれを見た際に3月1日を誕生日とする', () => { + const currentDate = new Date('2023-03-01'); + const result = isBirthday({ + birthday: '2000-02-29', + } as Misskey.entities.UserDetailed, currentDate); + + expect(result).toBe(true); + }); + + test('閏年に閏日生まれを見た際に2月29日を誕生日とする', () => { + const currentDate = new Date('2024-02-29'); + const result = isBirthday({ + birthday: '2000-02-29', + } as Misskey.entities.UserDetailed, currentDate); + + expect(result).toBe(true); + }); + + test('閏年に閏日生まれを見た際に3月1日を誕生日としない', () => { + const currentDate = new Date('2024-03-01'); + const result = isBirthday({ + birthday: '2000-02-29', + } as Misskey.entities.UserDetailed, currentDate); + + expect(result).toBe(false); + }); +});