How to add hours from 2 cells? - javascript

I have some google spreadsheet logbook where I store duration of some activities in hours format [[HH]:MM:SS]. The spreadsheet adds such cells with no issues. However when I try to add them via Google Script I get some garbage. What I found is that Date() object is implicitly created for such cells, but I cannot find API of that value type.
I know I can convert the data to "hour integers" by multiplying them by 24 but that is a nasty workaround as it demands duplication of many cells. I would rather like a solution that will allow to do that in google script itself.

here is a working function that does the trick.
I first tried to format it as a date but 36 hours is not really standard !! so I did a little bit of math :-) )
To get it working you should set a cell somewhere with value 00:00:00 that we will use as a reference date in spreadsheet standard. in my code it is cell D1(see comment in code, reference date in SS is in 1900 and in Javascript is in 1970 ... that's why it is a negative constant of 70 years in milliseconds...)
here is the code and below a screen capture of the test sheet + the logger
It would be a good idea to modify this code to make it a function that takes cell value as parameter and returns the result as an array for example ([h,m,s] or something similar), this code is only to show how it works.
function addHoursValues() {
var sh = SpreadsheetApp.getActive()
var hours1 = sh.getRange('A1').getValue();
var hours2 = sh.getRange('B1').getValue();
var ref = sh.getRange('D1').getValue().getTime();
//var ref = -2209161600000 // you could also use this but it would be less obvious what it really does ;-)
Logger.log(ref+' = ref');
var h1 = parseInt((hours1.getTime()/3600000)-ref/3600000);
var h2 = parseInt((hours2.getTime()/3600000)-ref/3600000);
Logger.log(h1+' + '+h2+' = '+(h1+h2))
var m1 = parseInt((hours1.getTime()-h1*3600000-ref)/60000);
var m2 = parseInt((hours2.getTime()-h2*3600000-ref)/60000);
Logger.log(m1+' + '+m2+' = '+(m1+m2))
var s1 = parseInt((hours1.getTime()-h1*3600000-m1*60000-ref)/1000);
var s2 = parseInt((hours2.getTime()-h2*3600000-m2*60000-ref)/1000);
Logger.log(s1+' + '+s2+' = '+(s1+s2))
var ts=s1+s2
var tm=m1+m2
var th=h1+h2
if(ts>59){ts=ts-60;tm++};
if(tm>59){tm=tm-60;th++}
Logger.log('sum = '+th+':'+tm+':'+ts)
}
EDIT : here are 2 "function" versions with corresponding test functions that show how to use it
function getHMS(hrs) {
var t = hrs.getTime()/1000;
var ref = -2209161600;
var h = parseInt((t-ref)/3600);
var m = parseInt((t-h*3600-ref)/60);
var s = parseInt(t-h*3600-m*60-ref);
return[h,m,s];// returns an array of 3 discrete values
}
function testHMS(){
var sh = SpreadsheetApp.getActive();
var hours1 = sh.getRange('A1').getValue();
var hours2 = sh.getRange('B1').getValue();
var sumS = getHMS(hours1)[2]+getHMS(hours2)[2];// add seconds
var sumM = getHMS(hours1)[1]+getHMS(hours2)[1];// add minutes
var sumH = getHMS(hours1)[0]+getHMS(hours2)[0];// add hours
if(sumS>59){sumS=sumS-60 ; sumM++}; // handles values >59
if(sumM>59){sumM=sumM-60 ; sumH++}; // handles values >59
Logger.log(sumH+':'+sumM+':'+sumS);
}
OR
function addHMS(hrs1,hrs2) {
var t1 = hrs1.getTime()/1000;
var t2 = hrs2.getTime()/1000;
var ref = -2209161600;
var h = parseInt((t1-ref)/3600)+parseInt((t2-ref)/3600);
var m = parseInt((t1-parseInt((t1-ref)/3600)*3600-ref)/60)+parseInt((t2-parseInt((t2-ref)/3600)*3600-ref)/60);
var s = parseInt(t1-parseInt((t1-ref)/3600)*3600-parseInt((t1-parseInt((t1-ref)/3600)*3600-ref)/60)*60-ref)
+parseInt(t2-parseInt((t2-ref)/3600)*3600-parseInt((t2-parseInt((t2-ref)/3600)*3600-ref)/60)*60-ref);
if(s>59){s=s-60 ; m++}; // handles values >59
if(m>59){m=m-60 ; h++}; // handles values >59
return[h,m,s];// returns sum in an array of 3 discrete values
}
function othertestHMS(){
var sh = SpreadsheetApp.getActive();
var hours1 = sh.getRange('A1').getValue();
var hours2 = sh.getRange('B1').getValue();
Logger.log(addHMS(hours1,hours2));
}

Related

Can I get name of cell by range in app script google sheet

I have code to calculate two cells by formula this is my code :
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("sheet29");
var maxR = sh.getMaxRows();
var first = sh.getRange(maxR,6,1);
var second = sh.getRange(maxR,7,1);
var calculate = sh.getRange(maxR,8,1);
first.setValue("2");
second.setValue("2");
var q = "F6"; // I want to change it to some thing like var q = first...;
var qq = "G6"; // I want to change it to some thing like var q = second...;
var formula= ("=sum("+q+"+"+qq+")");
calculate.setValue(formula);
}
This code is fine calculate by formula by this code
var formula= ("=sum("+q+"+"+qq+")");
calculate.setValue(formula);
what I need, I don't know what is the name of cells to calculate, this dynamic. Any way to find the name of cell by range or another method
You can get the A1 notation of a range by using range.getA1Notation() method and use it to set formula. Change your code
From:
var q = "F6";
var qq = "G6";
To:
var q = first.getA1Notation();
var qq = second.getA1Notation();
Reference:
Range.getA1Notation()

