0

EDIT: This is not a duplicate of round up/ round down a momentjs moment to nearest minute - because first I don't want to round to nearest minute; second, I don't want to round unconditionally, but only when the difference to .endOf('day') is not the whole hours I'd otherwise expect; and third, I want to round a moment.duration, not a moment.

Say I have a date/time stamp, "2017-02-17 21:00:00" and I want to find how many hours there are to the end of day. Mentally, if I think of 9 o'clock in the evening, I consider it 3 hours away from midnight, and that is the answer I'd want to obtain from momentjs. This is what I am doing (Javascript Web Console in Firefox):

var m1 = moment('2017-02-17 21:00:00');
<- undefined

var m2 = moment(m1).endOf('day');
<- undefined

m1
<- Object { _isAMomentObject: true, _i: "2017-02-17 21:00:00", _f: "YYYY-MM-DD HH:mm:ss", _isUTC: false, _pf: Object, _locale: Object, _a: Array[7], _d: Date 2017-02-17T20:00:00.000Z, _isValid: true, _z: null }

m2
<- Object { _isAMomentObject: true, _i: "2017-02-17 21:00:00", _f: "YYYY-MM-DD HH:mm:ss", _isUTC: false, _pf: Object, _locale: Object, _z: null, _a: Array[7], _d: Date 2017-02-17T22:59:59.999Z, _isValid: true }

var mdiff = moment(m2).diff(moment(m1))
<- undefined

mdiff
<- 10799999

var mddur = moment.duration(moment(m2).diff(moment(m1)))
<- undefined

mddur
<- Object { _isValid: true, _milliseconds: 10799999, _days: 0, _months: 0, _data: Object, _locale: Object }

So far, so good - now, to format the duration, I go as per Get the time difference between two datetimes (also duration formatting · Issue #1048 · moment/moment · GitHub); note that I want to use the same function I'd use to get correct durations larger than 24 hours to calculate this - even if this particular example has a duration shorter than 24h, so I use this:

Math.floor(mddur.asHours()) + moment.utc(mddur.asMilliseconds()).format(":mm:ss")
<- "2:59:59"

So, here I's want to obtain the answer "3:00:00" here, not "2:59:59" - though note, I'd still want "2:59:58" as is, and not rounded up.

I guess, if our duration in ms is 10799999, that is 10799999/1000=10799.999000 seconds, so if we have a duration that has millisecond remainder of 999 milliseconds, only then I would want a round up.

What would be the recommended way of achieving this with moment.js?

sdaau
  • 36,975
  • 46
  • 198
  • 278
  • 2
    Possible duplicate of [round up/ round down a momentjs moment to nearest minute](https://stackoverflow.com/questions/17691202/round-up-round-down-a-momentjs-moment-to-nearest-minute) – Nelson Teixeira Dec 27 '18 at 21:50
  • 1
    Especially [this answer by Vivek RC](https://stackoverflow.com/a/45518482/1913729) – blex Dec 27 '18 at 21:52
  • Thanks @NelsonTeixeira and blex - but not a duplicate, I do not want *unconditional* rounding - only when the difference to endOf('day') is not the whole hours I'd otherwise expect – sdaau Dec 27 '18 at 22:06
  • 1
    Maybe what's confusing in your question is that you mixed "2:59:59" format with number of milliseconds format. So at least I couldn't understand what you're asking. – Nelson Teixeira Dec 27 '18 at 22:12
  • 1
    You what to round up only when "2:59:59" has above .999 milliseconds ? is that it ? Can you reformulate please ? – Nelson Teixeira Dec 27 '18 at 22:13
  • Thanks @NelsonTexeira - `round up only when "2:59:59" has above .999 milliseconds` - yes, that's exactly it – sdaau Dec 27 '18 at 22:14
  • Well round to the starting second, check it's above 2:59:59 secs, add milliseconds, check it's equal or above 2:59:59.999". – Nelson Teixeira Dec 27 '18 at 22:16
  • 1
    Out of curiosity, might it have worked had you set `var m2 = moment(m1.add(1, 'day')).startOf('day');` giving you the 00:00:00 time portion? – Forty3 Dec 27 '18 at 22:39
  • 1
    Thanks @Forty3 - no, if I just replace your `m2` in code in OP, I get "-21:00:00" printed as final result. But even if it did work, my intent is to calculate remaining time from say 21 to end of the day it is in; having .startOf instead changes the semantic meaning, and it will likely confuse me when I have to look at my own code X years from now, after promptly forgetting it. Anyways, I posted a function that does what I want as an answer. – sdaau Dec 27 '18 at 22:50

1 Answers1

0

Ok, I ended up making a function that does what I want, here it is:

function padDigits(number, digits) { // SO:10073699
  return Array(Math.max(digits - String(number).length + 1, 0)).join(0) + number;
}

var durationToHoursStr = function(induration) {
  // copy the duration
  thisduration = moment.duration(induration);
  // to get seconds, we divide milliseconds with 1000;
  // check if the remainder of division of milliseconds with 1000 is 999:
  var remainder = thisduration.asMilliseconds() % 1000;
  //console.log("remainder", remainder);
  // only round up if the remainder is 999:
  if (remainder >= 999) {
    // in call moment.duration(Number), Number is interpreted as "length of time in milliseconds" (https://momentjs.com/docs/#/durations/)
    // so just add 1 ms, since anyways the previous remainder is 999, should be enough to flow over
    thisduration.add(moment.duration(1)); // should be in-place replacement - mutable
    //console.log("thisduration", thisduration);
  }
  return String(Math.floor(thisduration.asHours())) + ":" + padDigits(thisduration.minutes(), 2) + ":" + padDigits(thisduration.seconds(), 2);
};

var m1 = moment('2017-02-17 21:00:00');
var m2 = moment(m1).endOf('day');

// mdiff is int, total number of milliseconds, here 10799999
var mdiff = moment(m2).diff(moment(m1))

// mddur is moment.duration, also has the same amount of milliseconds, 
// mddur.asMilliseconds() = 10799999, mddur.asSeconds() = 10799.999, mddur.asHours() = 2.9999997222222223
var mddur = moment.duration(moment(m2).diff(moment(m1)));

var mddur_hoursString = durationToHoursStr(mddur);
console.log(mddur_hoursString); // prints "3:00:00"

var m3 = moment('2017-02-17 23:59:59');
var mddur3 = moment.duration(moment(m3).diff(moment(m1)));
var mddur3_hoursString = durationToHoursStr(mddur3);
console.log(mddur3_hoursString); // prints "2:59:59" - as it should, since we didn't really demand the difference to the end of day
sdaau
  • 36,975
  • 46
  • 198
  • 278