I am trying to create timeslot for my availability by providing each days working hours in numeric range. i provide some specific days availability in list of date range form. also providing blocked days as list of dates and booked date ranges as list of date ranges. i am also passing meeting duration, minute interval, month offset. and 2 time zones one is source time zone and another is target time zone. all i want to create a list of dates after removing all the booked datetimes and blocked days from source time zone to target time zone using luxon library.
like if i provide 540-720 as a numeric range with time interval of 30 minutes and duration of 30 mins with month offset as 0 (for current month) as a monday availability, it will return me list of dates for all the Monday of upcoming month like below
Jun 16, 2025 9:00 pm, Jun 16, 2025 9:30 pm, Jun 16, 2025 10:30 pm, Jun 16, 2025 11:00 pm,Jun 16, 2025 11:30 pm, Jun 16, 2025 12:00 pm.. here we dont have date Jun 16, 2025 10:00 pm as that time is booked.
In below code instance.publishState will return values from my bubble.io plugin to bubble.io app. Below code is converting my numeric ranges into list of dates when i provide Australia/Adelaide as timezone to my system’s current timezone but not to targetted timezone. you can see the actual working on calendly’s calendar link of event for the reference.
function(instance, properties, context) {
if (null !== properties.sunday_pattern ? sunRanges = properties.sunday_pattern.get(0, properties.sunday_pattern.length()) : sunRanges = [],
null !== properties.monday_pattern ? monRanges = properties.monday_pattern.get(0, properties.monday_pattern.length()) : monRanges = [],
null !== properties.tuesday_pattern ? tueRanges = properties.tuesday_pattern.get(0, properties.tuesday_pattern.length()) : tueRanges = [],
null !== properties.wednesday_pattern ? wedRanges = properties.wednesday_pattern.get(0, properties.wednesday_pattern.length()) : wedRanges = [],
null !== properties.thursday_pattern ? thuRanges = properties.thursday_pattern.get(0, properties.thursday_pattern.length()) : thuRanges = [],
null !== properties.friday_pattern ? friRanges = properties.friday_pattern.get(0, properties.friday_pattern.length()) : friRanges = [],
null !== properties.saturday_pattern ? satRanges = properties.saturday_pattern.get(0, properties.saturday_pattern.length()) : satRanges = [],
null !== properties.available_date_ranges ? availRanges = properties.available_date_ranges.get(0, properties.available_date_ranges.length()) : availRanges = [],
null !== properties.blocked_date_ranges ? blockedDays = properties.blocked_date_ranges.get(0, properties.blocked_date_ranges.length()) : blockedDays = [],
null !== properties.booked_date_ranges ? bookedDays = properties.booked_date_ranges.get(0, properties.booked_date_ranges.length()) : bookedDays = [],
"Calendar Month" == properties.typeAvail)
var localCalStart = luxon.DateTime.now().setZone(properties.time_zone).plus({
months: properties.offset
}), monthStart = localCalStart.startOf("month"), endMonthCal = localCalStart.endOf("month").plus({
days: 1
}), startFilter, setStart = startFilter = monthStart.minus({
days: 1
}), setEnd = endMonthCal;
if ("Between These Dates" == properties.typeAvail)
var setStart = luxon.DateTime.fromMillis(properties.set_start.getTime())
, setEnd = luxon.DateTime.fromMillis(properties.set_end.getTime())
, startFilter = setStart.setZone(properties.time_zone).minus({
days: 1
})
, monthStart = setStart.setZone(properties.time_zone)
, endMonthCal = setEnd.setZone(properties.time_zone).plus({
days: 1
});
var sundayLuxonDates = [];
if (sunRanges.length > 0)
for (var sunday = monthStart.startOf("week").plus({
days: 6
}); sunday <= endMonthCal; )
sunday >= startFilter && sundayLuxonDates.push(sunday),
sunday = sunday.plus({
weeks: 1
});
var mondayLuxonDates = [];
if (monRanges.length > 0)
for (var monday = monthStart.startOf("week"); monday <= endMonthCal; )
monday >= startFilter && mondayLuxonDates.push(monday),
monday = monday.plus({
weeks: 1
});
var tuesdayLuxonDates = [];
if (tueRanges.length > 0)
for (var tuesday = monthStart.startOf("week").plus({
days: 1
}); tuesday <= endMonthCal; )
tuesday >= startFilter && tuesdayLuxonDates.push(tuesday),
tuesday = tuesday.plus({
weeks: 1
});
var wednesdayLuxonDates = [];
if (wedRanges.length > 0)
for (var wednesday = monthStart.startOf("week").plus({
days: 2
}); wednesday <= endMonthCal; )
wednesday >= startFilter && wednesdayLuxonDates.push(wednesday),
wednesday = wednesday.plus({
weeks: 1
});
var thursdayLuxonDates = [];
if (thuRanges.length > 0)
for (var thursday = monthStart.startOf("week").plus({
days: 3
}); thursday <= endMonthCal; )
thursday >= monthStart && thursdayLuxonDates.push(thursday),
thursday = thursday.plus({
weeks: 1
});
var fridayLuxonDates = [];
if (friRanges.length > 0)
for (var friday = monthStart.startOf("week").plus({
days: 4
}); friday <= endMonthCal; )
friday >= monthStart && fridayLuxonDates.push(friday),
friday = friday.plus({
weeks: 1
});
var saturdayLuxonDates = [];
if (satRanges.length > 0)
for (var saturday = monthStart.startOf("week").plus({
days: 5
}); saturday <= endMonthCal; )
saturday >= monthStart && saturdayLuxonDates.push(saturday),
saturday = saturday.plus({
weeks: 1
});
var minuteInterval = properties.minute_interval;
if (null == minuteInterval || minuteInterval <= 0)
return console.log("Please enter a Minute Interval value. Must be a number value greater than zero"),
!1;
var availTimeSlots = []
, blockedLuxonDates = []
, bookingLength = properties.booking_duration;
if (null == bookingLength || bookingLength <= 0)
return console.log("Please enter a Booking Duration value. Must be a number value greater than zero"),
!1;
for (var x = 0; x < availRanges.length; x++) {
blockedLuxonDates.push(luxon.DateTime.fromMillis(availRanges[x][0].getTime()).setZone(properties.time_zone));
for (var i = 0; i <= availRanges[x][1].getTime() - availRanges[x][0].getTime() - 6e4 * bookingLength; i += 6e4 * minuteInterval)
availTimeSlots.push(availRanges[x][0].getTime() + i)
}
for (var x = 0; x < blockedDays.length; x++)
blockedLuxonDates.push(luxon.DateTime.fromMillis(blockedDays[x].getTime()).setZone(properties.time_zone));
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < sundayLuxonDates.length; i++)
sundayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && sundayLuxonDates.splice([i], 1);
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < mondayLuxonDates.length; i++)
mondayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && mondayLuxonDates.splice([i], 1);
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < tuesdayLuxonDates.length; i++)
tuesdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && tuesdayLuxonDates.splice([i], 1);
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < wednesdayLuxonDates.length; i++)
wednesdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && wednesdayLuxonDates.splice([i], 1);
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < thursdayLuxonDates.length; i++)
thursdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && thursdayLuxonDates.splice([i], 1);
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < fridayLuxonDates.length; i++)
fridayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && fridayLuxonDates.splice([i], 1);
for (var x = 0; x < blockedLuxonDates.length; x++)
for (var i = 0; i < saturdayLuxonDates.length; i++)
saturdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && saturdayLuxonDates.splice([i], 1);
for (var sunTimeSlots = [], sunLuxonIntervals = [], x = 0; x < sunRanges.length; x++)
for (var i = 0; i < sundayLuxonDates.length; i++)
sunLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: sundayLuxonDates[i].year,
month: sundayLuxonDates[i].month,
day: sundayLuxonDates[i].day,
hour: Math.floor(sunRanges[x][0] / 60),
minute: sunRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: sundayLuxonDates[i].year,
month: sundayLuxonDates[i].month,
day: sundayLuxonDates[i].day,
hour: Math.floor(sunRanges[x][1] / 60),
minute: sunRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < sunLuxonIntervals.length; x++)
for (var i = 0; i <= sunLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
sunLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && sunLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && sunTimeSlots.push(sunLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var monTimeSlots = [], monLuxonIntervals = [], x = 0; x < monRanges.length; x++)
for (var i = 0; i < mondayLuxonDates.length; i++)
monLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: mondayLuxonDates[i].year,
month: mondayLuxonDates[i].month,
day: mondayLuxonDates[i].day,
hour: Math.floor(monRanges[x][0] / 60),
minute: monRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: mondayLuxonDates[i].year,
month: mondayLuxonDates[i].month,
day: mondayLuxonDates[i].day,
hour: Math.floor(monRanges[x][1] / 60),
minute: monRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < monLuxonIntervals.length; x++)
for (var i = 0; i <= monLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
monLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && monLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && monTimeSlots.push(monLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var tueTimeSlots = [], tueLuxonIntervals = [], x = 0; x < tueRanges.length; x++)
for (var i = 0; i < tuesdayLuxonDates.length; i++)
tueLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: tuesdayLuxonDates[i].year,
month: tuesdayLuxonDates[i].month,
day: tuesdayLuxonDates[i].day,
hour: Math.floor(tueRanges[x][0] / 60),
minute: tueRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: tuesdayLuxonDates[i].year,
month: tuesdayLuxonDates[i].month,
day: tuesdayLuxonDates[i].day,
hour: Math.floor(tueRanges[x][1] / 60),
minute: tueRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < tueLuxonIntervals.length; x++)
for (var i = 0; i <= tueLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
tueLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && tueLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && tueTimeSlots.push(tueLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var wedTimeSlots = [], wedLuxonIntervals = [], x = 0; x < wedRanges.length; x++)
for (var i = 0; i < wednesdayLuxonDates.length; i++)
wedLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: wednesdayLuxonDates[i].year,
month: wednesdayLuxonDates[i].month,
day: wednesdayLuxonDates[i].day,
hour: Math.floor(wedRanges[x][0] / 60),
minute: wedRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: wednesdayLuxonDates[i].year,
month: wednesdayLuxonDates[i].month,
day: wednesdayLuxonDates[i].day,
hour: Math.floor(wedRanges[x][1] / 60),
minute: wedRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < wedLuxonIntervals.length; x++)
for (var i = 0; i <= wedLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
wedLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && wedLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && wedTimeSlots.push(wedLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var thuTimeSlots = [], thuLuxonIntervals = [], x = 0; x < thuRanges.length; x++)
for (var i = 0; i < thursdayLuxonDates.length; i++)
thuLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: thursdayLuxonDates[i].year,
month: thursdayLuxonDates[i].month,
day: thursdayLuxonDates[i].day,
hour: Math.floor(thuRanges[x][0] / 60),
minute: thuRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: thursdayLuxonDates[i].year,
month: thursdayLuxonDates[i].month,
day: thursdayLuxonDates[i].day,
hour: Math.floor(thuRanges[x][1] / 60),
minute: thuRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < thuLuxonIntervals.length; x++)
for (var i = 0; i <= thuLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
thuLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && thuLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && thuTimeSlots.push(thuLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var friTimeSlots = [], friLuxonIntervals = [], x = 0; x < friRanges.length; x++)
for (var i = 0; i < fridayLuxonDates.length; i++)
friLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: fridayLuxonDates[i].year,
month: fridayLuxonDates[i].month,
day: fridayLuxonDates[i].day,
hour: Math.floor(friRanges[x][0] / 60),
minute: friRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: fridayLuxonDates[i].year,
month: fridayLuxonDates[i].month,
day: fridayLuxonDates[i].day,
hour: Math.floor(friRanges[x][1] / 60),
minute: friRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < friLuxonIntervals.length; x++)
for (var i = 0; i <= friLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
friLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && friLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && friTimeSlots.push(friLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var satTimeSlots = [], satLuxonIntervals = [], x = 0; x < satRanges.length; x++)
for (var i = 0; i < saturdayLuxonDates.length; i++)
satLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
year: saturdayLuxonDates[i].year,
month: saturdayLuxonDates[i].month,
day: saturdayLuxonDates[i].day,
hour: Math.floor(satRanges[x][0] / 60),
minute: satRanges[x][0] % 60
}, {
zone: properties.time_zone
}), luxon.DateTime.fromObject({
year: saturdayLuxonDates[i].year,
month: saturdayLuxonDates[i].month,
day: saturdayLuxonDates[i].day,
hour: Math.floor(satRanges[x][1] / 60),
minute: satRanges[x][1] % 60
}, {
zone: properties.time_zone
})));
for (var x = 0; x < satLuxonIntervals.length; x++)
for (var i = 0; i <= satLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
satLuxonIntervals[x].start.plus({
minutes: i
}) >= setStart && satLuxonIntervals[x].start.plus({
minutes: i
}) <= setEnd && satTimeSlots.push(satLuxonIntervals[x].start.plus({
minutes: i
}).toMillis());
for (var allAvailTimeSlots = availTimeSlots.concat(sunTimeSlots, monTimeSlots, tueTimeSlots, wedTimeSlots, thuTimeSlots, friTimeSlots, satTimeSlots), bookedLuxonIntervals = [], i = 0; i < bookedDays.length; i++)
bookedLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromMillis(bookedDays[i][0].getTime()).setZone(properties.time_zone).minus({
minutes: bookingLength - 1
}), luxon.DateTime.fromMillis(bookedDays[i][1].getTime()).setZone(properties.time_zone)));
for (var i = 0; i < bookedLuxonIntervals.length; i++)
for (var x = allAvailTimeSlots.length - 1; x >= 0; x--)
bookedLuxonIntervals[i].contains(luxon.DateTime.fromMillis(allAvailTimeSlots[x])) && allAvailTimeSlots.splice([x], 1);
instance.publishState("available_date_times", allAvailTimeSlots),
instance.publishState("is_loaded", "done")
}