Simple javascript date math... not really - javascript

I am trying to create a simple script that gives me the next recycling date based on a biweekly schedule starting on Wed Jul 6, 2011. So I've created this simple function...
function getNextDate(startDate) {
if (today <= startDate) {
return startDate;
}
// calculate the day since the start date.
var totalDays = Math.ceil((today.getTime()-startDate.getTime())/(one_day));
// check to see if this day falls on a recycle day
var bumpDays = totalDays%14; // mod 14 -- pickup up every 14 days...
// pickup is today
if (bumpDays == 0) {
return today;
}
// return the closest day which is in 14 days, less the # of days since the last
// pick up..
var ms = today.getTime() + ((14- bumpDays) * one_day);
return new Date(ms);
}
and can call it like...
var today=new Date();
var one_day=1000*60*60*24; // one day in milliseconds
var nextDate = getNextDate(new Date(2011,06,06));
so far so good... but when I project "today" to 10/27/2011, I get Tuesday 11/8/2011 as the next date instead of Wednesday 11/9/2011... In fact every day from now thru 10/26/2011 projects the correct pick-up... and every date from 10/27/2011 thru 2/28/2012 projects the Tuesday and not the Wednesday. And then every date from 2/29/2012 (leap year) thru 10/24/2012 (hmmm October again) projects the Wednesday correctly. What am I missing? Any help would be greatly appreciated..
V

The easiest way to do this is update the Date object using setDate. As the comments for this answer indicate this isn't officially part of the spec, but it is supported on all major browsers.
You should NEVER update a different Date object than the one you did the original getDate call on.
Sample implementation:
var incrementDate = function (date, amount) {
var tmpDate = new Date(date);
tmpDate.setDate(tmpDate.getDate() + amount)
return tmpDate;
};
If you're trying to increment a date, please use this function. It will accept both positive and negative values. It also guarantees that the used date objects isn't changed. This should prevent any error which can occur if you don't expect the update to change the value of the object.
Incorrect usage:
var startDate = new Date('2013-11-01T11:00:00');
var a = new Date();
a.setDate(startDate.getDate() + 14)
This will update the "date" value for startDate with 14 days based on the value of a. Because the value of a is not the same is the previously defined startDate it's possible to get a wrong value.

Expanding on Exellian's answer, if you want to calculate any period in the future (in my case, for the next pay date), you can do a simple loop:
var today = new Date();
var basePayDate = new Date(2012, 9, 23, 0, 0, 0, 0);
while (basePayDate < today) {
basePayDate.setDate(basePayDate.getDate()+14);
}
var nextPayDate = new Date(basePayDate.getTime());
basePayDate.setDate(nextPayDate.getDate()-14);
document.writeln("<p>Previous pay Date: " + basePayDate.toString());
document.writeln("<p>Current Date: " + today.toString());
document.writeln("<p>Next pay Date: " + nextPayDate.toString());
This won't hit odd problems, assuming the core date services work as expected. I have to admit, I didn't test it out to many years into the future...

Note: I had a similar issue; I wanted to create an array of dates on a weekly basis, ie., start date 10/23/2011 and go for 12 weeks. My code was more or less this:
var myDate = new Date(Date.parse(document.eventForm.startDate.value));
var toDate = new Date(myDate);
var week = 60 * 60 * 24 * 7 * 1000;
var milliseconds = toDate.getTime();
dateArray[0] = myDate.format('m/d/Y');
for (var count = 1; count < numberOccurrences; count++) {
milliseconds += week;
toDate.setTime(milliseconds);
dateArray[count] = toDate.format('m/d/Y');
}
Because I didn't specify the time and I live in the US, my default time was midnight, so when I crossed the daylight savings time border, I moved into the previous day. Yuck. I resolved it by setting my time of day to noon before I did my week calculation.

Related

How to programmatically determine the prior time period based upon unix time stamps in Javascript?

