Date Math for Google Apps Script for User Submitted Dates - javascript

I created a Google Form to collect user input, including the expiration date of a contract.
I need to create a reminder date (6 months before the expiration date) in a new column of the gsheets that is linked to the form. Using the Event Object namedValues, I extracted the expiration date from gsheet. I converted the date to milliseconds and subtracted the number of milliseconds equal to 6 months (or thereabouts). However, the output that got sent back to the googlesheet is an undefined number.
I must be misunderstanding something and was hoping someone more skilled in this can help me out. Is the data type wrong? Thanks for any illumination you can provide.
function onFormSubmit(e) {
var responses = e.namedValues;
var MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
var expireDate = responses['Expiration Date'][0].trim();
var expireDate_ms = expireDate * 1000; // converting to milliseconds
var noticeDate = expireDate_ms - (183 * MILLIS_PER_DAY);
// Create a new column to store the date to send out notice of expiration or renewal
var sheet = SpreadsheetApp.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var column = e.values.length + 1;
sheet.getRange(row, column).setValue(noticeDate);
}

I’d recommend you to use a formula instead:
={"Reminder"; ARRAYFORMULA(IF(NOT(ISBLANK(B2:B)); EDATE(B2:B; -6); ""))}
Add this to the header of an empty column, it will generate all data in that column for you. Change B for the column you have the expiration date on.
If you really need to use Google Apps Script you can, but JavaScript is notorious for having bad date support (at least without an external library). To do it, you’ll have to manually parse the string, modify the date and format it back to the date number:
const dateParts = e.namedValues['Expiration Date'][0].trim().split('/')
const date = new Date(
Number(dateParts[2]), // Year
Number(dateParts[1]) - 1 - 6, // Month. -1 because January is 0 and -6 for the 6 months before
Number(dateParts[0]) // Day
)
const numericDate = Math.floor((date.getTime() - new Date(1900, 0, 1).getTime()) / (1000 * 60 * 60 * 24))
This example only works if the format used in the sheet is DD/MM/YYYY.
The numeral value of date is the number of days since the first day of 1900 (link to documentation). So we need to subtract it and change it from milliseconds to days. Math.floor ensures that it’s not decimal.
You can set numericDate to the cell but make sure the numeric format is Date.
References
EDATE - Docs Editors Help
ARRAYFORMULA - Docs Editors Help
Date - JavaScript (MDN)
DATE - Docs Editor Help

Marti's answer was helpful but the math didn't quite work out because the suggested solution was to minus 6 from the month retrieved. But the reminder date is supposed to be 6 months from the date (taking into account the year and the date), so it doesn't quite work.
The solution I worked out is the following:
function onFormSubmit(e) {
const expireDateParts = e.namedValues['Expiration Date'][0].trim();
if (expireDateParts != ""){
expireDateParts.split('/');
var MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
const expireDate = new Date(
Number(expireDateParts[2]), // Year
Number(expireDateParts[0]) - 1, // Month. -1 because January is '0'
Number(expireDateParts[1]) // Day
);
const reminderNumericDate = Math.floor((expireDate.getTime() - 183 * MILLIS_PER_DAY));
var reminderDate = formatDate(reminderNumericDate);
// Create a new column to store the date to send out notice of expiration or renewal
var sheet = SpreadsheetApp.getActiveSheet();
var row = sheet.getActiveRange().getRow();
var column = 14 // hard-coded to Column 0;
sheet.getRange(row, column).setValue(reminderDate);
// Set up Schedule Send Mail
createScheduleSendTrigger(reminderNumericDate);
var status = 'Scheduled';
sheet.getRange(row, column+1).setValue(status);
}
}

Related

Wrong time extraction in google spreadsheets? [duplicate]

