Exceeded maximum stack depth in a Custom Function for Google Sheets - javascript

I'm creating a custom function in Google Sheets to provide the end date for a task based on the start date and the number of hours I think the task will take.
i.e. end date = start date + hours.
The function aims to skip weekends and consider a working day between 9 and 5pm (I'm working up to excluding lunchtimes and specifying weekends and holidays, but all in good time).
The function works fine for about five activities, but then errors "Exceeded maximum stack depth". Here's a screenshot of what I'm referring to.
And here's the AppScript / JavaScript.
//var startdate = new Date(2016, 04, 16, 9, 0, 0);
//var addhours = 3;
Date.prototype.addHours = function(h) {
this.setHours(this.getHours() + h);
return this;
}
Date.prototype.addDays = function(days) {
var dat = new Date(this.valueOf());
dat.setDate(dat.getDate() + days);
return dat;
}
/**
* Adds hours to a date excludes weekends
*
* #param {number} startdate The date to add the hours to
* #param {number} addHours The hours to add
* #return The new date
* #customfunction
*/
function MYWORKDAY(startdate, addhours) {
var endDate = new Date();
var endTime = new Date(startdate).setHours(17, 0, 0);
var remainingEffortHrs = new Date();
var availableTimeHrs = endTime - startdate;
availableTimeHrs = (availableTimeHrs / 1000) / 60 / 60;
if (startdate.map) { // Test whether input is an array.
return startdate.map(MYWORKDAY); // Recurse over array if so.
} else {
// Add the hours to the start date
//endDate = new Date(startdate).addHours(addhours);
endDate = new Date(startdate).addHours(addhours);
// Calculate remaining effort - if the task ends after 5pm
if (endDate > endTime) {
remainingEffortHrs = ((Math.abs(endDate - endTime)) / 1000) / 60 / 60;
} else {
remainingEffortHrs = 0;
}
if (remainingEffortHrs > 0) {
startdate = new Date(startdate).addDays(1);
startdate = MYWORKDAY(startdate, remainingEffortHrs);
} else {
// Remaining effort is 0
startdate = endDate;
}
return GetNextWorking(startdate);
}
}
function GetNextWorking(endDate) {
// Get the next working day
if (endDate.getDay() != 0 && endDate.getDay() != 6) {
return endDate;
} else {
adjustedEndDate = new Date(endDate.setDate(endDate.getDate() + 1));
adjustedEndDate = new Date(adjustedEndDate);
// Recursively call the this function until the returned
// date is a working day
return adjustedEndDate = GetNextWorking(adjustedEndDate);
}
}
I hope this makes sense. This has taken a while to get to this stage and any suggestions as to how to improve the performance or refactor would be greatly appreciated.

Here's the working code. I ran into some serious problems whilst trying to add code to include lunchtimes, but this worked to highlight the flaws in my logic. This should now also take into account a lunchtime from a second sheet called 'Settings' in Google Sheets. (I hadn't quite worked out how to bypass the Reference error when working outside of Google Sheets). This does however solve the Exceeded maximum stack depth error. Maybe you can suggest an improvement?
var dayStartTime = getStartTime();
var dayEndTime = getEndTime();
var lunchtimeEnd = getLunchtimeEnd();
var lunchtimeStart = getLunchtimeStart();
/* Starts the next day
*
* #param {number} startdate The date to add the hours to
* #return The new date
* #customfunction
*/
Date.prototype.addDays = function(days) {
var dat = new Date(this.valueOf());
dat.setDate(dat.getDate() + days);
return dat;
}
function addHours(date, h) {
return new Date(date.getTime() + (h*60*60*1000));
}
function MYWORKDAY(startdate,effort) {
if (startdate.map) {
return startdate.map(MYWORKDAY);
} else {
var endTime = new Date();
var availableTimeHrs;
var endDate = 0;
while (effort > 0)
{
endTime = new Date(startdate).setHours(dayEndTime.getHours(), dayEndTime.getMinutes(), dayEndTime.getSeconds());
lunchtimeEnd = todaysLunchEnd(startdate);
lunchtimeEnd = new Date(lunchtimeEnd);
lunchtimeStart = todaysLunchEnd(startdate);
lunchtimeStart = new Date(lunchtimeStart);
endDate = addHours(startdate, effort);
if (startdate <= lunchtimeStart && endDate >= lunchtimeEnd) {
endDate = addHours(endDate, 1);
}
if(endDate > endTime)
{
effort = ((Math.abs(endDate - endTime)) / 1000) / 60 / 60;
startdate = new Date(startdate).addDays(1);
startdate = GetNextWorking(startdate);
startdate = new Date(startdate).setHours(dayStartTime.getHours(), dayStartTime.getMinutes(), dayStartTime.getSeconds());
startdate = new Date(startdate);
}
else
{
effort = 0;
}
}
}
return endDate;
}
function GetNextWorking(endDate) {
if (endDate.getDay() != 0 && endDate.getDay() != 6) {
return endDate;
} else {
adjustedEndDate = new Date(endDate.setDate(endDate.getDate() + 1));
adjustedEndDate = new Date(adjustedEndDate);
return adjustedEndDate = GetNextWorking(adjustedEndDate);
}
}
function MYSTARTDATE(startdate) {
//var startTime = getStartTime();
var morningStart = new Date();
if (startdate.getHours() == 17) {
morningStart = startdate.addDays(1);
morningStart = GetNextWorking(morningStart);
morningStart.setHours(9);
} else {
morningStart = startdate;
}
return morningStart;
}
function todaysLunchEnd(endDate) {
var lunchtimeEnd = getLunchtimeEnd();
lunchtimeEnd = new Date(endDate).setHours(lunchtimeEnd.getHours(), lunchtimeEnd.getMinutes(), lunchtimeEnd.getSeconds());
lunchtimeEnd = new Date(lunchtimeEnd);
return lunchtimeEnd;
}
function getStartTime() {
var settingsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Settings");
var range = settingsSheet.getRange("B5");
var startTime = range.getValue();
var startTime;
if (!startTime) {
startTime = new Date(28800000);
//startTime = new Date(32400000); // 09:00
}
return startTime;
}
function getEndTime() {
var settingsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Settings");
var range = settingsSheet.getRange("B6");
var endTime = range.getValue();
if (!endTime) {
endTime = new Date(57600000);
//endTime = new Date(61200000); // 17:00
}
return endTime;
}
function getLunchtimeStart() {
var settingsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Settings");
var range = settingsSheet.getRange("B7");
var startTime = range.getValue();
if (!startTime) {
startTime = new Date(39600000); //11am
//startTime = new Date(43200000); // 12pm
}
return startTime;
}
function getLunchtimeEnd() {
var settingsSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Settings");
var range = settingsSheet.getRange("B8");
var endTime = range.getValue();
if (!endTime) {
endTime = new Date(43200000); //12:00
//endTime = new Date(46800000); //13:00
}
return endTime;
}

