/** * Mid-session spoken birth-time windows are not a search-window change. * Detect them so the route can answer with a fixed reply and skip the model. * * Clock-shaped text is not enough (BUG-631): dated life events and other * non-birth sentences that happen to contain HH:MM must still reach the model. */ const CLOCK = /(?:[01]?\d|2[0-3])\s*[::点]\s*(?:半|[0-5]?\d(?:\s*分)?)?/; const RANGE_SEP = /\s*(?:到|至|[-–—~~])\s*/; const AROUND = /\s*(?:左右|前后)/; const RANGE_PATTERN = new RegExp(`(${CLOCK.source})${RANGE_SEP.source}(${CLOCK.source})`); const AROUND_PATTERN = new RegExp(`(${CLOCK.source})${AROUND.source}`); const BIRTH_CONTEXT = /出生时间|出生|生于|时辰|几点生|钟点是|(? 23) return null; return `${String(hour).padStart(2, "0")}:30`; } const withMinutes = /([01]?\d|2[0-3])\s*[::点]\s*([0-5]?\d)/.exec(raw); if (withMinutes) { const hour = Number(withMinutes[1]); const minute = Number(withMinutes[2]); if (!Number.isInteger(hour) || hour > 23 || !Number.isInteger(minute) || minute > 59) return null; return `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}`; } const hourOnly = /([01]?\d|2[0-3])\s*点/.exec(raw); if (!hourOnly) return null; const hour = Number(hourOnly[1]); if (!Number.isInteger(hour) || hour > 23) return null; return `${String(hour).padStart(2, "0")}:00`; }