Get "Exception: Service error: Spreadsheets" in range.setValues() Google Apps Script - javascript

My objective:
I get the rows where the date in column A is between Monday and Sunday of "Last Week".
Created a forEach array where I added the value "yes" to the cells in column L that correspond to the previously returned rows.
I insert a checkbox in the whole column L (only in the rows that contain data), and make the checkboxes where the cells contain "yes" be checked.
This process is repeated for "Last week", "This week", "Next week".
What's my problem:
The problem I have is that when I run this script, I get an error "Exception: Service error: Spreadsheets" specifically in the code part rango.setValues(valLastWeek); rango.setValues(valThisWeek); rango.setValues(valNextWeek);
I've searched for solutions here on stackoverflow but can't fix the problem. I have tried adding more rows, trying to find a problem with my code,... but I can't find the solution to my problem.
Here I leave a screenshot of what I want. https://ibb.co/d78vyBY
Here you have a sample sheet to reproduce the problem with the code already embedded in GAS: https://docs.google.com/spreadsheets/d/1D7DsMyGnXQlOY-hL9kkZsOml7xAoJDMNA2-uIYM8m3Y/edit?usp=sharing
This is my complete code:
function weeksRange() {
const sheetName = '📅 Todos los eventos'; // Please set your sheet name.
// Create an object including the last week, this week, and next week.
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
const first = now.getDate() - now.getDay() + 1;
const last = first + 6;
const obj = {
lastWeekFirstDay: new Date(year, month, first - 7).getTime(),
lastWeekLastDay: new Date(year, month, last - 7).getTime(),
thisWeekFirstDay: new Date(year, month, first).getTime(),
thisWeekLastDay: new Date(year, month, last).getTime(),
nextWeekFirstDay: new Date(year, month, first + 7).getTime(),
nextWeekLastDay: new Date(year, month, last + 7).getTime(),
};
// Retrieve values from Spreadsheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const [, ...values] = sheet.getDataRange().getValues();
// Split rows for the last week, this week, and next week.
const res = values.reduce((o, r) => {
const t = r[0].getTime();
["last", "this", "next"].forEach(e => {
if (t >= obj[`${e}WeekFirstDay`] && t < obj[`${e}WeekLastDay`]) {
o[`${e}Week`].push(r);
}
});
return o;
}, { lastWeek: [], thisWeek: [], nextWeek: [] });
console.log(res);
// LAST WEEK
var valLastWeek = res.lastWeek;
Logger.log(res.lastWeek);
var totalFilasLastWeek = valLastWeek.length;
var totalColLastWeek = valLastWeek[0].length;
var rango = sheet.getRange(2, 1, totalFilasLastWeek, totalColLastWeek);
valLastWeek.forEach((fila)=> {
Logger.log(fila);
fila[11] = 'yes';
//Logger.log(fila[11]);
})
rango.setValues(valLastWeek);
valLastWeek.insertCheckboxes('yes');
// THIS WEEK
var valThisWeek = res.thisWeek;
Logger.log(res.thisWeek);
var totalFilasThisWeek = valThisWeek.length;
var totalColThisWeek = valThisWeek[0].length;
var rango = sheet.getRange(2, 1, totalFilasThisWeek, totalColThisWeek);
valThisWeek.forEach((fila)=> {
Logger.log(fila);
fila[12] = 'yes';
})
rango.setValues(valThisWeek);
valThisWeek.insertCheckboxes('yes');
// NEXT WEEK
var valNextWeek = res.nextWeek;
Logger.log(res.nextWeek);
var totalFilasNextWeek = valNextWeek.length;
var totalColNextWeek = valNextWeek[0].length;
var rango = sheet.getRange(2, 1, totalFilasNextWeek, totalColNextWeek);
valNextWeek.forEach((fila)=> {
Logger.log(fila);
fila[13] = 'yes';
})
rango.setValues(valNextWeek);
valNextWeek.insertCheckboxes('yes');
}

