"Row Last Modified" Script Produces Wrong Month - google-apps-script

This code has been working well for me, but today (when I modified a cell) I noticed that the script is producing a date that is one month behind the actual date (i.e., today is 6/5/2013, but the script produced 5/5/2013). Can anyone see what might be causing this problem?
// * based on the script "insert last modified date" by blisterpeanuts#gmail.com *
// Update cell with the the last modified time of any (single) cell in that row, excluding row 1 and column 1
function onEdit() {
var d = new Date();
// format date nicely
var month_str = d.getMonth();
var day_str = d.getUTCDate();
var year_str = d.getYear().toString().substring(2);
// create the formatted time and date strings
var date_str = month_str + '/' + day_str + '/' + year_str;
// create the message (change this to whatever wording you prefer)
// note also that rather than all the above, you could just use d.toString()
// I didn't because I didn't want it printing the timezone.
var s = date_str;
var active_range = SpreadsheetApp.getActiveRange();
if (active_range.getHeight() == 1 && active_range.getWidth() == 1 && active_range.getRow != 1 && active_range.getColumn() != 1) {
var update_row = active_range.getRow().toString();
var update_cell = "AF" + update_row;
SpreadsheetApp.getActiveSheet().getRange(update_cell).setValue(s);
}
}

I don't think this has ever worked correctly for you. The documentation for Date.getMonth() says:
The value returned by getMonth is an integer between 0 and 11. 0
corresponds to January, 1 to February, and so on.
You need to increment the month by one.
var month_str = d.getMonth() + 1;
(Also the variable name month_str is misleading, it isn't a string, getMonth() returns an integer)

Related

day and month are swapped?

I have a column 'C' containing dates and in that some cell values are having values like " Due on Date". I have written app script code that if a cell value contains "Due on" it will be copied to another column,else copied to different column.But on running I found that cells having "due on " on running the date and month are interchanged. for eg: if a cell contains "Due on 08/02/2022(dd/MM/yyyy)" is changed to "02/08/2022(MM/dd/yyyy)". Is there any method to retain the same date format.I have already done the date format methods in the spreadsheet and maintained the same time zone .
Here is the sample code:
for(var i=value.length-1;i>=0;i--){
var chn = value[i];
if(chn[2]!="NA"){
// var rdate= new Date(chn[2]);
var dat=Utilities.formatDate(new Date(chn[2]), "GMT+5:30", "dd-MMMM-yyyy");
var mat= chn[2].toString();
if(mat.match(/Due on/)){
var d1= mat.replace("Due on", "");
var ds = new Date(dat);
var year = ds.getFullYear();
var month = ds.getDate();
var day = ds.getMonth();
Logger.log(chn[2]);
Logger.log(dat);
Logger.log(ds);
Logger.log(month);
// var pubdate = new Date(year, day,month);
// Logger.log(pubdate);
ss.getRange("C"+(i+2)).setValue("Valid till "+Utilities.formatDate(ds, "GMT+5:30", "dd-MMMM-yyyy"));
}
else{
.................
}
}
A copy of the spreadsheet and the executions log is attached here:
Execution log:
You code works for me correctly, however here are some thoughts for troubleshooting
You do not specify into which sheet you want to write (I assume ss is SpreadsheetApp.getActiveSpreadsheet()). This is dangerous when your spreadsheet has several sheets. It's best to sue the method getSheetByName()
Reduce your code snippet to something simpler to reduce potential error sources
Change you spreadsheet locale for changing the date formatting
Since your date is concatenated to a string (and the method formatDate() returns a date anyway), the output should not be affected by any locales and date formatting, however to be sure, try to set it explicitly to a string.
Make sure you pass to new Date() a valid date object or date string.
This code snippet works for me regardless of the spreadsheet locale and the number formatting of the cells:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
for(var i = 10; i >= 0; i--){
var dat=Utilities.formatDate(new Date("2022-05-15"), "GMT+5:30", "dd-MMMM-yyyy");
ss.getRange("C"+(i+2)).setValue(("Due on " + dat).toString());
}
}
UPDATE:
If the problem is the format of the original date string, you need to convert it to the necessary format for new Date(). To do so, you need to know the formatting of the original date string.
For a date string with the format ddmmyyyy , you can do it as following:
var chn = [];
chn[2] = "15052022";
var day = Number(chn[2].substring(0,2));
var month = Number(chn[2].substring(2,4));
var year = Number(chn[2].substring(4,8));
console.log("day: " + day)
console.log("month: " + month)
console.log("year: " + year)
var dat= Utilities.formatDate(new Date(year, month - 1, day), "GMT+5:30", "dd-MMMM-yyyy");
ss.getRange("C2").setValue(("Due on " + dat).toString());

How to use Google App Script to locate unique names and add up their valued numbers

So I used an importGoogleCalendar code to pull my workers' names and hours from Google Calendar to Google Sheets. However, I pulled EVEYRTHING. The names are duplicated a numerous amount of times in multiple rows for each day with their corresponding hours. How can I get one unique name for each worker along with their added up hours for each time their name appears on the sheet to another sheet so that it looks cleaner and easier to look at?
Example: I would like
Name
Hours
Sam
5
Sam
7
Bob
3
Sam
5
Sam
7
Bob
6
Joe
4
To look like
Name
Hours
Sam
24
Bob
9
Joe
4
Here is the code:
function importGoogleCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
var calendarId = sheet.getRange('B1').getValue().toString();
var calendar = CalendarApp.getCalendarById(calendarId);
// Set filters
var startDate = sheet.getRange('B2').getValue();
var endDate = sheet.getRange('B3').getValue();
var searchText = '';
// Print header
var header = [["Title", "Description", "Start", "End", "Duration"]];
var range = sheet.getRange("A6:E6");
range.setValues(header);
range.setFontWeight("bold")
// Get events based on filters
var events = (searchText == '') ? calendar.getEvents(startDate, endDate) : calendar.getEvents(startDate, endDate, {search: searchText});
// Display events
for (var i=0; i<events.length; i++) {
var row = i+7;
var details = [[events[i].getTitle(), events[i].getDescription(), events[i].getStartTime(), events[i].getEndTime(), '']];
range = sheet.getRange(row,1,1,5);
range.setValues(details);
// Format the Start and End columns
var cell = sheet.getRange(row, 3);
cell.setNumberFormat('mm/dd/yyyy hh:mm');
cell = sheet.getRange(row, 4);
cell.setNumberFormat('mm/dd/yyyy hh:mm');
// Fill the Duration column
cell = sheet.getRange(row, 5);
cell.setFormula('=(HOUR(D' + row + ')+(MINUTE(D' +row+ ')/60))-(HOUR(C' +row+ ')+(MINUTE(C' +row+ ')/60))');
cell.setNumberFormat('0.00');
}
}
I am willing to make another function if need be
Thank You and Stay Safe
I like the approach of using a pivot table (since they are perfect for handling such data). You get totals, and other features for free.
But if you only want to write summary data to your spreadsheet, you can use the following approach:
My starting point follows on from this line in your existing script:
// Get events based on filters
var events = (searchText == '') ? calendar.getEvents(startDate, endDate) : calendar.getEvents(startDate, endDate, {search: searchText});
From there I pass your events array to a new function:
function summarize(events) {
var totalsByName = new Map();
events.forEach((event) => {
let name = event.getTitle();
// duration in seconds (from milliseconds / 1000):
let duration = Math.abs(event.getEndTime() - event.getStartTime()) / 1000.0;
if (totalsByName.has(name)) {
// increment the existing duration for this person:
totalsByName.set(name, totalsByName.get(name) + duration);
} else {
// add the first entry for this person:
totalsByName.set(name, duration);
}
} );
// iterate over each entry in the map:
for (let [name, duration] of totalsByName) {
console.log(name + ' = ' + (duration / 3600.0));
}
}
The function populates a map of results - one entry per person's name.
In my example, all I do is print the data to the console.
console.log(name + ' = ' + (duration / 3600.0)); // convert seconds to hours
But you can instead adapt all your existing code to write this data to the spreadsheet instead, using my name and duration values.
You can apply additional logic to sort by names, if you wish, and round the numeric data to a preferred number of decimal places.

