Google Apps Script - Calendar Reservation - Fails to Create Event - google-apps-script

I have pasted the code below. To explain what the code is intended to do is block out a conference room so that it can not be reserved for use, it is a small "huddle room" that will be blocked out and only available to reserve a week in advance.
Anyway here is the problem I am encountering with the code below. If I run the code starting from Jan 1. The code will run and then part way through March stops creating events, if this happens exactly at the beginning of a month it wouldn't be an issue as I could either start easily from that point again, or assume the month is spelled wrong. But it creates reservations though march 18th. Also when I restarted this and set it to create blocked reservations starting at the beginning of April it got though December 8th.
My first guess is that I need to deal with reformatting the code to handle months without 31 days, but I assumed that those none existent days would just throw an error and the lop would continue, and it did get through February which is a short month.
Just thinking maybe someone who has more experience with Google Scripting may have an idea or see a flaw in something I am doing. Thanks for any help
function blockReservations(){
var roomcalendar = CalendarApp.getCalendarById('company.com_12458546525839392d898932#resource.calendar.google.com');
//for(var z=2014;z<=2020;z++){
//var year = z;
var year = '2014'; //This Line May be used in place of the above for loop to specify a specific year
for(var x=4;x<=12;x++)
{
if(x==1) var month = 'January';
else if(x==2) var month = 'February';
else if(x==3) var month = 'March';
else if(x==4) var month = 'April';
else if(x==5) var month = 'May';
else if(x==6) var month = 'June';
else if(x==7) var month = 'July';
else if(x==8) var month = 'August';
else if(x==9) var month = 'September';
else if(x==10) var month = 'October';
else if(x==11) var month = 'November';
else if(x==12) var month = 'December';
else month = 'null';
//var month = 'July'; //This Line May be used in place of the above for loop to specify a specific year
for(var y=1;y<=31;y++)
{
var date = y;
var startDateString = month + ' ' + date + ', ' + year +' 00:00:00';
var endDateString = month + ' ' + date + ', ' + year +' 24:00:00';
var event = roomcalendar.createEvent('Time Blocked', new Date(startDateString), new Date(endDateString));
}
}
// }
}

You don't mention any error messages, but I would expect that you're receiving a notification email reporting that the script was killed for running too long. Creating events in a loop can take lots of processing time.
I propose a different approach. Instead of creating daily events to reserve the room, why not create a recurring all-day event, starting a number of days in the future. Then each day, this reservation can be updated (by a timed trigger function) to revise the recurrence rule to start one day later.
/**
* Create or update a block reservation for a conference room,
* starting 'blockFrom' days from today.
*/
function updateBlockReservation() {
// Get Calendar
var calName = 'Huddle Room';
var cal = CalendarApp.getCalendarsByName(calName)[0];
var title = 'Reserved'; // Reserved events will have this title
var blockFrom = 7; // Days from now until room is blocked
var today = new Date(); // Today's date, ...
today.setHours(0,0,0,0); // at midnight.
var startDate // Daily block reservation starts here
= new Date(today.getTime() + (blockFrom * 24 * 60 * 60 * 1000));
var endTime = new Date(startDate.getTime() + (24 * 60 * 60 * 1000) - 1);
var recurrence = CalendarApp.newRecurrence().addDailyRule();
// Look for existing block reservation
var series = cal.getEvents(startDate, endTime, {search:title});
if (series.length == 0) {
// No block reservation found - create one.
var reserved = cal.createAllDayEventSeries(title, startDate, recurrence);
}
else {
// Block reservation exists - update the recurrence to start later.
reserved = series[0].getEventSeries();
reserved.setRecurrence(recurrence, startDate);
}
debugger; // Pause if running in debugger
}

Related

Export all events from a shared calendar, including events added by non-owners

