I made a few functions with GoogleSheets using AppsScripts for simple task a few times in previous years. I always had problems when taking dates from cells/ranges and processing them, but somehow alwaays found a workaround, so that I did not have to deal with it. Well, this time I can not find a workaround, so I will try to explain my problems with the following code:
function getDates(){
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('Dates');
var date = sht.getRange(2,1).getValues();
Logger.log(date[0][0]); //output is Tue Jun 08 18:00:00 GMT-04:00 2021
var datumFilter= Utilities.formatDate(date[0][0], "GMT+1", "dd/mm/yy");
Logger.log(datumFilter); //output is 08/00/21
var outrng = sht.getRange(25,1);
outrng.setValue(date);
}
The first targeted cell ('var date') has a value of "9.6.21" in the spreadsheet. The cell is formatted as a date and it opens a calendar when double-clicked. When I set the new cells values (with 'outrng.setValue(date);'), the result is OK, with the same date as in the original cell.
But I do not need to simply transfer the values, I want to implement them in some loops and I have no idea how to simply get the date in the same format or at least the same date in the script as it is in the cell. As you can see from the logger, the values there are different. A simple d/m/yy format would be sufficient.
My spreadsheet settings are set to my local time (Slovenia, GMT+1).
I am guessing that I am missing some basics here. I have spent many hours trying to understand it, so any help is highly appreciated!
Cooper already answered all your questions in the comment. I'd like to add on and show you an example on what it would like and add some modifications.
Code:
function getDates() {
var s = SpreadsheetApp.getActiveSpreadsheet();
var sht = s.getSheetByName('Dates');
// get last row of the sheet
var lastRow = sht.getLastRow();
// get your sheet's timezone
var timezone = SpreadsheetApp.getActive().getSpreadsheetTimeZone();
var output = [];
// getValues is mostly used for multiple cells returning a 2D array
// use getValue for single cells to return its actual value
// but since function name is getDates, I assume column A is all dates
// so we fetch the whole column (A2:A[lastRow]) except the header
var dates = sht.getRange("A2:A" + lastRow).getValues();
// for each date on that column, we format the date to d/M/yy
// m/mm = minute
// M/MM = month
dates.forEach(function ([date]){
Logger.log(date);
var datumFilter= Utilities.formatDate(new Date(date), timezone, "d/M/yy");
Logger.log(datumFilter);
// collect all dates in an array
output.push([datumFilter]);
});
// assign all the dates in the array onto range B2:B
sht.getRange(2, 2, output.length, 1).setValues(output);
}
Sample data:
Logs:
Output:
Note:
The output on sheets is not equal to logs due to the formatting of my sheet.
Related
I am using google apps script for a google sheet that is meant to sort ranges of cells into different sheets by date. I've entered a date in the cell A2, and then when I use my function, it sorts the sheet with the same date as what is in A2, and if there isn't one, then it will create a new sheet with that date. The problem that I am coming across is not with the cells getting to the right place, but for some reason the date changes to what seems to be an id of sorts. For example, for the date 01/01/2001, the first couple worked fine, and then for the next couple attempts it switched to have "36892" where the date should be.
I have not tried anything, as I am unsure of what to try. If I knew the command, I would just switch the format back after moving the cells to the right place.
function findDate() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
ss.setActiveSheet(ss.getSheets()[0])
var range = sheet.getRange("A2");
var A2 = SpreadsheetApp.getActiveSheet().getRange('A2').getValue();
var templateSheet = ss.getActiveSheet();
var val = Utilities.formatDate(new Date(A2), "GMT+1", "MM/dd/yyyy");
range.setValue(new Date(A2)).setNumberFormat("MM/dd/yyyy");
A2 = SpreadsheetApp.getActiveSheet().getRange('A2').getValue();
if( ss.getSheetByName(val) == null)
{
//if returned null means the sheet doesn't exist, so create it
ss.insertSheet(val, ss.getSheets().length, {template: templateSheet});
}
else
{
var sheet1 = ss.getSheetByName("GUI");
var sheet2 = ss.getSheetByName(val);
sheet1.getRange("A2:D2").copyTo(sheet2.getRange(sheet2.getLastRow()+1,1,1,4), {contentsOnly:true});
}
ss.setActiveSheet(ss.getSheets()[0]);
sheet.getRange("A2:D2").clearContent();
}
This is my function. Since I don't know where the problem lies, I copied the whole thing, sorry about the bulk.
Date Event Time Comments
01/01/2001 Movie 22:00 a
01/01/2001 Movie 11:00 PM a
36892 Movie 0.7083333333 a
36892 Movie 12:00 PM a
This was the result from adding the same date each time, and sorting them to the correct sheet using the function. The time is being odd, I really have no clue why, but I don't really care at this point, that's a future problem (unless anyone has any clue why it changes format too). The date, however, doesn't seem to be some random number as the same date gave the output of the same number. I would have expected it to look like:
Date Event Time Comments
01/01/2001 Movie 10:00 PM a
01/01/2001 Movie 11:00 PM a
01/01/2001 Movie (I forget) a
01/01/2001 Movie 12:00 PM a
Answer:
You need to remove {contentsOnly:true} from your copyTo() method.
Reasoning:
By setting the contentsOnly boolean to true you're telling sheets to copy what it sees in the cell, not what you see.
In Sheets, all days are serialized starting from the 1st January 1900 with a day of 1, so copying 01/01/2001 is day with serial number 36892.
As for the date - it's being read as a fraction of how far through the day it is - at 17:00h and with 24 hours in the day the date will be seen as 17/24 or 0.708333333333.
I am working with Google Scripts and here is my problem.
I am attempting to compare today's date with a date entered into column C in the spreadsheet. The code I have seems like it should work but when I use the logger to see the date it gives me "Wed Dec 31 19:00:00 GMT-05:00 1969" instead of 2018-07-29 1:00 PM.
I know I need to use the Utilities.formatDate in order to compare them but I can't seem to understand why it is not pulling the date in column C.
Here is my code below:
function sendEmail() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 3; // First row of data to process
var numRows = sheet.getLastRow()-1; // Number of rows to process
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 1, numRows, sheet.getLastColumn());
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//Logger.log(data); // give you the data you are looking at comparing
for (var i in data) {
var row = data[i];
var date = new Date();
//Logger.log(date); // sets to todays date and time.
var sheetDate = new Date(row[3]);
Logger.log(sheetDate); // date in the row you are comparing
You're not looking at the value in column C. The method getValues() returns a two-dimensional array, and arrays are 0-indexed. This means that you should be using a 2 (not a 3), when defining sheetDate.
var sheetDate = new Date(row[2]);
As mentioned in the comments, you don't need to use Utilities.formatDate(), but that's a separate issue. I would also try logging the value before converting it to a date (e.g. Logger.log(row[2])).
I've done some reading but my limited knowledge on scripts is making things difficult. I want to:
Copy a variable number of rows data range, known colums, from one sheet titled 'Download'
Paste that data in a new sheet titled 'Trade History' from Column B
In the new sheet, add today's date formatted (DD/MM/YYYY) in a new column A for each record copied
The data in worksheet 'Download' uses IMPORTHTML
The data copied from Download to store a historical record needs a date in Column A
I've managed to get 1 and 2 working, but can't work out the 3rd. See current script below.
function recordHistory() {
var ss = SpreadsheetApp.getActive(),
sheet = ss.getSheetByName('Trade_History');
var source = sheet.getRange("a2:E2000");
ss.getSheetByName('Download').getRange('A2:E5000').copyTo(sheet.getRange(sheet.getLastRow()+1, 2))
}
You need to use Utilities.formatDate() to format today's date to DD/MM/YYYY.
Because you're copying one set of values, and then next to it (in column A), pasting another, I altered your code a bit as well.
function recordHistory() {
var ss = SpreadsheetApp.getActive(),
destinationSheet = ss.getSheetByName('Trade_History');
var sourceData = ss.getSheetByName('Download').getDataRange().getValues();
for (var i=0; i<sourceData.length; i++) {
var row = sourceData[i];
var today = Utilities.formatDate(new Date(), 'GMT+10', 'dd/MM/yyyy'); // AEST is GMT+10
row.unshift(today); // Places data at the beginning of the row array
}
destinationSheet.getRange(destinationSheet.getLastRow()+1, // Append to existing data
1, // Start at Column A
sourceData.length, // Number of new rows to be added (determined from source data)
sourceData[0].length // Number of new columns to be added (determined from source data)
).setValues(sourceData); // Printe the values
}
Start by getting the values of the source data. This returns an array that can be looped through to add today's date. Once the date has been added to all of the source data, determine the range boundaries for where it will be printed. Rather than simply selecting the start cell as could be done with the copyTo() method, the full dimensions now have to be defined. Finally, print the values to the defined range.
I have a Google Sheet where information from a Google Form is dumped. Two of the columns create a date range (columns C and G) and I would like for the sheet to automatically create a new row of information for every date of the range and copy all the other information from the original row for every row that is created. In the end, every date in the range has it's own row regardless of it being 2 days or 25 and all the the information gathered through the form be present for each day. If there is not a date in column G, it is only a one day trip and there is no need for additional rows. To make things more difficult when someone submits a form, the information is entered into the row directly beneath the last one that it filled, so these new rows filled by the date range will need to be down the sheet, possibly beginning at row 2000 or more as this sheet will have a lot of information in a few months. As you may see in the sample, there is another sheet in the workbook that performs all the sorting. Thanks for any help.
Sample Document
You will need to create a form submit event and attach the following code to it. Also you'll need to create a sheet name 'ResponseReview'.
function formSubmitEvent1(e)
{
var ss=SpreadsheetApp.openById('SpreadsheetID');
var sht=ss.getSheetByName('ResponseReview');
sht.appendRow(e.values);
}
The above code will need the SpreadsheetId in the openById Method. This code will append any new rows to the end of the ResponseReview sheet.
The code below will expand any entrees that have a date in column 3 and 7 and it will also remove the end date in column 7 from that first row. I use the fact that if column 3 is not empty and column 7 is not and column 7 is no equal to column 3 then that's a row that needs to be expanded. So I have to remove the end date so that it won't continue to get expanded when it's run again in the future. We could figure something else out if you need to keep than end date. We could add a don't expand column at the end.
function convertRangetoRows()
{
var ss=SpreadsheetApp.getActive();
var sht=ss.getSheetByName('ResponseReview');
var rng=sht.getDataRange();
var rngA=rng.getValues();
var rngB=[];
var day=86400000;
rngB.push(rngA[0]);
for(var i=1;i<rngA.length;i++)
{
rngB.push(rngA[i]);
if(rngA[i][2] && rngA[i][6] && rngA[i][2]!=rngA[i][6])
{
var row=rngA[i].slice();//returns a new copy of the array by value
rngA[i][6]='';//deletes the end date by reference so it also deletes the one thats already been pushed into rngB
var dt0=new Date(row[2]);
var dt1=new Date(row[6]);
var days=(dt1.valueOf()-dt0.valueOf())/day;
var dt=dt0.valueOf();
for(j=0;j<days;j++)
{
dt+=day;
row[2]=Utilities.formatDate(new Date(dt), Session.getScriptTimeZone(), "MM/dd/yyyy");//original array unchanged
row[6]='';//original array unchanged
rngB.push(row.slice());//push in a copy
}
}
var intermediate='nothing';
}
var outrng=sht.getRange(1,1,rngB.length,rngB[0].length);
outrng.setValues(rngB);
var end='the end is near';
}
This is what my spreadsheet looks like before running the expansion function:
And After:
And now you can leave the sheet linked to the form alone and let it be an archive for submitted data.
I am using ColdFusion 9.0.1 and some database that I cannot change.
I am accessing a database that stores a date as an eight digit numeric with zero decimal places like this:
YYYYMMDD
I need to be able to read the date, add and subtract days from a date, and create new dates. I am looking for a ColdFusion solution to efficiently (not much code) to convert the date to our standard format, which is
MM/DD/YYYY
And then convert it back into the database's format for saving.
I need to code this in such a way that non-ColdFusion programmers can easily read this and use it, copy and modify it for other functions (such as adding a day to a date). So, I am not looking for the most least amount of code, but efficient and readable code.
Can you suggest anything that would make this code block more flexible, readable, or more efficient (less code)?
<cfscript>
// FORMAT DB DATE FOR BROWSER
DateFromDB = "20111116";
DatedToBrowser = createBrowserDate(DateFromDB);
writeOutput(DatedToBrowser);
function createBrowserDate(ThisDate) {
ThisYear = left(ThisDate, 4);
ThisMonth = mid(ThisDate, 4, 2);
ThisDay = right(ThisDate, 2);
NewDate = createDate(ThisYear, ThisMonth, ThisDay);
NewDate = dateFormat(NewDate, "MM/DD/YYYY");
return NewDate;
}
// FORMAT BROWSER DATE FOR DB
DateFromBrowser = "11/16/2011";
DateToDB = createDBDate(DateFromBrowser);
writeDump(DateToDB);
function createDBDate(ThisDate) {
ThisYear = year(ThisDate);
ThisMonth = month(ThisDate);
ThisDay = day(ThisDate);
NewDate = "#ThisYear##ThisMonth##ThisDay#";
return NewDate;
}
</cfscript>
First find who ever did the database and kick them in the nads...
Personally I'd Convert with sql so my code only dealt with date objects.
Select Convert(DateTime, Convert(VarChar(8),DateTimeInventedByIdjitColumn))
From SomeTable
As stated by our peers, store dates as dates.
'08/06/2011' could be 8th of june of the 6th of August depending on locale.
20111643 is a valid integer..
Not using a proper date type is just a massive collection of features and bugs that at best are waiting to happen.
You can actually rewrite each function into 1 line of code.
function createBrowserDate(ThisDate) {
return mid(ThisDate,4,2) & "/" & right(ThisDate,2) & "/" & left(ThisDate,4);
}
and
function createDBDate(ThisDate) {
return dateFormat( ThisDate, "YYYYMMDD" );
}
Don't keep dates as strings - keep dates as dates and format them when you need to.
If you can't correct the database to use actual date columns (which you should if you can), then you can use these two functions to convert to/from YYYYMMDD and a date object:
function parseYMD( YYYYMMDD )
{
if ( ! refind('^\d{8}$' , Arguments.YYYYMMDD ) )
throw "Invalid Format. Expected YYYYMMDD";
return parseDateTime
( Arguments.YYYYMMDD.replaceAll('(?<=^\d{4})|(?=\d{2}$)','-') );
}
function formatYMD( DateObj )
{
return DateFormat( DateObj , 'yyyymmdd' );
}
By using date objects it means that any level of developer can work with them, without needing to care about formatting, via built-in functions like DateAdd, DateCompare, and so on.
I'm not a regular expression fan since it's not that readable to me.
Since you're using CF9, I'd typed the argument and specify the returntype of the functions to be even more readable for the next person picking up your code.
First, right after I read the date from DB, I'd parse it to a Date object using parseDBDate()
Date function parseDBDate(required String dbDate)
{
var yyyy = left(dbDate, 4);
var mm = mid(dbDate, 4, 2);
var dd = right(dbDate, 2);
return createDate(yyyy , mm, dd);
}
Once you have the date object, you can use all those built-in Date functoin like DateAdd() or DateDiff().
Call browserDateFormat() right before you need to display it.
String function browserDateFormat(required Date date)
{
return dateFormat(date, "MM/DD/YYYY");
}
Call dBDateFormat() inside <cfqueryparam value=""> when it's time to persist to DB
String function dBDateFormat(required Date date)
{
return dateFormat(date, "YYYYMMDD");
}
One liner :)
myDateString = "20110203";
myCfDate = createObject("java","java.text.SimpleDateFormat").init("yyyyMMdd").parse(myDateString,createObject("java","java.text.ParsePosition").init(0*0));
If you want to parse different patterns, change "yyyyMMdd" to any other supported pattern.
http://download.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html
The ParsePosition is used to say where to start parsing the string.
0*0 is shorthand for JavaCast("int",0) - in the Adobe cf engine, 0 is a string, until you apply math to it, then it becomes a Double, which the ParsePosition constructor supports. Technically, it constructs with an int, but cf is smart enough to downgrade a Double to an int.
http://download.oracle.com/javase/1.5.0/docs/api/java/text/ParsePosition.html