Home > OS >  Filling missing time intervals for every 15 minutes using date-fns in JS
Filling missing time intervals for every 15 minutes using date-fns in JS

Time:11-10

Suppose I have an array of objects, each representing a certain time and a respective price value:

const prices = [
  {
    price_time: '2021-10-13T16:30:00 00:00',
    price: 3.5,
  },
  {
    price_time: '2021-10-13T18:15:00 00:00"',
    price: 5,
  },
  {
    price_time: '2021-10-13T19:15:00 00:00"',
    price: 6.5,
  },
];

Now assume I need to output a list of all prices with 15 minutes intervals for every hour existing between the price_time values of the first and last elements of the prices array.

The resulting intervals list for the defined 16:30, 18:15 and 19:15 times will then be:

[
  { price_time: '2021-10-13T16:30:00.000Z', price: 3.5 }, // first elem in the prices array
  { price_time: '2021-10-13T16:45:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:00:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:15:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:30:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:45:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T18:00:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T18:15:00.000Z', price: 5 }, // second elem in the prices array
  { price_time: '2021-10-13T18:30:00.000Z', price: 5 },
  { price_time: '2021-10-13T18:45:00.000Z', price: 5 },
  { price_time: '2021-10-13T19:00:00.000Z', price: 5 },
  { price_time: '2021-10-13T19:15:00.000Z', price: 6.5 } // last elem in the prices array
]

What would be the most optimal way to achieve this? I have been trying to get it done by iterating the array values, getting the hours between each two elements and then filling in the 00:00, 00:15, 00:30 and 00:45 intervals:

const _ = require('date-fns');

const prices = [
  { price_time: '2021-10-13T16:30:00 00:00', price: 3.5 },
  { price_time: '2021-10-13T18:15:00 00:00',price: 5 },
  { price_time: '2021-10-13T19:15:00 00:00', price: 6.5 },
];

function addMissingIntervals() {
  const allIntervals = [];
  prices.forEach((item, index) => {
    if (index !== prices.length - 1) { // if this isn't the last loop item
      const start = _.parseISO(item.price_time); // the datetime of the current loop item
      const end = _.parseISO(prices[index   1].price_time); // the datetime of the next loop item
      const allHours = _.eachHourOfInterval({ start: start, end: end }); // all the hours between these two
      allHours.map((hour) => {
        allIntervals.push({ price_time: hour, price: item.price }) // insert the start of the hour
        allIntervals.push(
          ...[15, 30, 45].map((t) => { // for each hour, add 15m, 30m & 45m
            return { price_time: _.addMinutes(hour, t), price: item.price };
          }),
        );
      });
    }
  });
  // Adding the last item from `prices` as well
  allIntervals.push(prices[prices.length - 1]);
  return allIntervals;
}

console.log(addMissingIntervals());

The result of the latter, however, includes all intervals for all hours, including those preceding or following the start/end dates:

[
  { price_time: '2021-10-13T16:00:00.000Z', price: 3.5 }, // should not be included
  { price_time: '2021-10-13T16:15:00.000Z', price: 3.5 }, // should not be included
  { price_time: '2021-10-13T16:30:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T16:45:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:00:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:15:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:30:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T17:45:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T18:00:00.000Z', price: 3.5 },
  { price_time: '2021-10-13T18:15:00.000Z', price: 3.5 }, // should not be included
  { price_time: '2021-10-13T18:30:00.000Z', price: 3.5 }, // should not be included
  { price_time: '2021-10-13T18:45:00.000Z', price: 3.5 }, // should not be included
  { price_time: '2021-10-13T18:00:00.000Z', price: 5 }, // should not be included
  { price_time: '2021-10-13T18:15:00.000Z', price: 5 },
  { price_time: '2021-10-13T18:30:00.000Z', price: 5 },
  { price_time: '2021-10-13T18:45:00.000Z', price: 5 },
  { price_time: '2021-10-13T19:00:00.000Z', price: 5 },
  { price_time: '2021-10-13T19:15:00.000Z', price: 5 }, // should not be included
  { price_time: '2021-10-13T19:30:00.000Z', price: 5 }, // should not be included
  { price_time: '2021-10-13T19:45:00.000Z', price: 5 }, // should not be included
  { price_time: '2021-10-13T19:15:00.000Z', price: 6.5 }
]

CodePudding user response:

I managed to solve this by using a modification of this answer (which utilizes moment.js instead of date-fns).

Essentially, I am having a loop running between each two dates, rounding each loop item time to the nearest 15 minutes and assigning the right price for that interval. The last item in the original prices array is ignored and added to the resulting array at following the termination of the loop:

const _ = require('date-fns');

const prices = [
  { price_time: '2021-10-13T16:30:00 00:00', price: 3.5 },
  { price_time: '2021-10-13T18:15:00 00:00', price: 5 },
  { price_time: '2021-10-13T19:15:00 00:00', price: 6.5 },
];

function intervalsGroup(startDate, endDate, price) {
  // Round to the nearest 15
  let current = _.setMinutes(startDate, Math.ceil(_.getMinutes(startDate) / 15) * 15);
  let group = [];
  while (endDate > current) {
    group.push({ price_time: current, price: price })
    current = _.addMinutes(current, 15);
  }
  return group;
}

function addMissingIntervals() {
  const allIntervals = [];
  prices.forEach((item, index) => {
    if (index !== prices.length - 1) { // if this isn't the last loop item
      const start = _.parseISO(item.price_time); // the datetime of the current loop item
      const end = _.parseISO(prices[index   1].price_time); // the datetime of the next loop item
      allIntervals.push(...intervalsGroup(start, end, item.price)); // generate the 15m intervals between these two days
    }
  });
  allIntervals.push(prices[prices.length - 1]);
  return allIntervals;
}

console.log(addMissingIntervals());
  • Related