Essentially I have two unix timestamps, representing the first and last days of a given month. Is it possible programmatically determine the timestamps for the first and last of the previous month?
For example, I have the following two timestamps:
1467331201 --> July 1, 2016
1469923201 --> July 31, 2016
Essentially, can I manipulate these two numbers in a consistent way in order to the unix time (or Date object) for June 1, 2016 and June 30, 2016, respectively? Problem that I'm running into is that you cannot simply subtract a given amount because the amount of days in a month is variable.
You could use this function:
function getPreviousMonthRange(unixTime) {
var dt = new Date(unixTime * 1000);
dt.setUTCDate(0); // flips to the last day of previous month
var unixLast = dt.getTime();
dt.setUTCDate(1); // back to the first day of that same month
var unixFirst = dt.getTime();
return [unixFirst / 1000, unixLast / 1000];
}
// given first and last date (only one is really needed)
var unixTimeFirst = 1467331201;
var unixTimeLast = 1469923201;
// get previous month's first & last date
var [first, last] = getPreviousMonthRange(unixTimeFirst);
// output
console.log('previous month first day: ', first, new Date(first*1000));
console.log('previous month last day: ', last, new Date(last*1000));
Take a look at the following example:
// Specify a timestamp
var timestamp = 1467331201;
// Create a date object for the time stamp, the object works with milliseconds so multiply by 1000
var date = new Date(timestamp * 1000);
// Set the date to the previous month, on the first day
date.setUTCMonth(date.getUTCMonth() - 1, 1);
// Explicitly set the time to 00:00:00
date.setUTCHours(0, 0, 0);
// Get the timestamp for the first day
var beginTimestamp = date.getTime() / 1000;
// Increase the month by one, and set the date to the last day of the previous month
date.setUTCMonth(date.getUTCMonth() + 1, 0);
// Explicitly set the time to 23:59:59
date.setUTCHours(23, 59, 59);
// Get the timestamp for the last day
var endTimestamp = date.getTime() / 1000;
// Print the results
console.log('Timestamps for previous month: ');
console.log('Begin timestamp: ' + beginTimestamp);
console.log('End timestamp: ' + endTimestamp);
A timestamp must be specified in the variable on the top, this might be one of the two timestamps you suggested in your question, anywhere in a month.
This code then calculates the begin and end timestamp for the previous month as you've requested, and prints the results to the console.
Please note, that in this example the begin timestamp uses 00:00:00 as time, and the end timestamp uses 23:59:59 as time (the last second of that day). This can be configured the way you'd prefer.
In this case, we're working with the ...UTC... Date functions, because a Unix timestamp is in UTC time, not in the timezone the user is in.
The statement date.setMonth(date.getMonth() + 1, 0); is used to select the last day in the month. The next month is selected first, but because the day is set to 0 (and not 1) one day is subtracted giving you the preferred result. This is described here.
You can consider using Moment.js. I'm sure this is not exactly how you'd end up handling it but see below for an example of some helpful methods.
var lastDayOfJuly = moment(1469923201);
var firstDayOfJuly = lastDayOfJuly.startOf('month');
var lastDayOfJune = firstDayOfJuly.subtract(1, 'day');
var firstDayOfJune = lastDayOfJune.startOf('month");
Moment.js

Comparing start time and end time

I know there are many questions asked about this topic here but no one is about the problem I have.
This script is for reservations where the user selects the date and the start and end time and makes a reservation.
I have a form with a date selector input field and two time selector input fields, one for the start time and one for the end time.
The problem is that the store which I'm writing the reservation script for is opened from 17:00 evening to 01:00 morning. So if someone is reserving from 23:00 to 01:00 the start time is always shown as bigger, which results in that the form is not validated.
Does anyone know if there is a solution to this or if there is a validator out there which can do this.
NOTE: I only want to compare the times and I don't want to add another date field.
var timeto=$('#timeto').val();
var timefrom=$('#timefrom').val();
if(timefrom>timeto){
alert('start time should be smaller')
}
So if time from is 23:00 and time to is 00:00 than the alert is shown,but in reality 00:00 is a greater time than 23:00
Just subtract one hour while creating object of date.
var timefrom = new Date();
temp = $('#timefrom').val().split(":");
timefrom.setHours((parseInt(temp[0]) - 1 + 24) % 24);
timefrom.setMinutes(parseInt(temp[1]));
var timeto = new Date();
temp = $('#timeto').val().split(":");
timeto.setHours((parseInt(temp[0]) - 1 + 24) % 24);
timeto.setMinutes(parseInt(temp[1]));
if (timeto < timefrom){
alert('start time should be smaller than end time!');
}
// get the times as strings
start_string = $('#timefrom').val();
end_string = $('#timeto').val();
// define an arbitrary start time since you are only comparing hours
start_time = new Date("May 26, 2016 " + start_string);
// define the end time as the same date + end time
end_time = new Date("May 26, 2016 " + end_string);
// now we need to check if your end time is beyond midnight, if so, we need to add one day to end_time
var stay_length = end_time.getTime() - start_time.getTime();
if (stay_length < 0 {
// end time is beyond midnight, re-calculate end_time with adding one to the day
end_time = new Date("May 27, 2016 " + end_string);
stay_length = end_time.getTime() - start_time.getTime();
} elseif (stay_length > 24 {
// The user probably reversed the times, so show an alert
alert("The start time must be before the end time")
} else {
// The user most likely put in correct times
}
As your times are stored as a string, you can try parsing them to a Date and compare them;
var timeto=$('#timeto').val();
var timefrom=$('#timefrom').val();
if(Date.parse(timefrom) > Date.parse(timeto) > true){
alert('start time should be smaller')
}
I think you should make the time as DateTime to compare easier. Please try this:
var end_time=$('#timeto').val();
var start_time =$('#timefrom').val();
var stt = new Date("May 26, 2016 " + start_time);
stt = stt.getTime();
var endt= new Date("May 26, 2016 " + end_time);
endt = endt.getTime();
if(stt >endt){
//do something
}

Get the most recently occurring Sunday

I need to display the current week in a calendar view, starting from Sunday.
What's the safest way to determine "last sunday" in Javascript?
I was calculating it using the following code:
Date.prototype.addDays = function(n) {
return new Date(this.getTime() + (24*60*60*1000)*n);
}
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
var lastSunday = today.addDays(0-today.getDay());
This code makes the assumption that every day consists of twenty four hours. This is correct, EXCEPT if it's a daylight savings crossover day, in which case the day could be twenty-three or twenty-five hours.
This week, In Sydney, Australia, we set our clocks forward an hour. As a result, my code calculates lastSunday as 23:00 on Saturday.
So what IS the safest and most efficient way to determine last Sunday?
To safely add exactly one day, use:
d.setDate(d.getDate() + 1);
which is daylight saving safe. To set a date object to the last Sunday:
function setToLastSunday(d) {
return d.setDate(d.getDate() - d.getDay());
}
Or to return a new Date object for last Sunday:
function getLastSunday(d) {
var t = new Date(d);
t.setDate(t.getDate() - t.getDay());
return t;
}
Edit
The original answer had an incorrect version adding time, that does add one day but not how the OP wants.
Try this jsfiddle
It uses only built in date methods
var now = new Date();
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
var lastSunday = new Date(today.setDate(today.getDate()-today.getDay()));
using date-fn library: previousSunday(date)
const now = new Date(); // the date to start counting from
previousSunday(now);
Docs: https://date-fns.org/v2.25.0/docs/previousSunday

How to efficiently calculate consecutive dates given an original date

This is for a system that essentially allows you to set the first date for a given event, then to set the recurrence period.
Eg. I set a date for a week from now, 19/07/2012, so I know that I have to put the cat out with the milk. I also set it to be a weekly notification, so in future weeks I want to be notified of the same.
That original date sits in my database, which is fine for week 1, but in week 2 I need to return the date as the original plus 1 week.
On the face of it, that may seem straightforward, but I need to make sure I can account for leap years and different recurrence frequencies (fortnightly, monthly, yearly, whatever).
I'd like to keep this as a javascript implementation - because it's quicker and I feel probably would require less code than updating dates in the database. Maybe it's not achievable, any pointers would be excellent.
I think these may be a starting point:
Given a start date , how to calculate number of years till current date in javascript
Given a date, how can I efficiently calculate the next date in a given sequence (weekly, monthly, annually)?
Update, I've written the below to return the amount of time to add in each different case, from there I can just use the answer below:
var strDate = $(this).find('.next').text();
var frequency = $(this).find('.occurs').text();
var frmDate = getDateObject(strDate);
var toDate = new Date();
var days = parseInt(Math.floor((frmDate - toDate) / 86400000));
if(days < 0) {
// find out how many WHOLE 'frequencies' have passed
var weeks = Math.ceil(0 - (days / 7));
var months = Math.ceil(0 - (monthDiff(toDate,frmDate)));
var years = Math.ceil(months / 12);
//alert(days + '/' + weeks + '/' + fortnights + '/' + months + '/' + quarters + '/' + years);
if(frequency == 'Weekly') { frmDate.add(weeks).weeks(); }
if(frequency == 'Fortnightly') { frmDate.add(weeks*2).weeks(); }
if(frequency == 'Monthly') { frmDate.add(months).months(); }
if(frequency == 'Quarterly') { frmDate.add(months*3).months(); }
if(frequency == 'Annually') { frmDate.add(years).years(); }
var newdate = frmDate.toString("dd/MM/yyyy");
//alert(newdate);
$(this).find('.next').text(newdate);
}
Also, the SQL implementation for this would be using DATEADD:
http://sql-plsql.blogspot.com/2010/07/dateadd.html
You don't have to worry about special dates like leap year and so forth, because most Date functions take care of that.
Alternatively, you can use the getDate(), getMonth() as the other user suggested.
var today = new Date();
today.setDate(today.getDate() + numberOfDaysToAdd);
What I would do (probably not the best solution, I'm just coming up with it right now) is to start from the initial date and use a loop: while the date you are observing is less than the current date, increment the observed date by a week (fortnight, month, year etc.). If you land on the current date, the event happens. Otherwise it's for another day.
You can use things like date.setDate(date.getDate()+1); to increment the date by a day, the same +7 for a week, using set/getMonth and set/getFullYear for months and years respectively. If you give a value out of bounds, JS will wrap it (so March 32nd becomes April 1st)
Please check out the following code for some raw idea
var someDate = new Date();
for(var i = 0 ; i < 7 ; i++)
{
someDate.setDate(someDate.getDate() + 1);
console.log(someDate)
}
You can test the same in the below fiddle
Consecutive 7 days from current day

Get a date object (six months prior) from another date object

How can I create a date object which is less than n number of months from another date object? I am looking for something like DateAdd().
Example:
var objCurrentDate = new Date();
Now using objCurrentDate, how can I create a Date object having a date which is six months older than today's date / objCurrentDate?
You can implement very easily an "addMonths" function:
function addMonths(date, months) {
date.setMonth(date.getMonth() + months);
return date;
}
addMonths(new Date(), -6); // six months before now
// Thu Apr 30 2009 01:22:46 GMT-0600
addMonths(new Date(), -12); // a year before now
// Thu Oct 30 2008 01:20:22 GMT-0600
EDIT: As reported by #Brien, there were several problems with the above approach. It wasn't handling correctly the dates where, for example, the original day in the input date is higher than the number of days in the target month.
Another thing I disliked is that the function was mutating the input Date object.
Here's a better implementation handling the edge cases of the end of months and this one doesn't cause any side-effects in the input date supplied:
const getDaysInMonth = (year, month) => new Date(year, month, 0).getDate()
const addMonths = (input, months) => {
const date = new Date(input)
date.setDate(1)
date.setMonth(date.getMonth() + months)
date.setDate(Math.min(input.getDate(), getDaysInMonth(date.getFullYear(), date.getMonth()+1)))
return date
}
console.log(addMonths(new Date('2020-01-31T00:00:00'), -6))
// "2019-07-31T06:00:00.000Z"
console.log(addMonths(new Date('2020-01-31T00:00:00'), 1))
// "2020-02-29T06:00:00.000Z"
console.log(addMonths(new Date('2020-05-31T00:00:00'), -6))
// "2019-11-30T06:00:00.000Z"
console.log(addMonths(new Date('2020-02-29T00:00:00'), -12))
// "2019-02-28T06:00:00.000Z"
Create date object and pass the value of n, where n is number(add/sub) of month.
var dateObj = new Date();
var requiredDate= dateObj.setMonth(dateObj.getMonth() - n);
var oldDate:Date = new Date();
/*
Check and adjust the date -
At the least, make sure that the getDate() returns a
valid date for the calculated month and year.
If it's not valid, change the date as per your needs.
You might want to reset it to 1st day of the month/last day of the month
or change the month and set it to 1st day of next month or whatever.
*/
if(oldDate.getMonth() < n)
oldDate.setFullYear(oldDate.getFullYear() - 1);
oldDate.setMonth((oldDate.getMonth() + n) % 12);
You have to be careful because dates have a lot of edge cases. For example, merely changing the month back by 6 doesn't account for the differing number of days in each month. For example, if you run a function like:
function addMonths(date, months) {
date.setMonth((date.getMonth() + months) % 12);
return date;
}
addMonths(new Date(2020, 7, 31), -6); //months are 0 based so 7 = August
The resulting date to return would be February 31st, 2020. You need to account for differences in the number of days in a month. Other answers have suggested this in various ways, by moving it to the first of the month, or the last of the month, or the first of the next month, etc. Another way to handle it is to keep the date if it is valid, or to move it to the end of the month if it overflows the month's regular dates. You could write this like:
function addMonths(date, months) {
var month = (date.getMonth() + months) % 12;
//create a new Date object that gets the last day of the desired month
var last = new Date(date.getFullYear(), month + 1, 0);
//compare dates and set appropriately
if (date.getDate() <= last.getDate()) {
date.setMonth(month);
}
else {
date.setMonth(month, last.getDate());
}
return date;
}
This at least ensures that the selected day won't "overflow" the month that it is being moved to. Finding the last day of the month with the datePart = 0 method is documented here.
This function still leaves a lot to be desired, as it doesn't add years and you can't subtract more than a year (or you will run into a new issue with negatives being involved). However, fixing those and the other issues you may run into (namely timezones) will be left as an exercise for the reader.

Categories