Utilities.formatDate() in Google Apps Script outputs previous date (e.g. input 25.05.2015 -> output 24.05.2015) - date

I have a problem with Google Docs' Utilities.formatDate() function.
I have a spreadsheet that contains all of the orders we place in the lab. When an order is delivered our lab manager enters the delivery date in the relevant cell in such a spreadsheet, in the following format: dd.MM.yyyy.
I created a script that, provided certain conditions, will email whoever placed that order alerting them that the order has been delivered on that particular date. Here is the code:
function DeliveryAlerts() {
try {
var email_dict = {"Y":"Y#Z.com"}
var spreadsheet = SpreadsheetApp.openById("ABC");
SpreadsheetApp.setActiveSpreadsheet(spreadsheet);
var sheet = spreadsheet.getSheetByName("Orders");
var values = sheet.getRange("A2:Q251").getValues();
var bgcolours = sheet.getRange("A2:Q251").getBackgrounds();
for(var i=0;i<=249;i++)
{
var j = i + 2;
if (values[i][16]=="Yes" && values[i][11]!="" && bgcolours[i][16]!="#b8b8b8")
{
var email_address = email_dict[values[i][13]];
var cur_date = Utilities.formatDate(values[i][11], "GMT+1", "EEE dd.MM.yyyy");
var message = "Hello there,\n\nYour order of " + values[i][4] + " has been delivered on "+ cur_date +".\n\nBest wishes";
var subject = "Delivery alert";
MailApp.sendEmail(email_address, subject, message,{replyTo:"abc#abc.com", name:"ABC"});
sheet.getRange("Q"+j).setBackground("#b8b8b8");
}
}
} catch (err) {
MailApp.sendEmail("abc#abc.com", "Delivery Alerts Script in Order Master List", err);
}
}
I use
Utilities.formatDate(values[i][11], "GMT+1", "EEE dd.MM.yyyy") to reformat the date from, say, 25.05.2015 (that is, the value in the cell) to Mon 25.05.2015. However, what I get instead is Sun 24.05.2015.
Does anybody know what is going on?
Thank you in advance.
Nicola

Check the time zone setting in the script editor. Under the FILE menu, choose PROJECT PROPERTIES in the script editor. It's possible to have a different time zone setting in Apps Script, than is in the spreadsheet. This is a common issue that arises. Apps Script allows a separate time zone setting from the spreadsheet. Also, even if the time is only off by one minute, if the time setting of the date is all zeros, it's common to get the problem that you are having. When a user enters a date, it's possible that no time setting is made. So the time is set to all zeros. The date is correct, but the time is all zeros. Even if the date was typed in at 3 in the afternoon, for example, and the date is correct, the time setting can be midnight of that day. So, even if you subtracted one second from that date, it would now be the day before.

Related

How to convert Date and Time with mailmerge google slides from sheets as it is in cell

