Moment: Rounding to the nearest 15 minute interval.

Created on 27 Jul 2013  ·  18Comments  ·  Source: moment/moment

I was wondering if there is a way for me to round up or down to the nearest 15 minute interval. I think this would be great feature to have in moment as some libs like datejs kind of have this functionality from what I seen when searching. My scenario is that I have data points that are segmented into 15 minute buckets, The selection tool returns any block of time and I need to round down to the closest 15 minutes and round up for the end time.

Doing it this way (http://stackoverflow.com/questions/4968250/how-to-round-time-to-the-nearest-quarter-hour-in-javascript) seems like it would be a pain.. when It would be useful to have in the lib itself.

todo

Most helpful comment

No need to complicate, simple math:

const rounded = Math.round(moment().minute() / 15) * 15;
const roundedDown = Math.floor(moment().minute() / 15) * 15;
const roundedUp = Math.ceil(moment().minute() / 15) * 15;

moment().minute(roundedUp).second(0)

Outputs to the closest 15 minute quarter.

All 18 comments

moment.fn.roundMinutes = function () { return ~(this.minutes() / 15) * 15; }

I created this for my own use and figured it may help others. Keep in mind it does round to the nearest NEXT 15 min.

moment.fn.roundNext15Min = function () {
var intervals = Math.floor(this.minutes() / 15);
if(this.minutes() % 15 != 0)
    intervals++;
    if(intervals == 4) {
        this.add('hours', 1);
        intervals = 0;
    }
    this.minutes(intervals * 15);
    this.seconds(0);
    return this;
}

EDIT: removed this.incrementHours() in favor of this.add('hours', 1);

Can you please add these functions to moment.. It would be really dang nice to be able to round to the nearest, next and previous time intervals... There are millions of google results for this and hundreds of stack overflow upvotes..

Now that I look at it again I have no idea how incrementHours was working.
I've edited my post to the issue with the corrected code. Enjoy!

On Mon, Mar 10, 2014 at 2:29 PM, Blake Niemyjski
[email protected]:

What was your function for incrementHours? I'm guessing that also did days
too.

Reply to this email directly or view it on GitHubhttps://github.com/moment/moment/issues/959#issuecomment-37223821
.

thanks for that method @zbarnett . Exactly what I needed for my project

Well we can use the startOf interface like this startOf(15, 'm'). It can work for all units. Awaiting PRs :)

Here's a pretty simple formula (in Coffeescript syntax, since that's the context where I ran into this issue):

round_interval = 15
remainder = time.minute() % round_interval
time.subtract('minutes', remainder).add('minutes', if remainder > round_interval / 2 then round_interval else 0)

I'm busy enough with other things right now, but if someone wants to take this code and put it straight into a PR, feel free.

Closing in favor of #1595

No need to complicate, simple math:

const rounded = Math.round(moment().minute() / 15) * 15;
const roundedDown = Math.floor(moment().minute() / 15) * 15;
const roundedUp = Math.ceil(moment().minute() / 15) * 15;

moment().minute(roundedUp).second(0)

Outputs to the closest 15 minute quarter.

I couldn't get @janwerkhoven's solution to work, so here's mine:

function round15(minute) {
  let intervals = [15, 30, 45, 59, 0];
  let closest;
  let min = 90;

  for (let i = 0; i < intervals.length; i++) {
    let iv = intervals[i];
    let maybeMin = Math.abs(minute - iv);

    if (maybeMin < min) {
      min = maybeMin;
      closest = iv;
    }
  }

  if (closest === 59) {
    closest = 0;
  }

  return closest;
}

let myMoment = moment();
myMoment.minutes(round15(myMoment.minutes()));

@ground5hark

function nearestMinutes(interval, someMoment){
  const roundedMinutes = Math.round(someMoment.clone().minute() / interval) * interval;
  return someMoment.clone().minute(roundedMinutes).second(0);
}

function nearestPastMinutes(interval, someMoment){
  const roundedMinutes = Math.floor(someMoment.minute() / interval) * interval;
  return someMoment.clone().minute(roundedMinutes).second(0);
}

