How to modify a specific instance of an event series - google-apps-script

Let's suppose I create a recurring event that starts on Monday at 9 AM and ends at 11 AM, this event repeats every day for 3 days.
Now I want (after I have created the events using recurrence) to change the start time of the event on Tuesday while leaving the other events unchanged, how could I do ?
I can easily get the recurrence rule for this eventSeries using the advanced calendar API, it returns something like
RRULE:FREQ=DAILY;COUNT=3
This rule can be modified at will to change all the events (and I can also update the events using patch but not just for a single event.
I tried the API tryout to get every instance of this eventSeries and I can indeed see every start and end times (*see below) but I didn't find any method to get that using the Calendar API in Google Apps Script.
I didn't find a way to modify a particular instance, i.e. to write back the modified values.
Since I can do this manually in the calendar web UI I guess I should be able to do it using a script but I really don't know how.
The code I use to get the event parameters is fairly simple :
...
for(var n = 1 ; n < data.length ; n++){
if(data[n][9] == ''){continue};
var advancedID = data[n][9].substring(0,data[n][9].indexOf('#'));
Logger.log(advancedID+'\n');
ChangeEventRecurrence(calID,advancedID);
}
}
function ChangeEventRecurrence(calID,advancedID){
var event = Calendar.Events.get(calID, advancedID);
Logger.log(event.recurrence+'\n'+JSON.stringify(event));
// here I should be able to get all the instances of this eventSeries and change them if needed
}
Here is a capture of an instance I get using the API tryout :

With CalendarApp, just specify the date range to look for the single event and loop through the result to then use .setTime() :
var cal = CalendarApp.getCalendarsByName('<YOUR_CAL>')[0];
var events = cal.getEvents(new Date("April 5, 2016 08:00:00 AM"), new Date("April 5, 2016 11:30:00 AM"));
events.forEach( function(e) {
//var e = events[0];
//if ( e.getTitle() === 'Your Title' )
e.setTime(new Date("April 5, 2016 11:00:00 AM"), new Date("April 5, 2016 01:00:00 PM"));
});
I can't tell if your "specific instance" means just a single event or every Tuesday event though, or whether you have a reason to even use the Advanced Calendar API for this case.

Related

Calendar API to modify the following events only using Google Apps Script

I have events those repeat for every Monday starting from Feb 8, 2021. I want to modify the upcoming 28th Feb 2021 so that all future events will be repated every Wednesday. Note that the event must be updated will ALL FOLLOWING EVENTS (preserving the eventID)
var resource = Calendar.Events.get(calendar.getId(), eventId);
if (resource) {
resource.start.date = Utilities.formatDate(new Date("28-02-2021"), Session.getScriptTimeZone(), "yyyy-MM-dd");
resource.end.date = Utilities.formatDate(new Date("28-02-2021"), Session.getScriptTimeZone(), "yyyy-MM-dd");
var result = Calendar.Events.patch(resource, calendar.getId(), eventId)
}
This code will update all the recurring events including the past ones. But I need only the following events to be updated.
Based on the official document for Recurring Events, preserving the eventId is not possible since you need to create a new event based on your preferred modification and update the old recurring event and change its UNTIL parameter to end the series.
Modifying all following instances
In order to change all the instances of a recurring event on or after a given (target) instance, you must make two separate API requests. These requests split the original recurring event into two: the original one which retains the instances without the change and the new recurring event having instances where the change is applied:
Call events.update() to trim the original recurring event of the instances to be updated. Do this by setting the UNTIL component of the RRULE to point before the start time of the first target instance. Alternatively, you can set the COUNT component instead of UNTIL.
Call events.insert() to create a new recurring event with all the same data as the original, except for the change you are attempting to make. The new recurring event must have the start time of the target instance.
(UPDATE)
Sample Code:
function updateRecurr() {
var calendarId = 'c_9cdqeqqluk7vsessartfxxxxx';
var eventId = '17a97q639vmtkoghcxxxxx';
//Update eventseries, change "until" parameter in rrule to Feb. 27, 2020
var event = {
recurrence:[
"RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20210227;BYDAY=MO"
]
}
updatedEvent = Calendar.Events.patch(event,calendarId,eventId);
Logger.log(updatedEvent);
//Create new event having the same important info with different start time and rrule
var newEvent = {
summary: updatedEvent.summary,
description: updatedEvent.description,
start:{
date: Utilities.formatDate(new Date("March 3, 2021"), Session.getScriptTimeZone(), "yyyy-MM-dd")
},
end:{
date: Utilities.formatDate(new Date("March 3, 2021"), Session.getScriptTimeZone(), "yyyy-MM-dd")
},
recurrence:[
"RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20210331;BYDAY=WE"
]
}
var newEventSeries = Calendar.Events.insert(newEvent,calendarId);
Logger.log(newEventSeries);
}
OUTPUT:
Before:
After:
Note:
In the newly created event series, you will notice that I set the start date to "March 3, 2021", the reason behind this is because If I set that to "February 28, 2021" an event will also be created on that particular date. But in real life, when I created an event using Google Calendar and set the start date to "February 28, 2021" with event series of weekly occurrence every Wednesday. The actual start date set in the event was "March 3, 2021".
Just copy all other important event details if there is any, in your previous event series. In the example provided I just configured the event title and the description.

