Doing Math with Date Values - google-apps-script

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

Related

The parameters (String,number) don't match the method signature for getRange()

I am trying to write a script to fetch data from Google Analytics. I wrote this using help from lots of sites and youtube videos but unable to fix 1 issue in my code.
function myFunction() {
var app = SpreadsheetApp
var doc = app.getActiveSpreadsheet().getActiveSheet();
for(var i=0;i<107;i++){
var viewId = doc.getRange(i+2,1).getValue(); // Your Google Analytics view ID
var startDate = Utilities.formatDate(doc.getRange("K1").getValue(), "GMT+2", "yyyy-MM-dd");
var endDate = Utilities.formatDate(doc.getRange("K2").getValue(), "GMT+2", "yyyy-MM-dd");;
var metric = 'ga:sessions, ga:newUsers, ga:bounceRate';
var option = {'segment': 'gaid::reM9CWAgR0ys4_Ng4N_aVw'};
var result = Analytics.Data.Ga.get(viewId, startDate, endDate, metric, option); // Make the request
var sessions = result.totalsForAllResults['ga:sessions'];
var newusers = result.totalsForAllResults['ga:newUsers'];
var bouncerate = result.totalsForAllResults['ga:bounceRate'];
var doc = app.getActiveSpreadsheet(); // Current document
var sheet = doc.getActiveSheet(); // Current sheet
sheet.getRange(i+2,2,1,1).setValue(sessions); // Write total sessions
sheet.getRange(i+2,3,1,1).setValue(newusers); // Write toal newusers
sheet.getRange(i+2,4,1,1).setValue(bouncerate); // Write total bouncerate
}
}
This Got auto resolved once I changed the View ID in my sheet. I guess the problem was with view ID
The error occurs because the script does not find the startDate or endDate in K1 and/or K2 cell or because there are less then 107 view ids and by looping it finds an empty cell in the first column (or do not start from the second cell).
It doesn't depend on Google Analytics.

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

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!
}
}

Google spreadsheet / docs , jump to current date cell on Open

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

How to limit the available Date ranges with the appsScript Datebox only Last one Month dates Should be available to select

How to limit the available Date ranges with the appsScript Datebox only Last one Month dates Should be available to select eg : If Today is 25/08/2014 then User can select date between the the range 25/07 - 25/08. i am using UI Service
It is unfortunately not possible to select the available range of a dateBox nor to check date values using client handler in UiApp, you will have to use a server handler function to get that functionality.
Below is an example and the corresponding code.
note that I didn't limit the highest date value but that will be easy to implement using one more condition on today's date in the handler function + adapting the Label warning message, I preferred keeping the example as readable as possible.
test online
function doGet() {
var app = UiApp.createApplication();
var p = app.createVerticalPanel().setStyleAttribute('padding','50px');
var oneMonthAgo = new Date(new Date().getTime()-31*86400000);
p.add(app.createLabel('Choose a date (later than '+Utilities.formatDate(oneMonthAgo,Session.getScriptTimeZone(),'MMM dd, yyyy ')+')'));
var dateHandler = app.createServerHandler('checkTime').addCallbackElement(p);
p.add(app.createDateBox().setName('date').setId('date').setValue(new Date()).addValueChangeHandler(dateHandler));
var btnHandler = app.createServerHandler('checkTime').addCallbackElement(p);
var btn = app.createButton('submit',btnHandler).setEnabled(false).setId('btn');
p.add(btn);
p.add(app.createLabel().setId('resp').setStyleAttribute('paddingTop','30px'));
app.add(p);
return app;
}
function checkTime(e){
var app = UiApp.getActiveApplication();
var date = e.parameter.date;
var oneMonthAgo = new Date().getTime()-31*86400000;
if(date.getTime()<oneMonthAgo){
app.getElementById('resp').setText('date must less than one month ago...');
return app;
}
if(e.parameter.source =='date'){
app.getElementById('resp').setText('date accepted with value '+date.toLocaleString());
app.getElementById('btn').setEnabled(true);
}else{
app.getElementById('resp').setText('request accepted.. continue the current job');
}
return app;
}
Instead of .createDateBox() try putting only the valid dates in a list box...
function doGet() {
var app = UiApp.createApplication();
var listBox = app.createListBox(false).setId('date').setName('date');
var today = new Date();
for ( var i=0; i < 29; i++ ) {
var date = Utilities.formatDate(new Date(today.getTime() - i * 86400000), Session.getScriptTimeZone(), "dd-MM-yyyy");
listBox.addItem(date)
}
app.add(listBox);
return app;
}