Google spreadsheet / docs , jump to current date cell on Open - google-apps-script

I've searched for this topic and have found a few threads - however, none of the answers (usually in the form of scripts) in those topics have proven to be successful to me yet. I guess I'm messing up some variables.
I have a spreadsheet in Google Docs that holds a work roster. In row B1:OI1 is just over a years worth of dates, i.e., B1 = Friday May 1st 2015, B2 = Saturday May 2nd 2015, OI = Wednesday June 1st 2016.
I want the sheet to jump to the roster of either today, or to the monday of the current week, whenever the sheet is opened.
How do I accomplish this?
Thanks in advance for the help!

I suppose you have seen this post where the OP wanted to change the background color of today's date in a sheet, your case is very similar except that - if I understood you well - today is not necessarily present in the sheet.
So what we need is to find the closest date to today ? You mention it has to be a Monday, I didn't go that far, the script below finds the closest date in column A, you will adapt it to your needs by simply adapting the index in the array. (don't forget arrays count from 0)
code :
function onOpen() { // runs automatically
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var data = sh.getDataRange().getValues();
var today = new Date().setHours(0,0,0,0);
var diffref = today;
var diff;
var idx;
for(var n=0;n<data.length;n++){
var date = new Date(data[n][0]).setHours(0,0,0,0);
diff=today-date;
if(diff==0){break}
Logger.log("diffref = "+diffref+" today-date = diff = "+diff);
if(diff < diffref && diff > 0){idx=n ; diffref=diff}
}
if(n==data.length){n=idx}
n++;
sh.getRange(n, 1).activate();
}
Edit :
To check the day of the week (yes, I'm curious by nature ;-) you can try to change the condition like this :
Logger.log("diffref = "+diffref+" today-date = diff = "+diff+" day = "+new Date(date).getDay());
if(diff < diffref && diff > 0 && new Date(date).getDay()==1){idx=n ; diffref=diff}
From my tests it seems to work as expected.
EDIT 2 :
Following your comment, it seems what you're looking for is much simpler :
function onOpen2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var data = sh.getDataRange().getValues();
var today = new Date().setHours(0,0,0,0);
for(var n=0;n<data[0].length;n++){
var date = new Date(data[0][n]).setHours(0,0,0,0);
if(date==today){break};
}
n++;
sh.getRange(1,n).activate();
}
EDIT 3
For some reason that I ignore (please anyone give advise !!) your sheet does not return date values from the date in cells but rather the native spreadsheet values which are integers corresponding to the number of days since december 30,1899...
So what I did is to subtract this offset value from the javascript today variable, divide it by the number of milliseconds in a day (JS counts in milliseconds) and take the integer part of the result.
A bit cumbersome I admit but I didn't find a simpler way...
What is really weird is that in any other sheet I try dates are always returned as dates...
Anyway, for the time being, here is a working code for your spreadsheet :
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
var data = sh.getDataRange().getValues();
var offsetToSS = new Date('1899/12/30').setHours(0,0,0,0);
var today = parseInt((new Date().setHours(0,0,0,0)-offsetToSS)/86400000,10)+1;
for(var n=0;n<data[0].length;n++){
var date = data[0][n];
Logger.log("date = "+data[0][n]+" =? "+today);
if(date==today){break};
}
n++;
sh.getRange(1,n).activate();
}
Last note : for a better user experience, add this line of code right after var sh = ss.getActiveSheet();
sh.getRange(1,sh.getLastColumn()).activate();
this will select the last column before activating today's column and will place the selection on the left (near the frozen column in your sheet) which is more "natural".

function onOpen() {
// Activate cell with current date
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(),
data = sheet.getDataRange().getValues(),
now = new Date(),
columnWithDate = 0,
i, delta,
epsilonInMs = 0;
for (i = 0; data.length > i; i++) {
delta = now - new Date(data[i][columnWithDate]);
if (delta < epsilonInMs) break;
}
sheet.getRange(i, columnWithDate + 1).activate();
}

