Google Forms createResponse with date decrements the day by 1 - google-apps-script

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)?

Related

How to get total and current numbers of Google Workspace Business Starter licenses using customerUsageReports.get

Please tell me how to get total and current numbers of Google Workspace Business Starter licenses using customerUsageReports.get (https://developers.google.com/admin-sdk/reports/reference/rest/v1/customerUsageReports/get). When I request accounts:gsuite_unlimited_total_licenses or any kind of license (https://developers.google.com/admin-sdk/reports/v1/appendix/usage/customer/accounts) I always get an empty report.
You are a legend mate!
The reason you get no response is because usage report is huge and takes time to generate, therefor is about 2 days behind.
So simply, use the "date" parameter to be atleast 2 days back from the day you run the report.
GET https://admin.googleapis.com/admin/reports/v1/usage/dates/{date}
That would be the following format
Represents the date the usage occurred. The timestamp is in the ISO 8601 format, yyyy-mm-dd.
We recommend you use your account's time zone for this.
Reference : https://developers.google.com/admin-sdk/reports/reference/rest/v1/customerUsageReports/get

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");
}

How to calculate the difference between days in a table field

Ok this should be a relatively easy thing to do, yet I'm at the head desk stage trying to figure out the insanity here.
I have a table called tblPersonnel. I'm tracking two document expiration dates in date/time fields called CED and PPED. When I run a query against tblPersonnel I need it to look at PPED, determine if that document is expired and if so use CED instead. I have a few fields in the query that need to use this concept to determine what the output value is, but I am hitting a wall here trying to get the query to spit out the correct value. Here's what I'm using for one of the fields - Document Expiration Date: IIf([PPED]-Now()<0,[CED],[PPED]). What's happening is that the expression is constantly popping as false, so PPED is getting used regardless if it's an expired date or not. Does anyone have any ideas as to what I'm doing wrong here?
I've also tried to set this up as its own field in tblPersonnel, but that's even more aggravating. If I try to set the field to just a text field - IIf([PPED]-Now()<0,"Yes","No"), the formula will accept the use of Now(), but it doesn't like the reference to the other fields in the table. If I set it as a calcuated column, I can reference the other fields but it doesn't like Now(). I'm at a loss here.
If PPED is less than Date(), it is expired. Don't need to subtract. Assuming CED and PPED are just date parts, no time, consider:
IIf([PPED] < Date(), [CED], [PPED])
If PPED could be null:
IIf(Nz([PPED],0) < Date(), [CED], [PPED])
Ok finally fixed it here. I had another issue in that I wasn't accounting for how Access would handle a Null or blank value in PPED. The functioning formula is Document Expiration Date: IIf(Len([PPED])>0,IIf([PPED]<Date(),[CED],[PPED]),[CED]) Thanks to June7 for helping me simplify the expression, as I was using DateDiff('d',[PPED],Date())<0 but their answer is just so much cleaner and quicker to type.

Timezone conversion in a Google spreadsheet