I'm trying to extract Google Calendar event data into a spreadsheet, which works. However, the code I use only exports data which has been created by the owner of the Google Account/Calendar, not events that have been added by other users.
Below is a sample of some of the code I am using to get to the calendar and to retrieve events details.
Is there any way of achieving the above with details added by all users..?
var cal = CalendarApp.getCalendarById("name of calendar");
var events = cal.getEvents(new Date("18/08/2018 12:00 AM"), new Date("30/08/2018 12:00 AM"))
Edit:
Further to my original post, this may help..
Thanks for the replies so far... I'm not entirely sure what is going on in the code that has just been quoted, my full function is below. It may also help too if I give the practical example of what I'm trying to do.
A warehouse moves in /out a number of pallets each day. Details of these various in/outs are added as events on the "in" and "out" calendars. One calendar for each type of operation. Details are only entered into the "Title" section (e.g. PO numbers, etc.) and a time given. I had the idea of putting the number of pallets into the "Description" section so that when we extract events into a spreadsheet, we get a count of pallets in and out, each day, hour, etc.
function goodsin() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var cal = CalendarApp.getCalendarById("name of calendar");
var events = cal.getEvents(new Date("18/08/2018 12:00 AM"), new Date("30/08/2018 12:00 AM"));
for (var i = 0; i < events.length; i++) {
var title = events[i].getTitle();
var sd = events[i].getStartTime();
var ed = events[i].getEndTime();
var des = events[i].getDescription();
ss.getRange(i+2, 1).setValue(title)
ss.getRange(i+2, 2).setValue(sd);
ss.getRange(i+2, 2).setNumberFormat("dd/mm/yyyy h:mm:ss AM/PM")
ss.getRange(i+2, 3).setValue(ed);
ss.getRange(i+2, 3).setNumberFormat("dd/mm/yyyy h:mm:ss AM/PM")
ss.getRange(i+2, 4).setValue(des);
}
}
I can't reproduce your issue.
Events in "default" calendars
Using account A, I created an event on its default calendar, and invited account B. Account B has not accepted the invitation.
From account B, if I run the following code, I get the event information in my Stackdriver logs:
function getNextEvent_(calendars) {
if (!calendars)
calendars = CalendarApp.getAllCalendars();
const now = new Date();
const end = new Date();
return calendars.map(function (cal) {
var weeks = 4;
end.setTime(now.getTime());
do {
end.setDate(end.getDate() + 7)
var e = cal.getEvents(now, end, {max: 1}).pop();
} while (!e && --weeks);
return cal.getName() + ': ' +
(!e
? 'No visible events up to ' + end.toLocaleString()
: e.getTitle() + ' (Org. by ' + e.getCreators().join(',') + ')'
);
});
}
function logNextEvents() {
console.log({
message: "Upcoming Events",
events: getNextEvent_()
});
}
If I dig deeper, the event is found for the default calendar of Account B, i.e. CalendarApp.getDefaultCalendar().getEvents(...) contains the event owned and organized by Account A.
Events in a shared calendar
Setup:
Account A makes a new Google Calendar.
Account B is added with the permission to make changes to events.
Account C is added with the permission to make changes to events.
All accounts create an event on the calendar (3 events created).
Code:
function querySharedCal() {
const id = 'notarealgroupid#group.calendar.google.com';
const cal = CalendarApp.getCalendarById(id);
if (!cal) throw new Error("no calendar");
const now = new Date();
const end = new Date();
end.setDate(end.getDate() + 4);
const events = cal.getEvents(now, end).map(function (e) {
return cal.getName() + ': ' + e.getTitle() +
' (Org. by ' + e.getCreators().join(',') + ')';
});
console.log({message:"search result by " + Session.getTemporaryActiveUserKey(), events: events});
}
Cases:
Account A runs the above code
Account B runs the above code
Account C runs the above code
Results:
All cases return the 3 events. Even if each of the accounts a, b, c writes their own code (rather than having a single project which each can execute), all queries are successful.
Perhaps there is something you aren't sharing with us regarding the permission levels of the calendar, the accounts that create the events, the event visibilities, and the account from which you perform the queries.

Gmail script run on new mail recieved

