Dates manipulation between Spreadsheet and Arrays - javascript

I'm trying to make a function to look over a list of days (a year school calendar), and, compare with a date from a user prompt and, until the date (all the days "lower" than the user date) set in the B column a string (in this case "Summer holidays", only if there's not another value in the B column corresponding to cell.
What I have:
What I expect, if I set Sept 12th in the input:
function setTrimesters() {
var sheet =
SpreadsheetApp.getActive().getSheetByName("calendar2017");
// Start of 1st trimester
var input = ui.prompt("Set first day of trimester (DD/MM)");
var value = input.getResponseText();
var allStartEndTrimesters = [valorInici1rTri]
// Get dataRange
var dataRange = sheet.getRange('A1:B'+sheet.getLastRow());
// Get dataRange values
var data = dataRange.getDisplayValues();
for (var i = 0 ; i < data.length ; i++) {
if (data[i][0] < value) {
if (data[i][1] == '') {
data[i][1] = "Summer holidays";
}
}
}
dataRange.setValues(data);
}
The script is working only with the day value of the date. Then, in October, from 1st to 11th the script assign too the value "Summer holidays".
I don't know how to get day and month values before comparing. I've tried to setNumberFormat to miliseconds, or days (similar to 42895 or so)... but there are some limitations with SpreadsheetApp and App Scripts working with dates.
Thanks in advance for helping

The problem is that you work with dates as strings, so they get compared in lexicographic order. With day being first, 4/9 precedes 5/7, which is not what you wanted. I suggest to
Use getValues instead of getDisplayValues. It will retrieve JavaScript date object instead of a string. Then the comparison < works correctly, but you also need the beginning date to be a Date object: see below.
Do not overwrite input data in column A. Separate input and output ranges.
Here is an example, with user-interface part removed:
function testSummer() {
var sheet = SpreadsheetApp.getActiveSheet();
var userEnteredDate = "26/07"; // what you get from user
var dateParts = userEnteredDate.split("/");
var beginning = new Date();
beginning.setMonth(dateParts[1] - 1, dateParts[0]);
beginning.setHours(0, 0, 0, 0); // so it's 0 hour of the day entered, in the current year
var inputData = sheet.getRange('A1:A'+sheet.getLastRow()).getValues();
var outputRange = sheet.getRange('B1:B'+sheet.getLastRow());
var outputData = outputRange.getValues();
for (var i = 0; i < inputData.length; i++) {
if (inputData[i][0] < beginning && outputData[i][0] == "") {
outputData[i][0] = "summer vacation";
}
}
outputRange.setValues(outputData); // not overwriting input
}

Related

Sorting sheets by name when the names are dates

I have a google spreadsheet where some of the sheets names are text and others are dates in "dd/mm/yyyy/" format. I need a function that can put the sheet called "Tablero" first, then sort the sheets named with dates descendingly, and leave at the end the rest of the sheets.
This is my code so far:
function testOrdenar() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetsCount = ss.getNumSheets();
var sheets = ss.getSheets();
// I need the sheet "Tablero" to go first
var tablero = ss.getSheetByName("Tablero");
ss.setActiveSheet(tablero);
ss.moveActiveSheet(1);
var names = []; // This is where the sheets named with dates will go
var j = 1; // I use this as a counter, but it is not absolutly necessary
for (var i = 0; i<sheetsCount; i++){
var sheetName = sheets[i].getName();
var pattern = /(\d{2})\/(\d{2})\/(\d{4})/;
var dt = new Date(sheetName.replace(pattern,'$3-$2-$1'));
if (dt instanceof Date && !isNaN(dt.valueOf())) {names[j] = dt; j++;} // This is to distinguish the sheets that are dates
}
// Here I sort the sheets descendingly
for (var m = 1;m<j;m++) {
for (var n = 1;n<j;n++) {
if (names[n] < names[m]) {
var aux = names[n];
names[n] = names[m];
names[m] = aux;
}
}
}
var pos = 2;
for (var a = 0;a<j;a++) {
var sheetName = Utilities.formatDate(new Date(names[a]), "GMT-3", "dd/MM/yyyy");
Logger.log(sheetName);
var sheet = ss.getSheetByName(sheetName);
ss.setActiveSheet(sheet);
ss.moveActiveSheet(pos);
pos++;
}
The sorting is correct, but I don't know why each date ends up being a day less. I tried adding one to de variable but it comes out as "Invalid Object". And I also need those dates as strings because that is how i can then call the sheets.
My questions:
1) How can I get the correct dates? (not a day before each one). Could it have something to do with the timezone? I'm in "GMT-3".
(If the answer is adding one, please tell me how because I tried that and comes back as an error.)
2) How can I get the sorted dates as strings in "dd/MM/yyyy" format?
Here are the screenshots of my sheets and the logs I get:
Cause:
As written in the documentation, Date.parse(timestring), where timestring is a date only iso8601 compatible string, returns a date in UTC. When you format the date in GMT-3, the date is offset 3 hours to the previous day.
Solution:
Use GMT as timezone argument of Utilities.formatDate
Alternatively, You can avoid converting to date at all and sort them as plain strings.
Sample script:
function testOrdenar1() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheets = ss.getSheets();
const pattern = /(?:Tablero)|(\d{2})\/(\d{2})\/(\d{4})/;
const map = sheets.reduce( //Create a map of sheet object : sheetName(as yyyyMMdd)
(m, sheet) =>
m.set(
sheet,
sheet
.getName()
.replace(pattern, (m, p1, p2, p3) =>
m === 'Tablero' ? '99999999' : p3 + p2 + p1//make Tablero highest 8 digit number string
)
),
new Map()
);
sheets.sort((a, b) => map.get(b) - map.get(a));//sort sheet objects by their name
let pos = 1;
sheets.forEach(sh => {
ss.setActiveSheet(sh);
ss.moveActiveSheet(pos++);
});
}