Send email from G-Sheets after 4 weeks

I am using Google Sheets, and I'm trying to send an email to myself, after 4 weeks of a specified date, with information in the body of that email extracted from a certain cell. The cell is in the same row (Col B) that the date is in (Col Q). I have successfully figured out the timing issue, but I just can't get the info from the cell to go into the body of the email.
Here is the code I have.
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getRange(9,17,31).getValues();
var now = Date.now();
for (var i = 0; i < data.length; i++) {
var row = data[i];
var date = new Date(row[0]);
var remind_date = new Date(date.getTime() + 28 * 24 * 60 * 60 * 1000);
var diff = now - remind_date;
var case_name = row[2];
if ((diff >= 0) && (diff < 24 * 60 * 60 * 1000)) {
GmailApp.sendEmail('myemail#me.com', 'Case Inquiry Reminder','Reminder, inquire about status of ', case_name);
}
}
}
First part
On the question it's mentioned that the date is on column Q but the code is getting column A instead.
var date = new Date(row[0]);
Maybe the correct code is
var date = new Date(row[16]);
While some Spreadsheet Service methods use 1 based index, JavaScript arrays use zero based index. Because of this, the index of column Q is 16.
Second part
For the same reason mentioned previously regarding JavaScript indexes, as you said that you need the value of column B, instead of
var case_name = row[2];
the code should be
var case_name = row[1];
Reference
Array
Third part
There is a problem on the following line
GmailApp.sendEmail('myemail#me.com', 'Case Inquiry Reminder','Reminder, inquire about status of ', case_name);
It uses four arguments but the the fourth isn't of the proper type. Maybe you intend to write
GmailApp.sendEmail('myemail#me.com', 'Case Inquiry Reminder','Reminder, inquire about status of ' + case_name);
(replace the last comma , by a plus sign +)
Reference
sendEmail(recipient, subject, body, options)