function nearestFutureMinutes(interval, someMoment){
  const roundedMinutes = Math.ceil(someMoment.minute() / interval) * interval;
  return someMoment.clone().minute(roundedMinutes).second(0);
}

const now = moment();
const nearest5min = nearestMinutes(5, now);
const nearest15min = nearestMinutes(15, now);
const nearest30min = nearestMinutes(30, now);
const nearestFuture5min = nearestFutureMinutes(5, now);
const nearestPast5min = nearestPastMinutes(5, now);

Tested on Code Pen

The crucial bit is doing .clone(). Without it you'd be editing the original moment() which is a common pitfall when using Moment.js.

Try this guys

time = moment("01:46", "HH:mm");
round_interval = 30;//15;
intervals = Math.floor(time.minutes() / round_interval);
minutesToRound = time.minutes() % round_interval;
minutesRounded = minutesToRound>round_interval/2 ? round_interval: 0;
minutes = intervals * round_interval + minutesRounded;
time.minutes(minutes);

alert(time.format("HH:mm"))

Late to the party, I know, but...

@janwerkhoven 's nearestMinutes function isn't right; that implies it will round up OR down depending on minute, so rounding to the nearest 15 we'd expect 12:34 -> 12:30 and 12:39 -> 12:45

const roundedMinutes = Math.round(someMoment.clone().minute() / interval) * interval;

// 12:34 is fine...
// => Math.round(34 / 15) * 15
// => 2 * 15
30

// ... but 12:39 is not - it should be 45
// => Math.round(39 / 15) * 15
// => 2 * 15
30

@Silviusconcept 's solution overcomes this with minutesRounded

@kevlarr
Math.round(39/15) returns 3 not 2.
Math.round(39/15) * 15 returns 45 not 30.

Haha that's what I get for reading too many threads at the end of the day when I haven't slept.. I was doing Math.floor

I like _zbarnett solution_! Easyto use
Little update to the function _this.milliseconds(0);_

var newdate=turno.date.clone().roundNext15Min().add(anticipation, 'minutes');

moment.fn.roundNext15Min = function () {
var intervals = Math.floor(this.minutes() / 15);
if(this.minutes() % 15 != 0)
intervals++;
if(intervals == 4) {
this.add('hours', 1);
intervals = 0;
}
this.minutes(intervals * 15);
this.seconds(0);
_this.milliseconds(0);_
return this;
}

My version of an answer

const startOf = (m, n, unit) => {
  const units = [
    'year',
    'month',
    'hour',
    'minute',
    'second',
    'millisecond',
  ];
  const pos = units.indexOf(unit);
  if (pos === -1) {
    throw new Error('Unsupported unit');
  }
  for (let i = pos + 1; i < units.length; i++) {
    m.set(units[i], 0);
  }
  m.set(unit, Math.floor(m.get(unit) / n) * n);

  return m;
};

const endOf = (m, n, unit) => {
  const units = [
    'year',
    'month',
    'hour',
    'minute',
    'second',
    'millisecond',
  ];
  const pos = units.indexOf(unit);
  if (pos === -1) {
    throw new Error('Unsupported unit');
  }
  for (let i = pos + 1; i < units.length; i++) {
    m.set(units[i], units[i] === 'millisecond' ? 999 : 59);
  }
  m.set(unit, Math.floor(m.get(unit) / n) * n + n - 1);

  return m;
};

Use like this:

startOf(moment(), 15, 'minute');
endOf(moment(), 15, 'minute')
// with moment-timezone
startOf(moment().tz("Europe/London"), 15, 'minute');

These functions will change original object, use clone() when needed

another solution would be:
let next15Minutes = moment().add(15, 'minutes'); next15Minutes.minutes(Math.floor(next15Minutes.minutes() / 15) * 15); next15Minutes.format('HH:mm');

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alvarotrigo picture alvarotrigo  ·  3Comments

tanepiper picture tanepiper  ·  3Comments

chitgoks picture chitgoks  ·  3Comments

danieljsinclair picture danieljsinclair  ·  3Comments

vbullinger picture vbullinger  ·  3Comments