Increment ID from last row google apps script

I coded something for google apps script. It is to increment ID +1 based on the last row. Everything is working so far except for the numbering of the new ID, instead of appearing as a number.
The result appears as R-NaN instead of R-002 or something similar
What do you think should I revise in my code? Thank you.
function new_item() {
// Get current spreadsheet
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var mysheet = ss.getActiveSheet();
// Set date to today in update field at the top of the sheet
var now = new Date();
mysheet.getRange(1,4).setValue(now);
// Last non-empty row
var rlast = mysheet.getLastRow();
Logger.log("Last row = " + rlast);
// Insert Row below
mysheet.insertRows(rlast+1);
var r = rlast+1;
// Copy format from row above
var sourcerange = mysheet.getRange(rlast + ":" + rlast);
var targetrange = mysheet.getRange(r + ":" + r);
sourcerange.copyTo(targetrange, {formatOnly:true});
// Col. 2 : Risk identity
var riskid = mysheet.getRange(rlast,2).getValue();
if (riskid.length > 3){
// Extract number ex. 3
var riskidnb = riskid.substring(1,riskid.length);
// Increase risk number +1
riskidnb++
// Convert to string "0004"
var s = "000" + riskidnb
// Write risk nb i.e. "R004"
mysheet.getRange(r,2).setValue("R-"+ s.substring(s.length-4))
}
``ยด
Explanation / Issue:
Your code really depends on the value of the cell in column B last row:
var riskid = mysheet.getRange(rlast,2).getValue();
There are two scenarios but I believe the second applies to your issue:
If the value in the cell is a number (e.g. 35233) then riskid will be an integer and therefore riskid.length will return null and as a result the if condition will evaluate to false. In this case, you can either use getDisplayValue or toString() instead to get the number as string and then you can apply .length to it:
var riskid = mysheet.getRange(rlast,2).getValue();
If the value in the cell is a string (e.g. R112) then the if condition will evaluate to true. If you do that:
var riskidnb = riskid.substring(1,riskid.length);
riskidnb will be 112 but this is still a string and therefore if you do riskidnb++ you will get NAN like the issue you have right now. In order to fix that, convert riskidnb to integer:
var riskidnb = parseInt(riskid.substring(1,riskid.length));
then you can do riskidnb++ and finally convert it back to string:
var s = "000" + riskidnb.toString();
Solution:
function new_item() {
// Get current spreadsheet
var app = SpreadsheetApp;
var ss = app.getActiveSpreadsheet();
var mysheet = ss.getActiveSheet();
// Set date to today in update field at the top of the sheet
var now = new Date();
mysheet.getRange(1,4).setValue(now);
// Last non-empty row
var rlast = mysheet.getLastRow();
Logger.log("Last row = " + rlast);
// Insert Row below
mysheet.insertRows(rlast+1);
var r = rlast+1;
// Copy format from row above
var sourcerange = mysheet.getRange(rlast + ":" + rlast);
var targetrange = mysheet.getRange(r + ":" + r);
sourcerange.copyTo(targetrange, {formatOnly:true});
// Col. 2 : Risk identity
var riskid = mysheet.getRange(rlast,2).getValue();
if (riskid.length > 3){
// Extract number ex. 3
var riskidnb = parseInt(riskid.substring(1,riskid.length));
// Increase risk number +1
riskidnb++
// Convert to string "0004"
var s = "000" + riskidnb.toString();
// Write risk nb i.e. "R004"
mysheet.getRange(r,2).setValue("R-"+ s.substring(s.length-4))
}
}
Output:

Google-App-Script Conditional Loop is running but is not behaving as expected (java script)

I am pulling data from a google sheets which looks like this:
Now, I want to generate google slides for ONLY rows where the Timestamp column is between Last Deadline and Next Deadline. In the example, it would pull the record in A2:B2 as the Timestamp is between these two dates. I added this logic to my script but when I run it, it does not generate the slide, i.e. it does behave as expected but neither do I get an error. What could it be?
function generateSlides_master()
{
var dataSpreadsheetUrl = "https://docs.google.com/spreadsheets/d/1hhf";
var ss = SpreadsheetApp.openByUrl(dataSpreadsheetUrl);
var deck = SlidesApp.getActivePresentation();
var sheet = ss.getSheetByName('Form_Responses');
var values = sheet.getRange('A2:N20000').getValues();
var slides = deck.getSlides();
var templateSlide = slides[1];
var last_deadline = sheet.getRange('P4:P4').getValues();
var next_deadline = sheet.getRange('P2:P2').getValues();
values.forEach(function(page){ //for each row in google sheets
if(page[0]){
if (page[0] > last_deadline && Work_Week<= next_deadline ){ //THIS IS NOT WORKING AS EXPECTED!
var Work_Week = Utilities.formatDate(page[0], "GMT", "MM/dd/yyyy");
var Email = page[1];
templateSlide.duplicate(); //duplicate the template page
slides = deck.getSlides(); //update the slides array for indexes and length
newSlide = slides[2]; // declare the new page to update
var shapes = (newSlide.getShapes());
shapes.forEach(function(shape){
shape.getText().replaceAllText('{{Email}}',Email);
});
presLength = slides.length;
newSlide.move(presLength);
}
}// end our conditional statement
}); //close our loop of values
//Remove the template slide
//templateSlide.remove();
}
You've defined last_deadline and next_deadline to be 2-dimensional arrays, so your if-statement isn't actually checking against the dates. Use getValue() instead to get the individual values.
var last_deadline = sheet.getRange('P4').getValue();
var next_deadline = sheet.getRange('P2').getValue();
I also think you meant page[0] <= next_deadline, instead of comparing against Work_Week.
Here's a heavily edited example that will simply log the timestamp rather than creating a slide.
function generateSlides_master() {
var dataSpreadsheetUrl = "https://docs.google.com/spreadsheets/d/1hhf";
var ss = SpreadsheetApp.openByUrl(dataSpreadsheetUrl);
var sheet = ss.getSheetByName('Form_Responses');
var values = sheet.getRange('A2:N20000').getValues();
var last_deadline = sheet.getRange('P4').getValue();
var next_deadline = sheet.getRange('P2').getValue();
values.forEach(function(page) { //for each row in google sheets
var timestamp = page[0];
if (timestamp) {
if (timestamp > last_deadline && timestamp<= next_deadline) {
Logger.log('Create slide ' + timestamp);
}
}
});
}

IF Function - Google Scripts - multiple criteria

I'm trying to run an IF function to match the date in the first column to "last month" and the date in the last column to "newest date" and copy and paste all of the rows matching this criteria (excluding the first and last column) to the bottom of the list.
This is the script I'm running and it isn't finding any matches when I know for a fact there are at least 100 rows matching this criteria:
function myFunction() {
var MCS = SpreadsheetApp.openById('[ID REMOVED FOR THIS Q]');
var MRB = MCS.getSheetByName('Media Rates Back');
var MRBrange = MRB.getRange(1,1,MRB.getLastRow(),1).getValues();
var dest = MRBrange.filter(String).length + 1;
var LM = new Date();
LM.setDate(1);
LM.setMonth(LM.getMonth()-1);
var LMs = Date.parse(LM);
var Datenew = MRB.getRange(MRB.getLastRow(),MRB.getLastColumn()).getValue();
var Datecol = MRB.getRange(1,6,MRB.getLastRow(),1).getValues();
var Datenews = Date.parse(Datenew);
for(var i=0; i<MRBrange.length; i++) {
if(Date.parse(MRBrange[i])==LMs && Date.parse(Datecol[i])==Datenews ) {
var NewRange = MRB.getRange(i,2,(MRB.getLastRow()-i),5);
var NewRangeV = NewRange.getValues();
var destination = MRB.getRange(MRB.getLastRow()+1,2);
Logger.log(NewRange);
NewRange.copyTo(destination);
}else{
Logger.log(i);
}
}}
Any help would be appreciated!
Rather than get the columns as separate ranges, I would get the entire range as one array, then loop over that and check the two columns.
I'm also assuming your values are formatted as dates in the Sheet, in which case you don't need to use Date.parse(), and that your actual date logic is correct.
You can try using the debugger and set a breakpoint at the IF, so you can check the values it is comparing. or put a Logger.log call to list your comparisons.
var last_month_column = 1;
var newest_date_column = MRB.getLastColumn();
var MRBrange = MRB.getRange(1,1,MRB.getLastRow(),newest_date_column).getValues();
for(var row in MRBrange) {
if(MRBrange[row][last_month_column]==LMs && Datecol[row][newest_date_column] ==Datenews ) {
/* your copy logic here */
}else{
Logger.log(i);
}
}
I think the problem may be that MRBrange is a 2d Array. So I used another loop to convert it to a 1d array.
function myFunction() {
var MCS = SpreadsheetApp.openById('[ID REMOVED FOR THIS Q]');
var MRB = MCS.getSheetByName('Media Rates Back');
var MRBrangeA = MRB.getRange(1,1,MRB.getLastRow(),1).getValues();//2d array
var MRBrange=[];
for(var i=0;i<MRBrangeA.length;i++)
{
MRBrange.push(MRBrangA[i][0]);//1d array
}
var dest = MRBrange.filter(String).length + 1;
var LM = new Date();//current day
LM.setDate(1);//first day of month
LM.setMonth(LM.getMonth()-1);//first day of last month
var LMs = Date.parse(LM);
var Datenew = MRB.getRange(MRB.getLastRow(),MRB.getLastColumn()).getValue();
var Datecol = MRB.getRange(1,6,MRB.getLastRow(),1).getValues();
var Datenews = Date.parse(Datenew);
for(var i=0; i<MRBrange.length; i++) {
if(Date.parse(MRBrange[i])==LMs && Date.parse(Datecol[i])==Datenews ) {
var NewRange = MRB.getRange(i,2,(MRB.getLastRow()-i),5);
var NewRangeV = NewRange.getValues();
var destination = MRB.getRange(MRB.getLastRow()+1,2);
Logger.log(NewRange);
NewRange.copyTo(destination);
}else{
Logger.log(i);
}
}}

How to convert UTC date format provided by SalesForce Api and convert it to local Date and Time format using Google Apps Script

I have looked at various solutions posted i.e. parsing, substrings and splitting and none of them either produce a value or the required value.
The format received via Salesforce API is "2014-08-19T02:26:00.000+0000"
Essentially I would like a custom function that can be used within Google Sheets to convert this date/time format and take daylight saving into consideration
Thank you beforehand
I use a simple function like below :
function parseDate(string) {
var parts = string.split('T');
parts[0] = parts[0].replace(/-/g, '/');
var t = parts[1].split(':');
var refStr = new Date(new Date(parts[0])).toString();// use this to get TZ for daylight savings
var fus = Number(refStr.substr(refStr.indexOf('GMT')+4,2));
return new Date(new Date(parts[0]).setHours(+t[0]+fus,+t[1],0));
}
firstly thank you for everyone's input. By using a combination of the info provided by RobG and Serge insas I revised the script and created one that suited my needs. Please see below, any further advice would be welcome.
/*
The script first has all variables declared.
As the script runs inconjunction with an API query running off single trigger for defined sequential functions where the previous parsed date records are cleared and then re-parsed and runs with loop function for a whole column of data within specified range
*/
function parseDate() {
var source_spreadsheet = SpreadsheetApp.openById("Sheet_Id");
SpreadsheetApp.setActiveSpreadsheet(source_spreadsheet);
var sheet = source_spreadsheet.getSheetByName("Sheet_Tab");
var startRow = 2;
var numRows = 4500;
var startCol = 1;
var numCols = 7;
var dataRange = sheet.getRange(startRow, startCol, numRows, numCols)
sheet.getRange(startRow, startCol + 1, numRows, numCols - 1).clear({contentsOnly: true});
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var SFConnectDate = row[0];
var DConnected = row[1];
var SFCutoverDate = row[2];
var DInUse = row[3];
var Lat = row[5];
var Long = row[6];
if (SFConnectDate != "" && DConnected == "" && Lat != "" && Long != "") {
var parts = SFConnectDate.split('T');
parts[0] = parts[0].replace(/-/g, '/');
var Fdd = parts[0].split('/');
var AllTime = parts[1].split('.');
var Ftt = AllTime[0].split(':');
var D = new Date(Fdd[0],(Fdd[1]-1),Fdd[2] ,Ftt[0],Ftt[1],Ftt[2]);
var TZ = (D.getTimezoneOffset())/60;
var DConnected = new Date(Fdd[0],(Fdd[1]-1),Fdd[2],(Ftt[0]-TZ),Ftt[1],Ftt[2]);
sheet.getRange(startRow + i, 2).setValue(DConnected);
}
}
}

Categories