Moment.JS - Change a two digit value to milliseconds using moment - javascript

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

Related

Javascript: Calculating time duration on ajax response

I am trying to calculate the time duration of a tasks, that I get from an ajax response.
Following are my table values:
Jobid techid, techtype, notes, starttime, stoptime
1 1 Brakes Break disc needed to be changed 2020-07-16 13:00:00 2020-07-16 13:40:00
1 2 Oil Change Replaced oil 2020-07-17 08:00:00 2020-07-17 09:00:00
1 3 Cleaning Cleaned the vehicle 2020-07-17 10:00:00 2020-07-17 10:30:00
On my ajax response, in the above case, I am getting 3 objects each having the start time, and stop time. I want to calculate total time spent in hours and minutes.
Is there an easy way to calculate the total duration?
With a string like 2020-07-16 13:00:00 you can construct a JS Date and get the milliseconds since the UNIX epoch with getTime() like so
new Date('2020-07-16 13:00:00').getTime()
Or, if you prefer, as pointed out by #Yousaf in the comments you can actually just use the - operator with Dates directly and get the millisecond difference
// resolves to 3600000, or 1 hour in milliseconds
new Date('2020-07-16 13:00:00') - new Date('2020-07-16 12:00:00')
Using that, you can get the difference in milliseconds between any two dates, and convert that to hours / minutes / whatever with straightforward arithmetic.
You can simply use Date to construct a date and then minus the start time from the end time.
Here I use getTime to get the millisecond difference, divide by 1000 to get seconds and divide by 60 to get minutes.
You could also use getMonth and such if you have bigger differences.
const starttime = '2020-07-16 13:00:00'
const stoptime = '2020-07-16 13:40:00'
const duration = new Date(stoptime) - new Date(starttime)
console.log(duration / 1000 / 60)
[UPDATE]
I think you can check this answer, but basically you should convert each date to js Date, get the milliseconds and just calculate endtime - startime.
const timelapse = new Date(endtime).getTime() - new Date(startime).getTime();
From there, you transform that in the unit you need (e.g: seconds = milliseconds/1000);
Sorry, my bad for writing fast.

Add a duration to a repeating event's start time so that it's end is always the same time (i.e 2pm to 4 pm)

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.

Moment.js diff wrong behaviour

I have a date and I want to substract today of this date. This is my example:
date.format('YYYY-MM-DD')
"2018-04-07"
moment().format('YYYY-MM-DD')
"2018-04-06"
date.diff(moment(), 'days')
0
The diff call returns 0 instead of 1. What is wrong here?
By default, moment#diff will truncate the result to zero decimal
places, returning an integer. If you want a floating point number,
pass true as the third argument. Before 2.0.0, moment#diff returned a
number rounded to the nearest integer, not a truncated number.
To see the full value, pass true as the third parameter:
now.diff(date, 'days', true)
If you want to compare just dates, then use:
var now = moment().startOf('day');
which will set the time to 00:00:00 in the local time zone. And compare with date
Use fromNow() function to understand why you are getting 0 instead of 1. It is very straight-forward.
Do like this :
moment(date).fromNow();
It will give you number of days passed if time is greater than 24 hours otherwise it will give to time in hours. e.g. 2 hours ago, 23 hours etc.
Below is example:
console.log(moment("2018-04-06", "YYYY-MM-DD").fromNow());
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>
So you can see it is returning 18 hours ago (as of now) which is less than 24hours i.e. 1 day.
I would suggest to use fromNow instead of diff to get exact difference.
Hope now it makes clear to you.
moment() returns a full moment including time, so it's doing a diff from today, including time, to midnight of the 7th of April, which isn't a full day.
I was also facing the same issue. So after a few research on StackOverflow and moment.js documentation I came up with this solution. Works perfectly for me.
const date1 = "2021-05-12T06:30:00.000Z"
const date2 = "2021-05-18T06:30:00.000Z"
const day1 = moment((moment(date1).format("YYYY-MM-DD")).split("-"))
const day2 = moment((moment(date2).format("YYYY-MM-DD")).split("-"))
const diff = day2.diff(day1,'days')

How does moment.js handle leap seconds?

I need a way to always add a whole minute to a timestamp, even if the minute is 61 seconds long due to a planned leap second. Does anyone know if moment().add(1, 'minute') adds a minute regardless of leap seconds? Or does it just always add sixty seconds?
I've found how it handles addition over daylight savings time and leap years, but nothing at all for leap seconds.
To give some background as to why this is important:
I need to create a CSV file with a bunch of minute-by-minute sensor data for various sensors, formatted like:
time,sensor1,sensor2
1491329921800,20,21
1491329981800,22,21
My data is stored with with the timestamp for the start of an hour, then an array of sixty data points for the hour.
{
timestamp: Date(2017,2,24,12,0,0),
temperature: [20, 22, 23, ... <60 elements total>]
}
I turn this into a bunch of timestamp'd data by giving the first data point the hour's timestamp and adding sixty seconds to that value for each subsequent data point (as leap seconds always happen at the end of the hour and I only ever do an hour at a time, this should be fine).
I then will need to build a dictionary mapping each timestamp to the value at that minute. This is necessary so that I can have the right data in the right row of the CSV; sensors may have started at different times or may have been powered off for a certain hour or not reported for a certain part of the hour; I can't just assume that all sensors have the same data.
I'm finding all of the timestamps over which the CSV will be created with the following code:
var date = moment(startDate);
var end = endDate.getTime();
var timestamps = [];
while(date.valueOf() < end) {
timestamps.push(date.valueOf());
date.add(1, 'minute')
}
timestamps.push(date.valueOf());
But I'm not sure if it's safe. If I need to, I could just change date.add(1, 'minute') to date.add(1, 'minute').startOf('minute'), but this could add a lot to the execution time and I'd like to avoid it if possible.
You don't need to worry about this, because JavaScript is unaware of leap-seconds. Consider this piece of code :-
// 1. Initialize date
var newDate = new Date('1 March 1972 11:30:00');
// 2. Convert to milliseconds and add 20 years
var msec = Date.parse(newDate) + 631152000000;
// 3. Convert milliseconds back to normal date format
newDate = new Date(msec);
The big number in step #2 is the number of milliseconds in 20 years, as given by the equation ((15 x 365) + (5 x 366)) x 24 x 60 x 60 x 1000; 15 years # 365 days per year plus 5 years # 366 days per year, 24 hours per day, 60 minutes per hour, 60 seconds per minute, 1000 milliseconds per second.
After execution the answer given is '1 March 1992 11:30:00'. If JavaScript took account of leap-seconds it would be '1 March 1992 11:30:16', because 16 extra seconds had happened (or '1 March 1992 11:29:44' depending how you look at it).
Summarily, if you need to know how many milliseconds have actually passed between two dates, then you need to write some code to do a lookup of how many leap-seconds occurred within the period they span (you can find the raw data for that here), but for normal time logging we can happily ignore this unwelcome little complication.

Reliable way to convert javascript timestamp into a date tuple

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

Categories