This question already has answers here:
Formatted dates in a Google Apps script
(3 answers)
Closed 7 years ago.
I'm trying to retrieve the time value from google sheet cell via GAS. I put in the cell value (10:30) and get it with the following code (video)
function test_time_getting() {
var sheet, value, p;
sheet = SpreadsheetApp.getActive().getSheetByName('mockup data');
value = sheet.getRange(1,1).getValue();
Logger.log(value);
}
But unexpectedly I received next result in the log:
[15-09-27 05:07:43:784 PDT] Sat Dec 30 10:26:59 GMT+02:27 1899
How it could happen and what to do to retrieve proper time value?
Right time extraction makes by following code:
function getValueAsSeconds(range) {
var value = range.getValue();
// Get the date value in the spreadsheet's timezone.
var spreadsheetTimezone = range.getSheet().getParent().getSpreadsheetTimeZone();
var dateString = Utilities.formatDate(value, spreadsheetTimezone,
'EEE, d MMM yyyy HH:mm:ss');
var date = new Date(dateString);
// Initialize the date of the epoch.
var epoch = new Date('Dec 30, 1899 00:00:00');
// Calculate the number of milliseconds between the epoch and the value.
var diff = date.getTime() - epoch.getTime();
// Convert the milliseconds to seconds and return.
return Math.round(diff / 1000);
}
Thus you get the right quantity of seconds then you can calculate quantity of hours and minutes:
seconds = getValueAsSeconds(range);
hours = Math.floor(seconds/3600);
minutes = (seconds/60) % 60;
The answer was found here

Javascript / Google Apps Script for loop find match but continue

