From 9268cac0302bba6c845fb2664dea9d1a69929a08 Mon Sep 17 00:00:00 2001 From: coji Date: Mon, 30 Mar 2026 17:21:21 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20sinceDate=20=E3=81=AE=E7=AE=97?= =?UTF-8?q?=E5=87=BA=E3=82=92=E7=B5=84=E7=B9=94=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=82=BE=E3=83=BC=E3=83=B3=E5=9F=BA=E6=BA=96=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit analysis/reviews と analysis/feedbacks の sinceDate 算出が UTC 固定だったのを、 analysis/inventory と同様に timezoneContext から組織タイムゾーンを取得して 日付境界を計算するように修正。 Closes #256 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../$orgSlug/analysis/feedbacks/_index/index.tsx | 11 +++++++++-- app/routes/$orgSlug/analysis/reviews/index.tsx | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx b/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx index 049553f2..d6873745 100644 --- a/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx +++ b/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx @@ -30,7 +30,7 @@ import { TableRow, } from '~/app/components/ui/table' import dayjs from '~/app/libs/dayjs' -import { orgContext } from '~/app/middleware/context' +import { orgContext, timezoneContext } from '~/app/middleware/context' import { listTeams } from '~/app/routes/$orgSlug/settings/teams._index/queries.server' import { DataTablePagination } from './+components/data-table-pagination' import { feedbackColumns } from './+components/feedback-columns' @@ -60,6 +60,7 @@ const VALID_PERIODS = [1, 3, 6, 12] export const loader = async ({ request, context }: Route.LoaderArgs) => { const { organization } = context.get(orgContext) + const timezone = context.get(timezoneContext) const url = new URL(request.url) const teamParam = url.searchParams.get('team') || undefined @@ -74,7 +75,13 @@ export const loader = async ({ request, context }: Route.LoaderArgs) => { const sinceDate = periodMonths === 'all' ? '2000-01-01T00:00:00.000Z' - : dayjs.utc().subtract(periodMonths, 'month').startOf('day').toISOString() + : dayjs + .utc() + .tz(timezone) + .subtract(periodMonths, 'month') + .startOf('day') + .utc() + .toISOString() const page = Number(url.searchParams.get('page') || '1') const perPage = Number(url.searchParams.get('per_page') || '20') diff --git a/app/routes/$orgSlug/analysis/reviews/index.tsx b/app/routes/$orgSlug/analysis/reviews/index.tsx index e4edb7bc..eb857f27 100644 --- a/app/routes/$orgSlug/analysis/reviews/index.tsx +++ b/app/routes/$orgSlug/analysis/reviews/index.tsx @@ -16,7 +16,7 @@ import { } from '~/app/components/ui/select' import { Stack } from '~/app/components/ui/stack' import dayjs from '~/app/libs/dayjs' -import { orgContext } from '~/app/middleware/context' +import { orgContext, timezoneContext } from '~/app/middleware/context' import { listTeams } from '~/app/routes/$orgSlug/settings/teams._index/queries.server' import { getOrgCachedData } from '~/app/services/cache.server' import { PRSizeChart } from './+components/pr-size-chart' @@ -50,6 +50,7 @@ const PERIOD_OPTIONS = [ export const loader = async ({ request, context }: Route.LoaderArgs) => { const { organization } = context.get(orgContext) + const timezone = context.get(timezoneContext) const url = new URL(request.url) const teamParam = url.searchParams.get('team') @@ -65,7 +66,13 @@ export const loader = async ({ request, context }: Route.LoaderArgs) => { const sinceDate = periodMonths === 'all' ? '2000-01-01T00:00:00.000Z' - : dayjs.utc().subtract(periodMonths, 'month').startOf('day').toISOString() + : dayjs + .utc() + .tz(timezone) + .subtract(periodMonths, 'month') + .startOf('day') + .utc() + .toISOString() const teams = await listTeams(organization.id) From 2a2f4a0b681ed3655e68aa00e3f8fb94feb4d041 Mon Sep 17 00:00:00 2001 From: coji Date: Mon, 30 Mar 2026 17:25:47 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20sinceDate=20=E7=AE=97=E5=87=BA?= =?UTF-8?q?=E3=82=92=20calcSinceDate=20=E3=83=A6=E3=83=BC=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=83=AA=E3=83=86=E3=82=A3=E3=81=AB=E5=85=B1=E9=80=9A?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit reviews, feedbacks, inventory の 3 ファイルで重複していた dayjs.utc().tz(timezone).subtract(...).startOf('day').utc().toISOString() パターンを app/libs/date-utils.ts の calcSinceDate() に抽出。 Co-Authored-By: Claude Opus 4.6 (1M context) --- app/libs/date-utils.ts | 18 ++++++++++++++++++ .../analysis/feedbacks/_index/index.tsx | 13 ++----------- .../$orgSlug/analysis/inventory/index.tsx | 9 ++------- app/routes/$orgSlug/analysis/reviews/index.tsx | 13 ++----------- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/app/libs/date-utils.ts b/app/libs/date-utils.ts index a6c9dd77..47e56d29 100644 --- a/app/libs/date-utils.ts +++ b/app/libs/date-utils.ts @@ -1,5 +1,23 @@ import dayjs from '~/app/libs/dayjs' +/** + * 組織タイムゾーン基準で N ヶ月前の日付境界を UTC ISO 文字列で返す。 + * 'all' の場合はエポック相当の固定値を返す。 + */ +export function calcSinceDate( + periodMonths: number | 'all', + timezone: string, +): string { + if (periodMonths === 'all') return '2000-01-01T00:00:00.000Z' + return dayjs + .utc() + .tz(timezone) + .subtract(periodMonths, 'month') + .startOf('day') + .utc() + .toISOString() +} + export const parseDate = (date: string | null, timeZone: string) => { const dt = date ? dayjs(date, 'YYYY-MM-DD') : dayjs() return dt.tz(timeZone).startOf('day') diff --git a/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx b/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx index d6873745..30342166 100644 --- a/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx +++ b/app/routes/$orgSlug/analysis/feedbacks/_index/index.tsx @@ -29,7 +29,7 @@ import { TableHeader, TableRow, } from '~/app/components/ui/table' -import dayjs from '~/app/libs/dayjs' +import { calcSinceDate } from '~/app/libs/date-utils' import { orgContext, timezoneContext } from '~/app/middleware/context' import { listTeams } from '~/app/routes/$orgSlug/settings/teams._index/queries.server' import { DataTablePagination } from './+components/data-table-pagination' @@ -72,16 +72,7 @@ export const loader = async ({ request, context }: Route.LoaderArgs) => { ? Number(periodParam) : 1 - const sinceDate = - periodMonths === 'all' - ? '2000-01-01T00:00:00.000Z' - : dayjs - .utc() - .tz(timezone) - .subtract(periodMonths, 'month') - .startOf('day') - .utc() - .toISOString() + const sinceDate = calcSinceDate(periodMonths, timezone) const page = Number(url.searchParams.get('page') || '1') const perPage = Number(url.searchParams.get('per_page') || '20') diff --git a/app/routes/$orgSlug/analysis/inventory/index.tsx b/app/routes/$orgSlug/analysis/inventory/index.tsx index bc33f245..1549892d 100644 --- a/app/routes/$orgSlug/analysis/inventory/index.tsx +++ b/app/routes/$orgSlug/analysis/inventory/index.tsx @@ -16,6 +16,7 @@ import { SelectValue, } from '~/app/components/ui/select' import { Switch } from '~/app/components/ui/switch' +import { calcSinceDate } from '~/app/libs/date-utils' import dayjs from '~/app/libs/dayjs' import { orgContext, timezoneContext } from '~/app/middleware/context' import { listTeams } from '~/app/routes/$orgSlug/settings/teams._index/queries.server' @@ -54,13 +55,7 @@ export const loader = async ({ request, context }: Route.LoaderArgs) => { const excludeBots = url.searchParams.get('excludeBots') !== '0' const unreviewedOnly = url.searchParams.get('unreviewedOnly') === '1' - const sinceDate = dayjs - .utc() - .tz(timezone) - .subtract(periodMonths, 'month') - .startOf('day') - .utc() - .toISOString() + const sinceDate = calcSinceDate(periodMonths, timezone) const now = dayjs.utc().toISOString() diff --git a/app/routes/$orgSlug/analysis/reviews/index.tsx b/app/routes/$orgSlug/analysis/reviews/index.tsx index eb857f27..34817f80 100644 --- a/app/routes/$orgSlug/analysis/reviews/index.tsx +++ b/app/routes/$orgSlug/analysis/reviews/index.tsx @@ -15,7 +15,7 @@ import { SelectValue, } from '~/app/components/ui/select' import { Stack } from '~/app/components/ui/stack' -import dayjs from '~/app/libs/dayjs' +import { calcSinceDate } from '~/app/libs/date-utils' import { orgContext, timezoneContext } from '~/app/middleware/context' import { listTeams } from '~/app/routes/$orgSlug/settings/teams._index/queries.server' import { getOrgCachedData } from '~/app/services/cache.server' @@ -63,16 +63,7 @@ export const loader = async ({ request, context }: Route.LoaderArgs) => { ? Number(periodParam) : 3 - const sinceDate = - periodMonths === 'all' - ? '2000-01-01T00:00:00.000Z' - : dayjs - .utc() - .tz(timezone) - .subtract(periodMonths, 'month') - .startOf('day') - .utc() - .toISOString() + const sinceDate = calcSinceDate(periodMonths, timezone) const teams = await listTeams(organization.id)