Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rounding to the nearest 15 minute interval. #959

Closed
niemyjski opened this issue Jul 26, 2013 · 18 comments
Closed

Rounding to the nearest 15 minute interval. #959

niemyjski opened this issue Jul 26, 2013 · 18 comments
Labels

Comments

@niemyjski
Copy link

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.

@ichernev
Copy link
Contributor

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

@zbarnett
Copy link
Contributor

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);

@niemyjski
Copy link
Author

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..

@zbarnett
Copy link
Contributor

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
notifications@github.comwrote:

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//issues/959#issuecomment-37223821
.

@jakecraige
Copy link

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

@ichernev ichernev reopened this Mar 26, 2014
@ichernev
Copy link
Contributor

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

@ichernev ichernev added the todo label Mar 26, 2014
@betesh
Copy link

betesh commented May 25, 2014

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.

@ichernev
Copy link
Contributor

ichernev commented Jun 5, 2014

Closing in favor of #1595

@ichernev ichernev closed this as completed Jun 5, 2014
@janwerkhoven
Copy link

janwerkhoven commented Aug 13, 2015

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.

@john-bernardo1
Copy link

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()));

@janwerkhoven
Copy link

@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.

@silviocacamo
Copy link

silviocacamo commented Sep 18, 2017

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"))

@kevlarr
Copy link

kevlarr commented Dec 13, 2017

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

@janwerkhoven
Copy link

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

@kevlarr
Copy link

kevlarr commented Dec 13, 2017

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

@yonemec
Copy link

yonemec commented Mar 23, 2018

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;
}

@GendelfLugansk
Copy link

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

@netzinkubator
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests