I am trying to get the date for the next instance of a weekday after a given date. The weekday may be any day from Monday through Sunday. I am using the Moment.js library but there doesn't seem to be a simple way to achieve what I am after.
This is what I have so far but it's not returning the date I want:
// competition.startDate = 2016-02-10 (Wednesday)
var startDate = moment(competition.startDate);
...
for (m = 0; m < matchesPerRound; m++) {
var date;
...
// matchTime[m].day = 3 (Wednesday)
date = startDate.startOf('week').add(7, 'days').subtract(matchTime[m].day, 'days');
console.log(date.format('YYYY-MM-DD'));
// Actual Output = 2016-02-11
// Expected Output = 2016-02-10
...
}
With the example above, I need it returning 2016-02-15 as this is the next Monday after 2016-02-10. This is no problem, and I can get that date. My problem is that I need something more dynamic, as matchTime[m].day could be any weekday number. For example, if matchTime[m].day = 3 // Wednesday, it should just return the same startDate 2016-02-10 as this is a Wednesday.
I'm trying to avoid conditional statements if possible, but I am wondering if Moment.js has the functionality out of the box to produce these results.
UPDATE
I've come up with a solution, and it's working with my requirements, but I feel it's very messy. Does someone else have a cleaner, more concise solution?
var date;
var startDate = moment(competition.startDate);
var proposedDate = moment(startDate).startOf('isoweek').add(matchTimes[0].day - 1, 'd');
if ( startDate.isAfter(proposedDate) ) {
date = startDate.startOf('isoweek').add(7 + Number(matchTimes[0].day) - 1, 'd');
} else {
date = startDate.startOf('isoweek').add(matchTimes[0].day - 1, 'd');
}
I don't know about moment.js, but it seems rather complex for something rather simple.
function firstMatchingDayOfWeek(day, date) {
var delta = date.getDay() - day,
result = new Date(date);
if (delta) {
result.setDate(result.getDate() + ((7 - delta) % 7));
}
return result;
}
I think the code is rather self-explanatory (don't we all ;-) ), but in order to clarify the steps:
firstMatchingDayOfWeek(day, date) > call the function specifying the day of week (0-6) and a date object
delta = date.getDay() - day > calculate how many days the date needs to shift in order to reach the same day of week
result = new Date(date) > create a new Date instance based on the input (so you get to preserve the original date)
result.setDate(result.getDate() + ((7 - delta) % 7)) > set the date part (day of month) to a week minus the delta, modulo a week (as delta can be negative and you want the first occurrence of the same day)
function firstMatchingDayOfWeek(day, date) {
var delta = date.getDay() - day,
result = new Date(date);
if (delta) {
result.setDate(result.getDate() + ((7 - delta) % 7));
}
return result;
}
// tests
for (var i = 1; i < 28; ++i){
var check = new Date('2016-02-' + ('00' + i).substr(-2)),
output = document.querySelector('#out')
.appendChild(document.createElement('pre')),
log = [];
log.push('input: ' + check);
for (var day = 0; day <= 6; ++day) {
var next = firstMatchingDayOfWeek(day, check);
log.push(day + ' > ' + next);
}
output.innerText = log.join('\n');
}
pre {
border: 1px solid gold;
}
<div id="out"></div>
Here's a possible answer too using momentjs (assumes match days will be 0 (sunday) - 6):
var startDate = moment(competition.startDate);
var matchDay = Number(matchTimes[0].day);
var daysToAdd = Math.ceil((startDate.day() - matchDay) / 7) * 7 + matchDay;
var proposedDate = moment(startDate).startOf('week').add(daysToAdd, 'd');
This is a pure JS version:
Logic
Find current day.
Find days to add to get to start of next week.
Get day number for the day to find date.
Compute date with this date.
JSFiddle.
function claculateNextDate() {
var date = new Date();
var nextDay = document.getElementById("dayOfWeek").value;
var day = date.getDay();
var noOfDaysToAdd = (7 - day) + parseInt(nextDay);
date.setDate(date.getDate() + noOfDaysToAdd);
document.getElementById("result").innerHTML = date.toDateString();
}
<select id="dayOfWeek">
<option value="0">Sunday</option>
<option value="1">Monday</option>
<option value="2">Tuesday</option>
<option value="3">Wednesday</option>
<option value="4">Thursday</option>
<option value="5">Friday</option>
<option value="6">Saturday</option>
</select>
<button onclick="claculateNextDate()">Calculate Next Date</button>
<p id="result"></p>
My solution
var weekDayToFind = moment().day('Monday').weekday() //change monday to searched day name
var givenDate = moment('2016-02-10') // pass the given date
var searchDate = moment(givenDate)
while (searchDate.weekday() !== weekDayToFind){
searchDate.add(1, 'day');
}
Related
I have question about getting full two years from the current date. So what i did id get the current month using the new date function and used the for loop to print each of the month. But, i cant really get it to work.... I will post the code that i did below. I would be really appreciate it if anyone can tell me the logic or better way of doing it.
For example: if today current date is august it store into an array from 8 / 2020 9/ 2020 ..... 12/ 2020, 1/2021 and goes to another year to 8/2022.
var d = new Date();
var year = d.getFullYear();
var dateStr;
var currentYear;
var storeMonthYear = [];
for(var i = 1; i <= 24; i++){
dateStr = d.getMonth() + i
currentYear = year;
if(dateStr > "12"){
dateStr = dateStr - 12
// currentYear = year;
// if(currentYear){
// }
storeMonthYear[i] = dateStr + "/" + (currentYear + 1);
}
else if(dateStr > "24"){
storeMonthYear[i] = dateStr + "/" + (currentYear + 1);
}
else{
storeMonthYear[i] = dateStr + "/" + currentYear;
}
storeMonthYear[i] = d.getMonth() + i
}
export const settlementPeriod = [
{
MonthYearFirstRow1: storeMonthYear[1],
MonthYearFirstRow2: storeMonthYear[2],
MonthYearFirstRow3: storeMonthYear[3],
MonthYearFirstRow4: storeMonthYear[4],
MonthYearFirstRow5: storeMonthYear[5],
MonthYearFirstRow6: storeMonthYear[6],
MonthYearFirstRow7: storeMonthYear[7],
MonthYearFirstRow8: storeMonthYear[8],
MonthYearFirstRow9: storeMonthYear[9],
MonthYearFirstRow10: storeMonthYear[10],
MonthYearFirstRow11: storeMonthYear[11],
MonthYearFirstRow12: storeMonthYear[12],
MonthYearSecondRow13: storeMonthYear[13],
MonthYearSecondRow14: storeMonthYear[14],
MonthYearSecondRow15: storeMonthYear[15],
MonthYearSecondRow16: storeMonthYear[16],
MonthYearSecondRow17: storeMonthYear[17],
MonthYearSecondRow18: storeMonthYear[18],
MonthYearSecondRow19: storeMonthYear[19],
MonthYearSecondRow20: storeMonthYear[20],
MonthYearSecondRow21: storeMonthYear[21],
MonthYearSecondRow22: storeMonthYear[22],
MonthYearSecondRow23: storeMonthYear[23],
MonthYearSecondRow24: storeMonthYear[24]
},
];
Create the date from today, get the month and year. Iterate from 0 to 24 for now till in 24 months. If month is 12 than set month to 0 and increment the year. Push the new datestring. Increment the month for the next step.
Note: Beacsue JS counts months form 0-11 you had to add for the datestring 1 for the month and make the change of year at 12 and not 13.
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth();
let res=[];
for (let i=0; i<=24; i++) {
if (month===12) {
month = 0;
year++;
}
res.push(month+1 + '/' + year);
month++;
}
console.log(res);
Here you go, you get an array of strings like "8/2020","9/2020" etc from starting month to the last month including both( in total 25 months).
If you don't want to include last month just delete +1 from for loop condition.
let currentDate = new Date();
let settlementPeriod = [];
let numberOfMonths = 24;
for(let i=0;i<numberOfMonths+1;i++){
settlementPeriod.push(currentDate.getMonth()+1+"/"+currentDate.getFullYear()); //We add current date objects attributes to the array
currentDate = new Date(currentDate.setMonth(currentDate.getMonth()+1)); //Every time we add one month to it
}
console.log(settlementPeriod);
There are a couple of things that stick out in your code sample:
You're comparing strings and numbers (e.g. dateStr > "12"). This will lead to some weird bugs and is one of JS's most easily misused "features". Avoid it where possible.
You increment the year when you reach 12 months from now, rather than when you reach the next January
You're overwriting your strings with this line storeMonthYear[i] = d.getMonth() + i so your array is a bunch of numbers rather than date strings like you expect
Here's a code sample that I think does what you're expecting:
function next24Months() {
const today = new Date()
let year = today.getFullYear()
let monthIndex = today.getMonth()
let dates = []
while (dates.length < 24) {
dates.push(`${monthIndex + 1}/${year}`)
// increment the month, and if we're past December,
// we need to set the year forward and the month back
// to January
if (++monthIndex > 11) {
monthIndex = 0
year++
}
}
return dates
}
In general, when you're dealing with dates, you're probably better off using a library like Moment.js - dates/times are one of the most difficult programming concepts.
While #Ognjen 's answer is correct it's also a bit waseful if your date never escapes its function.
You don't need a new date every time:
function getPeriods(firstMonth, numPers){
var d = new Date(firstMonth.getTime()); // clone the start to leave firstMonth alone
d.setDate(1); // fix after #RobG
var pers = [];
var m;
for(var i = 0; i< numPers; i++){
m = d.getMonth();
pers.push(`${m+ 1}/${d.getFullYear()}`)
d.setMonth(m + 1); // JS dates automatically roll over. You can do this with d.setDate() as well and when you assign 28, 29, 31 or 32 the month and year roll over automatically
}
return pers;
}
I have two dates: Startdate and enddate
startdate = "10/10/2018" enddate = "03/09/2019"
I am trying to create an array of dates between those 2 dates. I have the following code.
function getDateArray (start, end) {
var arr = [];
var startDate = new Date(start);
var endDate = new Date(end);
endDate.setMonth( endDate.getMonth());
while (startDate <= endDate) {
arr.push(new Date(startDate));
startDate.setMonth(startDate.getMonth() + 1);
}
return arr;
}
Then calculate the number of days between those months in between.
10/10/2018 to 11/10/2018 = 30 days
11/10/2019 to 12/10/2018 = 30 days or so depending on number of days between the 2 dates and then create an array of the dates.
[30,30,31....till end date]
function daysBetween(date1, date2 )
{
var timeDiff = Math.abs(date2.getTime() - date1.getTime());
var dayDifference = Math.ceil(timeDiff / (1000 * 3600 * 24));
return dayDifference;
}
I tried the following code and it's returning the array of number of dates however, it's not accurate. It keeps returning 32 days in October. The output it's giving right now is as follows. I am not sure what i am doing wrong here but it looks like it's only going till February and displaying the result.
Any help will be appreciated. Thank you.
Output: [32,30,31,31,28]
var dateArr = getDateArray(z, y);
console.log(dateArr);
var dayCounts = "";
for (var x = 0; x < dateArr.length-1; x++)
{
dayCounts += daysBetween(dateArr[x], dateArr[x+1]);
}
console.log("datearrlength" + dateArr.length);
console.log(dayCounts);
i think this will work for you,
Date.prototype.addDay= function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
}
function getDateBwStartandEnd(sdate, edate) {
var dateArray = new Array();
var currentDate = sdate;
while (currentDate <= edate) {
dateArray.push(new Date (currentDate));
currentDate = currentDate.addDay(1);
}
return dateArray;
}
** Shamelessly copied from web, but this works fine for me.
While the following doesn't answer your question it is an alternative approach to the overall problem you are attempting to solve.
One approach would be to simply get the time difference between the two dates and then divide by the number of microseconds in a day. As you will notice though it is not exact and so a floor is used to get the days. There are other concerns with this approach as well such as date ranges before the epoch but it is a very simplistic approach and might work depending on your needs.
const startDate = '10/10/2018';
const endDate = '03/09/2019';
const start = (new Date(startDate)).valueOf();
const end = (new Date(endDate)).valueOf();
const timeBetween = end - start;
console.log({timeBetween, days: Math.floor(timeBetween/86400000)});
A slightly more robust is to essentially use a counter that increments itself by adding 1 day to the counter and the start date while the start date is less than the end date. Again, there are some concerns with this approach but that also depends on your needs.
const startDate = '10/10/2018';
const endDate = '03/09/2019';
let start = new Date(startDate);
const end = (new Date(endDate)).valueOf();
let daysBetween = 0;
while (start.valueOf() < end) {
daysBetween++;
start.setDate(start.getDate() + 1);
}
console.log(daysBetween);
Finally, a more robust solution to avoid the variety of issues with manipulating and working with dates is to use a library like momentjs. Using its difference method would look like the following.
const start = moment([2018, 10, 10]);
const end = moment([2019, 3, 9]);
console.log(end.diff(start, 'days'));
<script src="http://momentjs.com/downloads/moment.min.js"></script>
Using the following code worked for me. I added 1 extra month to my end date and it gives the proper date range. Also, instead of Math.ceil, i used Math.round and it gives the right number of date.
function getDateArray (start, end) {
var arr = [];
var startDate = new Date(start);
var endDate = new Date(end);
endDate.setMonth( endDate.getMonth());
while (startDate <= endDate) {
arr.push(new Date(startDate));
startDate.setMonth(startDate.getMonth() + 1);
}
return arr;
}
I'm facing issue with excluding weekend dates in JavaScript.For my business requirement I want to exclude 3 days from date object Friday, Saturday and Sunday in every week.What I need here is the values of Friday should display as Monday, Saturday as Tuesday and Sunday as Wednesday. I'm able to do this.
The issue that I'm facing here is when we run the above example the a[0] value should be 21-SEP-2017 but I'm getting 20-SEP-2017 and remaining array values should not change. So please do help me out in resolving this issue
var a = ["21-SEP-2017", "22-SEP-2017", "23-SEP-2017", "24-SEP-2017", "25-SEP-2017"];
for (i = 0; i < a.length; i++) {
var startDate = a[i];
startDate = new Date(startDate.replace(/-/g, "/"));
var endDate = "",
noOfDaysToAdd = 1;
var count = 0;
endDate = new Date(startDate.setDate(startDate.getDate()));
if (startDate.getDay() != 0 && startDate.getDay() != 5 && startDate.getDay() != 6) {
endDate = new Date(startDate.setDate(startDate.getDate() + i - 1));
} else {
startDate.setDate(startDate.getDate() + 3)
endDate = new Date(startDate.setDate(startDate.getDate()));
}
console.log(endDate); //You can format this date as per your requirement
}
Your code seems not finished: the variables noOfDaysToAdd and count are never used, and if they were, they would be reset in every iteration of the loop, which cannot be the purpose.
That your output shows 20 September is because you did not output a stringified version of the date, but the date object itself, and then console.log will display the date as a UTC date (notice the time part matches the timezone difference). Instead use .toString() or another way to turn the date to a localised string.
Here is how you could do it:
function toDate(s) {
return new Date(s.replace(/-/g, '/'));
}
function toStr(dt) {
var months = ["JAN","FEB","MAR","APR","MAY","JUN",
"JUL","AUG","SEP","OCT","NOV","DEC"];
return [('0'+dt.getDate()).substr(-2), months[dt.getMonth()], dt.getFullYear()]
.join('-');
}
var a = ["21-SEP-2017", "22-SEP-2017", "23-SEP-2017", "24-SEP-2017", "25-SEP-2017"],
add = 0;
var result = a.map(toDate).map(dt => {
dt.setDate(dt.getDate()+add);
var move = [0, 6, 5].indexOf(dt.getDay()) + 1;
if (move) {
add += move;
dt.setDate(dt.getDate()+move);
}
return dt;
}).map(toStr);
console.log(result);
Before I am using angularjs-DatePicker from this npm.
Here,I am able to select the date from the date picker.But now I have to fields as FromDate and ToDate which means the week StartDate and EndDate should show when any date pick in that week.
Ex: Like in Calender 01-08-2017 Start on Tue, So whenever Selects Any date from 01 to 05 then the two fields should show as FromDate as 01 and TODate as 06 and in the same whenever the user selects the 31-07-2017 the the Two fields should show as 30 and 31 of july.
I have an idea to achieve the ToDate from FromDate Calender control onchange event in DotNet as like below mentioned code
Convert.ToDouble(objstart.DayOfWeek)).ToString("dd-MM-yyyy")
But how to achieve this usecase in the angularjs.
Thanks
Ok, so what I'd do is to calculate different dates, and take the min/max depending on the start or end of the week.
Here:
//Use the date received, UTC to prevent timezone making dates shift
var pickedDate = new Date("08-03-2017UTC");
var startSunday = new Date(pickedDate);
startSunday.setDate(pickedDate.getDate() - pickedDate.getDay());
var startMonth = new Date(pickedDate);
startMonth.setDate(1);
var startDate = Math.max(startMonth,startSunday);
console.log("Start:" , new Date(startDate));
var endSaturday = new Date(pickedDate);
endSaturday.setDate(pickedDate.getDate() + (7-pickedDate.getDay()));
var endMonth = new Date(pickedDate);
endMonth.setMonth(pickedDate.getMonth()+1);//Add a month
endMonth.setDate(0);// to select last day of previous month.
var endDate = Math.min(endMonth,endSaturday);
console.log("End" , new Date(endDate));
The trick was to play with the dates, find all the possible start and end dates, then choose the right one with Math.min and Math.max which will compare the dates using their timestamp.
There is very good Library available in JavaScript to handle Date Manipulations.
https://github.com/datejs/Datejs
There is a method
Date.parse('next friday') // Returns the date of the next Friday.
Date.parse('last monday')
Using these method you can get the start and ending date of the week based on the current week.
I hope that it will help.
You can simply achieve this using the library moment. There are a lot of useful functions in this library.
var selectedDate = moment('Mon Aug 10 2017');
//If you want to get the ISO week format(Monday to Sunday)
var weekStart = selectedDate.clone().startOf('isoweek').format('MMM Do');
var weekEnd = selectedDate.clone().endOf('isoweek').format('MMM Do');
//If you want to get the Sunday to Saturday week format
var weekStart = selectedDate.clone().startOf('week').format('MMM Do');
var weekEnd = selectedDate.clone().endOf('week').format('MMM Do');
No need angular directive here, you could use the JavaScript extension which is below.
//get week from date
Date.prototype.getWeekNumber = function (weekstart) {
var target = new Date(this.valueOf());
// Set default for weekstart and clamp to useful range
if (weekstart === undefined) weekstart = 1;
weekstart %= 7;
// Replaced offset of (6) with (7 - weekstart)
var dayNr = (this.getDay() + 7 - weekstart) % 7;
target.setDate(target.getDate() - dayNr + 0);//0 means friday
var firstDay = target.valueOf();
target.setMonth(0, 1);
if (target.getDay() !== 4) {
target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7);
}
return 1 + Math.ceil((firstDay - target) / 604800000);;
};
//get date rance of week
Date.prototype.getDateRangeOfWeek = function (weekNo, weekstart) {
var d1 = this;
var firstDayOfWeek = eval(d1.getDay() - weekstart);
d1.setDate(d1.getDate() - firstDayOfWeek);
var weekNoToday = d1.getWeekNumber(weekstart);
var weeksInTheFuture = eval(weekNo - weekNoToday);
var date1 = angular.copy(d1);
date1.setDate(date1.getDate() + eval(7 * weeksInTheFuture));
if (d1.getFullYear() === date1.getFullYear()) {
d1.setDate(d1.getDate() + eval(7 * weeksInTheFuture));
}
var rangeIsFrom = eval(d1.getMonth() + 1) + "/" + d1.getDate() + "/" + d1.getFullYear();
d1.setDate(d1.getDate() + 6);
var rangeIsTo = eval(d1.getMonth() + 1) + "/" + d1.getDate() + "/" + d1.getFullYear();
return { startDate: rangeIsFrom, endDate: rangeIsTo }
};
Your code can be look like this
var startdate = '01-08-2017'
var weekList = [];
var year = startdate.getFullYear();
var onejan = new Date(year, 0, 1);//first january is the first week of the year
var weekstart = onejan.getDay();
weekNumber = startdate.getWeekNumber(weekstart);
//generate week number
var wkNumber = weekNumber;
var weekDateRange = onejan.getDateRangeOfWeek(wkNumber, weekstart);
var wk = {
value: wkNumber
, text: 'Week' + wkNumber.toString()
, weekStartDate: new Date(weekDateRange.startDate)
, weekEndDate: new Date(weekDateRange.endDate)
};
weekList.push(wk);
I guess there is no directive or filter for this, you need to create one for yourself. you can refer date object from date-time-object
How would I write this expression in JavaScript?
It is to represent a date that is 2 weeks, counted by each passing Thursday, but excludes the thursday of the week the date was made.
NeededDay = Today + (18 - DayOfWeek(today))
or since it is Wednesday, it could be written?
var date = new Date();
var NeededDate = date.getDay() + (18-3);
or
I wrote this but I do not know if it is right?
var value = 3;
var GivenDate = value;
var GivenDay = value.getDay();
var daysToSecondThursday = Givenday2.setDate(GivenDay + Givenday2.setDate(18 - GivenDay));
alert("two weeks after next thursday is = " + daysToSecondThursday.val());
what is the correct way? ?
You could use:
function GetThursdayIn2Weeks(date)
{
var day = date.getDay();
// Add 2 weeks.
var newDate = new Date(date.setTime(date.getTime() + (14 * 86400000)));
// Adjust for Thursday.
var adjust = 4 - day;
if (adjust <= 0) // Might need to be changed - See comments!
adjust +=7;
// Apply Thursday adjustment.
newDate = new Date(newDate.setTime(newDate.getTime() + (adjust * 86400000)));
return newDate;
}
If the date passed in is Thursday, then it will return two weeks from the following Thursday. If this is not what you want, then adjust the if (adjust <= 0) code above to be:
if (adjust < 0)
Here is a jsFiddle: http://jsfiddle.net/kgjertsen/ec7vnezn/