95

I was trying to round the moment.js time object to next nearest 30 minute interval. But looks my logic us wrong.

Ex:

10:13am -> 10:30am
11:45am -> 12:00pm

Here is my current code

start = moment();
minuteReminder = start.minute() % 30;
start.add(minuteReminder, 'minutes');
start.format("D YYYY, h:mm:ss a");
halfer
  • 19,824
  • 17
  • 99
  • 186
Achintha Samindika
  • 1,944
  • 1
  • 20
  • 31

9 Answers9

157

Edit 2021 : easiest solution

const start = moment('2018-12-08 09:42');
const remainder = 30 - (start.minute() % 30);
 
const dateTime = moment(start).add(remainder, "minutes").format("DD.MM.YYYY, h:mm:ss a");

console.log(dateTime);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>

Million ways to do this. You don't need moment.js really. Anyway, here is one.

crg
  • 4,284
  • 2
  • 29
  • 57
jtromans
  • 4,183
  • 6
  • 35
  • 33
  • I think you are making a mistake , ``` remainder = (moment().minute()%30)-30``` – 32teeths May 24 '16 at 14:28
  • 5
    I think it should be `code` 30 - (start.minute() % 30) – artem_p Nov 15 '16 at 14:51
  • 2
    people that find this might want to include `.startOf('minute')` in order to reset seconds and milliseconds of the intrinsic date object. – Adrian Föder May 02 '20 at 16:44
  • 2
    This doesn't seem to do what was asked for. Even the sample snippet doesn't round 09:42 to the _nearest_ 30 minute interval; it rounds it to the _next_ 30 minute interval. The result should be 09:30, not 10:00. – Vala Jul 25 '20 at 12:50
  • 1
    The OP provided a number of examples in their original question, and this addresses those examples. The title of the post is ambiguous. – jtromans Aug 21 '20 at 09:19
  • 1
    This solution is broken - it adds 30min. if the date is already rounded. – hon2a Jun 06 '22 at 09:03
70

Based on @Volune and @Cabloo answers and comments, an updated version can look like:

function round(date, duration, method) {
    return moment(Math[method]((+date) / (+duration)) * (+duration)); 
}

Which then can be used like:

var date = moment();
var roundedDate = round(date, moment.duration(15, "minutes"), "ceil");
janhartmann
  • 14,713
  • 15
  • 82
  • 138
  • 1
    Brilliant way to allow extended functionality! – mdmb Feb 02 '18 at 01:32
  • 5
    Probably makes sense to restore the original timezone, otherwise this information gets lost. F.e. by adding .tz(date.tz()) to the end. – XZen Jun 20 '18 at 12:36
38

A generic solution:

var ROUNDING = 30 * 60 * 1000; /*ms*/
start = moment();
start = moment(Math.ceil((+start) / ROUNDING) * ROUNDING);
start.format("D YYYY, h:mm:ss a");

You can change ROUNDING from 30 minutes to whatever you want, and change Math.ceil by Math.round or Math.floor if you want another way to round the value.

Volune
  • 4,324
  • 22
  • 23
  • 8
    I like this one. You could convert the ROUNDING variable to a moment.duration(...), i.e. `function date_round(date, duration) { return moment(Math.floor((+date)/(+duration)) * (+duration)); }` Used like so `date_round(moment(), moment.duration(30, 'minutes'))` – Zane Sep 03 '14 at 01:47
4

You could do it with two ifs:

// Current date
let now = moment();

// Getting hour and minute
let hour   = now.hour();
let minute = now.minute();

// Rounding minute on 30 mins interval
if(minute <= 30) now.set({minute: 30});
if(minute >  30) now.set({hour: hour + 1, minute: 0});
Erik Martín Jordán
  • 4,332
  • 3
  • 26
  • 36
3

You can do it by a simple if-else clause:

if(moment().minute()> 30){
    var myTime = moment().minute(30).second(0);
} else {
    var myTime = moment().minute(0).second(0);
}
Kaspar Lee
  • 5,446
  • 4
  • 31
  • 54
AidaNow
  • 598
  • 5
  • 12
  • 1
    Although the word `round` was used in question title, OP wants to have something similar to `ceil`. Your solution provides neither, it instead gives an effect of `floor`. – Fr0zenFyr Mar 29 '18 at 05:53
2

For my case, I instead wanted something like

04-28-2021 20:00 => 04-28-2021 20:00
04-28-2021 20:30 => 04-28-2021 20:30
04-28-2021 20:11 => 04-28-2021 20:00
04-28-2021 20:35 => 04-28-2021 20:30

so the function below did the trick

function toNearest30Minutes(date) {
  const start = moment(date)
  let remainder: number
  const elapse = start.minute() % 30
  if (elapse === 0) {
    return moment(date).format()
  } else {
    remainder = 30 - elapse
    return moment(start).add(remainder, "minutes").format()
  }
}

The function above is just an adaptation of @jtromans answer higher above

giles
  • 85
  • 1
  • 9
2

One-line solution

 moment().add( moment().minute() > 30 && 1 , 'hours').minutes( moment().minute() <= 30 ? 30 : 0).format("hh:mm a")

Working exemple :

var min = moment().minute()
var dateTime = moment().add(min > 30 && 1 , 'hours').minutes(min <= 30 ? 30 : 0).format("hh:mm a")

console.log(dateTime);
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
crg
  • 4,284
  • 2
  • 29
  • 57
1

even though the question has been answered, I'd like to share my solution too.

var moment = require('moment');

const roundToNearestXXMinutes = (start, roundTo) => {
    let remainder = roundTo - (start.minute()+ start.second()/60) % roundTo;

    remainder = (remainder >  roundTo/2) ? remainder = -roundTo + remainder : remainder;
    const changedDate = moment(start).add(remainder, "minutes" ).seconds(0).format("DD.MM.YYYY HH:mm:ss");    
}

roundToNearestXXMinutes(moment(), 15);

EDIT: Thanks to Ishmael Sibisi for pointing to a flaw in my code! :)

-1

the code below rounds up the current time to the nearest 30 minutes and also flawlessly takes care of any trailing seconds

    var moment = require('moment')
    var main = Date.now() //2020-03-13T23:17:34+01:00
    var mainFormat = moment(main)
    var secs = mainFormat.second()
    var justMinutes = mainFormat.subtract(secs, 'seconds')
    var remainder = 30 - (justMinutes.minute() % 30);
    var dateTime = moment(justMinutes).add(remainder, 'minutes')
    var final = dateTime.format()
    console.log(final) 
    //2020-03-13T23:20:00+01:00
bello hargbola
  • 435
  • 6
  • 9