Search code examples
javascriptnode.jsdatetimemomentjszendesk-api

Check if time in minutes since start of week is between two values (value in minutes since start of week) at different intervals


So i'm using the Zendesk API to fetch the schedule of an instance, the API returns an object that contains an array called intervals which looks like this:

[
  { "start_time": 1980, "end_time": 2460 },
  { "start_time": 3420, "end_time": 3900 },
  { "start_time": 4860, "end_time": 5340 },
  { "start_time": 6300, "end_time": 6780 },
  { "start_time": 7740, "end_time": 8220 }
]

This example just translates to 9AM to 5PM in minutes for each business day of the week with the value being in minutes since the start of the week which is actually Midnight on Sunday (meaning a value of 720 would be noon on Sunday).

I'm trying to implement some server side logic that loops through the intervals and checks if the current time is between the intervals for that given day making it possible to detect if it's currently within business hours or not.

I was thinking the moment.js library might be useful for this and I have been playing around with the .startOf('week') and .diff() functions but I keep getting blocked and I fear it might not be as reliable as I'd like given that i'm unsure if moment starts the week on a Monday or Sunday.

So far i've got

let isBusinessHours = false;

function checkBusinessHours() {
  intervals.map((interval, key) => {
    //offset weekdays to match interval keys
    let currentDay = moment().weekday() -1; 

    //create comparison start & end times based on interval start & end times
    let startTime = moment().startOf('week').add(interval.start_time, 'minutes'); 
    let endTime = moment().startOf('week').add(interval.end_time, 'minutes');

    //check to see if it's business hours
    if (moment().isBetween(startTime, endTime) && currentDay === key) {
      isBusinessHours = true;
    } 
  })
  return isBusinessHours;
}

But i'm unsure if this is the best way to go about checking this?


Solution

  • Using an offset from the start of Sunday doesn't seem like a good idea where daylight saving is observed. Most places use Sunday as the changeover day, so on those days Sunday isn't 24 hours long and all offsets for that week will be ± the DST shift in minutes.

    Ignoring that issue, it's fairly simple to convert a date to an offset: subtract the previous Sunday and divide the difference by the milliseconds in one minute. Converting from an offset to Date is the opposite: add the minutes to a Date for the previous Sunday, e.g.

    // Return Date for start of Sunday 00:00 before d
    function startOfWeek(d = new Date()) {
      return new Date(d.getFullYear(), d.getMonth(), d.getDate() - d.getDay());
    }
    
    // Convert a Date to an offset in minutes
    // from start of previous Sunday
    function dateToOffset(d = new Date()) {
      return Math.floor((d - startOfWeek(d)) / 6e4);  
    }
    
    // Convert an offset to date, default is current week
    // Week starts on Sunday
    function offsetToDate(offset, d = new Date()) {
      let date = startOfWeek(d);
      date.setMinutes(offset);
      return date;
    }
    
    // Examples
    // Just a helper to format dates
    function format(d) {
      return d.toLocaleString('en', {
        weekday: 'short',
        hour: '2-digit',
        hour12: false,
        minute: '2-digit'
      });
    }
    
    // Show current offset
    let d = new Date();
    console.log(`For ${format(d)} the offset is ${dateToOffset(d)}`);
    
    // Convert offsets to dates
    let data = [
      { "start_time": 1980, "end_time": 2460 },
      { "start_time": 3420, "end_time": 3900 },
      { "start_time": 4860, "end_time": 5340 },
      { "start_time": 6300, "end_time": 6780 },
      { "start_time": 7740, "end_time": 8220 }
    ];
    data.forEach(o => console.log(
     `Start: ${format(offsetToDate(o.start_time))}  ` +
     `End  : ${format(offsetToDate(o.end_time))}`
    ));
    
    // Find out if dates are between a start and end
    [new Date(2021,10,25,12), // Thu 25 Nov 12:00
     new Date(2021,10,27,12), // Sat 25 Nov 12:00
     new Date()               // Now
    ].forEach(d => {
      let offset = dateToOffset(d);
      console.log(
        `Is ${format(d)} between a start and end? ` +
        `${!!data.find(obj => obj.start_time <= offset && obj.end_time > offset)? 'Yes' : 'No'}`
      )
    });