I have created a table with which I can record our check in times of our employees with the help of a generated Qr code in each line.The data in the table is generated as slides and converted into pdf. For this I use a script that I got to work with your help and it works. Here I would like to thank you especially #tanaike.
My problem is that the date and time are not copied to the slides to be generated as indicated in the cell but completely with Central European time and I added in the script to look in column if its empty to generate the slide. If it's not empty don't do anything. As I said everything is working except this two things.
I must confess I did not try to correct it somehow because I had already shot the script and I made some here despair. It would be really great if you write me the solutions and I can take them over. I will share the spreadsheet with you and the screenshot with ae and time. Thanks for your time and effort to help people like us; we are really trying.
As another approach, when I saw your question, I thought that if your Spreadsheet has the correct date values you expect, and in your script, you are retrieving the values using getValues, getValues is replaced with getDisplayValues(), it might be your expected result.
When I saw your provided sample Spreadsheet, I found your current script, when your script is modified, how about the following modification?
From:
var sheetContents = dataRange.getValues();
To:
sheetContents = dataRange.getDisplayValues();
Note:
When I saw your sample Spreadsheet, it seems that the column of the date has mixed values of both the string value and the date object. So, if you want to use the values as the date object using getValues, please be careful about this.
Reference:
getDisplayValues()
Added:
About your 2nd question of I mean that when a slide has been generated, the script saves the link from the slide in column D if the word YES is in column L. How do I make the script create the slide if there is JA in the column L and there is no link in column D. is a link in column D, the script should not generate a slide again. Thus, the script should only generate a slide if column D is empty and at the same time the word JA is in column L., when I proposed to modify from if (row[2] === "" && row[11] === "JA") { to if (row[3] == "" && ["JA", "YES"].includes(row[11])) {, you say as follows.
If ichanged as you descripted if (row[3] == "" && ["JA", "YES"].includes(row[11])) { i got this error. Syntax error: Unexpected token 'else' Line: 21 File: Code.gs
In this case, I'm worried that you might correctly reflect my proposed script. Because when I tested it, no error occurs. So, just in case, I add the modified script from your provided Spreadsheet as follows. Please test this.
Modified script:
function mailMergeSlidesFromSheets() {
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getDataRange();
sheetContents = dataRange.getDisplayValues(); // Modified
sheetContents.shift();
var updatedContents = [];
var check = 0;
sheetContents.forEach(function (row) {
if (row[3] == "" && ["JA", "YES"].includes(row[11])) { // Modified
check++;
var slides = createSlidesFromRow(row);
var slidesId = slides.getId();
var slidesUrl = `https://docs.google.com/presentation/d/${slidesId}/edit`;
updatedContents.push([slidesUrl]);
slides.saveAndClose();
var pdf = UrlFetchApp.fetch(`https://docs.google.com/feeds/download/presentations/Export?exportFormat=pdf&id=${slidesId}`, { headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() } }).getBlob().setName(slides.getName() + ".pdf");
DriveApp.getFolderById("1tRC505IWtTj8nnPB7XyydvTtCJmOb6Ek").createFile(pdf);
// Or DriveApp.getFolderById("###folderId###").createFile(pdf);
} else {
updatedContents.push([row[3]]);
}
});
if (check == 0) return;
sheet.getRange(2, 4, updatedContents.length).setValues(updatedContents);
}
function todaysDateAndTime() {
const dt = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM:dd:yyyy");
const tm = Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"HH:mm:ss");
Logger.log(dt);
Logger.log(tm);
}

How to send a conditional email based on the value of a cell from a form response?

I have a formula that calculates a number based on the response from a google form. Depending on what this number I want to send an email using details from the from as well as a pre typed email in another cell.
In Col1 is a time stamp, in col14 is an employee start date. My formula in Col33 works out how many days they have been employed at the time of submitting the form.
I want to send an email to the person if the number of days is less than 182.
I have an email pre typed out and can place this anywhere. At the moment I have it in all cells in col36. The email address will be in column32.
I have tried a number of different codes and none of them are sending the email no matter what the trigger I have set up is. I have very basic knowledge on apps script so my current code might be completely wrong, but it should show roughly what I'm getting at.
function sendEmail() {
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues()
for (i in values.length) {
var data = values[i][33];
var emailAddress = values[i][32];
var message = values[i][36];
if (data < 182); {
MailApp.sendEmail(emailAddress, "Flexible Working Request", message);
}
}
}
The current results have just been deleting the data in col33, Col34 & Col36 on the new form response row only.
Sorry if this question has been answered elsewhere, any other answer I found to similar issues I could not get to work.
I got someone who is much better at google apps script at work to give me a hand
It is to do with google forms pushing down formulas to the side
So we had to move the formula calculating the number of days to another sheet and then used this formula which worked
function sendEmailv2() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form responses
1');
var scrip = Session.getScriptTimeZone();
var date = sheet.getRange(sheet.getLastRow(),14).getValue();
var sub = sheet.getRange(sheet.getLastRow(),1).getValue();
Logger.log(date);
var fortmat = Utilities.formatDate(new Date(date), scrip, "dd/MM/yyyy");
var Subfortmat = Utilities.formatDate(new Date(sub), scrip, "dd/MM/yyyy");
var emailAddress = sheet.getRange(sheet.getLastRow(),32).getValue();
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet4');
var message = sheet2.getRange(1,1).getValue();
var days = sheet2.getRange(sheet2.getLastRow(),2).getValue();
if (days<182){
MailApp.sendEmail(emailAddress, "Flexible Working Request", message,{noReply:true});
}
}
Thanks!
You don’t need to go over all the columns to get a single cell value, so there is no need for a for loop. You can do it directly with:
var sheet = SpreadsheetApp.getActiveSheet().getSheets[0];
var cell = ["A33"];
var days_value = sheet.getRange(cell).getValue();
Then you can just make an if condition to send the email:
if (days_value < 182){
MailApp.sendEmail(emailAddress, "Flexible Working Request", message);
}
Hope this helps

Unable to set a javascript date object to a cell hidden by filter

