JavaScript: unexpected result getting number of days remaining in the month - javascript

I'm building a simple script to show the number of days to the end of the month to demonstrate an understanding of how dates work in Javascript. Which is just as well, because I clearly don't have one.
I want to get the current date, and compare it to the last day of the month, as date(this year, this month +1, day 0) - 1 day to see how many days are left. I could build an array telling it how many days are in each month, but I should be able to use the inbuilt understanding of dates to resolve this.
For expected output, last day of this month (Aug 31) - today (Aug 30) = 1. But I'm getting 0.
This is what I have so far:
<p id="demo"></p>
<script>
var d = new Date();
var nxt = new Date(d.getFullYear(), d.getMonth() + 1, 0);
nxt.setDate(nxt.getDate() - 1);
document.getElementById("demo").innerHTML = nxt.getDay() - d.getDay();
</script>
At the moment it's 11 in AEST (GMT + 1000), which is where I'm at in case that's of any help. Point is to make something that would work anywhere, any time.
My first suspect is timezones - I thought that this might even out if I used all UTC or no UTC explicits. It doesn't seem to be doing so though. I've tried asking for UTC values in a range of different places, but oddly only seem to be able to increase the inaccuracy so it returns -1.

I believe I just stumbled on the answer - when specifying a date months are counted from 0 to 11, but days are still counted from 1 to 31. Therefore, specifying
Date(d.getFullYear(), d.getMonth() + 1, 0);
is basically shorthand for the last day of the current month. The next line is then redundant:
nxt.setDate(nxt.getDate() - 1);
This will give an incorrect result, as it basically decrements a second time. The problem is not with UTC/nonUTC, but rather incorrect day specification. Not sure if this shorthand could cause other problems further down the line though.

For anyone wondering why the day zero value results in that behavior, JavaScript... return last available date of calendar provides a useful explanation.
The following vanilla js approach to calculating days remaining in the month is just a slight variation on what you are doing with brief comment explanation of how to take advantage of month index and day zero to get the last day of the current month.
const year = 2018;
const month = 7; // months indexed from 0 (Jan) to 11 (Dec)
const day = 30;
// current datetime
const date = new Date(year, month, day);
// add 1 to get next month, set day to 0 to roll it back to last day of current month
const last = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
// difference between last day of the month and input date
const diff = last - date.getDate();
console.log(diff);
Below is a snippet where you can experiment with the results for different dates.
const remainDays = (y, m, d) => {
const result = document.querySelector('#result');
const date = new Date(y, m, d);
const last = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
const diff = last - date.getDate();
result.textContent = diff;
};
const year = document.querySelector('#year');
const month = document.querySelector('#month');
const day = document.querySelector('#day');
const button = document.querySelector('button');
remainDays(year.value, month.value, day.value);
button.addEventListener('click', () => {
remainDays(year.value, month.value, day.value);
});
div {
padding: 5px;
display: flex;
flex-direction: column;
max-width: 400px;
}
<div>
<label for="year">Year</label>
<input id="year" name="year" type="text" value="2018" />
</div>
<div>
<label for="month">Month Index</label>
<input id="month" name="month" type="text" value="7" />
<span>Enter month index between 0 (Jan) and 11 (Dec)</span>
</div>
<div>
<label for="day">Day</label>
<input id="day" name="day" type="text" value="30" />
</div>
<div>
<button>Get Month Days Remaining</button>
</div>
<div>
<span>Result:</span>
<span id="result"></span>
</div>

You have to remember that in browser the dates are always in the current system setting timezone.
If you can use the moments library, that can solve all the date related common problems by providing you almost all the functions you may need
Here is the datediff sample code:
var today = new Date(), y = today.getFullYear(), m = today.getMonth();
var lastDay = new Date(y, m + 1, 0);
var diff = lastDay.diff(today, 'days', true);
now since both the dates are created in the same browser they will have same timezone an will not create any issue. But if one date is say returned from server and is represented in say GMT/UTC then you need to convert the other date too in the same timezone. Again, the moments.js will be able to help there.

Related

Only enable during 2 weeks date format html