Converting timestamps to dates in Google sheet script

I am writing a code that targets a column with dates and I would like to get the dates and compare it to the current date so that I can get the difference between the two.
I ran into a problem I am having trouble solving. It seems that when I used .getValues in my range, it placed each timestamp in an array then those arrays in another array. Like this: [[(new Date(1539619200000))], [(new Date(1540396800000))], [(new Date(1540828800000))]]
I would like to place all the values in 1 array only so I can start solving how to convert the timestamps in to normal dates. I am also a beginner with this so I am sorry if this seems like an basic question.
function datesincolumn() //collects only the date values in range
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow(); //will identify last row with any data
entered
var range = sheet.getRange('D2:D' + lastRow); //will get range of cells
with data present
var dates = range.getValues();//gets all values in the range
var CurrentDate = new Date();
var timestamps = [];
for (i = 0; i <= dates.length; i++)
{
if (dates[i] >= i)
{
timestamps.push(dates[i]);
}
}
Logger.log(dates);
}
When you use .getValues(), you will always get a 2-dimensional array. The outer array represents the row, and the inner array represents the columns. Since you're only getting one column, you can simply append [0] to your dates[i].
function datesincolumn() { //collects only the date values in range
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var lastRow = sheet.getLastRow(); //will identify last row with any data entered
var range = sheet.getRange('D2:D' + lastRow); //will get range of cells with data present
var dates = range.getValues();//gets all values in the range
var CurrentDate = new Date();
var timestamps = [];
for (var i = 0; i <= dates.length; i++) {
var date = dates[i][0];
if (date >= i)
timestamps.push(date);
}
Logger.log(dates);
}

Delete cells in range if less than current date

I have a range of dates in plain text format (must stay this way) and I need to clear them at 1am if the date in each range cell is less than the current date. I can set up the run schedule later, but can anyone tell me why this isn't working?
I'm using =TEXT(TODAY(),"m/dd") to insert the current date in the correct format in cell AE3.
Thank you!
function clearOldDate()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Loads");
var cells = sheet.getRange('AC9:AE33').getValues();
var data = [cells];
var date = sheet.getRange('AE3').getValues();
//var Sdate = Utilities.formatDate(date,'GMT+0900','MM/dd');
for(i=0; i<76; i++)
{
if (date > data[i])
{
data[i].clear();
};
};
};
This should do the trick
function clearOldDate()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("LogSheet");
//getValues gives a 2D array.
var data = sheet.getRange('AC9:AE33').getValues();
var date = sheet.getRange('AE3').getValues();
//var Sdate = Utilities.formatDate(date,'GMT+0900','MM/dd');
//This for loop with loop through each row
for(i=0; i<data.length; i++)
{
//This for loop with loop through each column
for (var j = 0; j < data[i].length ; j ++){
//This assumes Column AC has the dates you are comparing aganist
if (date > data[i][j])
{
//sets that the Row with index i to empty
data[i] = ["","",""]
};
}
};
//finally set the value back into sheet
sheet.getRange('AC9:AE33').setValues(data)
};
Basically, you need to setValues once data array is modified.
Note: This Statement (date > data[i][0]) doesn't work as expected when the values are stored as text.
Edit: Modified the code and added a new for loop to go through each column, based on comments

Google Sheets - Delete Expired Rows Based On Date