This works when dates are in first column (column A) and you want to go to the row near the current date (you can tweak the numbers to suit):
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var d1 = new Date().getTime();
var a;
for (a = 1; (Math.floor((d1-sheet.getRange(a,1).getValue())/86400000)) > 5; a++)
try { var range = sheet.getRange(a+25,2); }
catch(err) { var range = sheet.getRange(2, 2) }
sheet.setActiveSelection(range);
}

Related

Spreadsheet jump to current date issue

Hello dear community,
I am unfortunately not able to figure out where's the issue in #Serge insas AppScript to jump to Today's cell in my own spreadsheet.
Fixed potential issue with different timezones date format, however, the script jumps to the last cell as: sh.getRange(5,sh.getLastColumn()).activate(); indicates, but not to Today's cell.
Important to mention that my Spreadsheet is constructed horizontal, here is the link
Here is my version:
function JumpToToday() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
sh.getRange(5,sh.getLastColumn()).activate();
var data = sh.getDataRange("E5:NE5").getValues();
var offsetToSS = new Date('22/02/2022').setHours(0,0,0,0);
var today = parseInt((new Date().setHours(0,0,0,0)-offsetToSS)/86400000,10)+1;
for(var n=0;n<data[0].length;n++){
var date = data[0][n];
Logger.log("date = "+data[0][n]+" =? "+today);
if(date==today){break};
}
n++;
sh.getRange(1,n).activate();
}
Can the issue be due to the script going first to last column and then starts counting from n=0?
To get to the current date in your sheet.
You have to count the number of days from January 1 up to this day.
Use the number of days in the col parameter of Sheet.getRange(row, col, numRows, numCol) + the number of column used before the January 1 cell.
Here I created a code that will activate the cell with the current date with the help of user2501097's answer here:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var snoopiSheet = ss.getSheetByName("Snoopi");
var range = snoopiSheet.getRange(5, getCurrentDayofYear() + 5, 1,1);
range.activate();
}
function getCurrentDayofYear(){
var date = new Date();
return (Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) - Date.UTC(date.getFullYear(), 0, 0)) / 24 / 60 / 60 / 1000;
}
Output:
Note: I used Snoopi sheet in the example above, I added + 5 in the 2nd parameter of getRange() because the start of the count is in the 5th column or January 1. Also, when using multiple Sheets, make sure to use ss.getSheetByName(name) this is to prevent script from applying the code to different sheets which might happen if you use getActiveSheet()
References:
Spreadsheet.getSheetbyName(name)
Sheet.getRange(row, column, numRows, numColumns)
Well I learned something new today. Date.valueOf() and Date.setHours(0,0,0,0) return the same, the value of date in milliseconds. But your date is invalid.
new Date("22/02/2022") returns Invalid date.
function dummy() {
try {
var offsetToSS = new Date('02/22/2022').valueOf();
console.log(offsetToSS);
var offsetToSS = new Date('02/22/2022').setHours(0,0,0,0);
console.log(offsetToSS);
var today = parseInt((new Date().setHours(0,0,0,0)-offsetToSS)/86400000,10)+1;
console.log(today);
}
catch(err) {
console.log(err);
}
}
8:14:09 AM Notice Execution started
8:14:09 AM Info 1645506000000
8:14:09 AM Info 1645506000000
8:14:09 AM Info 1
8:14:09 AM Notice Execution completed

My Javascript IF statement seems to think two identical values from a google sheets sheet do not match

