moment.js time wrongly calculated - javascript

I am using the following in moment.js to convert seconds to Days Hours Minutes Seconds format
moment().startOf('year').seconds(1209600).format('DD HH:mm:ss')
But instead of getting 14 00:00:00, I am getting 15 00:00:00
What am I missing here?

1209600 seconds is 14 days, so because the first day of the year is day 01 00:00:00, if you add 14 days you get 15 00:00:00.
You don't say exactly what you're trying to do, but what you're getting is the right answer for "what's the date/time for 1209600 seconds into the year."

You're attempting to work with the concept of duration, but you're using the calendar to do it. This isn't a good idea for several reasons. As others pointed out, the calendar starts on the 1st, which is throwing you off. But also, you could have local time zone discontinuities affect your results, such as if your duration went far enough into the year to be caught by the spring-forward daylight saving time transition.
If you want to use Moment to work with durations, there is a separate API for that:
var d = moment.duration(1209600, 'seconds');
var h = d.hours();
var m = d.minutes();
var s = d.seconds();
There is currently not a format method built-in for durations, so you'd have to assemble these into a string yourself, applying zero-padding where necessary. However, there is the moment-duration-format third-party plugin, which would let you do it like this:
moment.duration(1209600, 'seconds').format('DD HH:mm:ss')

moment().startOf('year'); // set to January 1st, 12:00 am this year
So, startOf('year') method set moment starting point to 1st January of current year from 12.00 AM
, which is start of the day. and you are adding 14 days on top of that. But as the initial date started at 12.00 AM, its still a whole day (ends at 11.50 PM) which adds one additional day in final result.

Related

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.

How to calculate relative date from now in only days using momentjs?

I am trying to get the amount of days relative to the current time, returning only days as units. So if something happened a week ago, it would say 7 days. If it happened 2 months ago, it would return that time in days as well.
I am aware of how to get there but I am having trouble putting the pieces together.
I have my days as a data attribute "data-order" so data-order="2019-4-2 00:00" or "2019-4-2" if it makes calculations easier.
$(".pop-cal").each(function (i, obj) {
moment.relativeTimeThreshold("m", 1);
moment.relativeTimeThreshold("d", 25 * 100);
var date = $(this).attr("data-order");
var momentDate = moment(date).fromNow();
$(this).attr("data-content", momentDate);
});
This is getting me dates, but they are always off.
My expected results would be similar confirmation to using google search and saying "58 days ago" and getting Tuesday February 12th 2019.
What I am currently getting as result are "59 days ago" on a moment time created on "2019-2-12.
The fromNow method isn't suitable if you need the exact number of days, because it works by converting the time to a Duration (measured in months and milliseconds), and then converting the Duration to a human readable form (humanize method).
As the Duration docs state:
It is much better to use moment#diff for calculating days or years
between two moments than to use Durations.
You can see the problem if you perform fromNow on February 28th and March 1st, 1 day apart but giving a fromNow of 4 days apart (due to 28 days in February instead of 31 days).
moment("2019-03-01").fromNow() // "45 days ago"
moment("2019-02-28").fromNow() // "49 days ago"
The moment#diff method can give you the exact difference between two moments in days, e.g. between moments a and b:
a.diff(b, 'days')

How to use Momentjs to calculate number of hours on Day light savings Day