When I saw your script and spreadsheet and your expected result, I thought that in this case, the row number might be suitable instead of the row values. So, in your script, how about the following modification?
Modified script:
function weeksRange() {
const sheetName = '📅 Todos los eventos'; // Please set your sheet name.
// Create an object including the last week, this week, and next week.
const now = new Date();
const year = now.getFullYear();
const month = now.getMonth();
const first = now.getDate() - now.getDay() + 1;
const last = first + 6;
const obj = {
lastWeekFirstDay: new Date(year, month, first - 7).getTime(),
lastWeekLastDay: new Date(year, month, last - 7).getTime(),
thisWeekFirstDay: new Date(year, month, first).getTime(),
thisWeekLastDay: new Date(year, month, last).getTime(),
nextWeekFirstDay: new Date(year, month, first + 7).getTime(),
nextWeekLastDay: new Date(year, month, last + 7).getTime(),
};
// Retrieve values from Spreadsheet.
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const [, ...values] = sheet.getDataRange().getValues();
// Split rows for the last week, this week, and next week.
const res = values.reduce((o, r, i) => {
const t = r[0].getTime();
["last", "this", "next"].forEach(e => {
if (t >= obj[`${e}WeekFirstDay`] && t < obj[`${e}WeekLastDay`]) {
o[`${e}Week`].push(i + 2); // Modified
}
});
return o;
}, { lastWeek: [], thisWeek: [], nextWeek: [] });
console.log(res);
// I modified the below script.
const ranges = [...res.lastWeek.map(r => `L${r}`), ...res.thisWeek.map(r => `M${r}`), ...res.nextWeek.map(r => `N${r}`)];
sheet.getRangeList(ranges).check();
}
In this modification, console.log(res); shows the row numbers of each value. Using this value, as a sample, var v = res.lastWeek; is used as "LAST WEEK". When this script is run, the checkboxes of column "L" are checked and the value of "yes" is set for the rows of "LAST WEEK".
When you check "THIS WEEK", please use var v = res.thisWeek; instead of var v = res.lastWeek;.

Related

How do I get an array of all days in the week given the current date in javascript?

I have the weekday value (0-6 for Sun-Sat)...how do I get an array of days (starting on Sunday for the given week?
Here is what I'm trying to do:
A user clicks a day (ie: May 10) and it generates an array of the dates for the current week:
function selectWeek(date) {
selectedWeek = [];
let d = new Date(date.key);
console.log(d);
console.log(date.weekday, 'weekday');
console.log(date);
for (let i = 0; i < date.weekday; i++) {
console.log(i, 'pre');
let currD = d.setDate(d.getDate() - i).toString();
console.log(currD);
selectedWeek.push(currD);
}
for (let i = date.weekday; i < 7; i++) {
console.log(i, 'post');
selectedWeek.push(d.setDate(d.getDate() + i).toString());
}
console.log(selectedWeek);
}
I need Sunday through Saturday date objects.
I am not using a date library so prefer vanilla javascript solutions.
Create an array with 7 elements, subtract the weekday and add the index as day:
function selectWeek(date) {
return Array(7).fill(new Date(date)).map((el, idx) =>
new Date(el.setDate(el.getDate() - el.getDay() + idx)))
}
const date = new Date();
console.log(selectWeek(date));
I'm using
fill(new Date(date))
to create a copy and not modify the argument.
You can get the days of a month with the same concept:
function selectMonth(date) {
return Array(31).fill(new Date(date)).map((el, idx) =>
new Date(el.setDate(1 + idx))).filter(el => el.getMonth() === date.getMonth());
}
const date = new Date();
console.log(selectMonth(date));
here's how I would solve this one:
function selectWeek(date) {
let selectWeek = [];
for (let i=0; i<7; i++) {
let weekday = new Date(date) // clone the selected date, so we don't mutate it accidentally.
let selectedWeekdayIndex = date.getDay() // i.e. 5 for friday
let selectedDay = date.getDate() // 1-31, depending on the date
weekday.setDate(selectedDay - selectedWeekdayIndex + i)
selectWeek = [...selectWeek, weekday]
}
return selectWeek;
}
Let's take today's date as an example: 18.02.22. Friday.
We do 6 iterations. On first one we get the selectedWeekdayIndex, which is 5 for friday. We clone the date and re-set it's day (18) reducing it by this number: 18-5 = 13. This is the day for Sunday. Then we go on incrementing days by one to fill the rest of the week.
Of course it can be optimised and written much shorter, but I tried to show and explain the logic.
The .getDay() gives you the day of the week.
function selectWeek(date) {
const selectWeek = [];
let temp = date;
while (temp.getDay() > 0) temp.setDate(temp.getDate() - 1); // find Sunday
// get the rest of the week in the only do while loop ever created
do {
selectWeek.push(new Date(temp));
temp.setDate(temp.getDate() + 1);
} while (temp.getDay() !== 0);
return selectWeek;
/* for display purposes only */
// const dates = selectWeek.map((date) => date.toString());
// console.log(dates);
}
selectWeek(new Date());
selectWeek(new Date("2020-01-01"));
selectWeek(new Date("december 25, 1990"));
Did you check Date prototypes before?
I think Date.prototype.getDay() can do what you want:
function selectWeekDays(date) {
var i,
d = new Date(date),
a = [];
// console.log(d.getDay()); // Number of the day
d.setDate(d.getDate() - d.getDay() - 1); // Set date one day before first day of week
for (i=0; i<7; i++) {
d.setDate(d.getDate() + 1); // Add one day
a.push(d.valueOf());
}
return a;
}
var result = selectWeekDays('2022-01-01') // Satureday = 6
console.log(result);
result.forEach(e => console.log(new Date(e).toString()));

Display current week in react-native

I need to display the current week of the month in the following format in react-native:
(Week 2: 05.10 - 11.10) (example of week 2 of current month)
What would be some suggestions as how to achieve this? I know that there are packages such as momentjs to build this but would like some examples of how to achieve this
any help is appreciated!
You can adapt the code below. I say "adapt" because you haven't specified when your week starts (Sunday or Monday?) or how you want to count which week within the month it is (i.e. is week #1 the first full week? The code below assumes so).
Anyway, by clicking the "Run Code Snippet" button, you'll see what it does, including some intermediate steps, which are there to illustrate where the values are coming from, and therefore what you might want to "adapt" for your needs.
//get the first day of week and last day of week, borrowed from https://stackoverflow.com/a/64529257/1024832 above
const getWeek = (date = new Date()) => {
const dayIndex = date.getDay();
const diffToLastMonday = (dayIndex !== 0) ? dayIndex - 1 : 6;
const dateOfMonday = new Date(date.setDate(date.getDate() - diffToLastMonday));
const dateOfSunday = new Date(date.setDate(dateOfMonday.getDate() + 6));
return [dateOfMonday, dateOfSunday];
}
//get week number w/in the month, adapted from https://stackoverflow.com/a/57120367/1024832
const getWeekNumber = () => {
let todaysDate = moment(moment.now());
let endOfLastMonth = moment(todaysDate).startOf('month').subtract(1, 'week');
let weekOfMonth = todaysDate.diff(endOfLastMonth, 'weeks');
return weekOfMonth;
}
//capture/log some steps along the way
const [Monday, Sunday] = getWeek();
console.log("First/Last of week as Date Objects: ", Monday, Sunday);
let Monday_formatted = moment(Monday).format("DD.MM");
let Sunday_formatted = moment(Sunday).format("DD.MM");
console.log(Monday_formatted, "-", Sunday_formatted);
console.log("Week #:", getWeekNumber());
//set the DIV content
document.getElementById("datehere").innerText = `(Week ${getWeekNumber()}): ${Monday_formatted} - ${Sunday_formatted}`;
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<div id="datehere"></div>
Here's an answer as a function which returns the current week's Monday and Sunday in an array:
getWeek = (date = new Date()) => {
const dayIndex = date.getDay();
const diffToLastMonday = (dayIndex !== 0) ? dayIndex - 1 : 6;
const dateOfMonday = new Date(date.setDate(date.getDate() - diffToLastMonday));
const dateOfSunday = new Date(date.setDate(dateOfMonday.getDate() + 6));
return [dateOfMonday, dateOfSunday];
}
const [Monday, Sunday] = getWeek();
console.log(Monday, Sunday);
The response is two valid date objects. You can also pass a date object for the function to get Monday and Sunday of that date's week (e.g. getWeek(new Date(0));
But when you want to parse those dates, you should gain better knowledge of Date Object.

How to exclude weekends in date object in Javascript

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);