Related

Calculate datetime with javascript in days

I have a date in datetime and I need to calculate it with the current date in javascript to check if 7 days have passed.
var created_at = 2021-05-20; //return 2021-05-20 14:00:00
var data = new Date();
var dataAtual = data.getFullYear() + "-" + ("0" + (data.getMonth() + 1)).substr(-2) + "-" + ("0" + data.getDate()).substr(-2);
var result = data - created_at;
if(result < 7){
var create_date = true;
console.log(true);
} else {
var created_date = false;
console.log(false);
}
Can you try the below code
var date1 = new Date('2021-05-20 14:00:00')
var date2 = new Date()
var resulu = date2.getDate() - date1.getDate()
if(result < 7){
var create_date = true;
console.log(true);
} else {
var created_date = false;
console.log(false);
}
Get the difference of dates in milliseconds and convert into days.
const old_date = new Date('2021-05-20');
const today = new Date();
const diff_days = (today - old_date) / 24 * 60 * 60 * 1000;
if (diff_days < 7) {
console.log('older than a week');
} else {
console.log('in last week');
}

Calculate weekdays and weekends count between two dates without considering daylight saving - javascript

Due to daylight saving time in London, I'm not able to get the correct count for weekdays and weekend days. In the below fiddle 26/10/2019 is considering at times as weekday and at times as a weekend.
I want to get the output for the below fiddle is
"workingDays": 1,
"weekendDays": 2
Any suggestions, please?
var startDate = new Date('2019-10-26');
var endDate = new Date('2019-10-28');
var workingDays = number_of_working_days(startDate, endDate);
var weekendDays = number_of_weekend_days(startDate, endDate);
console.log({
'workingDays': workingDays,
'weekendDays': weekendDays
})
function number_of_working_days(startDate, endDate) {
var workingDays = 0;
for (var i = startDate; i <= endDate; i = i + (60 * 60 * 24)) {
if (i.getDay() <= 5) {
workingDays = workingDays + 1;
}
}
return workingDays;
}
function number_of_weekend_days(startDate, endDate) {
var weekendDays = 0;
for (var i = startDate; i <= endDate; i = i + (60 * 60 * 24)) {
if (i.getDay() > 5) {
weekendDays = weekendDays + 1;
}
}
return weekendDays;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

date difference greater than 90 days with JavaScript not working

The below script is failing for the scenario : start date 09/12/2009 end date 10/15/2009. The date difference is not more than 90 days, but still fails. can any one help?
var startDate = new Date(document.getElementById('ctl00$MainContent$FromYearTxt').value);
var endDate = new Date(document.getElementById('ctl00$MainContent$ToYearTxt').value);
var monthsDiff = endDate.getMonth() - startDate.getMonth();
var durationLimit = 0;
for (i = 1; i <= monthsDiff; i++) {
durationLimit += new Date(startDate.getFullYear(), startDate.getMonth() + i, 0).getDate();
}
var timeDiff = endDate.getTime() - startDate.getTime();
var daysDiff = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
if (daysDiff > durationLimit) {
args.IsValid = false;
} else {
args.IsValid = true;
}
If you are looking for a 3 month validation, then I think a better choice will be
function test() {
var args = {}; //creating for test
var startDate = new Date(document.getElementById('ctl00$MainContent$FromYearTxt').value);
var endDate = new Date(document.getElementById('ctl00$MainContent$ToYearTxt').value);
var maxDate = new Date(startDate);
maxDate.setMonth(maxDate.getMonth() + 3);
args.IsValid = endDate.getTime() <= maxDate.getTime();
document.getElementById('result').innerHTML = JSON.stringify(args)
}
<input id="ctl00$MainContent$FromYearTxt" value="03/13/2009" />
<input id="ctl00$MainContent$ToYearTxt" value="06/13/2009" />
<button onclick="test()">Test</button>
<div id="result"></div>

How to call other functions of same services in ionic (angular.js)

I am working on a mobile app using ionic framework. I have created a common Utility services like this
.service('CommonUtilityService', function($q) {
return {
parseJsonDate:function(jsonDate){
var offset = new Date().getTimezoneOffset() * 60000;
var parts = /\/Date\((-?\d+)([+-]\d{2})?(\d{2})?.*/.exec(jsonDate);
if (parts[2] == undefined)
parts[2] = 0;
if (parts[3] == undefined)
parts[3] = 0;
return new Date(+parts[1] + offset + parts[2]*3600000 + parts[3]*60000);
},
daysBetween:function(date1String, date2String){
var ONE_DAY = 1000 * 60 * 60 * 24;
var ONE_MINUTE = 1000 * 60;
var d1 = new Date(date1String);
var d2 = new Date(date2String);
var d1_ms = d1.getTime() - d1.getTimezoneOffset() * ONE_MINUTE;
var d2_ms = d2.getTime() - d2.getTimezoneOffset() * ONE_MINUTE;
return Math.floor(d1_ms - d2_ms/ONE_DAY);
},
getNumberOfDays:function(jsonDate){
// var date = parseJsonDate(jsonDate);
var date = new Date();
var today = new Date();
return this.daysBetween(today,date);
}
}
})
When I am trying to call daysBetween function inside getNumberOfDays function it is giving error
this.daysBetween is not a function
Can anyone tell me how can I call daysBetween function inside getNumberOfDays function.
Actually Service doesn't return a object where Factory return an object. You could try refactored service like this
Service
A service is a constructor function which creates the object using the
new keyword. You can add properties and functions to a service object
by using the this keyword. Unlike a factory, it doesn't return
anything (it returns an object which contains method).
Code
.service('CommonUtilityService', function($q) {
var CommonUtilityService = this;
CommonUtilityService.parseJsonDate = function(jsonDate) {
var offset = new Date().getTimezoneOffset() * 60000;
var parts = /\/Date\((-?\d+)([+-]\d{2})?(\d{2})?.*/.exec(jsonDate);
if (parts[2] == undefined)
parts[2] = 0;
if (parts[3] == undefined)
parts[3] = 0;
return new Date(+parts[1] + offset + parts[2] * 3600000 + parts[3] * 60000);
}
CommonUtilityService.daysBetween = function(date1String, date2String) {
var ONE_DAY = 1000 * 60 * 60 * 24;
var ONE_MINUTE = 1000 * 60;
var d1 = new Date(date1String);
var d2 = new Date(date2String);
var d1_ms = d1.getTime() - d1.getTimezoneOffset() * ONE_MINUTE;
var d2_ms = d2.getTime() - d2.getTimezoneOffset() * ONE_MINUTE;
return Math.floor(d1_ms - d2_ms / ONE_DAY);
}
CommonUtilityService.getNumberOfDays = function(jsonDate) {
// var date = parseJsonDate(jsonDate);
var date = new Date();
var today = new Date();
return CommonUtilityService.daysBetween(today, date);
}
})
You are using Pattern Three: Hybrid/Facade so in this you have to use this as Explained by #pankajparkar
OR
.service('CommonUtilityService', function($q) {
var parseJsonDate = function(jsonDate){
var offset = new Date().getTimezoneOffset() * 60000;
var parts = /\/Date\((-?\d+)([+-]\d{2})?(\d{2})?.*/.exec(jsonDate);
if (parts[2] == undefined)
parts[2] = 0;
if (parts[3] == undefined)
parts[3] = 0;
return new Date(+parts[1] + offset + parts[2]*3600000 + parts[3]*60000);
},
var getNumberOfDays = function(jsonDate){
// var date = parseJsonDate(jsonDate);
var date = new Date();
var today = new Date();
return this.daysBetween(today,date);
}
return {
parseJsonDate: parseJsonDate,
getNumberOfDays: getNumberOfDays
}
})

Calculate days left to a specific date with JavaScript

I was trying to calculate the days left until a specific date. I know that there are a million different approaches and tutorials about it, but I wanted to write a code by myself. The problem is that the output of the function is "NaN". I am very thankful for your help.
This is my code:
var daysLeft = function(input) {
var num = '';
var date = [];
var x = 0;
for (i = 0; i < input.length; i++) {
if (!isNaN(input.charAt(i))) {
num += input.charAt(i);
}
else {
date[x] = parseInt(num, 10);
x++;
}
}
var inputDate = new Date(date[2], date[1], date[0]);
var today = new Date();
var timeDiff = Math.abs(inputDate.getTime() - today.getTime());
return Math.ceil(timeDiff / (1000*3600*24));
};
daysLeft("11.12.2014");
BTW: I wrote this code, because the Date() function works with the American format of Dates (MM/dd/YYYY) and not with UTC dates. I am also aware that there is the Date.UTC() function, but anyway. I just wanted to turn around months and days myself.
When you parse num to set date[x], you need to reset num to ''.
...
else {
date[x] = parseInt(num, 10);
x++;
num = '';
}
You might consider using String.split() to separate your input at the periods.
My Solution is:
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
var daysLeft = function(input) {
var num = '';
var date = [];
var x = 0;
for (i = 0; i < input.length; i++) {
if (!isNaN(input.charAt(i)) && isNumeric(input.charAt(i))) {
num += input.charAt(i);
}
else {
date[x] = parseInt(num, 10);
x++;
num = '';
}
}
date[x] = parseInt(num, 10);
var inputDate = new Date(date[2], date[1], date[0]);
var today = new Date();
var timeDiff = Math.abs(inputDate.getTime() - today.getTime());
return Math.ceil(timeDiff / (1000*3600*24));
};
But a better would be:
function parseDate(input) {
var parts = input.split('-');
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2]); // Note: months are 0-based
}
var daysLeft = function(input) {
var inputDate = parseDate(input);
var today = new Date();
var timeDiff = Math.abs(inputDate.getTime() - today.getTime());
return Math.ceil(timeDiff / (1000*3600*24));
};
You should use something like this:
var daysLeft = function(input) {
var num = '';
var date = [];
var x = 0;
for (i = 0; i < input.length; i++) {
if (!isNaN(input.charAt(i))) {
num += input.charAt(i);
}
else {
date[x] = parseInt(num, 10);
x++;
num='';
}
}
date[x] = parseInt(num, 10);
var inputDate = new Date(date[2], date[1], date[0]);
var today = new Date();
var timeDiff = Math.abs(inputDate.getTime() - today.getTime());
return Math.ceil(timeDiff / (1000*3600*24));
};
daysLeft("11.12.2014");

Categories

Resources