I would like to know how to use Moment.JS to caluculate the number of hours on a given day.
The reason is that a regular day will be 24 hrs. But the day that daylight savings time starts in the Spring will be 25hrs.
OR how can I use moment.js or even js to calculate if daylight savings date in the spring has been reached bearing in mind that DST starts after 2a.m.
The code I am trying to use is
moment([2017, 2, 12]).isDST();
However how can i used it such that not only does it tell me if its DST but also can check if its after 2 a.m.
Simply get the difference in hours between the start of the day, and the start of the next day.
var m = moment([2017, 2, 12]); // your moment object, however you create it.
var a = moment(m).startOf('day');
var b = moment(m).add(1, 'day').startOf('day');
var h = b.diff(a, 'hours'); // 23, 24, 25, etc.
Note that time zones are different all over the world. Some do DST, some don't. Some do it on different dates and different times. It's even possible to get 23.5 or 24.5 as a result, since there is at least one place that has a 30-minute DST bias instead of the normal 1-hour (Lord Howe Island, Australia). And also there are places that have transitions not related to DST, such as when Venezuela moved their standard time from UTC-04:30 back to UTC-04:00 in May 2016.
Also, you said:
... But the day that daylight savings time starts in the Spring will be 25hrs.
It's actually the Fall that has an extra hour. In the Spring, it would be an hour short (23 hours).

Calculate difference between 2 dates considering Daylight Saving Time

Given a start date, and a number of days, I need to display the end date = start date + number of days.
So I did something like this:
var endDate=new Date(startDate.getTime()+ONE_DAY);
Everything works fine, except that for 25 and 26 October gives one day less.
Ex.:
2014-01-01 + 2 days = 2014-01-03
2014-10-25 + 2 days = 2014-10-26 (here is the case I need to treat).
This difference appear because of the clock going back 1 hour. Practically 2014-10-27 00:00:00 becomes 2014-10-26 23:00:00.
A simple solution would be to compute this at another hour (example 3 AM). But I want to just display a note when this happens.
For example, if user inputs 2014-10-25, I show a popup saying [something].
Now here is the real problem... I can't seem to find any algorithm that says when clocks goes back in year X.
Example... in 2014 the day is 26 October. In 2016 is 30 October (https://www.gov.uk/when-do-the-clocks-change). Why? This date looks random to be, but I don't think it is. So... when does clock go back/forward?
EDIT: All answers/comments are helpful related to how to fix the problem. But... I already passed that stage. Now I only have an itch about "how on earth are the days when clock is changed computed?".
To find the difference between two dates in whole days, create Date objects, subtract one from the other, then divide by the milliseconds in one day and round. The remainder will only be out by 1 hour for daylight saving so will round to the right value.
You may also need a small function to convert strings to Dates:
// Return Date given ISO date as yyyy-mm-dd
function parseISODate(ds) {
var d = ds.split(/\D/);
return new Date(d[0], --d[1], d[2]);
}
Get the difference in days:
function dateDiff(d0, d1) {
return Math.round((d1 - d0)/8.64e7);
}
// 297
console.log(dateDiff(parseISODate('2014-01-01'), parseISODate('2014-10-25')));
If you want to add days to a date, do something like:
// Add 2 days to 2014-10-25
var d = new Date(2014, 9, 25);
d.setDate(d.getDate() + 2);
console.log(d); // 2014-10-27
The built–in Date object takes account of daylight saving (thought there are bugs in some browsers).
I prefer adding days this way:
var startDate = //someDate;
var endDate = new Date(startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate()+1);
This way you don't have to worry about the days in the calendar.
This code add 1 day, if you want to add more, change the startDate.getDate()+1 for startDate.getDate()+NUMBER_OF_DAYS it works fine even if you are on the last day of month i.e. October 31th.
But maybe you can use #RobG solution which is more elegant than mine

Date without daylight savings time

I was trying to create a JavaScript calendar, but when I try to increment the days, I get a problem which seems to be the daylight savings change. For example, when I try to increment the days in March 2012 (myDate.setDate(myDate.getDate() + 1); I get 2 days of 24, 24 march 2012 0:00 and 24 march 2012 23:00 (I hope I remember correctly and it's 24). This happens only on some PCs (probably the ones who have it enabled on the operating system).
My question is, can I remove the auto daylight increment and use the date variable just to store the date and time datas and make it disabled to auto increment / decrement the numbers.
Thank you, Daniel!
using .setUTCDate() and .getUTCDate() will set and get the day of the month ignoring the timezone offset but lanzz is right that we'd need to see how you are initialising the myDate variable.

Categories