I need a JavaScript function that returns the number of days remaining from a particular date of every year.
I found the following code, but how can I make it repeatable for every year, instead of changing the year in the function manually?
function daysUntil(year, month, day) {
var now = new Date(),
dateEnd = new Date(year, month - 1, day), // months are zero-based
days = (dateEnd - now) / 1000/60/60/24; // convert milliseconds to days
return Math.round(days);
}
daysUntil(2013, 10, 26);
I think my question above is not clear enough, i need to show days remaining in 26th October. So this starts again every year on 27th October. I don't need a loop for that.
"how can i make it repeatable for every year, instead of changing the year in function manually?"
Well you can't do literally every year to infinity, but you can easily add a loop to get a specific range of years:
var d;
for (var y = 2013; y < 2099; y++) {
d = daysUntil(y, 10, 26);
// do something with d, e.g.,
console.log(d);
}
UPDATE: You added this detail to your question:
"I think my question above is not clear enough, i need to show days remaining in 26th October. So this starts again every year on 27th October. I don't need a loop for that."
OK, that's still not very clear, but I think you're saying that your input would be just the day and month and you want to calculate the number of days until the next time that day/month rolls around, e.g., the number of days until your next birthday. If so, perhaps something like this:
function daysUntil(month, day) {
var now = new Date(),
currentYear = now.getFullYear(),
dateEnd = new Date(currentYear, month - 1, day); // months are zero-based
if (dateEnd - now < 0) // have we already passed that date this year?
dateEnd.setFullYear(currentYear + 1);
return Math.ceil((dateEnd - now) / 1000/60/60/24);
}
console.log(daysUntil(10,11)); // 365 - results from today, Oct 11
console.log(daysUntil(10,26)); // 15
console.log(daysUntil(7,7)); // 269
Related
I am using Bootstrap Datepicker, and based on the date selection I need to display a message to the user.
I have never used the Date Constructor before so it's very new to me.
What I need to do is the following;
allow user to select a date
display a message / alert based of the logic below
If their selected date is within the last 6 months of today, they quality for discount.
If their selected date doesn't fall within the last 6 months of today, they don't.
Although it's not working correctly have created a fiddle here.
Any help would be appreciated. Code also below;
HTML
<input type="text" class="form-control" id="datepicker" placeholder="Year Graduated" value="" data-date-format="dd/mm/yyyy">
<p id="rate"></p>
JS
function compareDate() {
// get date from datepicker
var dateEntered = $("#datepicker").datepicker("getDate");
dateEntered = new Date(dateEntered).getTime();
//alert("date entered: " + dateEntered);
// set todays date
var now = new Date();
// set date six months before today
var sixMonthBeforeNow = new Date(now).setTime(now.getTime() - 3 * 28 * 24 * 60 * 60);
//alert("six months before: " + sixMonthBeforeNow);
// if date entered is within six months from today
if (dateEntered > sixMonthBeforeNow) {
alert("You qualify for the discount rate.");
$("#rate").html('discount rate');
}
// if date entered is over six months from today
if (dateEntered < sixMonthBeforeNow) {
alert("you graduated more than six months ago");
$("#rate").html('no discount');
}
}
$("#datepicker").datepicker({
weekStart: 1,
daysOfWeekHighlighted: "6,0",
autoclose: true,
todayHighlight: true
});
$("#datepicker").change(function() {
compareDate();
});
Note: I'd prefer not to use any other 3rd party JS library / plugin.
Just change your sixMonthBeforeNow with the below code, that should work.
var sixMonthBeforeNow = new Date(now).setMonth(now.getMonth() - 6);
You need to be careful with date arithmetic because it's not symmetric due to the uneven length of months, so you need rules to deal with it. E.g. what date is exactly 6 months before 31 August?
Before answering, consider:
28 February plus 6 months is 28 August
1 March plus 6 months is 1 September.
So what date is 6 months before 29, 30 and 31 August? Is it 28 February or 1 March?
Similar issues arise for any last day of a month where the month 6 months previous doesn't have 31 days. Should the limit be the 30th of the month or the 1st of the following month? When you've answered that question, then you can devise an algorithm to deliver the right answer and then the code to implement it.
If you want such cases to set the date to the end of the month 6 months before, then you can check the month resulting from subtracting 6 months and if it's not 6, set it to the last day of the previous month, e.g.
function sixMonthsPrior(date) {
// Copy date so don't affect original
var d = new Date(date);
// Get the current month number
var m = d.getMonth();
// Subtract 6 months
d.setMonth(d.getMonth() - 6);
// If the new month number isn't m - 6, set to last day of previous month
// Allow for cases where m < 6
var diff = (m + 12 - d.getMonth()) % 12;
if (diff < 6) d.setDate(0)
return d;
}
// Helper to format the date
function formatDate(d) {
return d.toLocaleString(undefined, {day:'2-digit', month:'short', year:'numeric'});
}
// Tests
[ new Date(2018, 7,31), // 31 Aug 2018
new Date(2018, 8, 1), // 1 Sep 2018
new Date(2018,11,31), // 31 Dec 2018
new Date(2019, 2,31) // 31 Mar 2019
].forEach( d => console.log(formatDate(d) + ' => ' + formatDate(sixMonthsPrior(d))));
If that is't the logic you wish to apply, you need to say what is.
PS. You can also implement the above logic by just comparing the start and end dates (day number). If they're different, it must have rolled over a month so set to 0.
This has been asked (badly) before - I don't think the answer in that post really addressed the issue, and then it went stale. I'm going to attempt to ask it again with a clearer demonstration of the issue.
The implementation of Javascript Date.setMonth() appears not to follow the principle of least surprise. Try this in a browser console:
d = new Date('2017-08-31') // Set to last day of August
d.getMonth() // 7 - months are zero-based
d.setMonth(8) // Try to set the month to 8 (September)
d.getMonth() // 9 - October. WTF Javascript?
Similarly:
d = new Date('2017-10-31')
d.getMonth() // 9
d.setMonth(8)
d.getMonth() // 9 (still?)
Firefox on Linux appears even worse - sometimes returning a date in October, and a result from getMonth() which doesn't match that month!
My question (and I think that of the OP from that linked question) is how to consistently implement a 'next' / 'prev' month function in, e.g. a datepicker? Is there a well known way of doing this which doesn't surprise the user by, for example, skipping September when they start on August 31st and click 'next'? Going from January 31st is even more unpredictable currently - you will end up on either March 2nd or March 3rd, depending on whether it's a leap year or not!
My personal view is that the least surprise would be to move to the last day of the next / previous month. But that requires the setMonth() implementation to care about the number of days in the months in question, not just add / subtract a fixed duration. According to this thread, the moment.js approach is to add / subtract the number of milliseconds in 30 days, which suggests that library would be prone to the same inconsistencies.
It's all simple and logic. Lets take your example and go see what id does.
So the first line
d = new Date('2017-08-31') // Set to last day of August
console.log(d); // "2017-08-31T00:00:00.000Z"
console.log(d.getMonth()); // 7 - months are zero-based
So all good so far. Next step: Your comment says it: // Try to set the month to 8 (September) So it's not done with trying. You either set it to september or you don't. In your example you set it to October. Explanation further down.
d = new Date('2017-08-31') // Set to last day of August
console.log(d); // "2017-08-31T00:00:00.000Z"
console.log(d.getMonth()); // 7 - months are zero-based
d.setMonth(8) // Try to set the month to 8 (September)
console.log(d); // but now I see I was wrong it is (October)
So the good question is WHY? From MDN
Note: Where Date is called as a constructor with more than one
argument, if values are greater than their logical range (e.g. 13 is
provided as the month value or 70 for the minute value), the adjacent
value will be adjusted. E.g. new Date(2013, 13, 1) is equivalent to
new Date(2014, 1, 1), both create a date for 2014-02-01 (note that the
month is 0-based). Similarly for other values: new Date(2013, 2, 1, 0,
70) is equivalent to new Date(2013, 2, 1, 1, 10) which both create a
date for 2013-03-01T01:10:00.
So that sayd September has only 30 Days but the Date Object has 31. This is why it gives you October and not September.
The simplest will be to take the date you have and set it to first day of month. Something like so:
var d = new Date('2017-08-31') // Set to last day of August
// simplest fix take the date you have and set it to first day of month
d = new Date(d.getFullYear(), d.getMonth(), 1);
console.log(d); // "2017-08-31T00:00:00.000Z"
console.log(d.getMonth()); // 7 - months are zero-based
d.setMonth(8) // Set the month to 8 (September)
console.log(d.getMonth()); // get 8 it is (September)
If setMonth is used when adding and subtracting months, then if the date of the start month doesn't exist in the end month, the extra days cause the date to "roll over" to the next month, so 31 March minus 1 month gives 2 or 3 March.
A simple algorithm is to test the start date and end date and if they differ, set the end date to 0 so it goes to the last day of the previous month.
One issue with this is that subtracting 1 month twice may not give the same result as subtracting 2 months once. 31 March 2017 minus one month gives 28 Feb, minus another month gives 28 Jan. Subtract 2 months from 31 March and you get 31 Jan.
C'est la vie.
function addMonths(date, num) {
var d = date.getDate();
date.setMonth(date.getMonth() + num);
if (date.getDate() != d) date.setDate(0);
return date;
}
// Subtract one month from 31 March
var a = new Date(2017,2,31);
console.log(addMonths(a, -1).toString()); // 28 Feb
// Add one month to 31 January
var b = new Date(2017,0,31);
console.log(addMonths(b, 1).toString()); // 28 Feb
// 29 Feb plus 12 months
var c = new Date(2016,1,29)
console.log(addMonths(c, 12).toString()); // 28 Feb
// 29 Feb minus 12 months
var c = new Date(2016,1,29)
console.log(addMonths(c, -12).toString()); // 28 Feb
// 31 Jul minus 1 month
var d = new Date(2016,6,31)
console.log(addMonths(d, -1).toString()); // 30 Jun
Since getMonth() returns an integer number, you can simply implement a generator over the date object, that sets the month + 1 or - 1 so long as your not at month 11 or month 0 respectively.
function nextMonth(dateObj) {
var month = dateObj.getMonth();
if(month != 11) dateObj.setMonth(month + 1);
return dateObj;
}
function prevMonth(dateObj) {
var month = dateObj.getMonth();
if(month != 0) dateObj.setMonth(month - 1);
return dateObj;
}
If you want to match the days in the previous month you can use an object lookup table.
Now, for your last day of the month problem:
function getLastDayofMonth(month) {
var lookUp = {
0:31,
1:28,
2:30,
3:31
};
return lookUp[month];
}
//and then a revised version
function nextMonth(dateObj) {
var month = dateObj.getMonth();
var day = dateObj.getDate();
if(month != 12) dateObj.setMonth(month + 1);
if(getLastDayofMonth(month)<day)dateObj.setDate(getLastDayofMonth(month));
return dateObj;
}
This should work for incrementing the month, you can use a similar strategy to decrement.
// isLeapYear :: Number -> Boolean
const isLeapYear = ((err) => {
return yr => {
// check for the special years, see https://www.wwu.edu/skywise/leapyear.html
if (yr === 0) {
throw err;
}
// after 8 AD, follows 'normal' leap year rules
let passed = true;
// not technically true as there were 13 LY BCE, but hey.
if (yr === 4 || yr < 0 || (yr % 4)) {
passed = false;
} else {
if (yr % 400) {
if (!(yr % 100)) {
passed = false;
}
}
}
return passed;
};
})(new Error('Year zero does not exist, refers to 1 BCE'));
const daysInMonth = [
31,
28,
31,
30,
31,
30,
31,
31,
30,
31,
30,
31
];
// isLastDay :: Number, Number -> Boolean
const isLastDay = (d, m, y) => {
let dm = isLeapYear(y) && m === 1 ? 29 : daysInMonth(m);
return dm === d;
};
// getLastDay :: Number, Number -> Number
const getLastDay = (m, y) => isLeapYear(y) && m === 1 ? 29 : daysInMonth[m];
// incMonth :: Date -> Date
const incMonth = d => {
let dd = new Date(d.getTime());
let day = dd.getDate();
let month = dd.getMonth() + 1;
dd.setDate(5); // should avoid edge-case shenanigans
dd.setMonth(month);
let year = dd.getFullYear();
if (isLastDay(day, month, year)) day = getLastDay(month, year);
dd.setDate(day);
return dd;
};
This was the solution I came up with, which seems small and reliable as far as I can tell. It doesn't need any extra data structures, and relies on setDate(0) to select the last day of the month in the edge cases. Otherwise it leaves the date alone, which is the behaviour I wanted. It also handles wrapping round from one year to the next (in either direction):
function reallySetMonth(dateObj, targetMonth) {
const newDate = new Date(dateObj.setMonth(targetMonth))
if (newDate.getMonth() !== ((targetMonth % 12) + 12) % 12) { // Get the target month modulo 12 (see https://stackoverflow.com/a/4467559/1454454 for details about modulo in Javascript)
newDate.setDate(0)
}
return newDate
}
Note I've only tested this with targetMonth being either one higher or lower than the current month, since I'm using it with 'next' / 'back' buttons. It would need testing further user with arbitrary months.
I know how to get a week number for a ISO format. There are some nice questions and answers about this.
I would like now to know how to find the week number in the US format. The US week number rules says the week 1 is always the week containing the 1st of january, so I'm safe in January. But how to know the week number of 1st of march of a given year?
Teoretically I imagine it has to take into account which day in the week the year starts, find out the length of that years februari and iterate untill I reach the right day, adding weeks to it.
In my logic should be something like:
function getUSweekNumber(y, m, d) {
var firstOfJanuary = new Date(y, 0, 1);
var dayOfWeek = firstOfJanuary.getDay() || 7;
var weekNr = 1;
for (var i = 0; i < m; i++) {
var daysInMonth = new Date(y, i + 1, 0).getDate();
weekNr += parseInt(daysInMonth / 7, 10);
dayOfWeek += daysInMonth % 7;
}
weekNr += parseInt((dayOfWeek + d) / 7, 10);
return weekNr;
}
but I get wrong results:
console.log(getUSweekNumber(2015, 0, 1)); // 1
console.log(getUSweekNumber(2015, 0, 15)); // 3
console.log(getUSweekNumber(2015, 7, 1)); // 32 <- wrong, whould be 31
What am I missing? Is there a better way?
jsFiddle: https://jsfiddle.net/Lqghrvw0/2/
A one line solution
It appears that the selected answer might have a bug. For example, when given the date 30-DEC-2015 it should return 1 because that week contains 1-JAN-2016. Yet, the function instead returns 53.
So the problem is how to handle the final days from the previous year that fall within the first week of the current year?
One possible way to deal with it is to adjust the target date so that it always starts on the last day of the week rather than any day of the week. And this can be done by using the day value (0-6) as an offset like so:
var endDate = targetDate.getUTCDate() + 6 - targetDate.getUTCDay();
This works for USA week numbering and could also be made to work with ISO-8601 week numbering with minor modifications (not shown). We can then plug this snippet into a one line solution like so:
var weeks=Math.ceil((((d.setUTCDate(d.getUTCDate()+6-d.getUTCDay())-d.setUTCMonth(0,1))/86400000)+d.getUTCDay()+1)/7);
And our function simply becomes:
function getWeekUS( date ) {
var d = new Date( date );
return Math.ceil(((( d.setUTCDate( d.getUTCDate() + 6 - d.getUTCDay()) - d.setUTCMonth( 0, 1 )) / 86400000 ) + d.getUTCDay() + 1 ) / 7 );
}
Explained:
We first make a copy of the date to avoid altering the original. We then add an offset so the target date falls on the last day of the week. Then we set the date to 1-JAN and find the difference between that and the target date. We divide that number by 8640000 (24*60*60*1000) to convert milliseconds to days. Finally, we add the starting date offset and then divide by 7 to get weeks.
And now our function returns the correct week for edge cases like 30-DEC-2015.
Your approach with a loop seems somewhat complicated. Your initial thinking that you need the day of the week is good, but you do not use it correctly.
The US week starts on a sunday, and the first week is the week of January 1st. Let's take a closer look of the first week of 2015:
| 28 | 29 | 30 | 31 | 1 | 2 | 3 |
| Su | Mo | Tu | We | Th | Fr | Sa |
For ease of computing, we want to shift the week, so that the first 7 days are week 1. How do we do that? Well, by using the day of the week. Because Sunday is already day 0, we don't need to alter it in any way. If January 1st is on a Sunday, we don't need to shift the week.
We can easily compute the amount of days between two dates. Because of daylight saving, we have to round the number we get, and since we don't shift the clock with more than 12 hours, we are fine normalizing that way. Then we just need to divide the number by 7 to get the week number.
function getUSweekNumber(y, m, d) {
//January 1st this year
var beginOfThisYear = new Date(y, 0, 1);
var dayOfWeek = beginOfThisYear.getDay();
//January 1st next year
var beginOfNextYear = new Date(y+1, 0, 1);
var dayOfWeekNextYear = beginOfNextYear.getDay();
//Provided date
var currentDay = new Date(y, m, d);
var oneDay = 1000 * 60 * 60 * 24;
var numberOfDays = 1 + Math.round((currentDay.getTime() - beginOfThisYear.getTime()) / oneDay);
var weekNr;
if( currentDay.getTime() >= beginOfNextYear.getTime() - (oneDay * dayOfWeekNextYear) ) {
//First week of next year
weekNr = 1;
} else {
//Shift week so 1-7 are week 1, then get week number
weekNr = Math.ceil((numberOfDays + dayOfWeek) / 7);
}
return weekNr;
}
console.log(getUSweekNumber(2015, 0, 1)); // 1
console.log(getUSweekNumber(2015, 0, 15)); // 3
console.log(getUSweekNumber(2015, 7, 1)); // 31
console.log(getUSweekNumber(2015, 11, 27)); //1
use http://momentjs.com. They define the method week:
function getUSweekNumber(y, m, d) {
var date = moment({ year: y, month: m, day: d });
return date.week();
}
I have 3 people's birthdays:
Steve -> May 3
Mark -> October 20
Robbin -> December 5
How to loop through the remaining days of the year and find which person still has a birthday coming (IN THIS CASE output MARK AND ROBBIN)?
I've been able to get the remaing days of the year, but I'm not sure how to loop through the remaing days of the year
and output those people's names (the ones that will have bithdays in the next months).
This is what I have done so far:
I created a funtion to get the remaming days:
function remainingDays(date1, date2) {
// The number of milliseconds in one day
var ONE_DAY = 1000 * 60 * 60 * 24
var date1_ms = date1.getTime()
var date2_ms = date2.getTime()
var difference_ms = Math.abs(date1_ms - date2_ms)
// Convert back to days and return
return Math.round(difference_ms/ONE_DAY)
}
As you can see the function returns the number of days left. And here's the values:
var current_date = new Date()
// Store the date of the next New Year's Day
var new_years_date = new Date()
new_years_date.setYear(new_years_date.getFullYear() + 1)
new_years_date.setMonth(0)
new_years_date.setDate(1)
// Call the remainingDays function
var days_left = remainingDays(current_date, new_years_date);
if (days_left > 1) {
document.write(days_left + " days left this year")
} else {
document.write(days_left + " day left this year")
}
Please help me....Thank you in advanced!!
What about something like this? (see the jsfiddle):
var birthdays = [
{
name: 'Mark',
date: 'October 20'
},
{
name: 'Robbin',
date: 'December 5'
}
];
var upcoming = birthdays.filter(function(person) {
var date = new Date(person.date + ' 2015');
// returns a boolean "true" or "false"
return date > new Date();
});
console.log(upcoming);
It simply filters the current birthdays into a new array, upcoming, if the birthday is greater than today's date. No need to overcomplicate it.
You could make it more semantic and extensible if you so desire:
var upcoming = birthdays.filter(afterToday);
function afterToday(person) {
return new Date(person.date + ' 2015') > new Date();
}
One of the key paradigms of programming is that simpler is better. Many algorithms can do the same thing (end up with the same results), but usually the simpler algorithm is better, all else being equal.
In other words:
The algorithm can loop through every single date remaining in the year, or
The algorithm can simply check if the date is greater than today
I think the second is simpler. Also, less lines of code, and more readable.
You can set a variable for each person and check if his birthday is coming up
var Mark = new Date(2015, 9, 20);
var now = new Date()
if(Mark > now){
doSomething()
}
Don't loop,
calculate julian date (julian date of a date is the order number of the day in the year, 32 for example is the julian date of 1st of February, see https://en.wikipedia.org/wiki/Julian_day)
Calculating Jday(Julian Day) in javascript
http://javascript.about.com/library/bljuldate.htm
Then see if current julian day is bigger or smaller than any of the birthday julian days
I am trying to determine the time elapsed between 2 dates using javascript. An example would be: "I quit smoking on January 5, 2008 at 3 A.M., how many years, months, and hours has elapsed since I quit?".
So my thoughts were:
Get "quit" date
Get current date
Convert to time (milliseconds)
Find the difference
Create a new date using the difference
Extract the years, months, etc. from that date
Well, it is acting strange and I can't pin point why. Any insight?
//create custom test date
var d1 = new Date(2012, 8, 28, 13, 14, 0, 0);
//create current date
var d2 = new Date();
//get date times (ms)
var d1Time = (d1.getTime());
var d2Time = (d2.getTime());
//calculate the difference in date times
var diff = d2 - d1;
//create a new date using the time differences (starts at Jan 1, 1970)
var dDiff = new Date();
dDiff.setTime(diff);
//chop off 1970 and get year, month, day, and hour
var years = dDiff.getFullYear() - 1970;
var months = dDiff.getMonth();
var days = dDiff.getDate();
var hours = dDiff.getHours();
You can see it in action at this temporary host.
Why don't you just do the math to calculate the values? What you are putting into Date when you do dDiff.setTime(diff); is meaningless to you. That is just going to give you the date diff ms from the epoch.
Changing part of your code may solve your problem. jsfiddle
var start = new Date(0); // pivote point of date.
var years = dDiff.getFullYear() - start.getFullYear();
var months = dDiff.getMonth() - start.getMonth();
var days = dDiff.getDate() - start.getDate();
var hours = dDiff.getHours() - start.getHours();;
console.log(years, months, days, hours);
But you have to manipulate these values based on there value( they may come negative).
Date represents a particular point in time, not a timespan between two dates.
You are creating a new date by setting dDiff milliseconds ellapsed since the unix epoch.
Once you have the milliseconds ellapsed, you should extract the information you need by dividing it. See this question.
May I recomend taking a look at Moment.js?
This won't be accurate as it does not take into account the leap dayys. Other than that, it is working correctly and I don't see any problem. The time difference is roughly 6.5 days. Taking into account timezone and the fact that 0 is Jan 1st, the value I see is as expected.
The accurate solution would be to
Convert the time difference into days
Subtract the number of leap years elapsed since the specified date
Divide the remaining by 365 to get the number of days
Create an array with the day count of each month (without considering leap days) and loop through the elapsed months, subtracting the day count for the completed months. The number of iterations will be your month count
The remainder is your day count
Various notes:
new Date(2012, 8, 28, 13, 14, 0, 0); is 28 September 2012 13:14:00 (not August if you would it)
new Date(0) returned value is not a constant, because of the practice of using Daylight Saving Time.
dDiff.getMonth(); return 0 for Jan, 1 for Feb etc.
The begin of date (1 Jan 1970) begin with 1 so in difference you should subtract this.
I think the second point is your mistake.
According with your algorithm, try this:
// create date in UTC
//create custom test date
var dlocaltime = new Date(2012, 8, 28, 13, 14, 0, 0);
var d1 = new Date(dlocaltime.getUTCFullYear(),dlocaltime.getUTCMonth(), dlocaltime.getUTCDate(), dlocaltime.getUTCHours(),dlocaltime.getUTCMinutes(),dlocaltime.getUTCSeconds());
//create current date
var now = new Date();
var d2 = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
console.log(d1);
console.log(d2);
//get date times (ms)
var d1Time = (d1.getTime());
var d2Time = (d2.getTime());
//calculate the difference in date times
var diff = d2 - d1;
//create a new date using the time differences (starts at Jan 1, 1970)
var dDiff = new Date();
dDiff.setTime(diff);
//chop off 1970 and get year, month, day, and hour
var years = dDiff.getUTCFullYear() - 1970;
var months = dDiff.getUTCMonth();
var days = dDiff.getUTCDate()-1; // the date of new Date(0) begin with 1
var hours = dDiff.getUTCHours();
var minutes = dDiff.getUTCMinutes();
var seconds = dDiff.getUTCSeconds();
console.log("Years:"+years);
console.log("months:"+months);
console.log("days:"+days);
console.log("hours:"+hours);
console.log("minutes:"+minutes);
console.log("seconds:"+seconds);