JavaScript - Get dates in between two dates from weekdays - javascript

I receive a start and end date and I want to extract all the dates that are between these two dates, but only on specified weekdays.
I wrote a small code that calculate the dates for "Tuesday, Wednesday and Friday" between the dates:
start_date = "2019-12-31"
end_date = "2020-01-31"
it works ok but the problem is that it start to calculate from 'Tuesday 07-01-2020' and I want to start to get the date from 'Tuesday 31-12-2019'. The code is as follows:
function nextWeekdayDate(date, day_in_week) {
var ret = new Date(date || new Date());
ret.setDate(ret.getDate() + (day_in_week - 1 - ret.getDay() + 7) % 7 + 1);
return ret;
}
var delivery_dates = []
var day_num = { "Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5 };
var days_of_week = ["Tue", "Wed", "Fri"];
start_date = new Date('2019-12-31');
end_date = new Date('2020-01-31');
var new_dates = []
while (start_date < end_date) {
for (var i = 0; i <= 2; i++) {
start_date = nextWeekdayDate(start_date, day_num[days_of_week[i]]);
delivery_dates.push(start_date)
}
}
console.log(delivery_dates);
Any idea what is wrong with my code? here is a jsfiddle:
https://jsfiddle.net/mpe49hun/8/

Since the first date is a Tuesday, the function
function nextWeekdayDate(date, day_in_week) {
var ret = new Date(date || new Date());
ret.setDate(ret.getDate() + (day_in_week - 1 - ret.getDay() + 7) % 7 + 1);
return ret;
}
adds seven days to ret when it should be zero
changing the "maths" to ret.setDate(ret.getDate() + ((7 + day_in_week - ret.getDay())) % 7); will fix this
function nextWeekdayDate(date, day_in_week) {
var ret = new Date(date || new Date());
ret.setDate(ret.getDate() + ((7 + day_in_week - ret.getDay())) % 7);
return ret;
}
var delivery_dates = []
var day_num = {
"Mon": 1,
"Tue": 2,
"Wed": 3,
"Thu": 4,
"Fri": 5
};
var days_of_week = ["Tue", "Wed", "Fri"];
start_date = new Date('2019-12-31');
end_date = new Date('2020-01-31');
var new_dates = []
while (start_date < end_date) {
for (var i = 0; i <= 2; i++) {
start_date = nextWeekdayDate(start_date, day_num[days_of_week[i]]);
delivery_dates.push(start_date)
}
}
console.log(delivery_dates);
However, given a start date of 2019-12-25, for example, the wednesday 25/12 and friday 27/12 wont' be included in the output - if it should be included, then the following code is probably what you want
var delivery_dates = []
var day_num = { "Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5 };
var days_of_week = ["Tue", "Wed", "Fri"];
var includedDays = days_of_week.map(d => day_num[d]);
start_date = new Date('2019-12-25');
end_date = new Date('2020-01-31');
while (start_date <= end_date) {
if (includedDays.includes(start_date.getDay())) {
delivery_dates.push(new Date(start_date));
}
start_date.setDate(start_date.getDate() + 1);
}
console.log(delivery_dates)

Related

Expand date range using JS?

I have this variable {{ $daterange }} with json like this
{
"starts_at": "2020-05-20",
"ends_at": "2020-05-23"
},
{
"starts_at": "2020-05-24",
"ends_at": "2020-05-26"
},
{
"starts_at": "2020-05-27",
"ends_at": "2020-05-29"
}
What I want to do is to expand something like this,
2020-05-20
2020-05-21
2020-05-22
2020-05-23
2020-05-24
2020-05-25
2020-05-26
2020-05-27
2020-05-28
2020-05-29
I'm planning to assign these dates inside of expandedDate variable
var expandedDate = [ ....dates ];
This should be done using jquery/js
UPDATE*
Recently this code works and can get all dates between 2 dates. It will list down all dates between 2 date range written in the code.
// Returns an array of dates between the two dates
var getDates = function(startDate, endDate) {
var dates = [],
currentDate = startDate,
addDays = function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
};
while (currentDate <= endDate) {
dates.push(currentDate);
currentDate = addDays.call(currentDate, 1);
}
return dates;
};
// Usage
var dates = getDates(new Date(2013,10,22), new Date(2013,11,25));
dates.forEach(function(date) {
console.log(date);
});
How can I populate {{ $daterange }} contains multiple date range.
Think I missed your update with existing code. The following code seems to get the desired output using javascript. Just added comments to each step as an explanation. Hope it is helpful.
//sample input data
var daterange = [{
"starts_at": "2020-05-27",
"ends_at": "2020-06-23"
},
{
"starts_at": "2020-05-24",
"ends_at": "2020-05-26"
},
{
"starts_at": "2020-05-27",
"ends_at": "2020-05-29"
}
];
// function to get dates between two dates
var getDaysAsArray = function(start_date, end_date) {
for (var arr = [], d = new Date(start_date); d <= end_date; d.setDate(d.getDate() + 1)) {
arr.push(new Date(d));
}
return arr;
};
// function to convert date into the format yyyy-mm-dd
var getFormattedDay = function(date) {
day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
month = date.getMonth()+1 < 10 ? "0" + (date.getMonth()+1) : date.getMonth()+1;
year = date.getFullYear();
return year + "-" + month + "-" + day;
}
//main logic
var expandedDate = [];
//Iterate through the list of arrays in the date range
for (var key in daterange) {
//get first pair of from and to date
var from_string = daterange[key].starts_at;
var to_string = daterange[key].ends_at;
// convert the string date to date format for from and to.
var from_date = new Date(from_string.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1/$2/$3"));
var to_date = new Date(to_string.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1/$2/$3"));
// call getDaysAsArray to convert dates into strings and into an array.
var daylist = getDaysAsArray(from_date, to_date);
// iterate through the daylist and push it into the final array you want to use
for (var day in daylist) {
expandedDate.push(getFormattedDay(daylist[day]));
}
}
// final result required
console.log(expandedDate);
Here's the complete code on how to solve this question
Based on #thommu
var daterange = [
{
"starts_at": "2020-05-24",
"ends_at": "2020-05-26"
},
{
"starts_at": "2020-05-27",
"ends_at": "2020-05-29"
}
];
// function to get dates between two dates
var getDaysAsArray = function(start_date, end_date) {
for (var arr = [], d = new Date(start_date); d <= end_date; d.setDate(d.getDate() + 1)) {
arr.push(new Date(d));
}
return arr;
};
// function to convert date into the format yyyy-mm-dd
var getFormattedDay = function(date) {
day = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
month = date.getMonth() < 10 ? "0" + date.getMonth() : date.getMonth();
year = date.getFullYear();
return year + "-" + month + "-" + day;
}
//main logic
var expandedDate = [];
//Iterate through the list of arrays in the date range
for (var key in daterange) {
//get first pair of from and to date
var from_string = daterange[key].starts_at;
var to_string = daterange[key].ends_at;
// convert the string date to date format for from and to.
var xfrom_date = new Date(from_string.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1/$2/$3"));
var xto_date = new Date(to_string.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1/$2/$3"));
//Add +1 month to correct the data
var from_date = new Date(xfrom_date.setMonth(xfrom_date.getMonth()+1));
var to_date = new Date(xto_date.setMonth(xto_date.getMonth()+1));
// call getDaysAsArray to convert dates into strings and into an array.
var daylist = getDaysAsArray(from_date, to_date);
// iterate through the daylist and push it into the final array you want to use
for (var day in daylist) {
expandedDate.push(getFormattedDay(daylist[day]));
}
}
//Filter Duplicated Dates
var dateDuplicate = expandedDate;
var uniqueDate = [];
$.each(dateDuplicate, function(i, el){
if($.inArray(el, uniqueDate) === -1) uniqueDate.push(el);
});
// final result required
console.log(uniqueDate);

Calculate the number of weekday and weekend hours between 2 timestamp params in javascript

I want to calculate the number of weekday-hours and weekend-hours within a given 2 timestamp range. It is sure the timestamp ranges are in hours, it doesn't contain minutes or seconds values in it.
attempt:
function getWeekdayWeekendHours(fromTimestamp, tillTimestamp) {
let weekdayhours = 0;
let weekendhours = 0;
// fix the GMT issue by decreasing timestamp by 5:30
const fromTime = fromTimestamp - 19800;
const tillTime = tillTimestamp - 19800;
let currentDate = new Date(fromTime * 1000);
const tillDate = new Date(tillTime * 1000);
while (currentDate < tillDate) {
if (currentDate.getDay() !== 0 && currentDate.getDay() !== 6)
weekdayhours += 1;
else weekendhours += 1;
currentDate = currentDate.addHours(1);
}
return { weekdayhours, weekendhours };
}
// eslint-disable-next-line no-extend-native
Date.prototype.addHours = function (h) {
this.setHours(this.getHours() + h);
return this;
};
Here's a function that should work, provided you don't mind using lodash & moment.
const moment = require("moment")
const lodash = require("lodash")
function getHours(ts1, ts2) {
const weekends = [6, 7] // the isoWeekday of saturday & sunday
const [m1, m2] = [moment(ts1), moment(ts2)].sort() // makes sure earlier ts is first
const numDays = Math.ceil(m2.diff(m1, "days", true)) + 1
let weekdayHrs = 0
let weekendHrs = 0
lodash.range(numDays).forEach(idx => {
let diffHours = 0
let start
let end
// figure out start, end timestamps
switch (idx) {
case 0:
start = m1
end = m1.clone().add(1, "days").hours(0).minutes(0).seconds(0).milliseconds(0)
break
case numDays - 1:
end = m2
start = m2.clone().hours(0).minutes(0).seconds(0).milliseconds(0)
break
default:
start = m1.clone().hours(0).minutes(0).seconds(0).milliseconds(0).add(idx, "days")
end = start.clone().add(1, "days")
end = end.isBefore(m2) ? end : m2
break
}
diffHours = end.diff(start, "hours")
const dayOfWeek = start.isoWeekday()
const isWeekend = weekends.includes(dayOfWeek)
if (isWeekend) {
weekendHrs += diffHours
} else {
weekdayHrs += diffHours
}
// you can remove these 2 lines from the function. This just prints the total per day.
const names = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
console.log(idx, names[dayOfWeek - 1], start.format("MMM/DD hh:mm A"), "to", end.format("MMM/DD hh:mm A"), "=", diffHours, isWeekend ? "weekend hrs": "hrs")
})
return { weekdayHrs, weekendHrs, total: weekdayHrs + weekendHrs }
}
Here is some example output:
const ts1 = new Date(2019, 9, 18) // Oct 18 2019 12:00 AM
const ts2 = new Date(2019, 9, 25) // Oct 25 2019 12:00 AM
console.log(getHours(ts1, ts2))
// output:= { weekdayHrs: 120, weekendHrs: 48, total: 168 }
const ts3 = new Date(2019, 9, 18, 10) // Oct 18 2019 10:00 AM
const ts4 = new Date(2019, 9, 22, 13) // Oct 22 2019 1:00 PM
console.log(getHours(ts3, ts4))
// output:= { weekdayHrs: 64, weekendHrs: 48, total: 112 }

Start week from monday JavaScript

I am using the calendar taken from https://github.com/500tech/angular-simple-calendar.
In this calendar the week starts from sunday and is calculated in this function :
calculateWeeks = function () {
$scope.weeks = [];
var week = null;
var daysInCurrentMonth = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth) + 1, 0).getDate();
for (var day = 1; day < daysInCurrentMonth + 1; day += 1) {
var dayNumber = new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth), day).getDay();
week = week || [null, null, null, null, null, null, null];
week[dayNumber] = {
year: $scope.selectedYear,
month: MONTHS.indexOf($scope.selectedMonth),
day: day
};
if (allowedDate(week[dayNumber])) {
if ($scope.events) { bindEvent(week[dayNumber]); }
} else {
week[dayNumber].disabled = true;
}
if (dayNumber === 6 || day === daysInCurrentMonth) {
$scope.weeks.push(week);
week = undefined;
}
}
};
What changes I should do to start week from Monday?
You could change the definition of dayNumber by adding 6 modulo 7:
var dayNumber = (new Date($scope.selectedYear, MONTHS.indexOf($scope.selectedMonth), day)
.getDay() + 6) % 7;

How to build week data in calendar for my case?

I am trying to build a calendar modal for my app by using Angular. I have a model like this
I have no problem building the year month but not sure how to setup the week that has other day. For example, first week of Oct has 28,29,30 in the first week.
//code to build day
var month = 9; // hardcoded for demo.
var monthA = [];
for (var m = 0; m <= 3; m ++) {
var weeks = [];
totalDay = new Date(year, month + m + 1, 0).getDate()
//build days
for (var i = 0; i <= totalDay; i++) {
//setting up weeks
if (i % 7 == 0) {
weeks.push([]);
}
weeks[weeks.length-1].push(i);
}
var monthObj = {
month : (month + m),
weeks:weeks
}
monthA.push(monthObj);
}
The above code will produce
monthA:
[
{
month: '10',
weeks: [
[1,2,3,4,5,6,7],
[8,9,10,11,12,13,14],
[15,16,17,18,19,20,21],
[21,22,23,24,25,26,27],
[28,29,30,31]
]
},
{
month: 'Nov',
weeks: [1,2,3,4,5,6,7] ...
}...
]
}
I was hoping to get
month:
[
{
month: '10',
weeks: [
[28, 29, 30, 1,2,3,4], //I haven o problem building the week from 1 to 4
[5,6,7,8,9,10,11],
[12,13,14,15,16,17,18],
[19,20,21,22,23,24,25],
[26,27,28,29,30,31,1]
]
},
{
month: '11',
weeks: [26,27,28,29,30,31,1] ...
}
]
Thanks so much for the help!
This function I just made should do what you want:
function getWeeksinMonth(year, month){ //month 1-12 based
var weeks = [];
lastDayinMonth = new Date(year, month, 0) // get last day in month
firstDayinMonth = new Date(year, month-1, 1) //get first day in month
start = new Date(firstDayinMonth.getTime()); //copy firstDay
start.setDate(start.getDate() - start.getDay() ); // set date to previous sunday
end=new Date(lastDayinMonth.getTime()); //copy lastDay
end.setDate(end.getDate() - end.getDay() ); // set date to previous sunday
if(end < lastDayinMonth){ // if previous sunday was not the lastDay itself, we want to add another week
end.setDate(end.getDate() + 7);
}
var counter=0;
var week=[]
for(;start<=end;start.setDate(start.getDate()+1)){ // traverse days
if(counter % 7==0 && counter != 0){ // every 7 days add a week
var copy=week.slice(0);
weeks.push(copy)
week=[];
}
week.push(start.getDate())
counter++
}
return weeks;
}
var weeks = getWeeksinMonth(2014,10);
for(var i=0;i<weeks.length;i++){
document.body.innerHTML+=weeks[i].toString() + '<br/>';
}