I'm currently trying to make a script or literally anything that will be able to delete a row after the given date in Column C.
The site is a giveaway site so I need the rows/entries to delete themselves once the date specified on Column C is passed.
Eg: If one giveaway had an expiration date # 20/13/2016, once the date reaches this date of 20/13/2016 it will delete the row. I am following the metric system of dd/mm/yy as a note.
I saw a question similar to this at Google Sheets - Script to delete date expired rows but the code won't work for my needs.
Here is the code that was used in the other question.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Foglio1");
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
var currentDate = new Date();
var oneweekago = new Date();
oneweekago.setDate(currentDate.getDate() - 7);
for (i=lastrow;i>=2;i--) {
var tempdate = values[i-1][2];// arrays are 0 indexed so row1 = values[0] and col3 = [2]
if(tempdate < oneweekago)
{
sheet.deleteRow(i);
}
}
}
If you could change it to work for my above needs it will be greatly appreciated!
Assuming your dates are in column C as stated, this should do it. The adjustment is just to the date to which we compare and to handle missing dates. I am also messing with the case on some names for readability.
function DeleteOldEntries() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Live Events");//assumes Live Events is the name of the sheet
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
var currentDate = new Date();//today
for (i=lastrow;i>=3;i--) {
var tempDate = values[i-1][2];// arrays are 0 indexed so row1 = values[0] and col3 = [2]
if ((tempDate!=NaN) && (tempDate <= currentDate))
{
sheet.deleteRow(i);
}//closes if
}//closes for loop
}//closes function

Check date and edit adjacent cell in Google Sheet with Google Script

I'm attempting to write a script that will change the status (Current vs Expired) in an adjacent cell when looping over the cell values of a column containing dates if the date is equal to or less than today's date. The script needs to work on column N (dates) and modify column O (statuses) across ALL sheets in the spreadsheet. That is why I have the Sheets loop in there FYI.
Here is what I have so far and I just keep hitting walls.
It's currently throwing an error at the currentValue variable for being out of range.
//----------------------------------------------------
// Look at Dates and Change Status if expired (Automatically)
function checkDates() {
//For each sheet in the Spreadsheet
for(v in sheets){
//Find the last row that has content *-2 is because of a strange return I don't understand yet
var lastRow = sheets[v].getLastRow()-2;
//Get Dates Range (excluding empty cells)
var dateRange = sheets[v].getRange("N2:N"+lastRow);
//Get the number of Rows in the range
var numRows = dateRange.getNumRows();
//For loop for the number of rows with content
for(x = 0; x <= numRows; ++x){
// Value of cell in loop
var currentValue = dateRange.getCell(x,2).getValue();
Logger.log(currentValue);
// Row number in Range
var currentRow = dateRange.getRow(x);
// Get adjacent cell Range
var adjacentCell = sheets[v].getRange(currentRow,15);
// If the date is less than or equal to today
if(new Date(currentValue) <= d){
// Change adjancet cell to Expired
adjacentCell.setValue("Expired");
// Else adjance cell is Current
} else if(listofDates != ""){
adjacentCell.setValue("Current");
}
}
}
}
//-----------------------------------------------------
The reason for currentValue to be out of range is that the getCell(x, 2) function first parameter is the row number. Your row number starts at 0, x = 0. If you change x to start at 1 it should stop giving you the error that the currentValue variable is out of range.
for(x = 1; x <= numRows; ++x){ ...
You are also selecting 2 columns across but you only selected out of row "N", change getCell(x, 2) to getCell(x, 1).
var currentValue = dateRange.getCell(x,1).getValue();
As I mentioned before your data range is only over colmn "N", it can make it easier if you select both column "N" and "O", var dateRange = sheets[v].getRange("N2:O");
I modified the rest of your script a bit. It is not pretty but I do hope it helps you.
function checkDates() {
//For each sheet in the Spreadsheet
for(v in sheets){
var lastRow = sheets[v].getLastRow();
//Get Dates Range (excluding empty cells)
var dateRange = sheets[v].getRange(2, 14, (lastRow - 1), 2);
//Get the number of Rows in the range
var numRows = dateRange.getNumRows();
//For loop for the number of rows with content
for(x = 1; x <= numRows; ++x){
// Value of cell in loop
var currentValue = dateRange.getCell(x,1).getValue();
var adjacentCell = dateRange.getCell(x,2);
Logger.log(currentValue);
// If the date is less than or equal to today
if(new Date(currentValue) <= new Date()){
// Change adjancet cell to Expired
adjacentCell.setValue("Expired");
// Else adjance cell is Current
} else if(currentValue != ""){
adjacentCell.setValue("Current");
}
}
}
}

Categories