I have a script which checks certain conditions to send reminders emails or sms to my clients. the only issues I'm finding is that if I try to write a cell that is hidden by a filter, the script executes but the data is not changed in any way.
I'll write a short version of the whole script:
function test(){
var nowTime = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var lastrow =sheet.getLastRow();
var lastcol =sheet.getLastColumn();
var fullData =cell.offset(0, 0,lastrow-
cell.getRow()+1,lastcol).getValues();
var cell = sheet.getRange("A2");
var i=0;
while (i<fullData.length){
var reminderType =0;
var row = fullData[i];
if (row[0] == 1) {sendreminder();cell.offset(i, 2).setValue(new Date());}
}
}
if for example the first column has hidden all the rows with 1 the script executes and sends all the reminders but ignore the setvalue(), if the rows are visible it works perfectly.
One solution can de to remove the filter but would be very annoying since we use the filter a lot and the script is triggered by time every 10 minutes so I would be working on the sheet and suddenly the filter get removed to run the script.
I have tried with cell.offset, getrange etc.. without any success ... Ideas?
EDIT: The problem seems to be only if I try to write a date if (row[0] == 1) {cell.offset(i, 1).setValue(new Date());}
For instance I'm writing another information (a number) in a different column and that cell gets updated.
The rest remain the same
here is a test sheet I created:
https://docs.google.com/spreadsheets/d/1-FNDGmvCc8nRFTG65Sj9L2RhGn8R3DtwR3llwBG5-FA/edit#gid=0
For now I added this code to save the state of the filter and reestablish it at the end. It's not really elegant and still add a lot of chances of something being messed up or the script failing but it's the less invasive solution I came out with. Does someone has a better one?
// at the beginning
if (sheet1.getFilter()){
var filterRange= sheet1.getFilter().getRange();
var filterSetting = [];
var i=0;
while (i<filterRange.getNumColumns()-1) {filterSetting[i]= sheet1.getFilter().getColumnFilterCriteria(i+1);sheet1.getFilter().removeColumnFilterCriteria(i+1);i++; }
}
// At the end
if (sheet1.getFilter()){
var i=0;
while (i<filterRange.getNumColumns()-1) {if (filterSetting[i]) sheet1.getFilter().setColumnFilterCriteria(i+1, filterSetting[i]);i++; }
}
Given the fact the problem is not writing in the hidden row as it was pointed out, the solution is to use the formatdate to format the value before writing it in the cell.
Not ideal either since regional formattings might prevent the script from working on different accounts but sure better solution than disabling the filters.
here is the code i needed to add:
var nowTime = new Date();
var timeZone = Session.getScriptTimeZone();
var nowTimeFormatted = Utilities.formatDate(nowTime, timeZone, 'MM/dd/yyyy HH:mm:ss');
any idea on how to improve this script or to solve the original problem without workarounds is appreciated :)

Getting a Google Form Script generated email to pipe data into Reponse Sheet