setDate(day+1) returns wrong days

Why does this:
startDate.setDate(startDate + 1);
Generates this strange order (broken at the beginning of the next month):
7, 27, 28, 30, 30, **1, 4, 3, 4, 4,** 6, 7, 10, 9, 10, 10, 12,
Update (more code):
renderWeekFromMonday : function(date) {
var dayContainer = $('#day');
// clear div
dayContainer.empty();
// Render days
for (var i = 0; i <= 7; i++) {
// Get mondday day (1-31)
var day = date.getDate();
// Today
var t = new Date();
// Create dayobject for usage inside for loop
var d = new Date(date);
// Render 7 days (1 week)
for (var i = 0; i < 7; i++) {
// New day (+1)
d.setDate(day + i)
console.info(d.getDate());
// Create html
var span = $("<span>").addClass("calElement").attr("time", d.getTime())
var w = 25;
span.html("<span class=dayNumber>" + d.getDate() + "</span><br>" + this.dayNames[d.getDay()]).css("width",w);
//span.html("<span class=dayNumber>" + d.getDate() + "</span>");
// Append day
dayContainer.append(span);
}
}
},
Just a guess, put perhaps you're looking for:
startDate.setDate(startDate.getDate() + 1);
startDate + 1 doesn't make much sense if startDate is a Date object.
After seeing updated code: Your problem is very likely in the fact that you have two nested loops, both of which increment i. Use a different variable for one of the loops.
Your code looks fine - I'm using FF4 and the date calculations look correct. I've posted the code here for a live example: http://jsfiddle.net/EbNcr/2/
Is there a specific browser or date(s) that you're testing with to get the strange results?
(I wish I could submit this as a comment, but I don't think I have the reputation for that yet...)
Thanks for helping. I just had a simple copy&paste error (two loops). I put the final code into a simple jquery plugin (inspiered by some oder plugins)
var MILLIS_IN_DAY = 86400000;
var MILLIS_IN_WEEK = MILLIS_IN_DAY * 7
jQuery.fn.calendarPicker = function(options) {
// -------------------------- start default option values --------------------------
options.date = new Date();
options.dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
options.firstDayOfWeek = 1;
options.showNavigation = true;
// -------------------------- end default option values --------------------------
var calendar = {
changeDate : function(date) {
// calculate new start date
this.currentFirstDayOfWeek = this._firstDayOfWeek(date);
// render days
for (var i = 0; i < 7; i++) {
// create dayobject
var day = new Date(this.currentFirstDayOfWeek.getTime() + MILLIS_IN_DAY * i);
// render container
var span = $("<span>").addClass("calElement").attr("time", day.getTime())
// render day
span.html("<span class=dayNumber>" + day.getDate() + "</span><br>" + options.dayNames[day.getDay()]);
if (day.getYear() == date.getYear() && date.getMonth() == day.getMonth() && day.getDate() == date.getDate())
span.addClass("today");
if (day.getYear() == date.getYear() && day.getMonth() == date.getMonth() && day.getDate() == date.getDate())
span.addClass("selected");
theDiv.append(span);
// render navigation
if (i == 0 && options.showNavigation) {
var prevBtn = $("<span>").addClass("prev").html("prev");
var self = this;
prevBtn.bind('click', function() { self.prevWeek(); });
theDiv.prepend(prevBtn);
} else if (i == 6 && options.showNavigation) {
var nextBtn = $("<span>").addClass("next").html("next")
var self = this;
nextBtn.bind('click', function() { self.nextWeek(); });
theDiv.append(nextBtn);
}
}
},
/*
* Go to the previous week relative to the currently displayed week
*/
prevWeek : function() {
//minus more than 1 day to be sure we're in previous week - account for daylight savings or other anomolies
var newDate = new Date(this.currentFirstDayOfWeek.getTime() - (MILLIS_IN_WEEK / 6));
this._clearCalendar();
this.changeDate(newDate);
},
/*
* Go to the next week relative to the currently displayed week
*/
nextWeek : function() {
//add 8 days to be sure of being in prev week - allows for daylight savings or other anomolies
var newDate = new Date(this.currentFirstDayOfWeek.getTime() + MILLIS_IN_WEEK + (MILLIS_IN_WEEK / 7));
this._clearCalendar();
this.changeDate(newDate);
},
/*
* returns the date on the first millisecond of the week
*/
_firstDayOfWeek : function(date) {
var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
var adjustedDate = new Date(midnightCurrentDate);
adjustedDate.setDate(adjustedDate.getDate() - this._getAdjustedDayIndex(midnightCurrentDate));
return adjustedDate;
},
_clearCalendar : function() {
theDiv.empty();
},
/*
* gets the index of the current day adjusted based on options (e.g.firstDayofWeek)
*/
_getAdjustedDayIndex : function(date) {
var midnightCurrentDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
var currentDayOfStandardWeek = midnightCurrentDate.getDay();
var days = [0,1,2,3,4,5,6];
this._rotate(days, this.options.firstDayOfWeek);
return days[currentDayOfStandardWeek];
},
/*
* rotate an array by specified number of places.
*/
_rotate : function(a, p ) {
for (var l = a.length, p = (Math.abs(p) >= l && (p %= l),p < 0 && (p += l),p), i, x; p; p = (Math.ceil(l / p) - 1) * p - l + (l = p)) {
for (i = l; i > p; x = a[--i],a[i] = a[i - p],a[i - p] = x);
}
return a;
},
};
// Pass options
calendar.options = options;
// build the calendar on the first element in the set of matched elements.
var theDiv = this.eq(0);//$(this);
theDiv.addClass("calBox");
// empty the div
theDiv.empty();
// entry point
calendar.changeDate(options.date);
return calendar;
};

Categories