Google Spreadsheet Makro - Add one time value to another - google-apps-script

var valueDate = Utilities.formatDate(sheet.getRange("D2").getValue(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "dd/MM/YYYY hh:mm:ss");
var valueResetCD = Utilities.formatDate(sheet.getRange("C2").getValue(), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "hh:mm:ss");
This are my 2 lines I currently have - As the Spreadsheet timezone may be different from the makro timezone I got told to always get the timezone from the Sheet.
In those 2 values I have the right values safed (tried to print them into a field)
first is 19/09/2020 22:34:52 (a specific date) and the second is 03:00:00.000 (3 Hours)
Now what I want to do is just add those 2 times together so I get as output: 20/09/2020 01:34:52
I tried it with: sheet.getRange("Data!$A$21").setValue(valueDate + valueResetCD) but I guess it is not that easy - it just copies the 2 string values of the times into one line.
How can I add those 2 Time / Date values to eachother so I get the output above

I believe your situation and goal as follows.
In your situation, as a sample value,
valueDate is 19/09/2020 22:34:52 retrieved from sheet.getRange("D2").getValue().
valueResetCD is 03:00:00.000 retrieved from sheet.getRange("C2").getValue().
Both values are the date object.
You want to add valueResetCD to valueDate.
When the sample values are used, you want to get 20/09/2020 01:34:52 as the result value.
Modification points:
In this case, I think that valueDate is the correct date time like 19/09/2020 22:34:52 as the date object.
But, I think that valueResetCD is 1899/12/30 03:00:00. Because the Spreadsheet uses the serial number.
From this situation, it is required to convert the serial number to unix time.
And also, in this case, the time difference is required to be considered.
Both values are added by converting from the date object to the unix time. And, the result value is converted to the date object.
When above points are reflected to the Google Apps Script, it becomes as follows.
Modified script:
function myFunction() {
var sheetName = "Sheet1"; // Please set the sheet name.
// 1. Retrieve values from the cells as the date object.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var valueDate = sheet.getRange("D2").getValue(); // As a sample value, it's `19/09/2020 22:34:52`.
var valueResetCD = sheet.getRange("C2").getValue(); // As a sample value, it's `03:00:00.000`.
// 2. Convert the date object to the unix time.
var valueDateUnix = valueDate.getTime();
var differenceBetweenSerialAndUnix = new Date("1970/01/01 00:00:00").getTime() - new Date("1899/12/30 00:00:00").getTime();
var timeDifference = (new Date().getTimezoneOffset()) * 60 * 1000;
var valueResetCDUnix = valueResetCD.getTime() + differenceBetweenSerialAndUnix - timeDifference;
// 3. Add both time and convert it to the date object.
var resultDateObject = new Date(valueDateUnix + valueResetCDUnix);
var resultString = Utilities.formatDate(resultDateObject, SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "dd/MM/YYYY hh:mm:ss");
console.log(resultDateObject)
console.log(resultString) // You can see "20/09/2020 01:34:52" at the log.
// 4. Put the value to the cell.
sheet.getRange("A1").setValue(resultDateObject);
}
Note:
In this answer, it supposes that the values from the cells "C2" and "D2" are the date object. If those values are the string which is not the date object, above script cannot be used. So please be careful this.
References:
Date
getTime()
getTimezoneOffset()

Related

Convert a string to a date format using a script or a Google Sheets built-in formula

DESCRIPTION:
I want to convert a DD/MM/YYYY HH:mm or 25/01/2022 11:00 string, in an accepted date format.
Doesn't matter which one, it just has to be recognized by Apps Script and Google Sheets and be able to work with it.
If you can provide an Apps Script's code (not a formula in Google Sheets like I attempted to do) that converts the string into a date and then set the values in another range, to work with them as dates, I would be grateful, thanks.
If it's a Google Sheet formula no problem, as long as it works.
TRIED:
After many attempts, I tried to build a custom formula putting pieces together around the web but it doesn't function
//formula is translated from italian
=ARRAYFORMULA(IF(F10:F="",,TEXT(DATE(
IF.ERROR(REGEXEXTRACT(F10:F, "/(\d+) "), YEAR(F10:F))*1,
IF.ERROR(REGEXEXTRACT(F10:F, "/(\d+)"), MONTH(F10:F))*1,
IF.ERROR(REGEXEXTRACT(F10:F, "\d+"), DAY(F10:F))*1)+
IF.ERROR(TIME.VALUE(F10:F), REGEXEXTRACT(F10:F, "\d+:\d+")+
IF(REGEXMATCH(F10:F, "PM"), 0.5, 0)), "yyyy-mm-dd hh:mm")))
It gives a #VALUE error, which says "'11:00' is a string and can't be recognized as a date" (11:00 is an example).
I've also got the Regular Expression, but I don't know if it's correct and how to use it in code:
/([\d])\w+\/([\d])\w+\/([\d])\w+\s([\d])\w+\:([\d])\w+/g
I also tried changing the time zone but it didn't work.
Keep in mind I'm using the Italian time zone, if it's possible I'd rather keep it as it is.
Table example (like I said, what's important is that dates are accepted as dates):
F: Column source strings
Q: Column desired dates recognizable as dates by Sheets
(Q because it's the real column where I want to put the formula)
F
..
Q
16/02/2023 16:00
16/02/2023 16:00:00
25/11/2022 15:00
25/11/2022 15:00:00
For #Cooper and the solution based on the script.
I've customized the script, but it doesn't recognize the split function anymore (copy and paste of your function logs what it expects in Apps Script), and doesn't get any results in overwriting the existing string dates.
let dateStringed; //source wrong dates
var i = 0;
var flatArray;
function expired() {
//bLast is the range Last Row
dateStringed = gen.getRange(10, 6, bLast, 1).getValues();
flatArray = [].concat.apply([], dateStringed);
while (i <= bLast) {
i++;
convert();
};
Logger.log(flatArray);
gen.getRange(10, 6, bLast, 1).setValues(flatArray);
};
function convert(s=flatArray[i]) { //instead of "25/01/2022 11:00"
let [d,m,y,hr,mn] = s.split(/[\/ :]/)
Logger.log('y: %s m: %s d: %s hr: %s mn: %s',y,m,d,hr,mn);
Logger.log(new Date(y,m - 1,d,hr,mn).toLocaleString());
//don't know if it's correct, but it logs the dates
//in an easier syntax
};
For #doubleunary solution:
Demo SHEET ITA
In the sheet I copied and pasted the first column of my private original sheet, the F column with the text dates, and the Q10 cell I've pasted the formula as it is
I made sure to set local to Italy but to display english name formulas.
I don't know why, here it colors green and it doesn't give me a result.
But I did a test, and set the sheet tu US time and it functions. Any idea on how to make it function in Italian version?
Demo SHEET US
Solved: I used this script
function dateCorrected(){
gen.getRange('N10:N').clearContent();
//get the formula from another code sheet:
//'=arrayformula( SE.ERRORE( 1 / VALORE(
//regexreplace( to_text(F10:F);
//"(\d+)/(\d+)/(\d+) (\d+):(\d+)"; "$3-$2-$1 $4.$5" ) ) ^ -1 ) )'
var dateCorr = codeSheet.getRange('T1').getFormula();
Logger.log(dateCorr);
gen.getRange('N10').setFormula(dateCorr);
gen.getFilter().sort(14, false);
gen.getRange('N10:N').clearContent();
gen.getRange('N10').setFormula(dateCorr);
}
And this gives me the possibility to delete rows that meet a certain date condition. Thank you all for the support.
It is usually easiest to do the text string to datetime conversion using a spreadsheet formula. You can convert text strings like 25/01/2022 11:00 to dates with this formula in cell G10:
=arrayformula( iferror( 1 / value( regexreplace( to_text(F10:F); "(\d+)/(\d+)/(\d+) (\d+):(\d+)"; "$3-$2-$1 $4.$5" ) ) ^ -1 ) )
Format the result column as Format > Number > Date time.
In the event you need to "fix" those datetime values in place, you can replace the formula results with static values with Control+C to copy and Control+Shift+V to paste values only, or do the same with a simple range.setValues(range.getValues()) script bit.
In the event you need to pass those datetime values to Apps Script, it is usually easiest to get them as Date objects rather than text strings. The Date objects will refer to the same moment in time (in UTC) as the date times in the spreadsheet (in the spreadsheet's time zone).
You should note that Apps Script is JavaScript which means that Date objects are always in the UTC timezone. If you log them or output them in some other way, they will not be shown in the Italian timezone as you expect.
There are two easy ways to present such dates in a human-readable format in the spreadsheet's timezone. The first is to directly get the data as a text string in the format that it is shown in the spreadsheet:
function test1() {
const ss = SpreadsheetApp.getActive();
const dateStrings = ss.getRange('Sheet1!G10:G')
.getDisplayValues()
.flat()
.filter(String);
console.log(dateStrings);
}
The second is to get the data as Date objects and convert them to text strings using the spreadsheet's timezone, like this:
function test2() {
const ss = SpreadsheetApp.getActive();
const timezone = ss.getSpreadsheetTimeZone();
const dates = ss.getRange('Sheet1!G10:G')
.getValues()
.flat()
.filter(String)
.map(date =>
Object.prototype.toString.call(date) === '[object Date]'
? Utilities.formatDate(date, timezone, 'dd/MM/yyyy HH:mm')
: date
);
console.log(dates);
}
Convert String to Date:
function convert(s="25/01/2022 11:00") {
let [d,m,y,hr,mn] = s.split(/[\/ :]/)
Logger.log('y: %s m: %s d: %s hr: %s mn: %s',y,m,d,hr,mn);
Logger.log(new Date(y,m - 1,d,hr,mn));
}
Execution log
10:58:11 AM Notice Execution started
10:58:12 AM Info y: 2022 m: 01 d: 25 hr: 11 mn: 00
10:58:12 AM Info Tue Jan 25 11:00:00 GMT-07:00 2022
10:58:13 AM Notice Execution completed
To convert a string to a Date object in Google Apps Script use Utilities.parseDate.
Example:
function myFunction(){
const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const timeZone = spreadsheet.getSpreadsheetTimeZone();
const date = Utilities.parseDate('25/01/2022 11:00',timeZone, 'dd/MM/yyyy HH:mm');
return date;
}
Using the above as a custom function might not make sense for some use cases since the same result might be achieved by using built-in functions which are more efficient and less prone to have problems.
The options for using built-in functions depends on the spreadsheet settings, i.e. DATEVALUE might return different results for ambiguos dates like 25/01/2022 as for certain regions the month goes first and for others the day of the month goes first.
=DATEVALUE("25/01/2022") works correctly when the spreasheet region is set to Italy. You might have to manually set the cell formatting to date in order to make it show a date instead of the time serialized value (a number).
To convert 25/01/2022 11:00 using formulas in the above spreadsheet, use
=INDEX(SPLIT("25/01/2022 11:00";" ");1) + SUBSTITUTE(INDEX(SPLIT("25/01/2022 11:00";" ");2);":";".")
The above formula has two main parts joined by using +. The first part returns the time serialized value corresponding to the date, the second part returns the time serialized value corresponding to the time.
Array formula
=ArrayFormula(DATEVALUE(REGEXEXTRACT(F10:F;"^([ˆ\d/]+) "))+TIMEVALUE(SUBSTITUTE(REGEXEXTRACT(F10:F;" ([ˆ\d/:]+)$");":";".")))
The same concept as the previous formula, but instead of INDEX it uses REGEXEXTRACT.
Google has a Utility to do just that!
let dateTime = '2022-12-16 13:00:00';
let timeZone = 'GMT';
let convertedDateTime = Utilities.formatDate(dateTime, timeZone, 'dd/MM/yyyy HH:ss')
check out Class Utilities for more info.

APP-SCRIPT Condition to check if date entered is before today's date

var date = Utilities.formatDate(new Date(), "GMT-8", "m/dd/yyyy")
if (formS.getRange("B7").getValue() != " " && formS.getRange("B7").getValue() != date)
{
SpreadsheetApp.getUi().alert("Please Enter A Valid Date");
return
}
Trying to make the condition above check if the cell is not empty and that it does not contain a date prior to Today's Date
function myfunk() {
const ss = SpreadsheetApp.getActive();
const formS = ss.getSheetByName('formS');
const dtv = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate()).valueOf();
if (!formS.getRange("B7").isBlank() && new Date(formS.getRange("B7").getValue()).valueOf() < dtv) {
SpreadsheetApp.getUi().alert("Please Enter A Valid Date");
return;
}
}
Checking Dates in Apps Script
In general you can use the Date object as you would in normal JavaScript code. There are just one main thing to bear in mind if your script needs to be sensitive to timezones.
The timezone is defined in the manifest:
This cannot be changed dynamically. So if you need to be sensitive to them, then you will need to manage the offsets in your code.
Your script
This line:
var date = Utilities.formatDate(new Date(), "GMT-8", "m/dd/yyyy")
Returns a string. Not a date object, so you can't compare it to another date object, such as what is returned from a sheet value if it is formatted as a date.
You could use Regex or split to get the year and month and compare it that way, but then you may run into issue when you use the script on the 1st of January. This is because by simply comparing the year, month and date of 31/12/2021 with 01/01/2022, then your conditional statements would be a bit tricky. Possible, but maybe a bit hard to read.
Initializing to midnight
What follows is one approach to take to carry out this comparison in a relatively simple way.
It seems convenient to get a date object initialized to 00:00:00 of today. Then you can quickly compare the date using Unix time.
var now = new Date()
now.setHours(0)
now.setMinutes(0)
now.setSeconds(0)
now.setMilliseconds(0)
You can also do this in a more concise way like this:
var now = new Date()
now.setHours(0,0,0,0);
Then you can use the getTime() method on the date objects to get Unic time in milliseconds and compare them.
var dateToCheck = formS.getRange("B7").getValue()
if (
!(dateToCheck instanceof Date) || // If value is not instance of a Date object
dateToCheck.getTime() <= now.getTime() // If date is before 00:00:00 today.
) {
SpreadsheetApp.getUi().alert("Please Enter A Valid Date");
return
}
}
Which seems like a concise way to do the comparison you are looking for.
References
Apps Script Dates
JS Date object

Google Sheet API V4 - Google Apps script - Sheets.Spreadsheets.Values.update - append Date

I'm using Sheets.Spreadsheets.Values.update to paste values into a sheet (due to performance issues using range.setValues().
In the case of dates, the source values are date objects (which were pasted fine when I used range.setValues([[]]).
The end result now is though cells with the string version of the date, e.g., Mon Feb 28 00:00:00 GMT+01:00 2022 which do not correspond to a valid gsheet date. I've tried some of the options but cannot find a way for this to work.
I believe your goal as follows.
You want to put the values to Google Spreadsheet using Sheets API with Google Apps Script.
In your current issue, the values include the date object. When this date object is put using spreadsheets.values.update method, the value cannot be used as the date object. You want to resolve this issue.
In order to achieve your goal, how about putting the values by converting from the date object to others?
Pattern 1:
In this pattern, the date object is converted to the serial number and put to the Spreadsheet.
const spreadsheetId = "###"; // Please set the Spreadsheet ID.
const values = [[new Date(), "b1", "c1"], [new Date(), "b2", "c2"]]; // This is a sample value for replicating your issue.
// Here, the date values are converted to the serial number.
const convertedValues = values.map(r => r.map(c => c instanceof Date ? (c.getTime() / 1000 / 86400) + 25569 : c));
Sheets.Spreadsheets.Values.update({values: convertedValues}, spreadsheetId, "Sheet1", {valueInputOption: "USER_ENTERED"});
(c.getTime() / 1000 / 86400) + 25569 for converting from the unix time to the serial number was referred from this answer.
In this script, the date object is put as the serial number. So, please set the number format of the column "A" as the date time. By this, the serial number can be seen as the date time.
Pattern 2:
In this pattern, the date object is converted to the string value for parsing as the date object and put to the Spreadsheet.
const spreadsheetId = "###"; // Please set the Spreadsheet ID.
const values = [[new Date(), "b1", "c1"], [new Date(), "b2", "c2"]]; // This is a sample value for replicating your issue.
// Here, the date values are converted to the string value for parsing as the date object.
const convertedValues = values.map(r => r.map(c => c instanceof Date ? Utilities.formatDate(c, Session.getScriptTimeZone(), "yyyy/MM/dd HH:mm:ss") : c));
Sheets.Spreadsheets.Values.update({values: convertedValues}, spreadsheetId, "Sheet1", {valueInputOption: "USER_ENTERED"});
In this script, the date object is put as the string value for parsing as the date object by USER_ENTERED.
This sample uses yyyy/MM/dd HH:mm:ss as the date format. When this format cannot be parsed, please modify this for your situation.
References:
Method: spreadsheets.values.update
formatDate(date, timeZone, format)

Sending Emails a day before a specified date based on a list of dates

I have a table in google sheets with several columns. I need to send an email reminder a day before dates specified in one of the columns.
My idea of how it should work:
Take date column
Loop through each date and subtract 1 day from it to get the reminder date.
Filter through by comparing "today's" date with the reminder date
Send emails with all rows that meet the filter criteria
I have tried the following:
function StatusAlert() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const ws = ss.getSheetByName("On hold procedure");
const lr = ws.getLastRow();
var data = ws.getRange(2, 2, lr, 8).getDisplayValues();
var today = new Date();
var exclusiondate = ws.getRange(2,8,lr).getDisplayValues();
for (var i = 0; i < data.length; i++) {
var alertdate = new Date(exclusiondate[I] - 1)
}
I'm struggling with referencing the date column and then applying the date math to it. Also, I am not quite sure that I needed the second getDisplayValues call (I was trying to reference the date column I need specifically).
Because Even if I manage to apply the change to every row based off of the second getDisplayValues call, I won't be able to go onto the next step of filtering the entire table and sending the relevant rows via email.
So ideally I would like to be able to reference the specific date column from the entire range, and apply date math to it.
Does anyone have any idea how I can best proceed on this?
Regards,
Michael
Try this:
function StatusAlert() {
const ss=SpreadsheetApp.getActive();
const ws=ss.getSheetByName("On hold procedure");
var data=ws.getRange(2,2,ws.getLastRow()-1,8).getValues();
var today=new Date();//add this `valueOf()` if you want to compare with another date value
var xd=ws.getRange(2,8,ws.getLastRow()-1).getValues();
for (var i=0;i<data.length;i++) {
var dt=new Date(xd[i][0]);
var alertdate=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()-1);//subtract off one day
}
}
If you want to compare today and alertdate then I would use valueOf() for both of them and then they are just numbers.
I would refer you to this post. I would use getValues instead of getDisplayValues because it returns a more reliable value. For example, if you change the formatting of your date in the spreadsheet, it will break the code.
Lastly, JavaScript is case sensitive do not capitalize "I" in the last line of code.

Date format issue, google apps script

I have a column contain a date in "Sep -13" format.
When I access it from code, it gives integer value. How can I get a date object?
If I use "dd/mm/yyyy" format in sheet, it gives me date object.
function check()
{
var source = SpreadsheetApp.openById('sheet id');
var sourcesheet = source.getSheetByName('sheet name');
var tt = sourcesheet.getRange('F700').getValue();
debugger;
}
Result:
That cells original value might be an integer. That could be happen if you copy and paste values only for a date. so .getValue() will give you that number.
You can use that number to create a date object. JavaScript dates can be constructed by passing milliseconds
//base date 01/01/1970 integer value :25569
//excelDate your date in integer
var myDate = new Date((excelDate - 25569)*86400*1000);