Set last day of month on YYYYMM date format - Javascript

I've a variable that has value of date in YYYYMM format. For example:
var givenDate = "201704"
How can I find out the last day of the given month and append to it. For example,
//last day of 2017 04 (April) is 30th so append value to givenDate + lastDate;
//that will be 20170430
var newFullGivenDate = "20170430";
const date = "201704";
const year = parseInt(date.substring(0, 4));
const month= parseInt(date.substring(4, 6));
const lastDay = (new Date(year, month, 0)).getUTCDate();
const newFullGivenDate = date + lastDay;
console.log(newFullGivenDate);
var givenDate = "201704";
var month = givenDate.substring(4, givenDate.length); // retrieves 04
var year = givenDate.substring(0, 4); // retrieves 2017
var d = new Date(year, month, 0);
alert(d.getDate());
Reference: MDN
To achieve expected result, use below option
last day of month - new Date(year,month ,0)
var givenDate = "201704";
var currDate = new Date(givenDate.substr(0,3),givenDate.substr(4) ,0)
var newFullGivenDate = givenDate + currDate.getDate();
console.log(newFullGivenDate)
Codepen URL for reference - https://codepen.io/nagasai/pen/OmgZMW
I would break it down into two functions:
// Get last day from year and month
let lastDayOf = (year, month) => (new Date(year, month, 0)).getDate();
// Add last day to string only if input is correct
let addLastDay = (input) => {
// In case you pass number (201705) instead of string ("201705")
if (Number.isInteger(input)) input = input.toString();
// Check if input is in correct format - 6 digit string
if (typeof input !== "string" || !input.match(/^\d{6}$/)) {
return input; // You can implement desired behavour here. I just return what came
}
const year = input.substr(0, 4);
const month = input.substr(4, 2);
return input + lastDayOf(year, month);
}
// Tests
console.assert(addLastDay("201704"), "20170430");
console.assert(addLastDay("201702"), "20170228");
console.assert(addLastDay("201202"), "20120229");
console.assert(addLastDay(201705), "20170531");
console.assert(addLastDay(20170), 20170); // Wrong input
// Interactive example
document.getElementsByTagName('button')[0].addEventListener('click', () => {
let input = document.getElementsByTagName('input')[0];
input.value = addLastDay(input.value);
});
<input type="text" value="201704"><button>Calculate</button>
If you are using moment js you can yry this:
var date = moment(newFullGivenDate ).format('YYYYMMDD');
date = date.add(-1 * parseInt(date.format('DD')), 'days').add(1, 'months');

