            begin,end=map(date.fromisoformat,data['window']);start=None;intervals=[]
            for event,reason in zip(data['events'],output['reason_by_event']):
                when=date.fromisoformat(event['at'])
                if reason=='stockout' and start is None:start=when
                elif reason=='restock' and start is not None:intervals.append((max(start,begin),min(when,end)));start=None
            if start is not None:intervals.append((max(start,begin),end))
            bymonth=Counter();all_days=set()
            for first,last in intervals:
                current=first
                while current<last:all_days.add(current);current+=timedelta(days=1)
            for day in all_days:bymonth[day.strftime('%Y-%m')]+=1
            check('半开区间排除补货日',sum(bymonth.values())==7);check('跨月只按实际日期归属',dict(bymonth)=={'2026-09':3,'2026-10':4});check('同一日期不被重复计数',len(all_days)==sum(bymonth.values()));cout('stockout-months.csv',[{'month':k,'days':v} for k,v in sorted(bymonth.items())]);result={'intervals':[[str(x),str(y)] for x,y in intervals],'by_month':dict(bymonth),'total_days':len(all_days),'interval_convention':'start inclusive, restock/end exclusive'}