I am currently trying to setup an approval workflow. I'm fairly junior when it comes to some of this stuff. But so far have it working at respectable level to fit our needs with the assistance of an example.
I was using the template/example from Email Approval using Google Script and a Form.
The issue I am running into, with a lack of functionality is that at the time of Accept or Deny via the Email that is generated for approval I would like it to then record that data back into the Google Form row in the last column that the email was generated from.
I may end up adding 2nd timestamp, reply email etc later.
So then that row would show the initial form filled out and then either blank/accepted/deny as the status.
Thank you for any time or assistance.
The approval workflow that James Ferreira originally wrote was intentionally very basic. Enhancing it to provide correlation between approvals and the original requests is simple in concept, yet complicates the code because of details and limitations.
Things that need to considered:
Form responses include a timestamp, which we can use as a sort of "serial number" for responses, since it has a very high probability of being unique in an application like this.
We can pass this identifier along with the rest of the URL embedded in the approval email, which will give it back as a parameter to the webapp, in turn.
Passing an object like a timestamp between applications can be troublesome. To ensure that the URL embedded in the approval email works, we need to encode the string representation of the timestamp using encodeURIComponent(), then decode it when the webapp consumes it.
Searching for matches is simplified by use of ArrayLib.indexOf() from Romain Vaillard's 2D Array library. This function converts all array data to strings for comparisons, so it fits nicely with our timestamp.
Alternatively, we could put effort into creating some other identifier that would be "guaranteed" to be unique.
The approval-response web app (doGet()) does not understand what "activeSpreadsheet" means, even though it is a script contained within a spreadsheet. This means that we need a way to open the spreadsheet and record the approval result. To keep this example simple, I've assumed that we'll use a constant, and update the code for every deployment. (Ick!).
So here's the Better Expense Approval Workflow script.
// Utilizes 2D Array Library, https://sites.google.com/site/scriptsexamples/custom-methods/2d-arrays-library
// MOHgh9lncF2UxY-NXF58v3eVJ5jnXUK_T
function sendEmail(e) {
// Response columns: Timestamp Requester Email Item Cost
var email = e.namedValues["Requester Email"];
var item = e.namedValues["Item"];
var cost = e.namedValues["Cost"];
var timestamp = e.namedValues["Timestamp"];
var url = ScriptApp.getService().getUrl();
// Enhancement: include timestamp to coordinate response
var options = '?approval=%APPROVE%&timestamp=%TIMESTAMP%&reply=%EMAIL%'
.replace("%TIMESTAMP%",encodeURIComponent(e.namedValues["Timestamp"]))
.replace("%EMAIL%",e.namedValues["Requester Email"])
var approve = url+options.replace("%APPROVE%","Approved");
var reject = url+options.replace("%APPROVE%","Rejected");
var html = "<body>"+
"<h2>Please review</h2><br />"+
"Request from: " + email + "<br />"+
"For: "+item +", at a cost of: $" + cost + "<br /><br />"+
"Approve<br />"+
"Reject<br />"+
"</body>";
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),
"Approval Request",
"Requires html",
{htmlBody: html});
}
function doGet(e){
var answer = (e.parameter.approval === 'Approved') ? 'Buy it!' : 'Not this time, Keep saving';
var timestamp = e.parameter.timestamp;
MailApp.sendEmail(e.parameter.reply, "Purchase Request",
"Your manager said: "+ answer);
// Enhancement: store approval with request
var sheet = SpreadsheetApp.openById(###Sheet-Id###).getSheetByName("Form Responses");
var data = sheet.getDataRange().getValues();
var headers = data[0];
var approvalCol = headers.indexOf("Approval") + 1;
if (0 === approvalCol) throw new Error ("Must add Approval column.");
// Record approval or rejection in spreadsheet
var row = ArrayLib.indexOf(data, 0, timestamp);
if (row < 0) throw new Error ("Request not available."); // Throw error if request was not found
sheet.getRange(row+1, approvalCol).setValue(e.parameter.approval);
// Display response to approver
var app = UiApp.createApplication();
app.add(app.createHTML('<h2>An email was sent to '+ e.parameter.reply + ' saying: '+ answer + '</h2>'))
return app
}
So, I tried this, but there an issue with the formatting for date when it's in the data array. In order to get this to work, you have to reformat the time stamp in the doGet using formatDate.
I used the following to get it to match the formatting that appears in the array.
var newDate = Utilities.formatDate(new Date(timestamp) , "PST", "EEE MMM d yyyy HH:mm:ss 'GMT'Z '(PST)'");
Lastly, of course update the indexof parameter that is passed to newDate.

Validating two dates in acrobat javascripts

well the code I posted below all works perfectly except for a small detail. When I input today date in the field dateEntered, the later rejects it, it validates if the date entered is before todays date, validate if the date falls on a weekends, but it also show an error message when it is todays date. Actually the user should be able to enter Today or after date.
Anyone can tell me where am wrong, already tried every possible ways but still not working even the ( ==) or (===) or (<=) ..nothing
if (event.value!="")
{
var e = util.scand("ddd, dd.mmm.yy", event.value);
var a = (e.getTime()) < (new Date().getTime());
if (a) {
app.alert("The Date cannot be before Today's Date", 1);
event.rc = null;
}
if (e.getDay()==6 || e.getDay()==0) {
app.alert("Cannot take permission on a Weekend!", 2);
event.rc=null;
}
}
I found the solution to my problem, I had to set the hour to 0. Thank to the one who updated this on stackoverflow and sorry forget to retain your name.
if (event.value!="")
{
var e = util.scand("ddd, dd.mmm.yy", event.value);
var b=new Date();
b.setHours(0,0,0,0);
if (e<b) {
app.alert("ERROR: Date cannot be before"+" "+ new Date(b), 5);
event.rc = null;
}
if (e.getDay()==6 || e.getDay()==0) {
app.alert("ALERT: The date you entered ("+event.value+") falls on a WEEKEND!", 3);
event.rc=null;
}
}
This codes also contains a condition of removing one weekend from the dates since the number of leaves allowed to take ranges from 1 to 7 thus only one weekend is remove.