Hello and thanks for the awesome library.
I was wondering if there's a simpler way to produce an object with these values: (years, months, days, hours, minutes, seconds, milliseconds) when starting from Duration.fromMillis(…)
I noticed that:
Duration.fromMillis(22930346000).normalize().toObject();
will only return an object with milliseconds.
If i provide a full object to begin with, then it works as expected:
Duration.fromObject({
years: 0,
months: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
milliseconds: 22930346000,
}).normalize().toObject();
I just wanted to know if there's something that i'm missing that would avoid passing a big object just to get back what i need. Thanks in advance!
PS: I made a jsfiddle if needed.
Yeah, what's happening is that Luxon thinks of normalizing as something that happens across the units included in the duration. For example, you excluded weeks from your object and so the normalization rolled days directly up to months. But it has to know to do that somehow.
Here's a shortcut:
luxon.Duration.fromMillis(22930346000).shiftTo('years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds').toObject()
You can simplify that in other ways:
const fullUnits = ['years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds'];
luxon.Duration.prototype.toFull = function() {return this.shiftTo.apply(this, fullUnits);}
luxon.Duration.fromMillis(22930346000).toFull().toObject();
That's easy enough in userland that I don't want to add it to the library.
@icambron
Sorry for pushing this old thread. I was just playing with Duration just now and I think the normalization is not always correct?
For exmaple:
const dayToMs = day => day * 24 * 60 * 60 * 1000
luxon.Duration.fromMillis(dayToMs(365)).shiftTo('years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'weeks').normalize().toObject() // { years: 1, ..., days: 0 } <--- correct
luxon.Duration.fromMillis(dayToMs(364)).shiftTo('years', 'months', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'weeks').normalize().toObject() // { years: 1, ..., days: 4 } <--- incorrect?
@ycmjason can you open a new ticket?
@icambron instead of shifting, do you think it's reasonable to convert toISO then fromISO? I have found this will provide the object as expected.
To give more insight into why I need the full object. I need to know the largest unit e.g. { years: 1, ..., days: 0 } I would like years and { days: 1, ..., seconds: 0 } I would like days, etc.
If there is another way to achieve this I would use that instead.
@jeffal I'm not sure what you mean. fromISO doesn't expand the object:
iso = Duration.fromObject({ days: 3 }).toISO() //=> "P3D"
Duration.fromISO(iso).toObject() //=> {days: 3}
I also don't really see why you need to do that to find the largest unit -- can't you just iterate through an ordered list of units and find the first one for which object[unit] is truthy?
```js
function largestUnit(dur) {
const obj = dur.toObject();
return ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"]
.find(unit => obj[unit]);
}
// skips years (not there), skips months (0), and settles on days
largestUnit(Duration.fromObject({ months: 0, days: 4, hours: 2 })) //=> "days"
@icambron Please, scratch what I said. While going fromMillis => toISO => fromISO => toObject does add seconds to the object, it does not provide the "full object" the same way your shortcut does.
Duration.fromMillis(3429535738).toObject() //=> {milliseconds: 3429535738}
Duration.fromISO(Duration.fromMillis(3429535738).toISO()).toObject() //=> {seconds: 3429535, milliseconds: 738}
Duration.fromMillis(3429535738).shiftTo('years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds').normalize().toObject() //=> {years: 0, months: 1, weeks: 1, days: 2, hours: 16, minutes: 38, seconds: 55, milliseconds: 738}
Thank you for providing this example to find the largest unit. This is better then what I currently have, something along the lines of...
const obj = Object.keys(obj).reduce((accumulator, currentValue) => {
if (obj[currentValue]) {
accumulator[currentValue] = obj[currentValue];
}
return accumulator;
}, {}); //=> {months: 1, weeks: 1, days: 2, hours: 16, minutes: 38, seconds: 55, milliseconds: 738}
Object.keys(obj)[0] //=> "months"
@jeffal Ah, yes, toISO upconverts milliseconds to seconds because ISO can only represent the millis as decimal seconds, so there's no other way to do it. So more of a side effect than anything else.
Most helpful comment
Yeah, what's happening is that Luxon thinks of normalizing as something that happens across the units included in the duration. For example, you excluded weeks from your object and so the normalization rolled days directly up to months. But it has to know to do that somehow.
Here's a shortcut:
You can simplify that in other ways:
That's easy enough in userland that I don't want to add it to the library.