@@ -23,7 +23,7 @@ const PIE_COLORS = [
2323] ;
2424
2525type OverviewMeta = { page : number ; pageSize : number ; totalModels : number ; totalPages : number } ;
26- type OverviewAPIResponse = { overview : UsageOverview | null ; empty : boolean ; days : number ; meta ?: OverviewMeta ; filters ?: { models : string [ ] ; routes : string [ ] } } ;
26+ type OverviewAPIResponse = { overview : UsageOverview | null ; empty : boolean ; days : number ; timezone ?: string ; meta ?: OverviewMeta ; filters ?: { models : string [ ] ; routes : string [ ] } } ;
2727
2828type PriceForm = {
2929 model : string ;
@@ -33,7 +33,6 @@ type PriceForm = {
3333} ;
3434
3535const hourFormatter = new Intl . DateTimeFormat ( "en-CA" , {
36- timeZone : "Asia/Shanghai" ,
3736 month : "2-digit" ,
3837 day : "2-digit" ,
3938 hour : "2-digit" ,
@@ -66,15 +65,21 @@ const numericTooltipFormatter: TooltipProps<number, string>["formatter"] = (valu
6665 return [ formatNumberWithCommas ( numericValue ) , name ] ;
6766} ;
6867
69- function formatHourKeyFromTs ( ts : number ) {
70- const parts = hourFormatter . formatToParts ( new Date ( ts ) ) ;
68+ function formatHourKeyFromTs ( ts : number , formatter : Intl . DateTimeFormat ) {
69+ const parts = formatter . formatToParts ( new Date ( ts ) ) ;
7170 const month = parts . find ( ( p ) => p . type === "month" ) ?. value ?? "00" ;
7271 const day = parts . find ( ( p ) => p . type === "day" ) ?. value ?? "00" ;
7372 const hour = parts . find ( ( p ) => p . type === "hour" ) ?. value ?? "00" ;
7473 return `${ month } -${ day } ${ hour } ` ;
7574}
7675
77- function buildHourlySeries ( series : UsageSeriesPoint [ ] , rangeHours ?: number ) {
76+ function buildHourlySeries ( series : UsageSeriesPoint [ ] , rangeHours ?: number , timezone ?: string ) {
77+ // Use the server's bucketing timezone for gap-fill labels so they match the
78+ // labels returned for real data points. Falls back to the module-level formatter
79+ // (browser timezone) when no timezone is provided.
80+ const gapFormatter = timezone
81+ ? new Intl . DateTimeFormat ( "en-CA" , { timeZone : timezone , month : "2-digit" , day : "2-digit" , hour : "2-digit" , hour12 : false } )
82+ : hourFormatter ;
7883 if ( ! series . length ) return [ ] as UsageSeriesPoint [ ] ;
7984
8085 const withTs = series
@@ -98,7 +103,7 @@ function buildHourlySeries(series: UsageSeriesPoint[], rangeHours?: number) {
98103 filled . push ( rest ) ;
99104 } else {
100105 filled . push ( {
101- label : formatHourKeyFromTs ( ts ) ,
106+ label : formatHourKeyFromTs ( ts , gapFormatter ) ,
102107 timestamp : new Date ( ts ) . toISOString ( ) ,
103108 requests : 0 ,
104109 tokens : 0 ,
@@ -167,6 +172,7 @@ export default function DashboardPage() {
167172 }
168173 } , [ ] ) ;
169174 const [ overview , setOverview ] = useState < UsageOverview | null > ( null ) ;
175+ const [ bucketTimezone , setBucketTimezone ] = useState < string | undefined > ( undefined ) ;
170176 const [ overviewError , setOverviewError ] = useState < string | null > ( null ) ;
171177 const [ overviewEmpty , setOverviewEmpty ] = useState ( false ) ;
172178 const [ loadingOverview , setLoadingOverview ] = useState ( true ) ;
@@ -730,6 +736,7 @@ export default function DashboardPage() {
730736 const data : OverviewAPIResponse = await res . json ( ) ;
731737 if ( ! active ) return ;
732738 setOverview ( data . overview ?? null ) ;
739+ setBucketTimezone ( data . timezone ) ;
733740 setOverviewEmpty ( Boolean ( data . empty ) ) ;
734741 setOverviewError ( null ) ;
735742 setPage ( data . meta ?. page ?? 1 ) ;
@@ -760,8 +767,8 @@ export default function DashboardPage() {
760767 if ( ! overviewData ?. byHour ) return [ ] as UsageSeriesPoint [ ] ;
761768 if ( hourRange === "all" ) return overviewData . byHour ;
762769 const hours = hourRange === "24h" ? 24 : 72 ;
763- return buildHourlySeries ( overviewData . byHour , hours ) ;
764- } , [ hourRange , overviewData ?. byHour ] ) ;
770+ return buildHourlySeries ( overviewData . byHour , hours , bucketTimezone ) ;
771+ } , [ hourRange , overviewData ?. byHour , bucketTimezone ] ) ;
765772
766773 const hourlyLineStyle = useMemo (
767774 ( ) => buildHourlyLineStyle ( hourlySeries . length , 3 ) ,
0 commit comments