EOMONTH equivalent in Google Scripts - google-apps-script

I'm writing a script that doing some date manipulation and I need to know how many days between current date and end of the month.
I've already got a function that I can put two dates into and it'll return the number of days between the two dates.
I'm looking for an equivalent of EOMONTH in scripts so I can return the date of the last day of any given month
(Hope that makes sense)
All I've been able to come up with so far is adding a day to the date until it becomes a new month and running a counter whilst doing so, but this just seems a really silly way of doing it.

function daysInMonth(m=0) {
return new Date(new Date().getFullYear(),m+1,0).getDate();
}

Related

Array Formula that also runs calculation on each cell's column header

I work at a school and at the end of each semester we need to total up instructional hours for each student in each class, then get the sum for the whole class. We keep our attendance on a Google Sheet each semester, so I can use fancy functions to do this. This function has worked perfectly the last couple years:
=arrayformula(sum(countifs([data range],{"","T","*all *","*no *"})))
I run this at the beginning of a row and it counts all instances where a student (or the entire class) would be counted as present, but doesn't count anything else; just like I need it (students are counted as present if the cell is left blank). I run this function on each row (for each individual student) and then run a quick sum at the bottom of the sheet and it's all good to go.
Well, this worked because our classes were all 1-hour long every day. Starting this semester, they are either 1 hour and 20 minutes or 32 minutes depending on the day of the week (each class has one occurrence that is an hour twenty and one that is thirty-two minutes each week). So the above function no longer works as it just totals how many classes a student showed up for. I now need something that will do that, but also multiply the result of each cell (1 or 0) by either 1.33 or .53 depending on the day of the week. This can be done by referencing each column's header, which has the date attached and running a WEEKDAY parameter nested in an IF parameter like so (using a class that has the longer instance on a Friday for example):
(if((weekday(Z$1))=6, 1.33, 0.53))
I put that together into a function that works:
=countifs(Z2,{"","T","*all *","*no "})(if((weekday(Z$1))=6, 1.33, 0.53))
... but I will have to create a dummy column next to the column that has each date's attendance data, tailor the formula to the column next to it and copy and paste the formula all the way down the column. I should only need to change the formula once per sheet, but I'll still need to create dummy columns next to each date and then run this formula by hand on each column. After all that, then I can sum up each row. It works... but I want something less time-intensive and prone to error.
What I am hoping for is a function that does what I have now (for single cells), but runs each portion of this function on each individual cell across an entire row of data after I add it to the beginning of the row and then, preferably, sums up the total hours at the end (should just be able to add a SUM parameter to the beginning of the function?); all in a one-cell function. I have tried using an array like I had before (in the first formula above), but the problem there (there's probably more than one problem), is that I can't get the function to reference each cell's header and run the IF function, I can only get the array to work on the COUNTIF portion of the function. Maybe this whole thing needs to be a script instead?
I hope what I am asking for makes sense and here is a link to a sample of the data I am using:
https://docs.google.com/spreadsheets/d/12fTE_AMlKtlqc_KToYB5TkvpYwFzON9Q6vvRfnDlr0A/edit?usp=sharing
Thank you for reading and I hope there is a reasonable solution to what I'm asking!
try:
=INDEX(BYROW(B2:N15, LAMBDA(x, SUM(
REGEXMATCH(x, "^$|no .+|all .+|T")*
IF(WEEKDAY(B1:N1)=6, 1.33, 0.53)))))

How do I tell google sheets to only make a calculation after a certain time period?

Context
So I am currently building a database of data for financial assets to conduct some machine learning from to build trading signals. I am trying to calculate the geometric mean but over a given period (monthly). I want to tell google sheets to only calculate the geometric mean after every month. I tried using this formula to no avail:
=IF(last date of the month - first date of month = total days in a month,
GEOMEAN(filter(abs(range),abs(range)>0)),""))
** There were values in the last date of the month - first date of month = total days in a month **
It ended up doing it for every day for the 10 year data set.
** Update
This is the data:
Date Close Cleaned Data Returns Gross returns Geometric average returns
13/11/2015 280 -0.0267 0 1
16/11/2015 280 -0.0267 0 1
17/11/2015 280 -0.0267 0 1
...
23/12/2016 296.4 0.0236 -0.1561348935 0.8438651065
28/12/2016 295.2 0.0199 -0.0770931339 0.9229068661
29/12/2016 294.7 0.0183 0.03341318035 1.03341318
30/12/2016 294.9 0.0190 0.3718276303 1.37182763
Problem (UPDATE)
How do I create a function to let google sheet do calculations only for the last day of every month for a given time series data? Say within this time period, (1 year) I want to calculate the geometric mean for each month in this period and for new data I might want to add later in the future.
To do this you will have to set a trigger event. This is found within the script editor under the edit tab, second to last option.
Image of where to find the trigger manager: It's in spanish, but it will be found in the same place
Once there, click on add trigger, which will be found on the bottom right corner. The first option will ask you which function do you want to run from the bound script. Then select the source of the event and select according to time (My platform is in spanish so I'm trasnlating it you might have it written differently). Then it will prompt you: if you want it to be at an exact date and time, every minute, every hour, day, week and month. Select month and select the day of the month you want the trigger to happen in the next prompt and select the time for the last prompt, then click save.
Finding the last day of a month:
function lastdayOfMonths() {
let html='';
let td=new Date();
for(var i=0;i<12;i++) {
let dt=new Date(td.getFullYear(),td.getMonth()+i+1,0);
let dts=Utilities.formatDate(dt, Session.getScriptTimeZone(),"E MMM dd,yyyy");
html+=Utilities.formatString('<br />%s',dts);
}
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Last Days of Months for next year");
}

SSRS Expression for getting current week Sunday-Saturday

I am looking to make a report that gets the current week from sunday to saturday, i was thinking oh the headers of it having the date of the current week of running the report, does anyone know how to do that?
I assuming what you are asking is how to filter your dataset to show only records within the same week as the current week?
if so then you can do something like this
=WEEK(today()) = WEEK(Fields!myDateField.Value)
This will return True if the week is in the same week as the current week.
For more info on the WEEK function see here
https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008/aa337345(v=sql.100)

Google Forms createResponse with date decrements the day by 1

I am programmatically generating new form responses from rows of data in a Google Sheet (one approval workflow system generates new approval requests in a second approval workflow system). The data includes dates. My script takes the date value as a string from a cell, converts it into a native JavaScript date object, then submits this to a new form response using createResponse(). Here's the pertinent code:
var startDate = googleFormsDateStringToDate(eventDetails['Event Start Date']);
var r = item.createResponse(startDate);
For the most part, the system works robustly. Except for one intriguing problem - for any date after 29/04/2020, the date stored in the response is decremented by 1 day. Any date on or before this date works as expected, any date after is decremented by a day.
I have tried a few things.
The dates correspond to a start date and end date for an event. If the start date is before this date and the end date after, the end date will be decremented whilst the start date is recorded accurately. So I am certain the issue is directly related to whether the date I am submitting falls before or after this date.
I have tested extensively and am absolutely certain that the string-date conversion is working. If I retrieve the value of the response immediately after creating it (before the form response is even submitted), I find that it has been decremented:
var startDate = dateStringToDate(eventDetails['Event Start Date']);
Logger.log(startDate.toString()); // startDate is always accurate
var r = item.createResponse(startDate);
Logger.log(r.getResponse()); // if after 29/04/2020, will be decremented by 1 day
This tells me the issue is occurring precisely when I create the response and that it is occurring "at Google's end".
One of my suspicions is that it may be a timezone issue, but that would not explain why the issue is linked directly to this specific date. My other suspicion is that it may be to do with the fact that 2020 is a leap year, so we get 29/02/2020, and the date after which the error occurs is 29/03/2020. Perhaps somewhere behind Google's implementation of createResponse it is failing to account for the leap year?
Until I can identify the error, I plan to implement a workaround in which I will use getResponse() to check if the response date matches the intended date and correct accordingly. But I would prefer to understand what is causing the error (so I know if it is a bug requiring reporting, or simply my lack of understanding) and, if possible, find a solution.
So, specific questions: What is the source of the error? Is there a solution (rather than a workaround)?
EDIT
Whilst figuring out a workaround, I have answered my first question. In the UK, daylight savings time starts on 29/04/2020. Since the dates entered via the Google Form have no time associated with them, the time appears to be stored as 00:00:00, or midnight. I assume that what is happening is that the adjustment for daylight savings time (1 hour) is subtracting an hour from this time, thereby rolling it back to the day before.
My final question stands: is there a means to reliably prevent this error from occurring rather than manually checking for inaccurate dates (or programming in daylight savings time dates)?

How can you save time by using the built in Date class?

The intention of this question is to gather solutions to date / time calculation using the built in Date class instead of writing long complicated functions.
I’ll write some answers myself, and accept an answer if anyone comes up with something very clever. But this is mostly meant as a collection of solutions, since I often see overly complicated code for handling dates.
Please remember this is not for long solutions for things the Date class can not do.
A good place to start is the reference found here:
http://help.adobe.com/en_US/AS3LCR/Flash_10.0/Date.html
You can easily find out if a year was a leap year without coding all the exceptions to the rule by using the Date class. By subtracting one day from marts the 1st (requesting marts the 0th), you can find the number of days in February.
Remember that month is zero-indexed so Marts being the 3rd month has index 2.
function CheckIfYearIsLeapYear(year:uint):Boolean
{
return new Date(year, 2, 0).Date == 29;
}
To properly compare to dates you need to use the getTime() function, it will give you the time in milliseconds since January 1, 1970. Which makes it easy to compare to dates, a later date will return a larger value.
You can subtract one from the other to get the difference, but unfortunately there is no built in time span class to handle this cleanly; you will have to use a bit of math to present the difference properly to the user (eg. dividing the difference with the number milliseconds in a day to get the difference in days).
var date1:Date = new Date(1994, 12, 24);
var date2:Date = new Date(1991, 1, 3);
if(date1.getTime() > date2.getTime())
trace("date1 is after date2");
else
trace("date2 is after or the same as date1");
There's also ObjectUtil.dateCompare(a,b)
To get the current system date simply create a new Date object without passing any values to the constructor. Like this:
var today:Date = new Date();
trace("The date and time right now is: " + today. toLocaleString());
trace("The date right now is: " + today. toLocaleDateString());
trace("The time right now is: " + today. toLocaleTimeString());
The built in Date class handles “overflow” very well, this can be used to add or subtract time. If one of the fields overflows, the date class handles it by adding or subtracting the overflow.
var date:Date = new Date(1993, 12, 28);
trace("7 days after the " + date.toLocaleDateString());
date.setDate(date.Date + 7);
trace("It was the " + date.toLocaleDateString());