I'm trying to write a script where the dates in a column are compared against todays date, and if the dates in the column match, an email is sent.
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var rotDate = sheet.getRange("F6").getValue();
var today = sheet.getRange("F1").getValue();
//var today = new Date().toLocaleDateString(); // Today's date, without time
var dumpCell = sheet.getRange("J3");
var dumpCell2 = sheet.getRange("J4");
var dumpCell3 = sheet.getRange("J5");
if(rotDate==today) {
//dumpCell is there to dump a value in a cell if the IF statement is true
dumpCell.setValue(rotDate);
MailApp.sendEmail("this is where my email would go", "subject", "body");
}
//these dump the compared vars into cells so they can be checked against one another manually
dumpCell2.setValue(rotDate)
dumpCell3.setValue(today)
}
This is as far as I've gotten. The Values in F6 and F1 are identical, I've typed them out, retyped them, copied and pasted, etc. But for some reason, my if statement just won't run. It behaves as if the two values are different, and I can't work out why.
If I change var rotDate and var today to matching strings, eg "123" then it seems to work as expected.
This is a screenshot of my test data sheet. There are other columns there with other data which were meant to be used for more testing, but I didn't get that far.
Does anyone know what I might be doing wrong?
After trying a variety of approaches, I cracked it using a code snippet from Jon Lin's answer here:
Compare two dates Google apps script
After realizing that the fault was with trying to compare two dates (either a date in an adjacent cell, or a procedurally generated date whenever the function is run, I knew I had to do some better formatting with the data I was intending to compare. This is my repaired code that now works as expected:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var rotDate = sheet.getRange("F6").getValues();
var today = new Date();
//sample values
var sYyyy = Utilities.formatDate(new Date(rotDate), "GMT+8","yyyy");
var sMm = Utilities.formatDate(new Date(rotDate), "GMT+8","MM");
var sDd = Utilities.formatDate(new Date(rotDate), "GMT+8","dd");
//Test Values
var tYyyy = Utilities.formatDate(new Date(today), "GMT+8","yyyy");
var tMm = Utilities.formatDate(new Date(today), "GMT+8","MM");
var tDd = Utilities.formatDate(new Date(today), "GMT+8","dd");
//var rotDate = sheet.getRange("F6").getValue();
//var today = sheet.getRange("F1").getValue();
//var today = new Date().toLocaleDateString(); // Today's date, without time
var dumpCell = sheet.getRange("J3");
var dumpCell2 = sheet.getRange("J4");
var dumpCell3 = sheet.getRange("J5");
if (sYyyy + sMm + sDd == tYyyy + tMm + tDd) {
//if(rotDate===today) {
//dumpCell is there to dump a value in a cell if the IF statement is true
dumpCell.setValue(rotDate);
MailApp.sendEmail("tv18766#gmail.com", "subject", "body");
}
//these dump the compared vars into cells so they can be checked against one another manually
dumpCell2.setValue(rotDate)
dumpCell3.setValue(today)
}

Doing Math with Date Values