Cannot find function getMonth in object 33463

I have created a script to send emails to a specific people with Birthday Reminder. This use to work till day before yesterday. I don't know why am I getting this error that Cannot find function getMonth, Can anyone tell where is the mistake
function emailAlert() {
// Short circuit if email notice is set to "No". This causes an error and the script stops.
if (turnOnEmailNotice.toLowerCase() == "no")
{
Logger.log("The Email Notification is NOT turned ON. System will Exit.");
exit
}
//Get the total number of filled row in the sheet.
var currentRowAT = 2;
var currentCellValueAT = "start";
while (currentCellValueAT != ""){
if (currentCellValueAT = birthdaysSheet.getRange("G" + currentRowAT).getValue() != ""){
currentRowAT = currentRowAT +1;
}
}
var birthdaysSheetLastRow = currentRowAT - 1;
// Get today's Date (with Month, Date and Year)
var today = new Date();
var todayMth = today.getMonth()+1;
var todayDate = today.getDate();
var todayYear = today.getFullYear();
// Check sheet cell for match to alertDate, k is the current row number being checked. Starting at 2 as the row #1 is for the headers
for (k=2; k < birthdaysSheetLastRow + 1; k++)
{
var targetBday = new Date();
targetBday = birthdaysSheet.getRange("P" + k).getValue();
// If Birthday is not speicified, continue with the next row
if (targetBday == ""){continue};
var unadjTargetBday = new Date();
var unadjTargetBdayMth = targetBday.getMonth()+1;
var unadjTargetBdayDate = targetBday.getDate();
var unadjTargetBdayYear = targetBday.getFullYear();
var unadjTargetBday = targetBday;
targetBday.setDate(targetBday.getDate()-daysInAdvance); // Calculating how many days in advance you want to trigger the notification. This is set in Settings Tab.
var targetBdayMth = targetBday.getMonth()+1;
var targetBdayDate = targetBday.getDate();
if (targetBdayMth + " " + targetBdayDate == todayMth + " " + todayDate)
{
var targetBirthDateYearsOld = (today.getYear() - unadjTargetBday.getYear())-1900;
prepareAndSendEmail(k, targetBirthDateYearsOld);
}
}
}
getValue will return the type string/date/number depending on the cell type in the spreadsheet (see menu Format -> Number). To be sure, just always convert to Date Type. This is the right way to convert it:
var targetBday = new Date(birthdaysSheet.getRange("P" + k).getValue());
Short answer
Check that the cell value is a valid date for Google Sheet.
Explanation
From https://developers.google.com/apps-script/reference/spreadsheet/range#getvalue (emphasis mine)
getValue()
Returns the value of the top-left cell in the range. The value may be
of type Number, Boolean, Date, or String depending on the value of the
cell. Empty cells will return an empty string.
To avoid this kind of problems, you may include some sort of data validation. You could use build-in features like conditional formatting, data validation, or something like a on edit script together with try / catch or a redundant validation in your emailAlert script.

Update 2nd column based on access from first column

I need to put the current date & time (on an individual row basis) in column K of my google docs spreadsheet, if either of column I or J is changed. This is my first attempt at using google scripts.
I've located a function that will do the time (and I'm hoping I can figure out how to do the date), but I don't know how to check for changes in cells, and update other cells. So for example if row 10 column I or J changes, row 10 column K should get the current date & time.
function myFunction() {
var d = new Date();
var timeStamp = d.getTime(); // Number of ms since Jan 1, 1970
// OR:
var currentTime = d.toLocaleTimeString(); // "12:35 PM", for instance
}
You can give this a shot - it uses string formatting to put the current date in the proper format (you can adjust as you need). One important thing to note: regardless of what you do here, the value will be displayed according to whatever formatting you have specified in the spreadsheet itself. Therefore if you don't do anything, the result is going to look like the traditional datetime representation. If you want it to appear in the format you specify, you can do two things: 1.) convert the entire column to text using the spreadsheet's Format menu; or 2.) (my choice) prefix the value you write with a ', which will essentially turn it into a string (that is what is done in the code below). Hope this helps.
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
// This was "MySheet" for me - change to whichever sheet you want to test
if( s.getName() == "MySheet" ) {
var r = s.getActiveCell();
// Check if the column is either I or J
var currColumn = r.getColumn();
if( currColumn == 9 || currColumn == 10) {
// If so, set dateCell = the current row at column J
var dateCell = s.getRange(r.getRow(), 11);
// Format the current date into datetime format (adjust to display how you want)
var dateTime = Utilities.formatDate(new Date(), "GMT-8", "MMM-dd-yyyy h:mm a");
// Set the cell
dateCell.setValue("'" + dateTime);
}
}
};