I'm definitely not an expert and write in google apps script for simple workflows. One to help with repetitive tasks and two to continually try and learn programming..
I'm looking for assistant is how to approach the following setup. I'm not sure if thinking a "for" loop and maybe "if / else" is the way to approach.
I'll be reading data from a spreadsheet, specifically 4 rows, 4 columns. One of the columns will have dates. I'm comparing those dates to the current date to see if the date is 7 days prior. If so do something but then continue looking over the dates to see if another day is also within seven days and if so also do something.
For example:
dates: 2/4/2014, 2/28/2014, 2/11/2014, 2/25/2014
today's date: 3/1/2014
I want to loop over the dates, see that 2/28/2014 is within seven days of today's date and send an email, then continue checking the dates, and see that 2/25/2014 is also within seven days and send another email.
I'm not looking for the answer I'm looking on how to approach this,
my thought (rough)
for (i=0, i<dates.length; i++) {
if (dates[i] <= (different between today's date and dates[i]) {
send an email;
continue looping;
} else if {
continue;
}
}
hopefully kinda of get the point. :)
EDIT: UPDATE**
Here is what I've come up with so far... Below is the code and spreadsheet values for the dates.
Dates in spreadsheet C1:C4 (2/5/2014, 2/15/2014, 2/28/2014, 3/2/2014)
function calculateDays() {
//Gets the dates of schedule
var ss = SpreadsheetApp.getActiveSheet();
var dataRange = ss.getRange("C1:C4");
var values = dataRange.getValues();
//Caculation for single day
var one_day = (1000 * 60 * 60 * 24);
//For loop to get the values from spreadsheet
for (i = 0; i < values.length; i++) {
//Puts dataRange values into date format
var scheduledDays = new Date(values[i]);
var today = new Date();
//grabs times from today and scheduled days in millseconds and subtracts
var difference = scheduledDays.getTime() - today.getTime();
//divides to get number of dates, rounds, and converts to positive integer
var diffDay = Math.abs(Math.floor(difference / one_day));
Logger.log(diffDay);
}
What i'm unsure about next is how to loop over the variable diffDays to determine if the days is seven days prior to today's date. I know one is.. Could be i'm just tired but thoughts would be helpful.
I tried
for (j = 0; j < diffDays; j++) {
Logger.Log(diffDays[j]);
}
As a starting point to see what was logged but it showed blank... That's why i fell stuck.
UPDATED Based on Serge reply - New issue
Here is the updated code using Serge's suggested code. Adjusted a bit to help understand values that are being returned to me in the logger. Also adjust adding 7 days to today date to get future date for comparison. (see below code for additional comments)
function myFunction() {
var data = SpreadsheetApp.getActiveSheet().getRange("C1:C4").getValues();
var week = (1000*60*60*24*7);
Logger.log('a week is ' + week);
var todayDaysTime = new Date().setHours(0,0,0,0);
Logger.log('this is today= ' +todayDaysTime);
var weekPlusSevenDays = week + todayDaysTime;
Logger.log('this is todayDaysTime + a week ' +weekPlusSevenDays);
for (i=0 ; i<data.length; i++) {
Logger.log('Value of date in cell = '+data[i][0].getTime());
if (data[i][0].getTime() == weekPlusSevenDays) {
//send an email;
Logger.log('mail sent');
}
}
}
I've updated the dates in the spreadsheet column C to be 3/9/2014, 3/10/2014, 3/11/2014, and 3/12/2014. So what i would expect is that is the code would run, see that value 3/11/2014 in the spreadsheet is equal to todaysDaysTime + week. I'm trying to take todays date and add seven days to it and see if it matches a date in the spreadsheet. So far its not matching. Here is a copy of the logger.log.
[14-03-04 09:55:09:770 PST] a week is 604800000
[14-03-04 09:55:09:770 PST] this is today= 1393920000000
[14-03-04 09:55:09:770 PST] this is todayDaysTime + a week 1394524800000
[14-03-04 09:55:09:770 PST] Value of date in cell = 1394352000000
[14-03-04 09:55:09:770 PST] Value of date in cell = 1394434800000
[14-03-04 09:55:09:770 PST] Value of date in cell = 1394521200000
[14-03-04 09:55:09:771 PST] Value of date in cell = 1394607600000
I'm not sure why its not matching to future date properly. I'm looking to have an exact match so that when the day in the spreadsheet matches today's date + a week (7 days), it sends an email.
Any thoughts on why its not matching?
JavaScript date is a representation of the number of milliseconds counted from January the first 1970. Knowing that, it is very simple to get a value of 7 days before : you just have to subtract from today's value the right number of milliseconds, in your example 7*24*3600*1000.
To remember that and to check it yourself just type this simple statement in the script editor and run any other function then look at the logger :
Logger.log('reference JS date = '+new Date(0));// new Date(0) is the reference date for Javascript date objects.
Then a simple comparison inside the loop will do everything you need. You don't even have to define an else statement since the loop will continue by default.
The other important point is that the data you read from a spreadsheet is a 2D array, i.e. a matrix of matrix so you will have to use 2 indexes to get the right data. In the example snippet below I assumed dates were in column 1 so I used index [0] for the second index, the one that selects the column equivalent.
function myFunction() {
var data = SpreadsheetApp.getActiveSheet().getRange(1,1,4,4).getValues();
var sevenDayBeforeToday = new Date().setHours(0,0,0,0)-(7*24*3600*1000);// value in milliseconds, parenthesis are not necessary but make the code more readable imho...
Logger.log('sevenDayBeforeToday = '+sevenDayBeforeToday);
for (i=0 ; i<data.length; i++) {
Logger.log('Value of date in cell = '+data[i][0].getTime());
if (data[i][0].getTime() <= sevenDayBeforeToday) {
//send an email;
Logger.log('mail sent');
}
}
}
EDIT : here is a version that uses string comparison for easier debug and same result.
(note that I checked on a test SS the code above and it worked as expected when timeZone settings are set the same as spreadsheet, see logger below :)
function myFunction() {
var data = SpreadsheetApp.getActiveSheet().getRange(1,1,4,4).getValues();
var sevenDayAfterToday = new Date().setHours(0,0,0,0)+(7*24*3600*1000);// value in milliseconds, parenthesis are not necessary but make the code more readable imho...
Logger.log('sevenDayAfterToday = '+Utilities.formatDate(new Date(sevenDayAfterToday),Session.getScriptTimeZone(),'MM-dd-yyyy'));
for (i=0 ; i<data.length; i++) {
Logger.log('Value of date in cell = '+Utilities.formatDate(new Date(data[i][0]),Session.getScriptTimeZone(),'MM-dd-yyyy'));
if (Utilities.formatDate(new Date(data[i][0]),Session.getScriptTimeZone(),'MM-dd-yyyy') == Utilities.formatDate(new Date(sevenDayAfterToday),Session.getScriptTimeZone(),'MM-dd-yyyy')) {
//send an email;
Logger.log('mail sent');
}
}
}

Java script - invoice due date miscalculation

Apologies in advance for my (something less than) basic knowledge of java script.
Can someone explain in basic – but specific – terms how this script is calculating?
Here is the script:
var d = new Date(year, month, day);
var e = new Date(d.getTime() + offset * 24 * 60 * 60 * 1000);
var month_out = e.getUTCMonth();
var day_out = e.getDate();
var year_out = e.getUTCFullYear();
date_out = month_out + "-" + day_out + "-" + year_out;
year = 2013
month = 12
day = 01
offset = 15
The offset is the “payment terms” and the date is the invoice date. The output is supposed to be the invoice date plus the offset to arrive at “12-16-2013”
That is NOT what it is kicking out and I need to figure out how to fix it.
One customer is having dates show up as: 0-16-2014
I don’t know which string is wrong or how it should read in its place. This is one piece of an entire function that pulls an invoice date from an XML file, strips the time from the date and creates a new "Date_Due" using the offset. This script is the only area that is failing.
Can anyone help?
getUTCMonth() returns 0 based months - 0 is January, 1 is February, etc.
Add 1 to this value to make it more human readable.
See docs for more info.

calculate age javascript in adobe acrobat

I am having some difficulties getting a field to populate in an interactive PDF form. I am using a javascript to calculate the current age of client from 2 date fields (DateToday and ClientDOB) already in the form and I need it to populate a "ClientAge" field. The DateToday field automatically populates when the form is opened. I would like for the ClientAge field to populate after the user selects the ClientDOB.
This is what I am trying to have it do. Should be simple I would think.
DateToday - ClientDOB = ClientAge
Here is my code:
var DateToday_ = Date2Num(DateToday.formattedValue, "MM/DD/YYYY")
var ClientDOB_ = Date2Num(ClientDOB.formattedValue, "MM/DD/YYYY")
var diff = DateToday_ - ClientDOB_
ClientAge.value = Floor(diff / 365.25)
I am not sure why the ClientAge field will not populate once the ClientDOB has been selected. Any replies would be helpful. Thanks.
This was taken from somewhere off the 'net. Can' remember where. However I have used this in a number of forms and it works fine. The idea is that the difference between dates is in milliseconds, and a given date is the number of seconds from a fixed date in the past. Once you have the difference in seconds between the dates (in this case DOB to the present) you can calculate how many years that is. Note that my format is in British date format (dd/mm/yy). If you operate in American format (mm/dd/yy) you must make the appropriate changes.
// get current date THIS NON AMERCAN DATE FORMAT
var oNow = new Date();
// get date from 'Demo.DOB' field
var oMyDate = util.scand('dd/mm/yy', this.getField('Demo.DOB').value);
// define second in milliseconds
var nSec = 1000;
// define minute in milliseconds
var nMin = 60 * nSec;
// define hour in milliseconds
var nHr = 60 * nMin;
// define day in milliseconds
var nDay = 24 * nHr;
// compute today as number of days from epoch date
var nNowDays = Number(oNow) / nDay;
// truncate to whole days
nNowDays = Math.floor(nNowDays);
// compute inputted date days from epoch data
var nMyDateDays = Number(oMyDate) / nDay;
// truncate to whole days
nMyDateDays = Math.floor(nMyDateDays);
// compute difference in the number of days
var nDiffDays = nNowDays - nMyDateDays;
// adjust difference for counting starting day as 1
++nDiffDays;
// convert days to years
var nYears = nDiffDays / 365.2525
// truncate to whole years
nYears = Math.floor(nYears);
// set field value number of years (nYears)
event.value = nYears;

Need to create a html page for adding X days to D.O.B

I'm looking for a HTML code that adds a fixed number of days to an entered Date of Birth. Thanks a lot! Even a partial code is much appreciated!
Here is how I'd do it. The function daysFromDate returns a Date object you can use. Instead of passing the DOB as a string you can also update it to use a Date object.
/*
days is the number of days to add, and dob a string like "4/24/2011"
*/
function daysFromDate(days, dob) {
var dates = dob.split('/');
var daysToMilli = days * 60 * 60 * 24 * 1000;
var d = new Date(parseInt(dates[2]), parseInt(dates[0])-1, parseInt(dates[1]));
var newTime = d.getTime() + daysToMilli;
return new Date(newTime);
}
To get the date 20 days from now you call: daysFromDate(20, "4/24/2011");
VBScript has DateAdd() so it's pretty simple. If you prefer JavaScript, just Google "javascript dateAdd" -- there are a zillion solutions out there. Here's one:
http://www.solutionbot.com/2008/06/20/javascript-dateadd-function/

Categories

Resources