I know this looks simple.
In a Google spreadsheet, I have a column where I enter time in one timezone (GMT)
And another column should automatically get time in another time zone(Pacific Time)
GMT | PT
----------|------------
5:00 AM | 9:00 PM
As of now I am using
=$C$3-time(8,0,0)
The problem here is, I want to change the time formula for Daylight savings.
Is there any function or script available which can take the daylight saving into calculation automatically.
Short answer
There is no built-in function but you could build a custom function.
Example
/**
* Converts a datetime string to a datetime string in a targe timezone.
*
*#param {"October 29, 2016 1:00 PM CDT"} datetimeString Date, time and timezone.
*#param {"Timezone"} timeZone Target timezone
*#param {"YYYY-MM-dd hh:mm a z"} Datetime format
*#customfunction
*/
function formatDate(datetimeString,timeZone,format) {
var moment = new Date(datetimeString);
if(moment instanceof Date && !isNaN(moment)){
return Utilities.formatDate(moment, timeZone, format)
} else {
throw 'datetimeString can not be parsed as a JavaScript Date object'
}
}
NOTE:
new Date(string) / Date.parse(string) implementation in Google Apps Script doesn't support some timezones abbreviations.
From https://tc39.es/ecma262/#sec-date-time-string-format
There exists no international standard that specifies abbreviations for civil time zones like CET, EST, etc. and sometimes the same abbreviation is even used for two very different time zones.
Related
Get UTC offset from timezone abbreviations
Explanation
In order to consider daylight saving time zones the input argument for of the value to be converted should include the date, no only the time of the day. You could set a default date and time zone to build the datetimeString by concatenating it before calling the formula.
=formatDate("October 29, 2016 "&A2&" GMT","PDT","hh:mm a")
For the target timezone besides the three letter abbreviation we could use TZ database names like America/Los_Angeles, example:
=formatDate("October 29, 2016 "&A2&" GMT","America/Los_Angeles","HH:mm")
If timezone abbreviation and TZ name fails for the datetimeString use time offsets (i.e. UTC-4).
See also
Calculating year, month, days between dates in google apps script
References
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date
This tutorial was amazingly helpful: https://davidkeen.com/blog/2017/01/time-zone-conversion-in-google-sheets/
Google Sheets does not have a built in way of converting time zone data but by using the power of Moment.js and Google’s script editor we can add time zone functionality to any sheet.
These are the files I copied into my script project:
https://momentjs.com/downloads/moment-with-locales.js saved as moment.js
https://momentjs.com/downloads/moment-timezone-with-data.js saved as moment-timezone.js
Make sure you add the moment.js script first and have it above the moment-timezone.js script because moment-timezone.js depends on it.
Then in your other script project, your Code.gs file can look like this:
var DT_FORMAT = 'YYYY-MM-DD HH:mm:ss';
/**
https://stackoverflow.com/questions/34946815/timezone-conversion-in-a-google-spreadsheet/40324587
*/
function toUtc(dateTime, timeZone) {
var from = m.moment.tz(dateTime, DT_FORMAT, timeZone);//https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/
return from.utc().format(DT_FORMAT);
}
/**
https://stackoverflow.com/questions/34946815/timezone-conversion-in-a-google-spreadsheet/40324587
*/
function fromUtc(dateTime, timeZone) {
var from = m.moment.utc(dateTime, DT_FORMAT);//https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/
return from.tz(timeZone).format(DT_FORMAT);
}
The easiest method is using a simple calculation.
Use =NOW() command in sheets and subtract it with the time difference divided by 24.
Example:
IST to Colombia
=NOW()-(10.5/24)
The time difference from India to Colombia is 10hours and 50min, we need to subtract it from the "Now" time and divide it by 24.
If the time zone is ahead of your place, then you need to add it.
Example:
IST to JAPAN:
=NOW()+(3.5/24)
=Now is set to US time by default, you can change it under general in settings.
enter image description here
enter image description here
I had the same problem (convert Manila Time to Sydney Time and automatically adjust for daylight saving time) when I found this page.
I didn't want to have a custom function but I found that, in Sydney, AEST (Australian Eastern Standard Time) starts on the first Sunday of April and AEDT (Australian Eastern Daylight Time) starts on the first Sunday of October.
So I thought, if I could find a formula that detects whether a date falls between the first Sunday of April and first Sunday of October (Standard Time) then I can automatically add 1 hour to the usual 2 hours to Manila time during Daylight Saving Time (dates falling outside the two dates) to have Sydney Time.
These two Excel solutions worked fine in Google Sheets:
How You Can Determine the First Sunday in a Month in Excel
How to determine if a date falls between two dates or on weekend in Excel
First Sunday of April this year (A1):
=CONCATENATE("4/1/",Year(today()))+CHOOSE(WEEKDAY(CONCATENATE("4/1/",Year(today())),1),7,6,5,4,3,2,1)
First Sunday of October this year (A2):
=CONCATENATE("10/1/",year(today()))+CHOOSE(WEEKDAY(CONCATENATE("10/1/",year(today())),1),7,6,5,4,3,2,1)
DST detector (A3) — if a date falls outside these two dates, it's DST in Sydney:
=IF(AND(today()>A1,today()<A2),"AEST","AEDT")
Time in Sydney (A4):
=NOW()+TIME(IF(A3="AEDT",3,2),0,0)
NOW() can be changed to any time format for tabulation:
I'm a new contributor and a novice, but I stumbled upon a function that had not been mentioned despite many hours of searching on the Sheets/Time Zone issue. Hoping this relatively simple solution will help.
For my sheet, I just want to add a row and automatically populate the local sheet date and time in the first two cells.
The .getTimezoneOffset() returns the difference in minutes between the client TZ and GMT, which in NY during Daylight Savings Time is 240. The function returns a positive number for the zones with "GMT-x", and vice versa for zones with "GMT+x". Hence the need to divide by -60 to get the correct hour and sign.
function addRow() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
sheet.insertRows(2, 1);
rightNow = new Date();
var tzOffset = "GMT" + rightNow.getTimezoneOffset() / -60;
var fDate = Utilities.formatDate(rightNow, tzOffset, "MM-dd-yyyy");
var fTime = Utilities.formatDate(rightNow, tzOffset, "HH:mm");
sheet.getRange('A2').setValue(fDate);
sheet.getRange('B2').setValue(fTime);
sheet.getRange('C2').setValue(tzOffset);
}
I've since found that I'm not the first person to respond to the GMT correction connundrum mentioning .getTimezoneOffset(). However, this thread has the most views on this topic, so I figured this option deserves a mention.
DST ends here on November 7th, 2021, so I'll report back if it doesn't adjust as expected to "GMT-5"
.getTimezoneOffset()
That can also be done without macros. Just using functions and data manipulation will suffice. Explaning the whole process here would be a bit cumbersome. Just do your research on how the various time functions work and use your creativity.
Hint: Use =NOW() if you want both current date and time. You'll actually need that if you need to find out the precise diff in time between to different dates.
Use =NOW()-INT(NOW()) when you only want the time (with date truncated if both times fall on the same date). Then format the corresponding cell or cells for time (i.e. 4:30 PM), not for date-time (3/25/2019 17:00:00). The latter is the format you'd use when you want to show both date and time... like when you use NOW().
Also search online for the Daylight Saving Time offset for the various standard time zones (PT, MT, CT, ET, AT) with respect to the Coordinated Universal Time (UTC). For example, in 2019 the offset for Pacific Time is UTC-7 when DST is observed starting on March 10 at 2 AM (Pacific) until November 3 at 2 AM. That means that the difference in time from UTC to Pacific is 7 hours. During the rest of the year is 8 hours (UTC-8). During DST observance starting sometime in March (the 10th this yr) it goes from PST to PDT by moving clocks forward 1 hr, or what we know as UTC-7 (that's summer time). After DST observance it goes from PDT to PST by moving clocks back 1 hr again, or what we know as UTC-8 (or winter time). Remember that the clock is advanced one hour in March to make better use of time. That's what we call DST, or Daylight Saving Time. So after March 8 at 2 AM (this year in 2019) we are in UTC-7. In November, we do the opposite. In Nov 3 at 2 AM the clock is taken back one hour as the winter kicks in. At that point we are back in Standard Time. Seems a bit confusing but it's really not.
So, basically, for folks in PT they go from PST to PDT in March and from PDT to PST in November. The exact same process goes on with Mountain Time, Central Time and Eastern Time. But they have different UTC time offsets. MT is either UTC-6 or UTC-7. CT is either UTC-5 or UTC-6. And ET is either UTC-4 or UTC-5. All depending on whether we are in summer time when Daylight Saving is observed to make better use of daylight and working hours, or in winter time (AKA, Standard Time).
Study these thoroughly and understand how they work, and play around with the various time functions in Excel or Google Sheets like the TIME(#,#,#) and NOW() functions and such, and believe me, soon you'll be able to do about anything like a pro with plain functions without having to use VBA Google Apps Script. You can also use the TEXT() function, though, with tricks like =TEXT(L4,"DDD, MMM D")&" | "&TEXT(L4,"h:mm AM/PM"), where L4 contains you date-timestamp, to display time and date formats. The VALUE() function also comes in handy every now and then. You can even design a numerical countdown timer without the use of macros. You'd need to create a circular reference and set iterations to 1, and time display to say every 1 min, in your spreadsheet settings for that.
The official timeanddate dot com website is a good source of info for all to know about time zones and how daylight time is handled. They have all UTC offsets there too.
Create your own Timezone Converter in Google Sheets:
Step 1: Create your table for the timezone converter.
Step 2: Enter the times for your time zones in a column.
Note: Ensure that you select date/time format(Select Cell(s) -> Format -> Number -> Time/Date)
Step 3: Write a formula to convert timezone using the following functions
Google Sheet Functions
=HOUR(A8)+(B3*C3) converts the hours.
=MINUTE(A8)+(B3*C3) converts the minutes.
Step 4: Convert back to time format using TIME(h,m,s) function
=TIME(HOUR(A8)+(B3*C3), MINUTE(A8)+(B3*C3), SECOND(A8))
This is a simple way to convert timezones.
However, if you want to implement an accurate timezone converter that takes care of the previous day, next day, and beyond 24 hours, beyond 60 minutes, please use MOD operations and handle all the cases.
Visit(or Use) this google sheet for reference:
https://docs.google.com/spreadsheets/d/1tfz5AtU3pddb46PG93HFlzpE8zqy421N0MKxHBCSqpo/edit?usp=sharing
just use the TZData format to "pull" a sync from UTC and display your choice.
Example in order to "change" the display of your cell to Berlin local time
=fromUTC(N82, "Europe/Berlin")
or for Tokyo
=fromUTC(N82, "Asia/Tokyo")
or San Francisco
=fromUTC(N82, "America/Inuvik")
point of reference for Time Zones is here >>
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones

Access data conversion issue

I'm using Access 2003. Have a table with some date values in a text data column like this;
May-97
Jun-99
Jun-00
Sep-02
Jan-04
I need to convert them to proper date format and into another Date/time column, So create a new Date/Time columns and just updated the values from the Text column into this new column. At first it looked fine, except for years after the year 2000. The new columns converted the dates as follows;
May-97 > 01/05/1997
Jun-99 > 01/06/1999
Jun-00 > 01/06/2000
Sep-02 > 01/09/2010
Jan-04 > 01/01/2010
As you can see any data with year after 2000 get converted to 2010. The same thing happens if I query the data using FORMAT(dateString, "dd/mm/yyyy").
Any ideas why this is so? Do I have to split the month and year and combine them again?
Thanks
Access/Jet/ACE (and many other Windows components) use a window for interpreting 2-digit years. For 00 to 29, it's assumed to be 2000-2029, and for 30-99, 1930-1999. This was put in place to address Y2K compatibility issues sometime in the 1997-98 time frame.
I do not allow 2-digit year input anywhere in any of my apps. Because of that, I don't have to have any code to interpret what is intended by the user (which could conceivably make mistakes).
This also points up the issue of the independence of display format and data storage with Jet/ACE date values. The storage is as a double, with the integer part indicating the day since 12/30/1899 and the decimal part the time portion within the day. Any date you enter is going to be stored as only one number.
If you input an incomplete date (i.e., with no century explicitly indicated for the year), your application has to make an assumption as to what the user intends. The 2029 window is one solution to the 2-digit year problem, but in my opinion, it's entirely inappropriate to depend on it because the user can change it in their Control Panel Regional Settings. I don't write any complicated code to verify dates, I just require 4-digit year entry and avoid the problem entirely. I have been doing this since c. 1998 as a matter of course, and everybody is completely accustomed to it. A few users squawked back then, and I had the "it's because of Y2K" as the excuse that shut them down. Once they got used it, it became a non-issue.
The date is ambiguous, so it is seeing 02 as the day number. Depending on your locale, something like this may suit:
cdate("01-" & Field)
However, it may be best to convert to four digit year, month, day format, which is always unambiguous.
Access seems to be get conduced between MM-YYYY format and MM-DD format. Don't know why it is doing it for dates after the year 2000, but solved it by converting the original string date to full date (01-May-01). Now Access converts the year into 2001 instead of 2010.
If you don't supply a year and the two sets of digits entered into a date field could be a day and month then Access assumes the current year. So your first three dates definitely have a year in them. But the last two don't.
Note that this isn't Access but actually the operating system doing the work. You get the same results in Excel. I had an interesting conversattion with some Microsoft employees on this issue and it's actually OLEAUT32.DLL.