Newer
Older
client_dwatch / lib / format / consecutive-days.ts
import { CONSECUTIVE_DAYS_DISPLAY } from "@/lib/config";

/**
 * 連続日数の表示上限(指示書AF 段階2 / ユーザー指示 2026-09-13)
 *
 * ★`lib/judge/` ではなく `lib/format/` に置いている★ —
 *   これは**判定**ではなく**表示整形**だから(`CLAUDE.md` §9.1:
 *   `lib/judge/` は「◎○△×、L1〜L3、KPI計算、丸め」)。
 *   L1〜L3 と R1〜R4 の判定は★上限をかける前の生値★で行う(`CLAUDE.md` §4.3 と同じ思想)。
 */

export interface CappedConsecutiveDays {
  /** 表示に使う日数。`rawDays` が上限以下ならそのまま */
  days: number;
  /** ★上限を「超えた」か★。★上限ちょうどは false★ */
  capped: boolean;
}

/**
 * 連続日数に表示上限をかける。★純粋関数★(設定の読み込みを混ぜない)。
 *
 * ★`capped` は「超えた」ときだけ true★ — 上限ちょうど(`rawDays === maxDays`)は
 *   「ちょうど30日」であって「30日以上」ではないので false。
 *   ★★指示書AC 段階6 でクエリを有界にするときは「上限+1日ぶん」読むこと★★。
 *   ぴったり上限ぶんしか読まないと `rawDays` が上限を超えられず、
 *   ★`capped` が永久に false になって「30日以上」が画面から消える★。
 *
 * ★`maxDays` は呼び出し側が渡す★ — 保存された設定値
 *   (`lib/settings/display-limits.ts` の `loadConsecutiveDaysMax()`)を渡すため。
 *   省略したときだけ `lib/config` の既定値を使う。
 */
export function capConsecutiveDays(
  rawDays: number,
  maxDays: number = CONSECUTIVE_DAYS_DISPLAY.defaultMax
): CappedConsecutiveDays {
  if (rawDays > maxDays) return { days: maxDays, capped: true };
  return { days: rawDays, capped: false };
}