I am trying to write a script that will forward mail from my work gmail to a personal email. The script needs to run only from Mon - Fri, between 4pm and 7am. The spot where I am getting stuck is being able to run the script every 5 mins during that time and forwarded only new mail.
Is their a google script api to do an action on received mail.(onRecieve)? Or I am I looking at adding a custom label and some if statements to ensure I don't forward duplicates.
Heres the generics of what I have going right now.
function startCustomTrigger()
{
ScriptApp.newTrigger('nonWorkHours').timeBased().everyMinutes(5).create();
}
function nonWorkHours() {
var date = new Date();
var day = date.getDay();
var hrs = date.getHours();
if ((day >= 1) && (day <= 5) && (hrs >= 16) && (hrs <= 7)) {
// forward email here
var thread = GmailApp.getInboxThreads(0,1)[0]; // get first thread in inbox
var message = thread.getMessages()[0]; // get first message
message.forward("example#example.com");
}
}
Updated script: FYI, works decent but needs some updating and cleaning.
/**
Forward unread inbox messages to personal email at desired hours. M - F from 4pm to 7am and Sat all day.
Also mark the messages that are forwarded with custom label "Forwarded(Non_Hours)" and marked as read.
Grab timestamp of last message that was forwarded and save as global script property.
tjones © 2015
TimeMailed
**/
// Custom trigger to run script every 5 mins
function startCustomTrigger()
{
ScriptApp.newTrigger('timeBound').timeBased().everyMinutes(5).create()
}
// Global Variables ====================================================================================================
var scriptProperties = PropertiesService.getScriptProperties();
//Grab current time of run
var startTime = new Date().getTime(); //Log the start of script--> combine with below Logger line
Logger.log("START OF RUN: " + Utilities.formatDate(new Date(startTime),Session.getScriptTimeZone(),'d MMM yy hh:mm:ss' )); //Log time into readable format
// Grab gmail inbox
var thread = GmailApp.search('is:unread'); //Grab all unread messages
var gmailMessages = GmailApp.getMessagesForThreads(thread); //Grab all the messages of given threads, set above
//Setup script timestamp properties
var lastForwardTime = scriptProperties.getProperties(); //Var for the timestamp of last message from last time scirpt ran
var keys = scriptProperties.getProperty('lastForward'); //The key from the lastForward timestamp
Logger.log("LAST TIMESTAMP OF MESSAGE FORWARDED: " + keys) //Log the key to the logger
Logger.log("label: " + GmailApp.createLabel("Forwarded(Non_Hours)")); //Create label, if exists will just overwrite
//Variable to set label "Forwarded(Non_Hours)" to threads being forwarded
var label = GmailApp.getUserLabelByName("Forwarded(Non_Hours)");
//Set some time formats to check if between M-F and 4pm to 7am
var date = new Date();
var day = date.getDay();
Logger.log(day);
var hrs = date.getHours();
Logger.log(hrs);
//=========================================================================================================================
if (hrs >= 16 && hrs <= 24) {
var inBound = true;
} else if (hrs >= 1 && hrs <= 6) {
inBound = true;
} else {
inBound = false;
}
function timeBound() {
if ((day >= 1) && (day <= 5) && (inBound == true)) {
timeMailed();
}
else if ((day >=6) && (day <=7)) {
timeMailed();
}
else {
Logger.log("Time is out of bounds, Within work hours: Sleeping for 5 mins");
}
}
// Meat and potatoes of forwarding
function timeMailed() {
for(var i=0;i<thread.length;i++){ //for loop for all the threads from above
var messagesForThread = gmailMessages[i]; //var for messages in threads
label.addToThread(thread[i]) // Set label to thread
GmailApp.markThreadRead(thread[i]); //Mark messages as read before forwarding them
for(var j=0;j<messagesForThread.length;j++){ //for loop to go through messages found above
// Get timestamps of messages for duplicate check
var messageDateTime = messagesForThread[j].getDate();
Logger.log(messagesForThread[j].getDate());
var messageEpocTime = messageDateTime.getTime();
Logger.log(messageDateTime.getTime());
// Compare message timestamp to lastForward key and make sure its newer than last check
if (messageEpocTime > scriptProperties.getProperty('lastForward')) {
scriptProperties.setProperty('lastForward', messageEpocTime); //Get date of messages and set as script property "lastForward"
messagesForThread[j].forward("tjones#livefake.com"); //forward the messages from above to forward address
Logger.log("Message with subject " + messagesForThread[j].getSubject() + " was forwarded")
}
else {
Logger.log("Message with subject " + messagesForThread[j].getSubject() + " was already forwarded within last 5 min check")
}
Logger.log("FINAL TIMESTAMP AT: " + scriptProperties.getProperty('lastForward') ); //Leave in final run to log last timestamp
}
}
}
what you have is a good start. to not forward duplicates you could use labels but its overkill in this case.
instead remember (in script properties service) the timestamp of the last email forwarded. then use search to only find emails "after" that time.
if you save the property after sending the email, you will never miss an email but may rarely send a duplicate if the sendmail call crashes right after sending.
If you instead save the property right before the mail you guarantee no duplicates but may miss some if it crashes right before sending the email.
look at docs and other s.o. questions for how to do a search "after:date". i have done this for dates but not datetime. if only date is supported you might need to skip some results or find a more efficient way to search.
using a label is good in the general case where there is no pattern to the sequence of emails. in your case new inbox mails are always consecutive.

