@@ -272,6 +272,61 @@ export class AvailableSlotsService {
272272 return periodType === PeriodType . ROLLING_WINDOW || periodType === PeriodType . ROLLING ;
273273 }
274274
275+ /**
276+ * Filters slots to only include dates within the requested range.
277+ * This is necessary because buildDateRanges uses a ±1 day buffer when checking
278+ * if date overrides should be included (to handle timezone edge cases), which can
279+ * cause slots from adjacent days to leak into the response.
280+ */
281+ private _filterSlotsByRequestedDateRange <
282+ T extends Record < string , { time : string ; attendees ?: number ; bookingUid ?: string } [ ] > ,
283+ > ( {
284+ slotsMappedToDate,
285+ startTime,
286+ endTime,
287+ timeZone,
288+ } : {
289+ slotsMappedToDate : T ;
290+ startTime : string ;
291+ endTime : string ;
292+ timeZone : string | undefined ;
293+ } ) : T {
294+ if ( ! timeZone ) {
295+ return slotsMappedToDate ;
296+ }
297+ const inputStartTime = dayjs ( startTime ) . tz ( timeZone ) ;
298+ const inputEndTime = dayjs ( endTime ) . tz ( timeZone ) ;
299+
300+ // fr-CA uses YYYY-MM-DD format
301+ const formatter = new Intl . DateTimeFormat ( "fr-CA" , {
302+ year : "numeric" ,
303+ month : "2-digit" ,
304+ day : "2-digit" ,
305+ timeZone : timeZone ,
306+ } ) ;
307+
308+ const allowedDates = new Set < string > ( ) ;
309+ for (
310+ let d = inputStartTime . startOf ( "day" ) ;
311+ ! d . isAfter ( inputEndTime , "day" ) ;
312+ d = d . add ( 1 , "day" )
313+ ) {
314+ allowedDates . add ( formatter . format ( d . toDate ( ) ) ) ;
315+ }
316+
317+ const filtered = { } as T ;
318+ for ( const [ date , slots ] of Object . entries ( slotsMappedToDate ) ) {
319+ if ( allowedDates . has ( date ) ) {
320+ ( filtered as Record < string , typeof slots > ) [ date ] = slots ;
321+ }
322+ }
323+ return filtered ;
324+ }
325+ private filterSlotsByRequestedDateRange = withReporting (
326+ this . _filterSlotsByRequestedDateRange . bind ( this ) ,
327+ "filterSlotsByRequestedDateRange"
328+ ) ;
329+
275330 private _getAllDatesWithBookabilityStatus ( availableDates : string [ ] ) {
276331 const availableDatesSet = new Set ( availableDates ) ;
277332 const firstDate = dayjs ( availableDates [ 0 ] ) ;
@@ -1431,8 +1486,15 @@ export class AvailableSlotsService {
14311486 ) ;
14321487 const withinBoundsSlotsMappedToDate = mapWithinBoundsSlotsToDate ( ) ;
14331488
1489+ const filteredSlotsMappedToDate = this . filterSlotsByRequestedDateRange ( {
1490+ slotsMappedToDate : withinBoundsSlotsMappedToDate ,
1491+ startTime : input . startTime ,
1492+ endTime : input . endTime ,
1493+ timeZone : input . timeZone ,
1494+ } ) ;
1495+
14341496 // We only want to run this on single targeted events and not dynamic
1435- if ( ! Object . keys ( withinBoundsSlotsMappedToDate ) . length && input . usernameList ?. length === 1 ) {
1497+ if ( ! Object . keys ( filteredSlotsMappedToDate ) . length && input . usernameList ?. length === 1 ) {
14361498 try {
14371499 await this . dependencies . noSlotsNotificationService . handleNotificationWhenNoSlots ( {
14381500 eventDetails : {
@@ -1474,7 +1536,7 @@ export class AvailableSlotsService {
14741536 : null ;
14751537
14761538 return {
1477- slots : withinBoundsSlotsMappedToDate ,
1539+ slots : filteredSlotsMappedToDate ,
14781540 ...troubleshooterData ,
14791541 } ;
14801542 }
0 commit comments