change Time inside object

My code creates an event inside Google Calendar, I need to get the value of start time and add 2 hours to get my end time.
Here's an example of the array my function returns:
[[kljlkjl, Manaf, Tue Jun 25 16:00:00 GMT+03:00 2019]]
This is part of the code that I want to fix:
var data = ss.getRange("A"+activeRow+":G"+activeRow).getValues();
if(cellContent === "Manaf") {
Logger.log(data);
Manaf.createEvent(data[0][0],data[0][2],data[0][2],{description: "First call "+ data[0][0]}) ;
Solution
To increment hours by 2, you can utilize getHours() and setHours() methods of the Date built-in object with the instance written into data variable. As you need to increment hours dynamically, you’ll have to pair these methods like this: dateInstance.setHours(dateInstance.getHours()+2).
Sample
So, your script with this modification will look like this (please, note that I also updated your code to work directly with 1-d Array as your range is always one-row):
var data = ss.getRange("A"+activeRow+":G"+activeRow).getValues()[0];
var start = data[2];
var end = new Date(start); //create new Date to persist start;
end.setHours(start.getHours()+2); //add 2 hours;
if(cellContent === "Manaf") {
Manaf.createEvent(data[0],start,end,{description: "First call "+ data[0]});
}
Useful links
Date built-in object reference;
getHours() method deeplink;
setHours() method deeplink;

Error Cannot convert Array to (class)[] in making a recurring calendar event with Google Apps Script

I am trying to create a recurring event in a Google Calendar but I keep getting the following error: Cannot convert Array to (class)[]
The problem lies in that I am trying to grab data from a cell to fill in the class. The code is the following:
var recur4 = CalendarApp.newRecurrence().addWeeklyRule().onlyOnWeeks([rep]);
var ne4 = c.createAllDayEventSeries(title, start, recur4, options);
Now, the variable rep is equal to cell H2 which has the following text in it: 31,36
When I put Logger.log(rep); it outputs 31,36 so there is no problem there either.
When I take out rep and put in 31,36 in the brackets, the script works perfectly and adds the events to the calendar, so I know that the problem is not anywhere else in the script.
I suppose that the problem has to do with the formatting in the cell, but I have no idea. Any help would be appreciated.
UPDATE
OK so based on the comment below, I changed the script to the following:
var sp = rep.split(",");
for(var i=0; i<sp.length; i++) { sp[i] = +sp[i]; }
var recur4 = CalendarApp.newRecurrence().addWeeklyRule().onlyOnWeeks(sp);
var ne4 = c.createAllDayEventSeries(title, start, recur4, options);
This got rid of the error, BUT now it is adding events every Friday. In the debugger, it now shows that the array is an integer array and comes out like this: [31,36] which should represent the two weeks I need, but something still does not work and the recur4 remains as undefined instead of an object.
UPDATE
Based on the comments that people gave below, the final script that worked fine was the following:
var recur4 = CalendarApp.newRecurrence().addYearlyRule().onlyOnWeeks(rep.split(",")).onlyOnWeekday(CalendarApp.Weekday.FRIDAY);
var ne4 = c.createEventSeries(title, start, stop, recur4, options);
The issue you have with your EventRecurrence specification is that you are specifying that this event should repeat weekly, but then use a restriction that is incompatible with a weekly restriction.
If you describe your condition with words, note that you cannot avoid saying "year". This is a strong indication that perhaps your recurrence period is incorrect.
E.g. "repeat this event every week, on weeks 31 and 36 of the year" vs. "repeat this event every year, on weeks 31 and 36"
Indeed, changing your restriction from weekly to yearly results in a valid RecurrenceRule:
var recur = CalendarApp.newRecurrence()
.addYearlyRule()
.onlyOnWeeks(rep.split(",").map(
function (week) {
return parseInt(week, 10);
})
);
References:
onlyOnWeeks
addYearlyRule
PS: the EventRecurrence and RecurrenceRule classes are pretty much interchangeable.

Google Apps Script: Calendar Service: Find first event in CalendarEventSeries

I'd like to calculate the age of a person whose birthday exists as event series within my calendar. To do this I need to know the first event within this series and that's the question: how to get the first event of a series from an actual event?
Thanks
Ronny
The other answer doesn't actually answer the initial request, the CalendarApp has no method to get the start date of a recurring event.
You should use the advanced Calendar API (must be enabled manually, see below and follow instructions)
Then use the advanced API to get the information you want, (the auto complete feature works on these methods too so you can easily see what is available)
Test code below, note that event ID is different for the advanced Calendar API, you have to remove the part after '#'.
function createTestEvents() {
var recurrence = CalendarApp.newRecurrence().addWeeklyRule().times(10);
var testEvent = CalendarApp.getDefaultCalendar().createEventSeries('test event serie', new Date('2016/05/10'), new Date(new Date('2016/05/10').getTime()+12*3600000), recurrence);
var id = testEvent.getId();
Logger.log('Event Series ID: ' + id);
viewTestEvent(id)
}
function viewTestEvent(id){
var event= CalendarApp.getDefaultCalendar().getEventSeriesById(id);
var calId = CalendarApp.getDefaultCalendar().getId();
Logger.log('event title = '+event.getTitle());
var AdvanncedId = id.substring(0,id.indexOf('#'));
Logger.log('AdvanncedId = '+AdvanncedId);
var testEvent = Calendar.Events.get(calId, AdvanncedId);
Logger.log('testEvent start = '+ testEvent.start);
return testEvent;
}
function test(){ // a few examples of what you can retrieve...
var event = viewTestEvent('59buf7nq6nr6qo79bh14kmsr6g#google.com');
Logger.log('\n\nstart = '+event.start);
Logger.log('\n\ncreated on = '+event.created);
Logger.log('\n\nend on = '+event.end);
Logger.log('\n\nrecurrence = '+event.recurrence);
}
You need to use the startDate parameter to get the date of the first event in the series (only the day is used; the time is ignored).
var eventSeries = CalendarApp.getDefaultCalendar().createAllDayEventSeries('No Meetings',
new Date('January 2, 2013 03:00:00 PM EST'),
CalendarApp.newRecurrence().addWeeklyRule()
.onlyOnWeekday(CalendarApp.Weekday.WEDNESDAY)
.until(new Date('January 1, 2014')));
Logger.log('Event Series ID: ' + eventSeries.getId());
You can also get the event series with the given ID using getEventSeriesById(iCalId).
If the ID given is for a single CalendarEvent, then a CalendarEventSeries will be returned with a single event in the series. Note that if the event series belongs to a calendar other than the default calendar, this method must be called from that Calendar; calling CalendarApp.getEventSeriesById(id) directly will only return an event series that exists in the default calendar.
Hope this helps!

Calculating runtime minus Timestamp

I have a form which activates a procedure via an "On form submit" trigger. At the end of this routine I want to insert the difference in time between the form's Timestamp and the current time at the end of the routine (the difference of which is only a matter of a few seconds).
I've tried many things so far, but the result I typically receive is NaN.
I thought that my best bet would be to construct the runtime elements (H,M,S) and similarly deconstruct the time elements from the entire Timestamp, and then perform a bit of math on that:
var rt_ts = Math.abs(run_time - ts_time);
(btw, I got that formula from somewhere on this site, but I'm obviously grasping at anything at this point. I just can't seem to find a thread where my particular issue is addressed)
I've always found that dealing with dates and time in Javascript is tricky business (ex: the quirk that "month" start at zero while "date" starts at 1. That's unnecessarily mind-bending).
Would anyone care to lead me out of my current "grasping" mindset and guide me towards something resembling a logical approach?
You can simply add this at the top of your onFormSubmit routine :
UserProperties.setProperty('start',new Date().getTime().toString())
and this at the end that will show you the duration in millisecs.
var duration = new Date().getTime()-Number(UserProperties.getProperty('start'))
EDIT following your comment :
the time stamp coming from an onFormSubmit event is the first element of the array returned by e.values see docs here
so I don't really understand what problem you have ??
something like this below should work
var duration = new Date().getTime() - new Date(e.values[0]).getTime();//in millisecs
the value being a string I pass it it 'new Date' to make it a date object again. You can easily check that using the logger like this :
Logger.log(new Date(e.values[0]));//
It will return a complete date value in the form Fri Mar 12 15:00:00 GMT+01:00 2013
But the values will most probably be the same as in my first suggestion since the TimeStamp is the moment when the function is triggered...
I have a function which can show the times in a ss with timestamps in column A. It will also add the time of the script itself to the first timestamp (in row 3) and show this in the Log.
Notice that the google spreadsheet timestamp has a resolution in seconds and the script timestamp in milliseconds. So if you only add, say, 300 milliseconds to a spreadsheet timestamp, it might not show any difference at all if posted back to a spreadsheet. The script below only takes about 40 milliseconds to run, so I have added a Utilities.sleep(0) where you can change the value 0 to above 1000 to show a difference.
function testTime(){
var start = new Date().getTime();
var values = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
for(var i = 2; i < values.length ; i++){
Logger.log(Utilities.formatDate(new Date(values[i][0]),Session.getTimeZone(),'d MMM yy hh:mm:ss' )); // this shows the date, in my case same as the ss timestamp.
Logger.log( new Date(values[i][0]).getTime() ); // this is the date in Milliseconds after 1 Jan 1970
}
Utilities.sleep(0); //you can vary this to see the effects
var endTime = new Date();
var msCumulative = (endTime.getTime() - start);
Logger.log(msCumulative);
var msTot = (msCumulative + new Date(values[2][0]).getTime());
Logger.log('script length in milliseconds ' + msTot );
var finalTime = Utilities.formatDate(new Date(msTot), Session.getTimeZone(), 'dd/MM/yyyy hh:mm:ss');
Logger.log ( finalTime); //Note that unless you change above to Utilities.sleep(1000) or greater number , this logged date/time is going to be identical to the first timestamp since the script runs so quickly, less than 1 second.
}