Retreving Google Calendar Events using GAS

Am trying to get all the events in my calendar and i come up with the below code. But the problem is am getting only one event from the calender.(i.e, event which is nearby). can someone please help me where am going wrong and suggest me a way. Thanks in advance.
var cal = CalendarApp.getAllOwnedCalendars();
Logger.log("Length is :" +cal.length);
for(var i=0; i<cal.length;i++)
{
var cid = cal[i].getEvents(now, later) ;
Logger.log(cid[i].getTitle());
}
You can't get all the events if you don't know the end date. There is no way to determine the end date. You can retrieve all the events for today:
// Determines how many events are happening today.
var today = new Date();
var events = CalendarApp.getDefaultCalendar().getEventsForDay(today);
Logger.log('Number of events: ' + events.length);
for(var i=0; i<cal.length;i++) {
var eventTitle = events[i].getTitle();
Logger.log('eventTitle: ' + eventTitle);
}
You could set an end date that was many years into the past I suppose.
var now = new Date();
var later = new Date(2010, 0, 1);
Sets the later date to Jan. 1st, 2010

How to use timeZone of calendar to set timeZone for date object

I want to use the calendar timezone to set the time zone of a date object. I'm looking for the proper technique. We have several bases around the nation, and each has their own calendar for journal and daily activities. We have multiple scripts that post to the calendars. I want to use the timezone of the calendar to set the date Object timezone, because the users travel around to different bases, and their computers might not be set to the correct time zone. We want to avoid incorrect time settings.
Should the script's timeZone be set to UTC?
This is where I'm currently at:
function submitUiTest(e) {
var app = UiApp.getActiveApplication();
var cal = CalendarApp.getCalendarById('calendarId');
var timeZone = cal.getTimeZone();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var startTime = e.parameter.startDate
startTime.setHours(e.parameter.startHour, e.parameter.startMin, 0)
startTime = formatTime(startTime, timeZone);
Logger.log(startTime)
var endTime = e.parameter.endDate
endTime.setHours(e.parameter.endHour, e.parameter.endMin, 0);
endTime = formatTime(endTime, timeZone);
Logger.log(endTime)
cal.createEvent('TimeZone Test', new Date(startTime), new Date(endTime));
ss.appendRow([startTime, endTime]);
return app;
}
function formatTime(time, timeZone){
return Utilities.formatDate(time, (timeZone-time.getTimezoneOffset()), 'M/d/yyyy HH:mm');
}
Edit:
Currently there are 3 calendars, they are not user calendars, just each a separate calendar created for individual Air Stations. The air stations are each in separate time zone's. As crew members work at these stations they post daily activities to the calendars, and there are also several Ui scripts we have that post to the same calendars ex. a flight log. When an entry to a calendar is posted to any calendar, the time relates only to the timezone set on the script, not the timezone on the calendar. When the date or timestamp object is created, how can I use the timeZone that the calendar itself is set to.
What is best practice for scripts that record dates for different time zones?
Set the script timezone to UTC and do the conversion?
What do you use to get the user's timezone or in this case, I don't care what the user's timezone is set too, I need to use the timezone of the calendar.
Here is a modified version of the answer, without the UI stuff:
function gasTimezoneOffset(date, hour){
var cal,calTimeZone,calTz,date,dateString,scriptTimeZone,sessionTz;
var testMonth = "March",
testDayOfMnth = "26",
testYr = "2016",
hour = "10:00",
timeZoneDiff = 0;
Logger.log("Script Time Zone: " + Session.getScriptTimeZone());
dateString = testMonth + " " + testDayOfMnth + ", " + testYr;
Logger.log("dateString: " + dateString);
date = new Date(dateString);
cal = CalendarApp.getDefaultCalendar();
calTimeZone = cal.getTimeZone();
calTimeZone = Utilities.formatDate(date, calTimeZone, 'Z');
scriptTimeZone = Utilities.formatDate(date, Session.getTimeZone(), 'Z');
calTz = Number(calTimeZone.slice(0,3));
sessionTz = Number(scriptTimeZone.slice(0,3));
//If both time zones are the same sign, get the difference between the
//two. E.g. -4 and -2. Difference is 2
//if each time zone is a different sign, add the absolute values together.
//-4 and +1 should be 5
if (calTz < 0 && sessionTz > 0 || calTz > 0 && sessionTz < 0){
timeZoneDiff = Math.abs(Math.abs(calTz) + Math.abs(sessionTz));
} else {
timeZoneDiff = Math.abs(Math.abs(calTz) - Math.abs(sessionTz));
};
hour = Number(hour.slice(0,2));
return hour + timeZoneDiff;
};
EDIT : I wrote a small test UI and ran it with 2 calendars in 2 different timezones.
Calendar events are created in the Calendar timezone and the user interface shows user values. Google takes care of timezone settings in both calendars and I didn't notice any anomalies : events where created as I wanted to, ie at the time shown in the UI in the calendar Timezone.
(screen capture)
Here is the code I used for testing :
var tz = [['test_agenda(pacific Time)','test_agenda'],['testencodage(Belgium time)','testencodage']]
function doGet() {
var app = UiApp.createApplication().setStyleAttribute('padding','15PX');
var hpanel = app.createHorizontalPanel();
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox');
var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=0;h<25;++h){
if(h/2==parseInt(h/2)){hour.addItem(parseInt(h/2)+':00')
}else{
hour.addItem(parseInt(h/2)+':30')
}
}
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Submit').setId('submit');
var tzList = app.createListBox().setName('tzList');
for(var c in tz){tzList.addItem(tz[c][0],tz[c][1])}
var handler1 = app.createClientHandler().validateMatches(dateBox, '2','g').forEventSource().setEnabled(false);
var handler2 = app.createServerHandler('createE').validateMatches(dateBox, '2','g').addCallbackElement(hpanel).addCallbackElement(tzList);
submit.addClickHandler(handler1).addClickHandler(handler2);
hpanel.add(dateBox).add(hour).add(amPm)
app.add(hpanel);
app.add(tzList).add(submit);
app.add(dateTimeLabel);
return app;
}
function createE(e) {
var app = UiApp.getActiveApplication();
var date = e.parameter.dateBox;
var cal = CalendarApp.getCalendarsByName(e.parameter.tzList)[0]
var timeZone = cal.getTimeZone();
var sessionTz = Session.getTimeZone()
Logger.log(sessionTz)
Logger.log(timeZone)
var hour = Number(e.parameter.hour.split(':')[0]);
var min = Number(e.parameter.hour.split(':')[1]);
var amPm = e.parameter.amPm;
if (amPm == 'PM' ){hour = hour+12}; // ADD 12 HOURS TO PM HOURS, EXCEPT 12PM
if (hour == 24){hour = 0;amPm='AM'}; // HANDLE 12AM HOUR CORRECTLY
var newDate=new Date(date)
newDate.setHours(hour,min,0,0)
Logger.log('start : '+newDate)
var newDateString = Utilities.formatDate(newDate, sessionTz, 'MM/dd/yyyy hh:mm aaa');
app.getElementById('dateTimeLabel').setText('tz = '+timeZone+' - '+newDateString);
Logger.log('end : '+new Date(newDate.getTime()+3600000))
cal.createEvent('test Event in TZ '+timeZone, newDate, new Date(newDate.getTime()+3600000))
app.getElementById('submit').setEnabled(true);
return app;
}
first comment :
I began to write a comment to ask you some questions but it was getting too long... so please consider this as a comment, not an answer ;-).
I'm not sure I understand what you say about 'their computer not being set to the right timezone' the timezone of their computer is not relevant if they use Google Calendar, it is only a matter of parameter in Google Cal. If I understood your goal is that if the script sets an appointment at 8:00 AM they will see it shown at 8:00 AM in their Calendar wherever they are (8:00 AM in their 'local' timezone), right ?
So to summarize, you are running this script from one place and creating events for people in other timezones in their own calendars ? And are the users moving across different timezones ? These are 2 separate questions and it doesn't have one single solution.
If a single user is moving across timezones all the events will be shifted unless he doesn't change his GCal parameters. But if he does so he won't be notified at the right time and the Calendar interface will be out of time... so that's not a reasonable solution.
My last question : when you create an event in another calendar (in another tz) can it be an appointment between you and this person ? in other words is the absolute time of any importance for you use case ?
Ok here is a solution to the problem. I've probably went way out of my way or missed something simple but this finally works like I was hoping. Feel free to critique. I set the webapp timezone to GMT-0000 just for simplicity.
function uiTest() {
var app = UiApp.createApplication();
var startDate = app.createDateBox().setName('startDate').setId('startDate').setWidth('75');
var startHour = app.createListBox().setName('startHour').setId('startHour').setWidth('45');
var startMin = app.createListBox().setName('startMin').setId('startMin').setWidth('45');
var endDate = app.createDateBox().setName('endDate').setId('endDate').setWidth('75');
var endHour = app.createListBox().setName('endHour').setId('endHour').setWidth('45');
var endMin = app.createListBox().setName('endMin').setId('endMin').setWidth('45');
for (h=0;h<24;++h){
if(h<10){
var hourstr='0'+h
}else{
var hourstr=h.toString()
}
startHour.addItem(hourstr)
endHour.addItem(hourstr)
}
for (m=0;m<60;++m){
if(m<10){
var minstr='0'+m
}else{
var minstr=m.toString()
}
startMin.addItem(minstr)
endMin.addItem(minstr)
}
var grid = app.createFlexTable().setId('grid');
app.add(grid);
grid.setWidget(0, 0, app.createLabel('Start Date'));
grid.setWidget(1, 0, startDate);
grid.setWidget(0, 1, app.createLabel('Hour'));
grid.setWidget(1, 1, startHour);
grid.setWidget(0, 2, app.createLabel('Min'));
grid.setWidget(1, 2, startMin);
grid.setWidget(2, 0, app.createLabel('End Date'));
grid.setWidget(3, 0, endDate);
grid.setWidget(2, 1, app.createLabel('Hour'));
grid.setWidget(3, 1, endHour);
grid.setWidget(2, 2, app.createLabel('Min'));
grid.setWidget(3, 2, endMin);
app.add(app.createButton('Submit', app.createServerHandler('submitUiTest').addCallbackElement(grid)));
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function submitUiTest(e) {
var app = UiApp.getActiveApplication();
var cal = CalendarApp.getCalendarById('');//Info Sys Calendar set to Central Time
//var cal = CalendarApp.getCalendarById('');//Fort Bliss (Mountain Time)
var calTimeZone = cal.getTimeZone();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var startTime = e.parameter.startDate
startTime.setHours(gasTimezoneOffset(startTime, e.parameter.startHour, calTimeZone), e.parameter.startMin, 0);
Logger.log('startTime: '+Utilities.formatDate(startTime, calTimeZone, 'M/d/yyyy HH:mm z'));
var endTime = e.parameter.endDate
endTime.setHours(gasTimezoneOffset(endTime, e.parameter.endHour, calTimeZone), e.parameter.endMin, 0);
Logger.log('endTime: '+endTime)
var timeStamp = Utilities.formatDate(startTime, calTimeZone, 'M/d/yyyy HH:mm z');
cal.createEvent(timeStamp, new Date(startTime), new Date(endTime));
ss.appendRow([startTime, endTime]);
return app;
}
function gasTimezoneOffset(date, hour, calTimeZone){
var calTz = new Number(Utilities.formatDate(date, calTimeZone, 'Z').substr(1,2));
var sessionTz = new Number(Utilities.formatDate(date, Session.getTimeZone(), 'Z').substr(1,2));
switch (Utilities.formatDate(date, calTimeZone, 'Z').substring(0,1)){
case '+':
var timeZoneOffset = sessionTz - calTz;
break;
case '-':
var timeZoneOffset = sessionTz + calTz;
break;
}
hour = new Number(hour);
return hour + timeZoneOffset;
}
Here's the version of Sandy's code that I ended up using to create a function that takes the Calendar object and the "script time" and returns the "calendar time":
/**
* Given a script date object, return the time in the user's calendar
*
* Sandy Good's answer to this SO question:
*
* http://stackoverflow.com/questions/15645343/how-to-use-timezone-of-calendar-to-set-timezone-for-date-object
*
* #param {Date} scriptDateTime
* #param {Calendar} calendar
*
* #return {Date} calendarDateTime
*/
function getCalendarDateTime (scriptDateTime, calendar) {
Logger.log('scriptDateTime: ' + scriptDateTime)
var calendarTimeZoneString = calendar.getTimeZone()
var calendarTimeZone = Utilities.formatDate(scriptDateTime, calendarTimeZoneString, 'Z')
var calendarTz = Number(calendarTimeZone.slice(0,3))
Logger.log('calendarTimeZone: %s (%s)', calendarTimeZoneString, calendarTz)
var scriptTimeZoneString = Session.getScriptTimeZone()
var scriptTimeZone = Utilities.formatDate(scriptDateTime, scriptTimeZoneString, 'Z')
var sessionTz = Number(scriptTimeZone.slice(0,3))
Logger.log('scriptTimeZone: %s (%s)', scriptTimeZoneString, sessionTz)
// If both time zones are the same sign, get the difference between the
// two. E.g. -4 and -2. Difference is 2
//
// If each time zone is a different sign, add the absolute values together.
// -4 and +1 should be 5
var timeZoneDiff
if (calendarTz < 0 && sessionTz > 0 || calendarTz > 0 && sessionTz < 0) {
timeZoneDiff = Math.abs(Math.abs(calendarTz) + Math.abs(sessionTz))
} else {
timeZoneDiff = Math.abs(Math.abs(calendarTz) - Math.abs(sessionTz))
}
Logger.log('timeZoneDiff: ' + timeZoneDiff)
var scriptHour = scriptDateTime.getHours()
var calendarHour = scriptHour + timeZoneDiff
var calendarDateTime = new Date(
scriptDateTime.getYear(),
scriptDateTime.getMonth(),
scriptDateTime.getDate(),
calendarHour,
scriptDateTime.getMinutes())
Logger.log('calendarDateTime: ' + calendarDateTime)
return calendarDateTime
}
// Script is PST (GMT-8) and calendar is GMT
function test_getCalendarDateTime() {
var calendar = CalendarApp.getDefaultCalendar()
var scriptDateTime = new Date(2017, 0, 30, 12, 0) // 2017-01-30 12:00 PST
var calendarDateTime = getCalendarDateTime(scriptDateTime, calendar) // 2017-01-30 20:00 PST
}
Could try deploying a separate web app for each time zone. Set the time zone of each app to match that of the unique calendar they are linked to. Since you only have 3 calendars in different time zones, seems like it would work for your case.

CalendarEventSeries, missing setRecurrence() method?

Google scripts doc states CalendarEventSeries should have at least two methods setRecurrence(). When I'm trying to use it:
setRecurrence(new Date(), CalendarApp.newRecurrence().addYearlyRule().interval(1));
I'm getting this error:
Cannot find method (class)setRecurrence(object,$Proxy754). (line 60,
file "Sync").
The method is invoked on CalendarEventSeries for sure. How to resolve that error?
The example you show applies to all day Events (specifying only one date for the first occurrence), are you applying to such an event or to a 'normal' event ?
In this last case you should provide 2 dates (start and end) for the first occurrence.
The link above shows the documentation that is pretty clear about it...
Here is an "end-to-end" example that shows how it works, the first function creates an event with a yearly recurrence starting today, the second one changes this recurrence to be daily, starting today...
It uses your default calendar
function createTestSerie(){
var cal = CalendarApp.getDefaultCalendar()
if (cal) {
var title = 'Test Event';
var desc = 'Created using Google Apps Script';
var loc = 'Here, there and everywhere...';
var recur = CalendarApp.newRecurrence().addYearlyRule().interval(1)
var start = new Date();
var end = new Date(start.valueOf()+3600*1000);
var event = cal.createEventSeries(title, start, end,recur,{description:desc,location:loc});// event will be every year and last 1 hour
}
}
function modifyRecurrence(){
var cal = CalendarApp.getDefaultCalendar()
if (cal) {
var start = new Date();
var end = new Date(start.getTime()+3600*1000*2);
var events = cal.getEvents(new Date("February 16, 2013 08:00:00 PDT"), new Date("February 19, 2013 08:00:00 PDT"))
for(i in events){
if(events[i].getTitle()=='Test Event'){
var recur = CalendarApp.newRecurrence().addDailyRule().interval(1)
var eventId = events[i].getId()
cal.getEventSeriesById(eventId).setRecurrence(recur, start, end);// now events will be lasting 2 hours evey day starting today
}
}
}
}
EDIT following your comment : It works exactly the same for allDayEvents, here is a modified example code :
function createAllDaySerie(){
var cal = CalendarApp.getDefaultCalendar()
if (cal) {
var title = 'All Day Test Event';
var start = new Date();
var desc = 'Created using Google Apps Script';
var loc = 'home';
var recur = CalendarApp.newRecurrence().addYearlyRule().interval(4)
var start = new Date();
var event = cal.createAllDayEventSeries(title, start,recur,{description:desc,location:loc});// event will be every 4 years starting today
}
}
function modifyAllDayRecurrence(){
var cal = CalendarApp.getDefaultCalendar()
if (cal) {
var start = new Date("February 19, 2010 08:00:00 PDT");// here you can choose any date you like that will be the new start date.
var events = cal.getEvents(new Date("February 16, 2013 08:00:00 PDT"), new Date("February 22, 2013 08:00:00 PDT"))
for(i in events){
if(events[i].getTitle()=='All Day Test Event'){
var recur = CalendarApp.newRecurrence().addYearlyRule().interval(1)
var eventId = events[i].getId()
cal.getEventSeriesById(eventId).setRecurrence(recur, start);// now events will occur once a year starting on february 19, 2010 (see screen capture below)
}
}
}
}
And we have the solution for my problem. There is a bug in API documentation. It is written there:
method setRecurrence(startDate, recurrence)
Changes the recurrence rules of this series to a new rule, and to an all day event.
But in fact method setRecurrence() takes those arguments in upside down order:
method setRecurrence(recurrence, startDate)
Many thanks to Serge for help, it was crutial! Unfortunately without reputation 15 I'm not able to give you any points :(