I am trying to use the difference between two dates in my Google Sheet to determine the number of rows down that the originRange will be copied.
I'm getting an error 'Cannot convert NaN to (class)' so I'm assuming I need to change the format of the date in some part of the formula but I am completely stuck.
The dates in the Google Sheet are formatted "10/26/2016" & "12/31/2016" if that helps at all.
function fillNewReport () {
var startDate = responseSheet.getRange('C2');
var endDate = responseSheet.getRange('G2');
var dateDiff = (endDate - startDate) + 1;
var newReport = ss.getSheetByName('Copy of Form Responses 1');
var originRange = newReport.getRange(2,2,1,22);
var newRange = newReport.getRange(2,2,dateDiff,22);
originRange.copyTo(newRange);
}
Any help with this will be greatly appreciated.
Thank you,
EDIT: Part 2
I think I've got the right formatting now, as I'm no longer getting an error. But the formula is not responding. No rows are being copied. Any suggestions?
function fillNewReport () {
var startDateValue = responseSheet.getRange('C2').getValue();
var endDateValue = responseSheet.getRange('G2').getValue();
var startDateMilli = new Date(startDateValue);
var endDateMilli = new Date(endDateValue);
var startDate = startDateMilli.getDate();
var endDate = endDateMilli.getDate();
var dateDiff = (endDate - startDate) + 1;
var newReport = ss.getSheetByName('Copy of Form Responses 1');
var originRange = newReport.getRange(2,2,1,22);
var newRange = newReport.getRange(2,2,dateDiff,22);
originRange.copyTo(newRange);
}
In your code startDate and endDate are ranges, not values !
So first thing is to change that using getValue().
You will then get date objects that wont behave as you expect because unlike days in spreadsheets javascript date objects are more complex objects, they have date and time properties and have a native value of the number of milliseconds since the reference date...
Anyway, all you have to do is using the date methods described in all javascript reference on the internet, ask Google for "JS DATE"
Then do the math you need, this part will be easy I think.
edit
As I wrote in the comments, I'm not sure I understand what result you want to get...
I tried a test changing a few details and I get a result... you tell me if that's what you wanted.
function fillNewReport () {
var startDateValue = responseSheet.getRange('C2').getValue();
var endDateValue = responseSheet.getRange('G2').getValue();
var startDateMilli = new Date(startDateValue);
var endDateMilli = new Date(endDateValue);
Logger.log('startDateMilli = '+startDateMilli+' endDateMilli = '+endDateMilli);
var startDate = startDateMilli.getDate();
var endDate = endDateMilli.getDate();
var dateDiff = (endDate - startDate) + 1;
Logger.log('dateDiff = '+dateDiff);
var newReport = ss.getSheetByName('Copy of Form Responses 1');
var originRange = newReport.getRange(2,2,1,22);
var newRange = newReport.getRange(2,2,dateDiff,22);
originRange.copyTo(newRange);
}
or like this to copy only one row
...
var originValues = newReport.getRange(2,2,1,22).getValues();
Logger.log('originValues = '+originValues);
var newRange = newReport.getRange(2+dateDiff,2,1,22); // copies to the other sheet but dateDiff rows underneath...
newRange.setValues(originValues);
}

setFormula then get and set Value - is this the best way?

I have a growing dataset on a google sheet with which I need to run some complex filters/queries/vlookups on. However I only need to do this daily as the new data arrives. The number of complex formulas is starting to slow the spreadsheet to a grinding halt, particularly with our woeful broadband connection.!
I therefore came up with a workaround of using GAS to set the formula in a cell, then to get the Value and then to set the Value, knowing that GAS doesn't run the spreadsheet functions natively (as per VBA). As I have already worked up the filters and vlookups on the sheets I need, I didn't go into scripting the formulas to achieve the same thing.
Here is a simplified version of the code:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('Sheet1');
var rng = sh.getRange('B11');
rng.setFormula('=sum(filter(C2:E5,A2:A5="Bob"))');
var val = rng.getValue();
rng.setValue(val);
}
In my production spreadsheet I can have # 300 formulas on each of 30 sheets, so if these are all pinging away at the dataset I get lengthy periods of the grey progress bar. (In essence the formulas are filtering/summing or counting daily data to weekly data) My example above shows everything happening on one sheet.
Wondered if there was a better/different way of doing this?
Thanks
Tim
Well, I have not come up with anything better so will post my solution. Two scripts. First one checks that the user actually want to update their values, if they say yes, then checks with the user again and shows them the date range it will update. Then runs the second script, which in simple terms just applies a formula to a cell then copies the value generated and pastes the value. On testing with full data load, spreadsheet does no "waiting/progress grey box" at all so solves my issue.
function runWriteBehavs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sy = ss.getSheetByName("summary");
//gets last row in column B - data is contigious
var BVals = sy.getRange("B1:B").getValues();
var BLast = BVals.filter(String).length;
var rowBeh = BLast + 1;
var lastDate = sy.getRange("A" + rowBeh).getValue();
var lastEndDate = new Date(lastDate.getTime()+6*3600000*24);;
//formats dates
lastDate = Utilities.formatDate(new Date(lastDate), "GB", "dd/MM/yyyy");
lastEndDate = Utilities.formatDate(new Date(lastEndDate), "GB", "dd/MM/yyyy");
//message box for confirmation
var output = Browser.msgBox("This script will run the summaries for the week:\\n" + lastDate + " - " + lastEndDate + "\\n Are you really sure you want to continue?",Browser.Buttons.YES_NO);
if ( output == "yes" ) {
//calls main update script
writeBehavs();
}
}
//Needs to be run once all data is entered
function writeBehavs() {
//get variables
var ss = SpreadsheetApp.getActiveSpreadsheet();
var db = ss.getSheetByName("database");
var sy = ss.getSheetByName("summary");
var sL = ss.getSheetByName("lists");
//gets number of behaviours, a counta() of list on sheet
var bCount = sL.getRange("H1").getValue();
//gets column listing hard coded on sheet
var bCol = sL.getRange("H2:H30").getValues();
//gets last row in column B - data is contigious
var BVals = sy.getRange("B1:B").getValues();
var BLast = BVals.filter(String).length;
//for each number on behaviour count
for (var i=0; i<bCount; ++i) {
//set the column
var colBeh = [bCol[i]];
//set the correct row for data entry and start date check
var rowBeh = BLast + 1;
//sets correct row for end date check
var rowBeh2 = rowBeh + 1;
//gets first empty row in Column from iteration
var rng = sy.getRange(colBeh+rowBeh);
//enters the formula in the cell
rng.setFormula('=iferror(sum(filter(database!$E$2:$E,database!$D$2:$D='+ colBeh + '$1,database!$A$2:$A=lists!$G$2,database!$B$2:$B>=$A' + rowBeh + ',database!$B$2:$B<$A' + rowBeh2 + ')),"0")');
//captures the value generated by the formula
var val = rng.getValue();
//pastes the formula to the cell
rng.setValue(val);
//Job Done!
}
}

