Newer
Older
client_dwatch / lib / format / schedule.ts
/**
 * スケジュール関連の表示整形(`prismaSource.schedules()` 専用。DBに無い項目のサーバ生成)。
 *
 * ★曜日の起点は日曜★(`lib/fixtures/schedules.ts:6`「weekdays は『日月火水木金土』の7桁」)。
 * この起点をフロントと2箇所に持たないこと(CLAUDE.md §9.6 / 指示書L 段階5-3)。
 */

const WEEKDAY_CHARS = ["日", "月", "火", "水", "木", "金", "土"] as const;

/**
 * `weeks`(7桁。清掃する曜日が"1")から表示用ラベルを生成する。
 *
 * 「毎日」「平日のみ」「土日のみ」の3特例を先に判定し、それ以外は該当曜日を連結する。
 * 該当曜日が1つも無ければ "なし" を返す(空文字を返すと画面が空欄になり、不具合と区別できない)。
 * 桁が足りない文字列でも例外を投げない(存在しない桁は "1" と一致せず、単に選ばれないだけ)。
 */
export function weekdaysLabel(weeks: string): string {
  switch (weeks) {
    case "1111111":
      return "毎日";
    case "0111110":
      return "平日のみ";
    case "1000001":
      return "土日のみ";
  }
  const days = WEEKDAY_CHARS.filter((_, i) => weeks[i] === "1");
  return days.length > 0 ? days.join("") : "なし";
}

function pad2(n: number): string {
  return String(n).padStart(2, "0");
}

/** `start_time`(HHMM。0000〜2359のみ。D-057 B-5)を "HH:MM" に整形する */
export function formatStartTime(hhmm: string): string {
  const hour = Number.parseInt(hhmm.slice(0, 2), 10);
  const minute = hhmm.slice(2, 4);
  if (!(hour >= 0 && hour <= 23)) {
    throw new RangeError(`formatStartTime: 想定外の時刻です(0〜23時のみ対応): ${hhmm}`);
  }
  return `${pad2(hour)}:${minute}`;
}

/**
 * `end_time`(HHMM。時は24〜29を取りうる。D-057 B-5 / D-060)を整形する。
 *
 * 24〜29時は日跨ぎを表す。時から24を引いて「翌HH:MM」にする(例: "2830" → "翌04:30")。
 * ★0〜29時の範囲外(例: "3000")は例外を投げる★ — DBの値域外なので、
 * 静かに変な表示になるより止まるほうがよい。
 */
export function formatEndTime(hhmm: string): string {
  const hour = Number.parseInt(hhmm.slice(0, 2), 10);
  const minute = hhmm.slice(2, 4);
  if (hour >= 24 && hour <= 29) {
    return `翌${pad2(hour - 24)}:${minute}`;
  }
  if (hour >= 0 && hour <= 23) {
    return `${pad2(hour)}:${minute}`;
  }
  throw new RangeError(`formatEndTime: 想定外の時刻です(0〜29時のみ対応): ${hhmm}`);
}