I have a problem enabling only during 2 weeks data format. For example, I want only to show today and before 14 days. Now my coding just can lock before days.
Scenario:
If today 03 Feb 2021, I want to enable dates are 20 Jan 2021 until 03 Feb 2021. Other dates will be disabled.
var today = new Date().toISOString().split('T')[0];
document.getElementsByName("accident")[0].setAttribute('min', today);
<input type="date" class="form-control" id="accident" name="accident" value="" title="Date of Accident">
Now my result like below the picture:
Hope someone can guide me on which part I am getting wrong it. Thanks.
According to MDN Documentation. You need to set min and max values to specify an interval
// Get date objects
const today = new Date();
const twoWeeksAgo = new Date();
twoWeeksAgo.setDate(today.getDate() - 14);
// Then set in input
const input = document.querySelector('[name=accident]');
input.setAttribute('min', twoWeeksAgo.toISOString().slice(0, 10));
input.setAttribute('max', today.toISOString().slice(0, 10));
<input type="date" name="accident" />
You only set min, but you did not set max.
Because of this relationship, it only knows your minimum date, but does not know your maximum date, so the previous result is normal, as long as you make up the setting, it will work.
For details, please refer to here.
const getDateStr = (d) => d.toISOString().split('T')[0];
const daysRange = (days) => {
const d = new Date();
const when = new Date(d.setDate(d.getDate() + days));
return [new Date(), when].map(m=>getDateStr(m));
};
const limit = daysRange(-14);
const picker = document.getElementsByName("accident")[0];
picker.setAttribute('min', limit[1]);
picker.setAttribute('max', limit[0]);
picker.setAttribute('value', limit[0]);
label {
display: block;
font: 1rem 'Fira Sans', sans-serif;
}
input,
label {
margin: .4rem 0;
}
<label for="start">date:</label>
<input type="date" name="accident">
The solution you're looking for is something like this,
const twoWeeksAgo = 14 * 24 * 60 * 60 * 1000;
let dateElement = document.querySelector('#accident');
dateElement.min = new Date(Date.now() - twoWeeksAgo).toISOString().split('T')[0];
dateElement.max = new Date().toISOString().split('T')[0];
<input type="date" class="form-control" id="accident" name="accident" value="" title="Date of Accident">
You can use the max and min attributes of the HTML5 date element (documented here) to restrict your element to only show certain values.
In this particular case, the min attribute is set to the date (in yyyy-mm-dd format) two weeks ago and the max attribute is set to the current date.
The magic computation twoWeeksAgo is the number of milliseconds in 14 days which will allow you to compute the date 14 days ago.
The code new Date(Date.now() - twoWeeksAgo) gives us a Date object set to two weeks ago and the .toISOString() function returns the date in YYYY-MM-DDTHH:mm:ss.sssZ format, i.e., a date-time string.
Since the min attribute only requires the date and not the time, we then split the obtained string using 'T' as a delimiter, which would give us the date in YYYY-MM-DD format.
Putting it all together we get this line for the date two weeks ago
dateElement.min = new Date(Date.now() - twoWeeksAgo).toISOString().split('T')[0];
And similarly for the upper limit date,
dateElement.max = new Date().toISOString().split('T')[0];

Countdown to a single day each year (and find the date of that day in current year)