OnOpen or time trigger - move rows to another spreadsheet if 2 criteria met

I have very little experience, in fact near none so some of my terminology is just recently 'herd' not known.
I have two Spreadsheets with 1 sheet named the same in each.
Spreadsheet "Production" with sheet "Master" - Sheet ID is 1goG0TS1_2jwlGRetREYNRVk-Q6TEy3iWG_5VXFoZlus
Spreadsheet "Archived_Production" "Master" - Sheet ID is 1Mr4LJmp1SmpDs1i8U_PE5uLOEVjLc599Vq87m9HwCx0
With an onOpen script, or maybe timed trigger, set in my "Production" spreadsheet I need to MOVE all rows to my "Archived_Production" spreadsheet that meet 2 criteria
Criteria 1 - the Date in Column B is 7 days older than the current date
BUT ONLY IF, also
Criteria 2 - the entry in Column O is "100%"
In the included screenshot example, given today's date is 11/19/14, only the first entry highlighted in yellow would get moved upon next opening of the spreadsheet.
Thank you, as always, in advance for any help.
Sorry, not enough postings yet to allow image upload, but hopefully text explains.
You can use this function to perform the operation as you want on onOpen() script.
function onOpen() {
// I moved data from sheet1 to sheet2
var production_sheet_id = 'Put your production sheet's id here'; // This is your source sheet
var archived_sheet_id = 'Put your archived sheet's id here'; // This is your destination sheet
var sheet1 = SpreadsheetApp.openById(production_sheet_id).getSheets()[0];
var sheet2 = SpreadsheetApp.openById(archived_sheet_id).getSheets()[0];
var rows = sheet1.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i < numRows; i++) {
var oldDate = values[i][1]; // or var oldDate = new Date('values[i][1]')
var curDate = new Date();
// Count time difference in milliseconds, convert them into days then.
var diffInMilliSecs = (curDate.getTime() - oldDate.getTime());
var diffInDays = diffInMilliSecs/1000/60/60/24;
var diffInDays = Math.round(diffInDays);
if(values[i][14] == 1.0 && Math.abs(diffInDays+30) >= 7) // It evaluates 100% to 1.0
sheet2.appendRow(values[i]);
}
}