Openstatus www.openstatus.dev

fix: maintenance window minute per day (#1399)

authored by

Maximilian Kaske and committed by
GitHub
2d20bac1 cf85b3d2

+47 -17
+1 -1
apps/web/next.config.js
··· 165 165 166 166 // Automatically tree-shake Sentry logger statements to reduce bundle size 167 167 disableLogger: true, 168 - } 168 + }, 169 169 );
+46 -16
packages/api/src/router/statusPage.utils.ts
··· 271 271 ); 272 272 } 273 273 274 + // Helper to calculate total minutes in a day (handles today vs past days) 275 + function getTotalMinutesInDay(date: Date): number { 276 + const now = new Date(); 277 + const startOfDay = new Date(date); 278 + startOfDay.setUTCHours(0, 0, 0, 0); 279 + 280 + if (isToday(date)) { 281 + const minutesElapsed = Math.floor( 282 + (now.getTime() - startOfDay.getTime()) / MILLISECONDS_PER_MINUTE, 283 + ); 284 + return minutesElapsed; 285 + } 286 + return 24 * 60; 287 + } 288 + 289 + // Helper to calculate duration in minutes for a specific event type 290 + function calculateEventDurationMinutes(events: Event[], date: Date): number { 291 + const totalDuration = getTotalEventsDurationMs(events, date); 292 + return Math.round(totalDuration / MILLISECONDS_PER_MINUTE); 293 + } 294 + 295 + // Helper to calculate maintenance duration in minutes for a specific day 296 + function getMaintenanceDurationMinutes( 297 + maintenances: Event[], 298 + date: Date, 299 + ): number { 300 + return calculateEventDurationMinutes(maintenances, date); 301 + } 302 + 303 + // Helper to get adjusted total minutes accounting for maintenance 304 + function getAdjustedTotalMinutesInDay( 305 + date: Date, 306 + maintenances: Event[], 307 + ): number { 308 + const totalMinutes = getTotalMinutesInDay(date); 309 + const maintenanceMinutes = getMaintenanceDurationMinutes(maintenances, date); 310 + return Math.max(0, totalMinutes - maintenanceMinutes); 311 + } 312 + 274 313 function getTotalEventsDurationMs(events: Event[], date: Date): number { 275 314 if (events.length === 0) return 0; 276 315 ··· 453 492 return Math.round(totalDuration / MILLISECONDS_PER_MINUTE); 454 493 } 455 494 456 - // Helper to calculate total minutes in a day (handles today vs past days) 457 - function getTotalMinutesInDay(date: Date): number { 458 - const now = new Date(); 459 - const startOfDay = new Date(date); 460 - startOfDay.setUTCHours(0, 0, 0, 0); 461 - 462 - if (isToday(date)) { 463 - const minutesElapsed = Math.floor( 464 - (now.getTime() - startOfDay.getTime()) / MILLISECONDS_PER_MINUTE, 465 - ); 466 - return minutesElapsed; 467 - } 468 - return 24 * 60; 469 - } 470 - 471 495 // Helper to create duration card data for a specific status 472 496 function createDurationCardEntry( 473 497 status: "error" | "degraded" | "info" | "success", 474 498 events: Event[], 475 499 date: Date, 476 500 durationMap: Map<string, number>, 501 + maintenances: Event[] = [], 477 502 ): { 478 503 status: "error" | "degraded" | "info" | "success"; 479 504 value: string; ··· 484 509 // biome-ignore lint/suspicious/noAssignInExpressions: <explanation> 485 510 durationMap.forEach((minutes) => (totalEventMinutes += minutes)); 486 511 487 - const totalMinutesInDay = getTotalMinutesInDay(date); 512 + // Use adjusted total minutes accounting for maintenance 513 + const totalMinutesInDay = getAdjustedTotalMinutesInDay( 514 + date, 515 + maintenances, 516 + ); 488 517 const successMinutes = Math.max(totalMinutesInDay - totalEventMinutes, 0); 489 518 490 519 if (successMinutes === 0) return null; ··· 634 663 events, 635 664 date, 636 665 durationMap, 666 + maintenances, 637 667 ); 638 668 }) 639 669 .filter((item): item is NonNullable<typeof item> => item !== null);