1

I'm creating something of a booking system which is nearly complete. Currently I'm collecting data from a form, passing it to a sheet, and then using some of that info to create a calendar event. This all works fine.

On creating the event, I'm also collecting the eventID so that I can use it to identify the event and update it from the spreadsheet. These updates are also working, with the exception of updating the start/end date and time which causes the following error:

TypeError: Cannot find function setTime in object CalendarEventSeries.

This is the code I'm working with:

var eventStart =  sh.getRange("D"+rowNumber).getValues();
var eventEnd =  sh.getRange("E"+rowNumber).getValues();
event.setTime(eventStart, eventEnd);

I'm doing exactly the same thing with setLocation and setTitle without a problem.

I'm new to this, I don't know what an object is and so the error message means very little to me! But I can see that setTime is a method outlined in the class 'CalendarEvent' (https://developers.google.com/apps-script/reference/calendar/calendar-event#setTime(Date,Date)), but not in 'CalendarEventSeries'. All my events are on-off events anyway?

Thanks in advance for any pointers.

UPDATE

I have integrated Mogsdad's Advanced Calendar Service code, and after

"var endTime = parseDate(event.end.date||event.end.dateTime);"

I am checking/logging 'startTime' and 'event'. 'startTime' is coming back as 'invalid date' (bad thing?) and 'event' is coming back with all the calendar entry info I can imagine (good thing I hope?!).

Where should the parseDate function actually go? Maybe I have it in the wrong place (I've tried it all over the place!) and this isn't being used?

Also, now the event I want to edit has been identified, are the dates parsed and used to search for the event I have already found in order to return a Calendar event that I can ultimately use setTime on? Is that the whole point of this?

Thanks for bearing with me.

UPDATE 2 - INVALID DATE?

If I skip the parsing and log the variable like this:

var startTime = event.start.dateTime;

The result is 2015-05-24T02:00:00+01:00 which I think is spot on. So the invalid date is definitely a case of something going wrong during the parse function as it only then that it returns 'invalid date'.

Code in context below.

function onOpen() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var menuEntries = [{name: "Create Event Document Manually", functionName: "addSheet"},{name: "Update Calendar Entry", functionName: "getEventById"}
  ];


  ss.addMenu("Select Row & Click Here", menuEntries);
}



/**
 * Retrieve a CalendarApp CalendarEvent object from IDs.
 * This version utilizes the Advanced Calendar Service, which must be
 * enabled before use.
 *
 * @param {string} calendarId   ID of calendar to be searched
 * @param {string} eventId      Event ID to match
 *
 * @returns {CalendarEvent}     CalendarApp CalendarEvent object or null
 */


function getEventById(calendarId, eventId) {

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sh = ss.getActiveSheet();
  var rowNumber = sh.getActiveRange().getRowIndex();
  var myEventId = "q8q533oioluipirksmno88qv2g";
  var calendarId = "mydomain.tv_q9608ku2min78rasgt2s2n233c@group.calendar.google.com";


  // Get event by ID.
  var event = Calendar.Events.get(calendarId, myEventId);
  // This isn't a CalendarApp CalendarEvent though, so use the info
  // in the event to find it AGAIN as the right type.




  // Get start & end times of event. All-day events start at start.date,
  // while other events start at start.datetime. Likewise for end.

  var startTime = parseDate(event.start.date||event.start.dateTime);
  var endTime = parseDate(event.end.date||event.end.dateTime);



   Logger.log('Variables: ' + startTime + ' and ' + endTime);

  // Get array of events that fall between startTime & endTime
  var calEvents = CalendarApp.getEvents(startTime, endTime);

  // Search through those events, looking for a match by ID
  for (var i=0; i<calEvents.length; i++) {
    var curId = calEvents[i].getId().split('@')[0];  // extract id from id@calendar.google.com
    if (curId == eventId) {
      // Mission accomplished; we have an Event object with given id!
      return calEvents[i];

    }
  }
  // We did not find matching event
  return null;


}




  function parseDate(string) {
  var parts = string.split('T');
  parts[0] = parts[0].replace(/-/g, '/');
  return new Date(parts.join(' '));
}
James
  • 83
  • 2
  • 6
  • Yes, the edited update is right. That's a way to get the right event object. – Serge insas Jun 05 '15 at 21:25
  • Excellent, thanks for clarifying Serge. This feels like progress! When I log 'event', this is the data I receive regarding the start date/time: "start":{"dateTime":"2015-05-24T02:00:00+01:00","timeZone":"Europe/London"}, Is this as expected and is there any reason why this wouldn't parse and cause the invalid date? – James Jun 05 '15 at 21:45
  • The helper function Mogsdad writes below ( parseDate(string)) deals with that format, it splits the string on the letter "T", takes the first part only and replaces hyphens by "/" so that the format is usable as an argument for the new Date statement. You shouldn't get an error there. – Serge insas Jun 05 '15 at 21:58
  • Indeed, I can see what the function is doing and why it's necessary, but in this case I really don't know if it's doing it or not. If the date is formatted incorrectly, before or after it was parsed, I would still expect to see it instead of 'invalid date' when I log the startTime/endTime variables, no? What is deciding the date is invalid? Invalid for what?! I'm not trying to do anything with it yet! I will post my code in it's entirety to the original post, it's likely a stupid mistake on my part... – James Jun 05 '15 at 23:10

2 Answers2

2

Serge has it right - the problem is that you've retrieved a CalendarEventSeries object, not a CalendarEvent. Since the only method in the service that will look for an event by ID is getEventSeriesById(iCalId), you're kinda stuck.

One option is to use the Advanced Calendar Service instead:

var event = Calendar.Events.get(calendarId, eventId);

For new code, that's a great option, especially for developers already used to Javascript APIs. If you're a beginner or not familiar with the Advanced Services, though, you'll find that there's a steeper learning curve than for the Calendar Service.

In that case, these utilities should help you stick with the CalendarApp and its Classes and Methods, by filling the need for a getEventById() function.

Advanced Calendar Service under the hood

There are two versions of getEventById(). This first one utilizes the Advanced Calendar Service, which must be enabled before use. The code is fairly straight-forward. You must provide the Calendar ID explicitly, since this isn't a Class Method. Example:

var calendarId = CalendarApp.getDefaultCalendar().getId();
var eventId = "smmd8h1dfe9lo9bip52hidnqk0";
var event = getEventById(calendarId, eventId);

Code:

/**
 * Retrieve a CalendarApp CalendarEvent object from IDs.
 * This version utilizes the Advanced Calendar Service, which must be
 * enabled before use.
 *
 * @param {string} calendarId   ID of calendar to be searched
 * @param {string} eventId      Event ID to match
 *
 * @returns {CalendarEvent}     CalendarApp CalendarEvent object or null
 */
function getEventById(calendarId, eventId) {
  // Get event by ID.
  var event = Calendar.Events.get(calendarId, eventId);
  // This isn't a CalendarApp CalendarEvent though, so use the info
  // in the event to find it AGAIN as the right type.

  // Get start & end times of event. All-day events start at start.date,
  // while other events start at start.datetime. Likewise for end.
  var startTime = parseDate(event.start.date||event.start.dateTime);
  var endTime = parseDate(event.end.date||event.end.dateTime);

  // Get array of events that fall between startTime & endTime
  var calEvents = CalendarApp.getEvents(startTime, endTime);

  // Search through those events, looking for a match by ID
  for (var i=0; i<calEvents.length; i++) {
    var curId = calEvents[i].getId().split('@')[0];  // extract id from id@calendar.google.com
    if (curId == eventId) {
      // Mission accomplished; we have an Event object with given id!
      return calEvents[i];
    }
  }
  // We did not find matching event
  return null;
}

Calendar API via UrlFetchApp

This version utilizes the Calendar API via UrlFetchApp, which doesn't require any special enablement. However, the code is more complex than the previous version.

/**
 * Retrieve a CalendarApp CalendarEvent object from IDs.
 * This version utilizes the Calendar API via UrlFetchApp, so
 * requires no enablement. However, it's more complex.
 *
 * @param {string} calendarId   ID of calendar to be searched
 * @param {string} eventId      Event ID to match
 *
 * @returns {CalendarEvent}     CalendarApp CalendarEvent object or null
 */
function getEventById(calendarId, eventId) {
  // Prepare a GET request to API URL, to Get event by ID.
  var url = "https://www.googleapis.com/calendar/v3/calendars/calendarId/events/eventId"
            .replace("calendarId",calendarId)
            .replace("eventId",eventId);

  var options = {
    headers: {
      'Authorization': 'Bearer ' +  ScriptApp.getOAuthToken()
    }
  }

  // Send request
  var response = UrlFetchApp.fetch(url, options);
  var rc = response.getResponseCode();
  var text = response.getContentText();

  // If result code is 200OK, process response text
  if (rc == 200) {
    // The event is contained in the response text; parse it into an object
    var event = JSON.parse(text);
    // This isn't a CalendarApp CalendarEvent though, so use the info
    // in the event to find it AGAIN as the right type.

    // Get start & end times of event. All-day events start at start.date,
    // while other events start at start.datetime. Likewise for end.
    var startTime = parseDate(event.start.date||event.start.dateTime);
    var endTime = parseDate(event.end.date||event.end.dateTime);

    // Get array of events that fall between startTime & endTime
    var calEvents = CalendarApp.getEvents(startTime, endTime);

    // Search through those events, looking for a match by ID
    for (var i=0; i<calEvents.length; i++) {
      var curId = calEvents[i].getId().split('@')[0];  // extract id from id@calendar.google.com
      var desc = calEvents[i].getDescription();
      if (curId == eventId) {
        // Mission accomplished; we have an Event object with given id!
        return calEvents[i];
      }
    }
    // We did not find matching event
    return null;
  }
  else
    // An error in fetch, anything BUT 200
    throw new Error( ""+rc+JSON.parse(text).message );
}

Helper function

Both versions of getEventById() require this helper function, provided in Google's documentation.

/**
 * From https://developers.google.com/apps-script/advanced/calendar#listing_events
 *
 * Parses an RFC 3339 date or datetime string and returns a corresponding Date
 * object. This function is provided as a workaround until Apps Script properly
 * supports RFC 3339 dates. For more information, see
 * https://code.google.com/p/google-apps-script-issues/issues/detail?id=3860
 * @param {string} string The RFC 3339 string to parse.
 * @return {Date} The parsed date.
 */
function parseDate(string) {
  var parts = string.split('T');
  parts[0] = parts[0].replace(/-/g, '/');
  return new Date(parts.join(' '));
}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
  • Thanks so much for this Mogsdad. I have been looking at the Advanced Calendar Service option, on the face of it the code looks more accessible to me. I will update my initial post with progress... – James Jun 05 '15 at 15:36
  • Hi Mogsdad, the helper function doesn't work anymore. Any idea why ? I wrote a new one myself but I still wonder why it suddenly stopped working.I used an example value like this : 2016-03-30T06:00:00.000Z , values with TZ indication work normally (but client javascript in a webapp returns the first model !) – Serge insas Mar 31 '16 at 19:27
  • Hi Serge. It works for me - I just grabbed the code from the docs, enabled the calendar API, and ran it. Not sure what you mean by the client js part. – Mogsdad Mar 31 '16 at 20:23
  • As of Jul 2017 (https://issuetracker.google.com/issues/36761294), the issue has been tagged as fix and the helper function `parseDate(...)` is no longer needed as you can now use `new Date(event.start.date || event.start.dateTime`. – Kenston Choi Sep 17 '17 at 09:42
  • For `getEventById(...)`, `var calEvents = CalendarApp.getEvents(startTime, endTime);` didn't work for me, but I have to pass my `calendar` instance and use it as `var calEvents = calendar.getEvents(startTime, endTime);` – Kenston Choi Sep 17 '17 at 09:57
1

When you retrieve your event from its ID you are (most probably) using the method getEventSerieById(ID) which returns a CalendarEventSeries, not a CalendarEvent.

If you take a look at these 3 referenced docs, you'll notice that CalendarEventSeries class has no setTime() method.

The error message you get is actually quite explicit in this case.

You should find a few posts that deal with this issue, this one for example : Create Google Calendar Events from Spreadsheet but prevent duplicates

Community
  • 1
  • 1
Serge insas
  • 45,904
  • 7
  • 105
  • 131
  • Thanks for the info Serge. I'll give that read now. As far as I'm aware though, I'm not using anything related to series... when I initially create the event I store the ID using this: dbsheet.getRange("P"+rowNumber).setValue(event.getId()); which generates something like this: '38be9v478l3ga8pjo58ht0i008@google.com and when I want to edit it (in a separate function) I pull it back out for use like this: var id = sh.getRange("P"+rowNumber).getValues(); I'm sure there's good reason for the error - but I'm failing to see it! – James Jun 04 '15 at 23:03
  • The reason for the error is described in the answer Serge linked to. Using an `eventID` to retrieve an event means that you must use `getEventSeriesById(iCalId)` - there is no other method that accepts an ID to grab an event. That method returns an `eventSeries` object - even for single events. The `eventSeries` object does not have a method `setTime()`. See [issue 1154](http://code.google.com/p/google-apps-script-issues/issues/detail?id=1154). – Mogsdad Jun 05 '15 at 02:15
  • Apologies, I was indeed using getEventSeriesById, I have no recollection of doing that at all... but there it is! var event = MDcalendar.getEventSeriesById(id); So yes, exactly why setTime() isn't an available method like all the others I'll never understand, but thanks for clarifying the error for me. – James Jun 05 '15 at 14:48