I am looking to do something quite complex and I've been using moment.js or countdown.js to try and solve this, but I think my requirements are too complex? I may be wrong. Here is the criteria...
I need to be able to have the following achievable without having to change the dates manually each year, only add it once and have many countdowns on one page.
Find current date
Find current year
Find current month
Find day within week of month that applies
¬ 3rd Sunday or 2nd Saturday
Convert to JS and output as html and run countdown
When past date - reset for following year
Pretty mental. So for example if an event is always on the 3rd Sunday of March. The date would not be the same each year.
2016 - Sunday March 19th
2017 - Sunday March 20th
2018 - Sunday March 18th etc.
I hope this is explained well, I realise it may be a total mess though. I managed to get it resetting each year with the date added manually but then someone threw in the spanner of the date being different each year.
var event = new Date();
event = new Date(event.getFullYear() + 1, 3 - 1, 19);
jQuery('#dateEvent').countdown({ until: event });
<div id="dateEvent"></div>
I have edited this answer as I have now put together a solution that works for me. As I believe this isn't simple coding due to the fact it wasn't actually answered 'Please, this is basic coding. pick up a javascript book and learn to code', yeah thanks...
// get the specific day of the week in the month in the year
function getDay(month) {
// Convert date to moment (month 0-11)
var myMonth = moment("April", "MMMM");
// Get first Sunday of the first week of the month
var getDay = myMonth.weekday(0); // sunday is 0
var nWeeks = 3; // 0 is 1st week
// Check if first Sunday is in the given month
if (getDay.month() != month) {
nWeeks++;
}
// Return 3rd Sunday of the month formatted (custom format)
return getDay.add(nWeeks, 'weeks').format("Y-MM-D h:mm:ss");
}
// print out the date as HTML and wrap in span
document.getElementById("day").innerHTML = '<span>' + getDay() + '</span>';
Using
<script src="moment.js"></script>
Hope it helps someone - I'll update when I figure how to + 1 year after it's checked current date and event has passed. I'll look in that JS book.
Please take a look at the below code, I explained in the comment what what does.
You use it by supplying a javascript Date object of any wished start date, and then add as a second value the corresponding year you wish to know the date in.
var date = new Date("2016-03-20");
function getDayInYear(startDate, year) {
// get a moment instance of the start date
var start = moment(startDate);
// collect the moment.js values for the day and month
var day = start.day();
var month = start.month();
// calculate which week in the month the date is.
var nthWeekOfMoth = Math.ceil(start.date() / 7);
// Build up the new moment with a date object, passing the requested year, month and week in it
var newMoment = moment(new Date(year,month,(nthWeekOfMoth * 7)));
// Return the next instance of the requested day from the current newMoment date value.
return newMoment.day(day);
}
var oldMoment = moment(date);
var newMoment2017 = getDayInYear(date,2017);
var newMoment2018 = getDayInYear(date,2018);
console.log(oldMoment.format('YYYY MMMM dddd DD'));
console.log(newMoment2017.format('YYYY MMMM dddd DD'));
console.log(newMoment2018.format('YYYY MMMM dddd DD'));
/** working from today up to 10 years into the future **/
var date = new Date();
var year = date.getFullYear();
for(var i = 0; i < 11; i++) {
console.log(getDayInYear(date, year+i).format('YYYY MMMM dddd DD'));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.js"></script>

Update Date with Javascript into Textbox add Months and correct year etc

So the premise is there is a text box that denotes the start date then another input box which onchange should run a javascript function that adds the value of months in the tenure box to the contract start date and give a contract end date.
I have tried the following code:
<form name="FormName">
Contract Start Date: <input type="text" name="Start" value="<?php echo date("Y/m/d"); ?>" onchange="myFunction()" />
<br />
Tenure (Months): <input type="text" name="Months" value="" onchange="myFunction()" />
<br />
Contract End Date: <input type="text" name="TextBoxName" value="" />
</form>
<script>
function myFunction()
{
var val = document.forms["FormName"].elements["Months"].value;
var d = new Date(document.forms["FormName"].elements["Start"].value);
var year = d.getFullYear();
var month = d.getMonth() + val;
var day = d.getDate();
document.forms["FormName"].elements["TextBoxName"].value = year + "/" + month + "/" + day;
}
</script>
But this does not seem to deal with it as a date object and I get values back like 2014/22/31 where 22 is the months and should obviously carry over to the year component.
I'm guessing its treating it as a string when the var's are defined into they're seperate Year, Month, Day parts... I need it to return a date that is in the format as date("Y/m/d"); in PHP.
Any assistance would be greatly appreciated!
as #user2310289 suggested, you should do this:
var endDate = new Date(d.setMonth(d.getMonth()+val));
var year = endDate.getFullYear();
var month = endDate.getMonth();
var day = endDate.getDate();
Try this:
// let your contract start date be '2013/12/11'
var c_start_date = '2013/12/10';
var tenure = 5; // 5 months
var c_start_date_obj = new Date(c_start_date);
var c_end_date_obj = new Date(c_start_date_obj.getFullYear(), c_start_date_obj.getMonth() + tenure, c_start_date_obj.getDate());
var c_end_date = c_end_date_obj.getFullYear() + '/' + c_end_date_obj.getMonth() + '/' + c_end_date_obj.getDate();
alert(c_end_date);
JSFiddle link
Your first issue is to correctly parse the date string. The format you are using is not one specified in ES5 so it will be entirely implementation dependent unless you parse it yourself. So:
// Parse y/m/d to create a Date
function parseString(s) {
s = s.split(/\D/);
return new Date(s[0], --s[1], s[2]);
}
Once you convert the string to a Date, you can add months:
var d = parseString(document.forms["FormName"].elements["Start"].value);
// Copy d for use later
oDate = d.getDate();
d.setMonth(d.getMonths() + Number(val));
You will have cases where you add months to a date that is say 31 July that will evaluate to say 31 June which will automatically be converted to 1 July.
You can fix this by looking at the date. If the adjusted date is not the same as the start date, set the adjusted date to the last day of the previous month by setting the date to 0, e.g.
if (d.getDate() != oDate) {
d.setDate(0);
}
Note that subtracting months may not get you back to the start, e.g.
Jan 31 + 1 month => 28 Feb // for a non-leap year
28 Feb - 1 month => 28 Jan
After a lot of playing around and searching the net I implemented the datejs library. This resolved a lot of the issues I was having.

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

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