Create array of available time slots between two dates

I am weak on front end work and need a little help here.
I am developing a basic form that gives the users available schedule options for booking an appointment during the next 3 days inclusive.
So I have thus far written:
var today = new Date();
var lastday = new Date(today);
lastday.setDate(today.getDate() + 4);
var daterange = getAllDays(today, lastday);
function getAllDays(today, lastday)
{
var s = new Date(today);
var e = new Date(lastday);
var a = [];
//this gets my start time days
while (s < e)
{
//if day Saturday hours change
if(s.getDay() == 0)
{
//if Sunday skip
}
else if (s.getDay() == 6)
{
//push start onto array
a.push(setWorkingHours(s, '10'));
//push end onto array
a.push(setWorkingHours(s, '20'));
}
else
{
//push start onto array
a.push(setWorkingHours(s, '13'));
//push end onto array
a.push(setWorkingHours(s, '20'));
}
s = new Date(s.setDate(s.getDate() + 1))
}
return a;
};
function setWorkingHours(date, hour)
{
var dateTime = new Date();
dateTime.setDate(date.getDate());
dateTime.setHours(hour, '0', '0');
return dateTime;
}
alert(daterange.join('\n'));
Here is a fiddle.
Now this I know could use refinement and I am open to any improvements.
So the above code gets me an array of Days with a start and stop time. Now I'm struggling to figure out how I am going get an array of the hours within each days start and stop time.
Further once I have the hours I have a query to google calendar that returns events so I will then parse that into an array.
Upcoming events:
Bob Builder (2015-08-07T10:00:00-08:00)
John Doe (2015-08-08T11:00:00-08:00)
Mary Jane (2015-08-10T18:00:00-08:00)
Finally I will need to "intersect" the available array with the booked array and return what is left.
As for the appointments themselves. If a person picked a time I would then schedule a two hour block. Appointments can start at the top or bottom of the hour.
I would use something along the lines of the getTimeSlotsForDay function below to get an array of the available start times for a given day.
var GoogleCalenderAppointments = null;
var today = new Date();
var lastDay = new Date(today);
lastDay.setDate(today.getDate() + 4);
function checkGoogleCalendarConflict(date) {
var hasConflict = false;
if (!GoogleCalenderAppointments) {
//logic to get scheduled appointments
}
//iterate through relevant scheduled appointments
//if argument `date` has conflict, return true
//else, return false
return hasConflict
}
function getTimeSlotsForDay(date) {
var timeSlots = []
var dayStart = new Date(date)
var dayEnd = new Date(date)
switch (date.getDay()) {
case 0: //Sunday
return timeSlots;
case 6: //Saturday
dayStart.setHours(10, 0, 0, 0)
dayEnd.setHours(20, 0, 0, 0)
break;
default:
dayStart.setHours(13, 0, 0, 0)
dayEnd.setHours(20, 0, 0, 0)
}
do {
if (!checkGoogleCalendarConflict(dayStart)) {
timeSlots.push(new Date(dayStart))
}
dayStart.setHours(dayStart.getHours(), dayStart.getMinutes() + 30)
} while (dayStart < dayEnd);
return timeSlots
}
var message = ""
for (var i = new Date(today); i < lastDay; i.setDate(i.getDate() + 1)) {
message += i.toDateString() + ":\n"
message += getTimeSlotsForDay(i).map(function(it) {
return it.toTimeString();
}).join(",\n") + "\n";
}
alert(message)
Referring to your array issue make an object of day arrays. Import momentjs. Make a two item array of start and end. Their date object is more full and they have great functions to check out.
Calendar = {
January-1: [[new Date(), new Date() ], [new Date(), new Date() ]]
}
For the second part underscorejs is nice. Use _.zip(array, array). (you will need to format the Google array to match your array).

Categories