@@ -36,6 +36,7 @@ class DailyBucket:
3636
3737 date : str # ISO YYYY-MM-DD
3838 status_level : int # 0 ok, 1 minor (degraded/unknown), 2 major (partial/down), 3 maintenance
39+ observed_seconds : int # elapsed/observable seconds in this day (86400 for past days, partial for today)
3940 operational_seconds : int
4041 degraded_seconds : int
4142 down_seconds : int
@@ -162,10 +163,13 @@ async def daily_buckets(
162163 if secs > 0 and STATUS_RANK [status ] > STATUS_RANK [worst_status ]:
163164 worst_status = status
164165 worst_level = _LEVEL_BY_STATUS [worst_status ]
166+ observed_end = min (anchor , day_end )
167+ observed_seconds = max (0 , int ((observed_end - day_start ).total_seconds ()))
165168 buckets .append (
166169 DailyBucket (
167170 date = day_start .date ().isoformat (),
168171 status_level = worst_level ,
172+ observed_seconds = observed_seconds ,
169173 operational_seconds = seconds [ComponentStatusValue .OPERATIONAL ],
170174 degraded_seconds = seconds [ComponentStatusValue .DEGRADED ],
171175 down_seconds = seconds [ComponentStatusValue .DOWN ] + seconds [ComponentStatusValue .PARTIAL ],
@@ -190,7 +194,10 @@ async def uptime_percent(
190194 buckets = await self .daily_buckets (component_id , days = days , now = now )
191195 operational = sum (b .operational_seconds for b in buckets )
192196 maintenance = sum (b .maintenance_seconds for b in buckets )
193- non_maintenance = days * 86_400 - maintenance
197+ # Use elapsed/observable time, not days * 86400: the current day is only
198+ # partially elapsed, so counting its future remainder would understate uptime.
199+ observable = sum (b .observed_seconds for b in buckets )
200+ non_maintenance = observable - maintenance
194201 if non_maintenance <= 0 :
195202 return None
196203 observed = operational + sum ((b .degraded_seconds + b .down_seconds + b .unknown_seconds ) for b in buckets )
0 commit comments