I'm having trouble understanding how duration works. To make it simpler to explain consider the following example:
moment.duration(123, 'seconds').humanize()
// => "2 minutes". So far so good
const duration = moment.duration(123123123, 'seconds')
duration.humanize()
// => "4 years". Hmm, I want to know the specific date
duration.format("YYYY/MM/DD")
// => Uncaught TypeError. Well, let's search other solutions
moment.utc(duration.asMilliseconds()).format("YYYY/MM/DD")
// => "1973/11/26". What?! That's not 4 years ago!
So I think I assumed .duration() somehow knows the datetime from X seconds ago, but I suppose it actually just calculates how much seconds is a time equivalent to. For example, 123 seconds is around 2 minutes, but the function itself doesn't know the time from around 2 minutes ago, which ended up being a bit unintuitive for me to understand.
That said, I'm not really sure how then I am supposed to translate duration to a date. The expected result is whatever date was 123123123 seconds ago. How do I achieve that?
Duration is not related to specific dates. You can use subtract to get the datetime when the duration period started (assuming it ended in this particular moment):
console.log(
moment()
.subtract(123123123, 'seconds') // your duration
.format('MMMM Do YYYY, h:mm:ss a')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
Related
I have done a lot of research to find code examples for this, or simillar question but I didn't find any.
I have a react app that has some customer requests in it, and I want to show next to each request something like: 'before: 2 Days, 4 Hours, 56 Minutes.'
I want to calculate the difference between the current Date and the time the request was sent, and analyze this difference to days, hours, and minutes.
note: The date difference won't be longer than one month.
I know that I can do: dt1 - dt2 to get the difference, But this returns a long integer which is hard to analyze.
Edit:
It suppose to be like this:
days + hours + minutes == difference
So if you try to convert days,hours,minutes to milliseconds and sum them together, it should result simillar difference of (d1-d2)
Any ideas? Thanks for the help!
What you are looking for better human readability.
There are multiple packages that would achieve that.
For your use case user this https://www.npmjs.com/package/humanize-duration
humanizeDuration(dt1-dt2) // Will result in what you want
I have a bunch of rrules (implemented in rrule.js) that gives me an array of event start times (see the demo). rrule.js doesn't actually provide the concept of an event duration or endtime... So it can tell me the precise date when the millionth occurrence of a repeating event will start but not when it will end. Turns out I actually want to know when an event ends so I'll have to get creative. As far as I see it I've got two options
DB SIDE: Store an rrule string + an event duration.
CLIENT SIDE: Reconstitute events start date array from rrule string. Only start times would be known and end times would be calculated by adding the duration as an offset to each start time in the array.
DB SIDE: Store a modified rrule string which encodes an endtime.
CLIENT SIDE: A special wrapper function reads the modified rrule string and reconstitutes it as two date arrays; one representing event start times and the other end times.
Option 1 seems easier but I suspect it will run into problems with daylight savings. For example, say I've an event that is every Tuesday from 6pm to 2 am Wednesday. In that case I'd store a duration of 8 hours in my database alongside that stringified rrule. Now let's fast forward to any 6pm Tuesday in the future. Does my event always end on Wednesday at 2am (or does that 8 hour duration sometimes make my event end at 1am or 3am)? How do I get it to always end at 2am?
... If you know the answer then just stop reading here.
How I've seen others handle duration offset
According to Kip in How to add 30 minutes to a JavaScript Date object? the smart way to offset a date time is to use a fancy library like moment.js.
He emphasizes that point by showing how easily things go wrong using non fancy date time libraries (showing how a naive minute offset function fails due to daylight savings)
function addMinutes(date, minutes) {
return new Date(date.getTime() + minutes*60000);
}
addMinutes(new Date('2014-11-02'), 60*24) //In USA, prints 11pm on Nov 2, not 12am Nov 3!
But something weird happens for me. The function above was supposed to output 11pm on Nov 2 - which is the wrong answer i.e. it was supposed to fail because of daylight savings. When I run it, it actually outputs the right time 12am on Nov 3 (note: I'm in Chicago/Central time).
When I compare the output of his naive function to the output of moment.js and luxon.js, I get the same answer as you can see in this observable notebook.
Scratching my head
What's more, if using luxon or moment, when you add a days worth of minutes to 2014-11-02 you get2014-11-03T00:00:00.000Z but if you just directly add a day to 2014-11-02 you get 2014-11-03T01:00:00.000Z - it's an hour off.
So am I better off pursuing option 2?
Now let's fast forward to any 6pm Tuesday in the future. Does my event always end on Wednesday at 2am (or does that 8 hour duration sometimes make my event end at 1am or 3am)? How do I get it to always end at 2am?
The standard Javascript Date object automatically handles the daylight savings shift for you. Even if you add 8 hours to a date at 6pm the day before daylight savings, the new date will still end at 2am the next day.
Incidently, I implemented duration support in rSchedule and since it supports both the standard javascript Date as well as moment/luxon dates, you can test a recurring event with a duration using either library and see that they both produce the same result.
This example can be seen on stackblitz.
import { Schedule } from '#rschedule/rschedule';
import { StandardDateAdapter } from '#rschedule/standard-date-adapter';
// This example will also work with `moment`, `moment-timezone`, and `luxon`
// (assuming you import the proper date adapter -- see rSchedule docs)
const schedule = new Schedule({
rrules: [
{
start: new Date(2019,9,10,18),
frequency: "DAILY",
duration: 1000 * 60 * 60 * 8,
count: 30
}
],
dateAdapter: StandardDateAdapter,
});
schedule.occurrences().toArray().forEach(adapter => {
console.log(
{
start: adapter.date.toLocaleString(),
end: adapter.end.toLocaleString(),
}
)
})
Turns out I actually want to know when an event ends
To find out when this event ends, you could do:
const iterator = schedule.occurrences({ reverse: true })
const { end } = iterator.next().value
This trick would only work with an event that actually has an end date (so not an event with infinite occurrences).
I wrote the original answer you are referring to about a decade ago. Seven years later, I made an edit, changing new Date(2014, 10, 2) to new Date('2014-11-02'). I thought this would be easier to read (because you don't have to explain that the months in that version of the constructor start at 0 instead of 1). But as #RobG pointed out, formatting in this way causes it to be parsed as UTC. I've gone back and fixed this now (thanks for pointing it out).
To get to your "scratching my head" part of your question:
What's more, if using luxon or moment, when you add a days worth of minutes to 2014-11-02 you get 2014-11-03T00:00:00.000Z
The Z at the end of that timestamp means it is in UTC, and UTC does not observe daylight savings time. So if you start with 2014-11-02T00:00:00.000Z, and add 24 hours, you get 2014-11-03T00:00:00.000Z. When you add hours/minutes/seconds, there's no need to worry about daylight saving time.
but if you just directly add a day to 2014-11-02 you get 2014-11-03T01:00:00.000Z - it's an hour off.
In this case what is happening is you are starting with 2014-11-02T00:00:00.000Z, but when you tell the library to add one day, and you don't specify a time zone, the library is assuming you are in your local time zone, so it adds one local day. Because you cross a DST boundary, that day is 25 hours long, and when you print it as an ISO timestamp in UTC, you end up with 2014-11-03T01:00:00.000Z (25 hours later).
Time zone stuff is hard, even if you are using a library. Most people can get by for a long time not knowing or caring that for many users one day a year is 25 hours long. But if these edge cases will matter to you, the best approach is to play around with them like you're doing, and make sure you really understand what is happening and why.
Im trying to convert duration in seconds to human friendly output using moment js and combination of duration and format.
Im getting the duration in seconds and then formatting it like so:
const value = 337650
const duration = (durationSeconds) => {
const duration = moment.duration(durationSeconds, 'seconds')
return moment.utc(duration.asMilliseconds()).format('D [days] HH:mm:ss')
}
console.log(`Duration: ${duration(value)}`)
outputs
"Duration: 4 days 21:47:30"
JSBin here
The problem I have is that it seems wrong. Online services like https://www.tools4noobs.com/online_tools/seconds_to_hh_mm_ss/ shows one day less.
Im not sure what library/algorithm these services are using and if they are showing the correct time elapsed or moment js.
Any ideas greatly appreciated!
You seem to be converting the duration into an epoch time, then formatting it, which will be wrong.
Take a look at this Moment issue for details about duration formatting. https://github.com/moment/moment/issues/1048
Simple formatting can be done using the humanize method:
let value = 337650
let duration = moment.duration(value, 'seconds')
duration = duration.humanize()
console.log(duration)
"4 days"
Fore more sophisticated formatting you may want to dive into: https://github.com/jsmreese/moment-duration-format.
try something simple:
var x = 337650%86400;
var days = (337650 - x)/86400;
var y = x%3600;
var hours= (x-y)/3600;
var sec = y%60;
var mins=(y-sec)/60;
alert(days + ' days ' + hours+':'+mins+':'+sec)
You can use the moment-duration-format plugin to format durations, the previous approach (creating a new absolute date from a duration) is not going to work. When you add 337650 seconds to the Unix Epoch start, you get January 4, 1970, 21:47:30, which is why you're seeing the day as 4.
As a further example of how this will go badly wrong, imagine we need to format a duration of, say, 2700000 seconds. This will give us an output of "1 days 06:00:00". Why? Because it's equivalent to February 1 1970, 06:00. (The real result will be 31 days, 06:00:00).
I don't think we can blame the authors of moment.js (they've done an amazing job), it's just not using the format function on moment.js as it is supposed to be used.
By using the format function from moment-duration-format, we're formatting as a duration rather than as an absolute time and we get the right result!
momentDurationFormatSetup(moment);
const value = 337650
const duration = (durationSeconds) => {
const duration = moment.duration(durationSeconds, 'seconds')
return duration.format('D [days] HH:mm:ss');
}
console.log(`Duration: ${duration(value)}`)
document.getElementById('output').innerHTML = `Duration: ${duration(value)}`;
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-duration-format/2.2.2/moment-duration-format.min.js"></script>
<div id="output">
</div>
JSFiddle: https://jsfiddle.net/t07ez8o4/9/
At the moment I have an input field that allows you to choose an hour by entering two digits. for example you could choose 12 for 12 hours.
Is there a way using moment to convert that number into milliseconds using MomentJS?
At the moment I am having to do the below maths. I can't see in the Moment docs that this is doable.
var timeHH = scope.session.timeHH * 3600;
This works out the seconds, then later I mulitply it by 1000 for the milliseconds value.
Is there a way using moment to convert that number into mini milliseconds using moment?
There may be, but there's absolutely no reason to use MomentJS for this, and doing so would be roundabout and inefficient. It's quite straightforward: hours * 3600000 is milliseconds. There are no weird special cases to handle, etc., unless you're handling converting a particular period of hours of a real time (say, the 10 hours from 8 p.m. December 31st 2005 GMT) and want to handle leap second insertions (there was one that night at midnight), but MomentJS doesn't do that anyway.
For this particular use case, the right thing to do is a manual calculation. For the sake of completeness and in the event you needed to do more complicated calculations, to do this with Moment, you would use the duration type. The duration type will allow you to convert from one unit value to another, and if you had to make several unit conversions instead of just this one it would be a good choice.
moment.duration(12, 'hours').asMilliseconds()
43200000
In addition to converting to milliseconds, you could convert to any other unit:
var dur = moment.duration(12, 'hours');
dur.asMilliseconds();
43200000
dur.asDays();
0.5
dur.asYears();
0.001368953503494254
In addition to the as functions, you can get the parts of the duration broken out:
var dur = moment.duration(12.5, 'hours');
dur.hours();
12
dur.minutes();
30
dur.seconds();
0
Or call humanize to get a human readable string that is an estimate of the duration's length:
var dur = moment.duration(42, 'hours');
dur.humanize();
"2 days"
You can also do math with durations. See the docs for more info: http://momentjs.com/docs/#/durations/
You can try the following hack(assuming hours in 24-Hours format) :
var momObj = moment('12', 'HH');
var x = momObj.diff(moment().startOf('day'));
console.log(x) //43200000
I want to convert javascript time stamps to erlang dates. I am using the qdate library to help me do that since it also provides functions for date arithmetic.
Calling it's to_date function first before midnight and then after midnight results in time displacement of 24 hrs. For example:-
qdate:to_date(Timestamp div 1000).
%% {2015,5,2} before midnight
qdate:to_date(After_midnight_Timestamp div 1000)
%%{2015,5,2} after midnight should be 3 instead of 2
I googled around a bit and found this in the erlang calender docs
The time functions local_time/0 and universal_time/0 provided in this module both return date and time. The reason for this is that separate functions for date and time may result in a date/time combination which is displaced by 24 hours. This happens if one of the functions is called before midnight, and the other after midnight. This problem also applies to the Erlang BIFs date/0 and time/0, and their use is strongly discouraged if a reliable date/time stamp is required.
I am having trouble understanding this. Which one of the functions from local_time/0 and universal_time/0 always gives the correct results? By correct I mean I want the right date to be shown after midnight. The resolution of the time is only {y,m,d}. Don't care for hours, minutes and seconds or anything finer than that.
So how do I reliably convert a javascript timestamp to a date in erlang?
Looks like it was just a timezone issue :) Since I was working with javascript timestamps the default timezone of the javscript time stamp is my localtimzone which is "IST". Now internally when qdate sees an integer in qdate:to_date(Timestamp). it automatically selects a UTC timezone for it. Relevant code on line 256:-
raw_to_date(Unixtime) when is_integer(Unixtime) ->
unixtime_to_date(Unixtime);
%% other clauses
and on line 654
unixtime_to_now(T) when is_integer(T) ->
MegaSec = floor(T/1000000),
Secs = T - MegaSec*1000000,
{MegaSec,Secs,0}.
unixtime_to_date(T) ->
Now = unixtime_to_now(T),
calendar:now_to_datetime(Now).
The final clue comes from the erlang calendar documentation itself
now_to_datetime(Now) -> datetime1970()
Types: Now = erlang:timestamp()
This function returns Universal Coordinated Time (UTC) converted from the return value from erlang:now().
So the solution to this problem was to simply supply an IST string with qdate:to_date() like so:-
qdate:to_date("IST",Timestamp div 1000)
and it started returning correct dates. I wasn't sure of the solution so I ran a test with qdate:to_date(erlang:now()) and the value returned was exactly 5:30 hrs behind my clock time. So it seems that supplying the timezone string works :)