{"id":55000416,"date":"2024-02-01T00:00:00","date_gmt":"2024-02-24T18:01:10","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=416"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Google_Calendar_per_RestAPI_programmieren_Teil_2","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/Google_Calendar_per_RestAPI_programmieren_Teil_2\/","title":{"rendered":"Google Calendar per Rest-API programmieren, Teil 2"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg08.met.vgwort.de\/na\/93461d3238bd494396b6e83dc8656e45\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Im ersten Teil unserer Artikelserie haben wir grundlegende Funktionen vorgestellt, die es erm&ouml;glichen, Termine zwischen Outlook und Google Calendar zu synchronisieren. Jetzt gehen wir einen Schritt weiter und optimieren diese Funktionen an, um die Aktualisierung von Terminen effektiver zu gestalten. Zum Beispiel ist es von Vorteil, die EventID eines im Google Calendar erstellten Termins im entsprechenden Outlook-Termin zu speichern, um &Auml;nderungen zwischen beiden Kalendern synchron zu halten. Au&szlig;erdem f&uuml;gen wir neue Funktionen zur Aktualisierung von Kalendereintr&auml;gen und zum Abrufen von Terminen anhand ihrer EventID hinzu. Dar&uuml;ber hinaus diskutieren wir Herausforderungen, auf die wir gesto&szlig;en sind, und pr&auml;sentieren L&ouml;sungen sowie Erweiterungen, die wir implementiert haben.<\/b><\/p>\n<h2>Anpassungen der bestehenden Funktionen<\/h2>\n<p>Die Funktionen aus dem ersten Teil dieser Artikelreihe, <b>Google Calendar per Rest-API programmieren <\/b>(<b>www.vbentwickler.de\/410<\/b>), haben wir noch ein wenig angepasst, damit wir besser f&uuml;r Exporte und anschlie&szlig;end notwendige Aktualisieren vorbereitet sind.<\/p>\n<p>Wenn wir also einen Termin aus Outlook zum Google Calendar exportieren wollen, ist es sinnvoll, dass wir die <b>EventID<\/b> f&uuml;r den im Google Calendar angelegten Termin im Outlook-Termin zu speichern. Auf diese Weise k&ouml;nnen wir &Auml;nderungen an diesem Termin in Outlook auf den Termin im Google Calendar &uuml;bertragen.<\/p>\n<p>Und auch wenn der Termin in Outlook gel&ouml;scht wird, wollen wir dies gegebenenfalls im Google Calendar abbilden &#8211; also passen wir die Funktion, mit der wir einen Termin im Google Calendar anlegen, so an, dass diese die <b>EventID<\/b> des neu angelegten Termins zur&uuml;ckgibt.<\/p>\n<p>Diese schreiben wir in der Funktion <b>InsertEvent <\/b>in den Parameter <b>strEventID<\/b> (siehe Listing 1). Au&szlig;erdem haben wir noch weitere Parameter hinzugef&uuml;gt, mit denen man die Zeitzone f&uuml;r das Start- und das Enddatum &uuml;bergeben kann. Diese beiden Parameter hei&szlig;en <b>strStartTimeZone <\/b>und <b>strEndTimeZone <\/b>und erhalten den Standardwert <b>Europe\/Berlin<\/b>. Und mit dem Parameter <b>bolAllDayEvent<\/b> k&ouml;nnen wir angeben, ob es sich bei dem anzulegenden Termin um einen Ganztagestermin handelt.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>InsertEvent(strToken<span style=\"color:blue;\"> As String<\/span>, strCalendarID<span style=\"color:blue;\"> As String<\/span>, strSummary<span style=\"color:blue;\"> As String<\/span>, strDescription _\r\n        <span style=\"color:blue;\"> As String<\/span>, datStart<span style=\"color:blue;\"> As Date<\/span>, datEnd<span style=\"color:blue;\"> As Date<\/span>, <span style=\"color:blue;\">Optional<\/span> bolAllDayEvent<span style=\"color:blue;\"> As Boolean<\/span>, <span style=\"color:blue;\">Optional<\/span> strStartTimeZone _\r\n        <span style=\"color:blue;\"> As String<\/span> = \"Europe\/Berlin\", <span style=\"color:blue;\">Optional<\/span> strEndTimeZone<span style=\"color:blue;\"> As String<\/span> = \"Europe\/Berlin\", <span style=\"color:blue;\">Optional<\/span> intColor _\r\n        <span style=\"color:blue;\"> As <\/span>eColor = colLavendel, <span style=\"color:blue;\">Optional<\/span> strResponse<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> ByRef strEventID<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strURL<span style=\"color:blue;\"> As String<\/span>, strMethod<span style=\"color:blue;\"> As String<\/span>, strAuthorization<span style=\"color:blue;\"> As String<\/span>, objJSON<span style=\"color:blue;\"> As Object<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strJSON<span style=\"color:blue;\"> As String<\/span>, dictMain<span style=\"color:blue;\"> As <\/span>Dictionary, dictStart<span style=\"color:blue;\"> As <\/span>Dictionary, dictEnd<span style=\"color:blue;\"> As <\/span>Dictionary\r\n     <span style=\"color:blue;\">Set<\/span> dictMain = <span style=\"color:blue;\">New<\/span> Dictionary\r\n     <span style=\"color:blue;\">Set<\/span> dictStart = <span style=\"color:blue;\">New<\/span> Dictionary\r\n     <span style=\"color:blue;\">Set<\/span> dictEnd = <span style=\"color:blue;\">New<\/span> Dictionary\r\n     dictMain.Add \"start\", dictStart\r\n     dictMain.Add \"end\", dictEnd\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> bolAllDayEvent<span style=\"color:blue;\"> Then<\/span>\r\n         dictStart.Add \"dateTime\", Format(datStart, \"yyyy-mm-ddThh:nn:ss\")\r\n         dictEnd.Add \"dateTime\", Format(datEnd, \"yyyy-mm-ddThh:nn:ss\")\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         datStart = Int(datStart)\r\n         datEnd = datStart + 1\r\n         dictStart.Add \"dateTime\", Format(datStart, \"yyyy-mm-ddThh:nn:ss\")\r\n         dictEnd.Add \"dateTime\", Format(datEnd, \"yyyy-mm-ddThh:nn:ss\")\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     dictStart.Add \"timeZone\", strStartTimeZone\r\n     dictEnd.Add \"timeZone\", strEndTimeZone\r\n     dictMain.Add \"summary\", strSummary\r\n     dictMain.Add \"description\", strDescription\r\n     dictMain.Add \"colorId\", intColor\r\n     strJSON = ConvertToJson(dictMain)\r\n     strURL = cStrURLBase & \"\/calendars\/\" & strCalendarID & \"\/events\"\r\n     strMethod = \"POST\"\r\n     strAuthorization = \"Bearer \" & strToken\r\n     <span style=\"color:blue;\">If <\/span>RefreshAccessToken(\"InsertEvent\")<span style=\"color:blue;\"> Then<\/span>\r\n         Select Case HTTPRequest(strURL, strAuthorization, strMethod, , strJSON, strResponse)\r\n             <span style=\"color:blue;\">Case <\/span>200\r\n                 InsertEvent = <span style=\"color:blue;\">True<\/span>\r\n                 <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strResponse)\r\n                 strEventID = objJSON.Item(\"id\")\r\n             <span style=\"color:blue;\">Case Else<\/span>\r\n                 <span style=\"color:blue;\">MsgBox<\/span> strResponse\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Access-Token ung&uuml;ltig oder nicht aktualisierbar.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Neue Version der Funktion InsertEvent<\/span><\/b><\/p>\n<p>F&uuml;r den Parameter <b>intColor <\/b>haben wir als Standardwert den Wert <b>colLavendel <\/b>vorgemerkt.<\/p>\n<p>Die Zeitzonen &uuml;bergeben wir in der Funktion jeweils f&uuml;r das Element <b>timeZone<\/b>.<\/p>\n<p>F&uuml;r die Verarbeitung des Parameters <b>bolAllDayEvent <\/b>mussten wir eine <b>If&#8230;Then<\/b>-Bedingung zur Funktion hinzuf&uuml;gen. Wenn <b>bolAllDayEvent <\/b>den Wert <b>False <\/b>enth&auml;lt, werden die Werte f&uuml;r Start- und Enddatum wie zuvor hinzugef&uuml;gt. Im <b>Else<\/b>-Teil der Bedingung behandeln wir den Fall eines ganzt&auml;gigen Termins.<\/p>\n<p>Hier ermitteln wir als Startzeit das Datum der mit <b>datStart <\/b>&uuml;bergebenen Startzeit und stellen die Uhrzeit auf <b>00:00 <\/b>ein, indem wir die Nachkommastellen von <b>datStart <\/b>mit der <b>Int<\/b>-Funktion entfernen.<\/p>\n<p>Als Enddatum legen wir in <b>datEnd <\/b>das Datum des folgenden Tages fest &#8211; wieder mit der Uhrzeit <b>00:00<\/b>. Die Rest-API von Google Calendar sieht keine spezielle Eigenschaft f&uuml;r einen ganzt&auml;tigen Termin vor, sodass wir diese Notation verwenden.<\/p>\n<p>In der Benutzeroberfl&auml;che sehen wir anschlie&szlig;end allerdings einen Ganztagestermin (siehe Bild 1). Wenn die Funktion <b>HTTPRequest <\/b>den Wert <b>True <\/b>zur&uuml;ckliefert, lesen wir aus dem mit <b>strResponse <\/b>gelieferten JSON-Dokument den Wert <b>id <\/b>aus und tragen diesen f&uuml;r den Parameter <b>strEventID <\/b>ein. Diesen kann man dann nach dem Aufruf verarbeiten, wenn dies n&ouml;tig ist.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_416_001.png\" alt=\"Ein ganzt&auml;giger Termin im Google Calendar\" width=\"499,6267\" height=\"281,9833\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Ein ganzt&auml;giger Termin im Google Calendar<\/span><\/b><\/p>\n<h2>Rest-API-Funktion zum Abrufen eines Termins per EventID<\/h2>\n<p>Bei den im ersten Teil dieses Artikels beschriebenen Funktionen k&ouml;nnen wir die kompletten Informationen zu einem Termin abrufen. In den Beispielen speichern wir diese Daten in einer Access-Tabelle, darunter auch die <b>EventID<\/b>, ein eindeutiges Merkmal eines Google Calendar-Termins. Damit k&ouml;nnen wir wiederum gezielt erneut auf einen solchen Termin zugreifen &#8211; beispielsweise um den aktuellen Stand abzufragen, denn ein Termin kann ja auch durch den Benutzer in Google ge&auml;ndert werden.<\/p>\n<p>Dies erledigen wir mit der Funktion <b>GetEventByID<\/b> (siehe Listing 2). Diese erwartet folgende Parameter:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>GetEventByID(strToken<span style=\"color:blue;\"> As String<\/span>, strCalendarID<span style=\"color:blue;\"> As String<\/span>, strEventID<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> strJSON _\r\n        <span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strURL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strMethod<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strResponse<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAuthorization<span style=\"color:blue;\"> As String<\/span>\r\n     strURL = cStrURLBase & \"\/calendars\/\" & strCalendarID & \"\/events\/\" & strEventID\r\n     strMethod = \"GET\"\r\n     strAuthorization = \"Bearer \" & strToken\r\n     <span style=\"color:blue;\">If <\/span>RefreshAccessToken(\"GetEventByID\")<span style=\"color:blue;\"> Then<\/span>\r\n         Select Case HTTPRequest(strURL, strAuthorization, strMethod, , , strResponse)\r\n             <span style=\"color:blue;\">Case <\/span>200\r\n                 strJSON = strResponse\r\n                 GetEventByID = <span style=\"color:blue;\">True<\/span>\r\n             <span style=\"color:blue;\">Case Else<\/span>\r\n                 GetEventByID = <span style=\"color:blue;\">False<\/span>\r\n         <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"Access-Token ung&uuml;ltig oder nicht aktualisierbar.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Funktion zum Einlesen eines Termins von Google<\/span><\/b><\/p>\n<ul>\n<li><b>strToken<\/b>: Access-Token f&uuml;r den Zugriff auf den Google Calendar<\/li>\n<li><b>strCalendarID<\/b>: Bezeichnung des Kalenders, in der Regel die Google-E-Mail-Adresse<\/li>\n<li><b>strEventID<\/b>: ID des zu ermittelnden Termins<\/li>\n<li><b>strJSON<\/b>: Parameter, der das Ergebnis der Abfrage entgegennimmt<\/li>\n<\/ul>\n<p>Die Funktion stellt die URL f&uuml;r den Zugriff auf die Rest-API zusammen, die beispielsweise so aussieht:<\/p>\n<pre>https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/andre.minhorst@googlemail.com\/events\/36kis3n1kt1sc5ch82a54k4u5k<\/pre>\n<p>Als Methode verwenden wir <b>GET<\/b>, und mit der Variablen <b>strAuthorization <\/b>&uuml;bergeben wir das Token an die Funktion, die den eigentlichen Request ausf&uuml;hrt. Zuvor rufen wir noch die Prozedur <b>CheckToken <\/b>aus, welche die G&uuml;ltigkeit des Tokens pr&uuml;ft.  Ist dieses ung&uuml;ltig, erfolgt erst gar kein Zugriff auf die Rest-API und es erscheint eine Meldung. Danach erfolgt der Aufruf der Funktion <b>HTTPRequest<\/b>. Das Ergebnis ist ein Status. Lautet dieser <b>200<\/b>, war der Zugriff erfolgreich und wir geben die Antwort &uuml;ber den R&uuml;ckgabeparameter <b>strJSON <\/b>zur&uuml;ck. Au&szlig;erdem stellen wir den R&uuml;ckgabewert der Funktion auf <b>True <\/b>ein.<\/p>\n<p>Diese Funktion rufen wir beispielsweise wie folgt auf:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Test_GetEventByID()\r\n     <span style=\"color:blue;\">Dim <\/span>strToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strCalendarID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strJSON<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>objJSON<span style=\"color:blue;\"> As Object<\/span>\r\n     strToken = GetAppSetting(\"AccessToken\")\r\n     strCalendarID = GetAppSetting(\"CalendarID\")\r\n     strEventID = \"36kis3n1kt1sc5ch82a54k4u5k\"\r\n     If GetEventByID(strToken, strCalendarID, strEventID, _\r\n             strJSON) = <span style=\"color:blue;\">True<\/span> Then\r\n         <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strJSON)\r\n         <span style=\"color:blue;\">Debug.Print<\/span> GetJSONDOM(strJSON, <span style=\"color:blue;\">True<\/span>)\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Die beiden Parameter <b>strToken <\/b>und <b>strCalendarID <\/b>lesen wir aus der Registry aus, die <b>EventID <\/b>haben wir hier fest angegeben. War der Aufruf erfolgreich, lassen wir uns das Zugriffsmodell f&uuml;r das JSON-Dokument im Direktbereich ausgeben. Hier k&ouml;nnen wir uns dann die Elemente heraussuchen, die wir in der aufrufenden Prozedur weiterverarbeiten wollen (siehe Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_416_002.png\" alt=\"Ausdr&uuml;cke zum Auslesen von Termineigenschaften\" width=\"549,6265\" height=\"370,8219\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Ausdr&uuml;cke zum Auslesen von Termineigenschaften<\/span><\/b><\/p>\n<h2>Rest-API-Funktion zum Aktualisieren eines Kalendereintrags<\/h2>\n<p>Wenn wir einen vorhandenen Termin im Google Calendar aktualisieren wollen, haben wir zwei M&ouml;glichkeiten. Die erste ist die Patch-Methode. Damit k&ouml;nnen wir gezielt einzelne Eigenschaften anpassen. Mehr dazu liest Du hier:<\/p>\n<pre>https:\/\/developers.google.com\/calendar\/api\/v3\/reference\/events\/patch?hl=en<\/pre>\n<p>Bei dieser Methode werden nur die Eigenschaften des Termins aktualisiert, die wir im Aufruf der Rest-API an Google &uuml;bergeben. Die &uuml;brigen Eigenschaftswerte werden beibehalten. Die Alternative ist das Update. Damit aktualisieren wir den kompletten Termin:<\/p>\n<pre>https:\/\/developers.google.com\/calendar\/api\/v3\/reference\/events\/update?hl=en<\/pre>\n<p>Damit werden alle Eigenschaften aktualisiert. Wir machen es uns einfach und aktualisieren einfach den kompletten Termin. Die dazu verwendete Funktion <b>UpdateEvent <\/b>ist der Funktion <b>InsertEvent <\/b>sehr &auml;hnlich, daher haben wir in nur die Teile abgebildet, wo sich Unterschiede befinden.<\/p>\n<p>Neben dem Funktionsnamen ist dies vor allem die Methode des Aufrufs, hier <b>PUT <\/b>statt <b>POST<\/b>. Die URL wird um die EventID aus der Variablen <b>strEventID <\/b>erg&auml;nzt. Au&szlig;erdem enth&auml;lt die Zeile, die dem R&uuml;ckgabewert den Wert <b>True <\/b>zuweist, den Namen der Funktion (siehe Listing 3).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>UpdateEvent(strToken<span style=\"color:blue;\"> As String<\/span>, strCalendarID<span style=\"color:blue;\"> As String<\/span>, strSummary<span style=\"color:blue;\"> As String<\/span>, strDescription _\r\n        <span style=\"color:blue;\"> As String<\/span>, datStart<span style=\"color:blue;\"> As Date<\/span>, datEnd<span style=\"color:blue;\"> As Date<\/span>, bolAllDayEvent, <span style=\"color:blue;\">Optional<\/span> strStartTimeZone _\r\n        <span style=\"color:blue;\"> As String<\/span> = \"Europe\/Berlin\", <span style=\"color:blue;\">Optional<\/span> strEndTimeZone<span style=\"color:blue;\"> As String<\/span> = \"Europe\/Berlin\", <span style=\"color:blue;\">Optional<\/span> _\r\n         intColor<span style=\"color:blue;\"> As <\/span>eColor = colLavendel, <span style=\"color:blue;\">Optional<\/span> strResponse<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> ByRef strEventID<span style=\"color:blue;\"> As String<\/span>) _\r\n        <span style=\"color:blue;\"> As Boolean<\/span>\r\n     ''... Identisch mit InsertEvent\r\n         strURL = cStrURLBase & \"\/calendars\/\" & strCalendarID & \"\/events\/\" & strEventID\r\n         strMethod = \"PUT\"\r\n         strAuthorization = \"Bearer \" & strToken\r\n         <span style=\"color:blue;\">If <\/span>RefreshAccessToken(\"UpdateEvent\")<span style=\"color:blue;\"> Then<\/span>\r\n             Select Case HTTPRequest(strURL, strAuthorization, strMethod, , strJSON, strResponse)\r\n                 <span style=\"color:blue;\">Case <\/span>200\r\n                     UpdateEvent = <span style=\"color:blue;\">True<\/span>\r\n                     <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strResponse)\r\n                     strEventID = objJSON.Item(\"id\")\r\n                 <span style=\"color:blue;\">Case <\/span>404\r\n                     <span style=\"color:blue;\">MsgBox<\/span> \"Der Termin mit der EventID ''\" & strEventID & \"'' konnte nicht gefunden werden.\"\r\n                 <span style=\"color:blue;\">Case Else<\/span>\r\n                     <span style=\"color:blue;\">MsgBox<\/span> strResponse\r\n             <span style=\"color:blue;\">End Select<\/span>\r\n         <span style=\"color:blue;\">Else<\/span>\r\n             <span style=\"color:blue;\">MsgBox<\/span> \"Access-Token ung&uuml;ltig oder nicht aktualisierbar.\"\r\n         <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Funktion zum Aktualisieren eines Termins<\/span><\/b><\/p>\n<p>Schlie&szlig;lich haben wir die <b>Select Case<\/b>-Bedingung, mit der wir den R&uuml;ckgabewert der Funktion <b>HTTPRequest <\/b>abfragen, um einen Zweig f&uuml;r den Wert 404 erweitert. Dieser wird angesteuert, wenn wir eine EventID angeben, die es nicht gibt, und gibt eine entsprechende Meldung aus.<\/p>\n<h2>&#8220;L&ouml;schen&#8221; von Terminen im Google Calender<\/h2>\n<p>Um die Funktion <b>UpdateEvent <\/b>auszuprobieren, haben wir einen Termin mit der Funktion <b>InsertEvent <\/b>im Google Calendar angelegt. Die Funktion hat daraufhin die <b>EventID <\/b>zur&uuml;ckgeliefert:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Test_InsertEvent()\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     If InsertEvent(GetAppSetting(\"AccessToken\"), _\r\n         \"andre.minhorst@googlemail.com\", \"Summary\", _\r\n         \"Description\", Now, Now + 1 \/ 24, , , , , , _\r\n         strEventID) = <span style=\"color:blue;\">True<\/span> Then\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"Termin ''\" & strEventID & \"'' angelegt.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Danach haben wir diese EventID als letzten Parameter eines Aufrufs der Funktion <b>UpdateEvent <\/b>genutzt und konnten damit den Termin in Google aktualisieren:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Test_UpdateEvent()\r\n     <span style=\"color:blue;\">Dim <\/span>strEventID<span style=\"color:blue;\"> As String<\/span>\r\n     strEventID = \"leka4d24rapsjiu77fqikvturk\"\r\n     If UpdateEvent(GetAppSetting(\"AccessToken\"), _\r\n             \"andre.minhorst@googlemail.com\", _\r\n             \"Neue Summary\", Description\", Now, _\r\n             Now + 1 \/ 24, , , , , , _\r\n             strEventID) = <span style=\"color:blue;\">True<\/span> Then\r\n         <span style=\"color:blue;\">Debug.Print<\/span> \"Termin ''\" & strEventID & \"'' ge&auml;ndert.\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Dies hat den Betreff des Termins wie gew&uuml;nscht ge&auml;ndert.<\/p>\n<p>Dann haben wir den Termin durch Markieren im Google Calendar und anschlie&szlig;endes Bet&auml;tigen der <b>Entfernen<\/b>-Taste gel&ouml;scht.<\/p>\n<p>Danach haben wir erneut die Prozedur <b>Test_UpdateEvent <\/b>aufgerufen. Dies f&uuml;hrte allerdings nicht zu der Meldung, dass es keinen Termin mit dieser <b>EventID<\/b> gibt, sondern es erschien eine Meldung &uuml;ber die erfolgreiche Aktualisierung.<\/p>\n<p>Au&szlig;erdem erschien der bereits gel&ouml;schte Termin wieder im Google Calendar. Das konnten wir auch reproduzieren, wenn wir die Funktion <b>DeleteEvent <\/b>verwendet haben, um den Termin zu l&ouml;schen.<\/p>\n<p>Also haben wir den Termin nochmals gel&ouml;scht und uns diesen mit der Funktion <b>GetEventByID <\/b>erneut geholt, um seinen JSON-Code anzusehen. Dieser sieht wie folgt aus und die entscheidende Eigenschaft hei&szlig;t <b>status <\/b>&#8211; hier finden wir den Wert <b>cancelled <\/b>vor:<\/p>\n<pre>{\r\n  \"kind\": \"calendar#event\",\r\n  \"etag\": \"\\\"3408935113444000\\\"\",\r\n  \"id\": \"e4d35j9hsdovo57be5kigrg2f0\",\r\n  \"status\": \"cancelled\",\r\n  ...\r\n  \"eventType\": \"default\"\r\n}<\/pre>\n<p>Das hei&szlig;t, dass das L&ouml;schen eines Termins lediglich zur &Auml;nderung des Status in den Wert <b>cancelled <\/b>f&uuml;hrt.<\/p>\n<p>Mehr Informationen dazu finden wir auf der folgenden Seite, welche die Eigenschaften eines Event-Elements erl&auml;utert:<\/p>\n<pre>https:\/\/developers.google.com\/calendar\/api\/v3\/reference\/events?hl=en<\/pre>\n<p>Hier lesen wir von folgenden Werten f&uuml;r den Status:<\/p>\n<ul>\n<li><b>confirmed<\/b>: Der Termin ist best&auml;tigt.<\/li>\n<li><b>tentative<\/b>: Der Termin ist vorl&auml;ufig best&auml;tigt.<\/li>\n<li><b>cancelled<\/b>: Der Termin wurde abgesagt (gel&ouml;scht).<\/li>\n<\/ul>\n<p>Termine werden als <b>cancelled <\/b>markiert und nicht gel&ouml;scht, weil er unter Umst&auml;nden mehrere Teilnehmer hat. Wenn ein Termin, der bereits an Teilnehmer kommuniziert wurde, gel&ouml;scht wird, sollten auch alle Teilnehmer &uuml;ber die Absage des Termins informiert werden k&ouml;nnen. Deshalb wird der Termin nicht direkt gel&ouml;scht. Wir k&ouml;nnen aber mit einem bestimmten Parameter daf&uuml;r sorgen, dass direkt beim L&ouml;schen alle Teilnehmer von der Absage informiert werden.<\/p>\n<h2>Kalendereintr&auml;ge einlesen<\/h2>\n<p>Im ersten Teil der Artikelreihe mit dem Titel <b>Google Calendar per Rest-API programmieren <\/b>(<b>www.vbentwickler.de\/410<\/b>) haben wir gezeigt, wie wir Kalendereintr&auml;ge aus dem Google Calendar einlesen k&ouml;nnen.<\/p>\n<p>Als wir die Funktion im Rahmen dieses Artikels getestet haben, hat alles wie gew&uuml;nscht funktioniert &#8211; anscheinend passte die Konstellation zu diesem Zeitpunkt und mit den get&auml;tigten Abfragen.<\/p>\n<p>Mittlerweile haben wir die Erfahrung gemacht, dass es etwas komplizierter ist, &uuml;ber die Rest-API an die gew&uuml;nschten Events zu kommen.<\/p>\n<p>In einem neueren Test im Rahmen des vorliegenden Artikels konnten wir pl&ouml;tzlich keinerlei Events mehr auslesen.<\/p>\n<p>Die gute Nachricht war: Es lag nicht an unserem Code.<\/p>\n<p>Wir haben die gleiche Abfrage n&auml;mlich auf der Webseite von Google ausgef&uuml;hrt, wo wir einfach einmal alle Termine einlesen wollten:<\/p>\n<pre>https:\/\/developers.google.com\/calendar\/api\/v3\/reference\/events\/list<\/pre>\n<p>Hier haben wir nur f&uuml;r <b>calendarId<\/b> den Namen unseres Google-Kontos eingegeben und alle anderen Parameter leer gelassen (siehe Bild 3).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_416_003.png\" alt=\"Testen einer Termin-Abfrage\" width=\"700\" height=\"492,9785\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 3: Testen einer Termin-Abfrage<\/span><\/b><\/p>\n<p>Das Ergebnis nach einem Klick auf die Schaltfl&auml;che <b>Execute <\/b>lieferte zwar keine Fehler, aber auch keine Termine, obwohl wir definitiv einige Termine angelegt hatten (siehe Bild 4).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_01\/pic_416_004.png\" alt=\"Ergebnis eines ersten Tests\" width=\"424,6267\" height=\"529,6775\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 4: Ergebnis eines ersten Tests<\/span><\/b><\/p>\n<p>Also untersuchen wir einmal genauer, woran dies liegt. Unten sehen wir ein leeres Element namens <b>items<\/b>. Interessanterweise befindet sich dar&uuml;ber ein Element namens <b>nextPageToken<\/b>. Wir haben damit ausprobiert, ob sich die gesuchten Events vielleicht mit einer weiteren Abfrage finden lassen, die wir mit dem Wert aus <b>nextPageToken <\/b>f&uuml;r die Eigenschaft <b>pageToken <\/b>ausgef&uuml;hrt haben. Es erscheint der gleiche Inhalt, aber diesmal mit einem anderen Wert f&uuml;r <b>nextPageToken<\/b>. Vielleicht ist dies ein Bug.<\/p>\n<h2>Nur Einzeltermine ausgeben<\/h2>\n<p>Als N&auml;chstes haben wir ausprobiert, durch Einstellen der Option <b>singleEvents <\/b>auf den Wert <b>true <\/b>nur einzelne Termine auszulesen und keine Serientermine. Dies lieferte 250 Eintr&auml;ge<\/p>\n<p>Die Einstellung <b>false <\/b>f&uuml;r den Wert <b>singleEvents <\/b>lieferte wie erwartet keine Eintr&auml;ge, da wir nur einzelne Termine angelegt hatten.<\/p>\n<p>Die Abfrage der <b>singleEvents <\/b>enthielt jedoch wieder den Parameter <b>nextPageToken<\/b>, den wir bei der n&auml;chsten Abfrage f&uuml;r die Eigenschaft <b>pageToken <\/b>angegeben haben. Dies lieferte die n&auml;chsten 250 Eintr&auml;ge. Auf diese Weise k&ouml;nnen wir alle Eintr&auml;ge durchlaufen &#8211; zumindest die als <b>singleEvents <\/b>angelegten.<\/p>\n<p>Weiterhin haben wir dann mit den Parametern f&uuml;r die Datums- und Zeitangabe experimentiert und alles in eine Prozedur gepackt. Dabei handelt es sich um eine Weiterentwicklung der Prozedur <b>GetEventList <\/b>aus dem ersten Teil der Artikelreihe (siehe Listing 4).<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>GetEventList(strToken<span style=\"color:blue;\"> As String<\/span>, strCalendarID<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> intSingleEvents<span style=\"color:blue;\"> As Integer<\/span> = 1, _\r\n         <span style=\"color:blue;\">Optional<\/span> strPageToken<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> datStart<span style=\"color:blue;\"> As Date<\/span>, <span style=\"color:blue;\">Optional<\/span> datEnd<span style=\"color:blue;\"> As Date<\/span> , <span style=\"color:blue;\">Optional<\/span> intTimeZone<span style=\"color:blue;\"> As Integer<\/span>)\r\n     <span style=\"color:blue;\">Dim <\/span>strURL<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strMethod<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strResponse<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strAuthorization<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>objJSON<span style=\"color:blue;\"> As Object<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>strNextPageToken<span style=\"color:blue;\"> As String<\/span>\r\n     <span style=\"color:blue;\">Dim <\/span>lngItems<span style=\"color:blue;\"> As Long<\/span>\r\n     strURL = cStrURLBase & \"\/calendars\/\" & strCalendarID & \"\/events\"\r\n     Select Case intSingleEvents\r\n         <span style=\"color:blue;\">Case <\/span><span style=\"color:blue;\">False<\/span>\r\n             strURL = strURL & \"?singleEvents=false\"\r\n         <span style=\"color:blue;\">Case <\/span><span style=\"color:blue;\">True<\/span>\r\n             strURL = strURL & \"?singleEvents=true\"\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strPageToken) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n         AddAndOrQuestionmark strURL\r\n         strURL = strURL & \"pageToken=\" & strPageToken\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> datStart = 0<span style=\"color:blue;\"> Then<\/span>\r\n         AddAndOrQuestionmark strURL\r\n         strURL = strURL & \"timeMin=\" & Format(DateAdd(\"h\", -intTimeZone, datMin), \"yyyy-mm-ddThh:nn:ssZ\")\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> datEnd = 0<span style=\"color:blue;\"> Then<\/span>\r\n         AddAndOrQuestionmark strURL\r\n         strURL = strURL & \"timeMax=\" & Format(DateAdd(\"h\", -intTimeZone, datMax), \"yyyy-mm-ddThh:nn:ssZ\")\r\n     <span style=\"color:blue;\">End If<\/span>\r\n     strMethod = \"GET\"\r\n     strAuthorization = \"Bearer \" & strToken\r\n     Select Case HTTPRequest(strURL, strAuthorization, strMethod, , , strResponse)\r\n         <span style=\"color:blue;\">Case <\/span>200\r\n             <span style=\"color:blue;\">Set<\/span> objJSON = ParseJson(strResponse)\r\n             SaveEventsToTable objJSON\r\n             lngItems = objJSON.Item(\"items\").Count\r\n             <span style=\"color:blue;\">Debug.Print<\/span> lngItems, objJSON.Item(\"nextPageToken\")\r\n             <span style=\"color:blue;\">If <\/span>lngItems &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n                 strNextPageToken = objJSON.Item(\"nextPageToken\")\r\n                 <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> <span style=\"color:blue;\">Len<\/span>(strNextPageToken) = 0<span style=\"color:blue;\"> Then<\/span>\r\n                     GetEventList strToken, strCalendarID, intSingleEvents, strNextPageToken\r\n                 <span style=\"color:blue;\">End If<\/span>\r\n             <span style=\"color:blue;\">End If<\/span>\r\n         <span style=\"color:blue;\">Case Else<\/span>\r\n             <span style=\"color:blue;\">MsgBox<\/span> strResponse\r\n     <span style=\"color:blue;\">End Select<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Funktion zum Auslesen von Terminen von Google<\/span><\/b><\/p>\n<p>Diese Prozedur erwartet nun einige Parameter mehr als die erste Version:<\/p>\n<ul>\n<li><b>strToken<\/b>: Access-Token<\/li>\n<li><b>strCalendarID<\/b>: ID des Kalenders, in der Regel die E-Mail-Adresse des Google-Kontos<\/li>\n<li><b>bolSingleEvents<\/b>: Gibt an, ob wiederkehrende Ereignisse auf Instanzen erweitert werden und nur einzelne einmalige Ereignisse und Instanzen wiederkehrender Ereignisse zur&uuml;ckgegeben werden sollen, aber nicht die zugrunde liegenden wiederkehrenden Ereignisse selbst. Optional. Die Standardeinstellung ist <b>False<\/b> (&uuml;bernommen aus der Google-Dokumentation). <\/li>\n<li><b>strPageToken<\/b>: Gibt das PageToken f&uuml;r die n&auml;chste Seite an, wenn eine Folgeseite eingelesen werden soll.<\/li>\n<li><b>datMin<\/b>: Parameter, der die Obergrenze f&uuml;r die Startzeit der gesuchten Ereignisse enth&auml;lt. Muss im Format <b>YYYY-MM-DDTHH:NN:SSZ <\/b>vorliegen &#8211; gegebenenfalls noch mit Zeitzone: <b>YYYY-MM-DDTHH:NN:SSZ-HH:NN<\/b>.<\/li>\n<li><b>datMax<\/b>: Parameter, der die Untergrenze f&uuml;r die Endzeit der gesuchten Ereignisse enth&auml;lt. Muss im Format <b>YYYY-MM-DDTHH:NN:SSZ <\/b>vorliegen &#8211; gegebenenfalls noch mit Zeitzone: <b>YYYY-MM-DDTHH:NN:SSZ-HH:NN<\/b>.<\/li>\n<li><b>intTimeZone<\/b>: Faktor f&uuml;r die Zeitzone. In Deutschland (<b>+01:00<\/b>) gibt man hier einfach den Wert <b>1 <\/b>an. Wir k&ouml;nnten diesen Parameter auch mit den Standardwert <b>1 <\/b>versehen, da dieser Code vermutlich prim&auml;r in Deutschland verwendet wird.<\/li>\n<\/ul>\n<p>Die vier letzten Parameter sind optional. Die Prozedur stellt zun&auml;chst die Basis-URL zusammen, die wie folgt aussieht:<\/p>\n<pre>https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/andre.minhorst@googlemail.com\/events<\/pre>\n<p>Der erste Parameter wird verwertet, indem wir in einer <b>Select Case<\/b>-Bedingung seinen Wert untersuchen. Lautet der Wert <b>False<\/b>, wird der Ausdruck <b>?singleEvents=false <\/b>an die URL angeh&auml;ngt. Lautet der Wert <b>True<\/b>, h&auml;ngen wir den Ausdruck <b>?singleEvents=true <\/b>an die URL an. Hier k&ouml;nnen wir problemlos das Fragezeichen (<b>?<\/b>) als Verbindungsoperator hinzuf&uuml;gen, da es sich um den ersten Parameter der URL handelt.<\/p>\n<p>Grunds&auml;tzlich gilt hier: Vor dem ersten als Parameter verwendeten Name-Wert-Paar muss ein Fragezeichen stehen, vor allen folgenden das Und-Zeichen (<b>&#038;<\/b>).<\/p>\n<p>F&uuml;r den zweiten Parameter <b>strPageToken <\/b>m&uuml;ssen wir also pr&uuml;fen, ob zuvor bereits ein anderer Parameter angeh&auml;ngt wurde und davon abh&auml;ngig das Fragezeichen oder das Und-Zeichen voranstellen. Da wir das noch &ouml;fter machen m&uuml;ssen, verwenden wir dazu eine kleine Funktion. Diese sieht wie folgt aus:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>AddAndOrQuestionmark(strURL<span style=\"color:blue;\"> As String<\/span>) _\r\n        <span style=\"color:blue;\"> As Boolean<\/span>\r\n     <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">InStr<\/span>(1, strURL, \"?\") = 0<span style=\"color:blue;\"> Then<\/span>\r\n         strURL = strURL & \"?\"\r\n     <span style=\"color:blue;\">Else<\/span>\r\n         strURL = strURL & \"&\"\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Diese Funktion nimmt die URL entgegen und pr&uuml;ft, ob diese bereits ein Fragezeichen enth&auml;lt. Falls ja, h&auml;ngt sie das Und-Zeichen an, falls nein, das Fragezeichen.<\/p>\n<p>Danach h&auml;ngt die Prozedur <b>GetEventList <\/b>den Ausdruck <b>pageToken=[pageToken] <\/b>an die URL an.<\/p>\n<p>Schlie&szlig;lich h&auml;ngen wir noch das Startdatum und das Enddatum an. Das Startdatum muss kleiner sein als das Enddatum, wenn beide angegeben sind. Dies pr&uuml;fen wir in einer Bedingung, die wir nicht im Listing abgebildet haben:<\/p>\n<pre><span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> datMin = 0 And <span style=\"color:blue;\">Not<\/span> datMax = 0<span style=\"color:blue;\"> Then<\/span>\r\n     <span style=\"color:blue;\">If <\/span>datMin &gt; datMax<span style=\"color:blue;\"> Then<\/span>\r\n         <span style=\"color:blue;\">MsgBox<\/span> \"datMin muss kleiner als datMax sein.\", _\r\n             vbExclamation + vbOKOnly, \"Ung&uuml;ltige Parameter\"\r\n         <span style=\"color:blue;\">Exit Sub<\/span>\r\n     <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End If<\/span><\/pre>\n<p>Wenn <b>datStart <\/b>nicht gleich <b>0 <\/b>ist, also ein Datum &uuml;bergeben wurde, f&uuml;hren wir wieder die Funktion <b>AddAndOrQuestionmark <\/b>aus. Anschlie&szlig;end h&auml;ngen wir das &uuml;bergebene Datum, gegebenenfalls mit Uhrzeit, an <b>strURL <\/b>an. Dabei formatieren wir das Datum im oben angegebenen Format und ziehen noch den Faktor f&uuml;r die Zeitzone aus dem Parameter <b>intTimeZone <\/b>ab, sodass der angeh&auml;ngte Ausdruck hinter dem Und- oder Fragezeichen wie folgt aussieht:<\/p>\n<pre>timeMin=2024-01-01T00:00:00Z<\/pre>\n<p>Auf die gleiche Weise h&auml;ngen wir den Parameter f&uuml;r <b>timeMax <\/b>an:<\/p>\n<pre>timeMax=2024-01-01T00:00:00Z<\/pre>\n<p>Danach folgt die Zuweisung der Methode (<b>GET<\/b>) und der aufruf der <b>HTTPRequest<\/b>-Methode mit der URL, der Authentifizierung, der Methode und dem R&uuml;ckgabeparameter <b>strResponse <\/b>f&uuml;r die Antwort der Rest-API.<\/p>\n<p>Im Falle des R&uuml;ckgabewertes 200 war der Aufruf erfolgreich. Dann schreiben wir die Antwort in das Objekt <b>objJSON<\/b>, auf das wir dann mit der &uuml;blichen Syntax zugreifen k&ouml;nnen. Um die Ausdr&uuml;cke f&uuml;r den Zugriff auszugeben, kann an dieser Stelle die folgende Anweisung eingebaut werden:<\/p>\n<pre>GetJSONDOM strResponse, True, <span style=\"color:blue;\">True<\/span><\/pre>\n<p>Danach rufen wir die Prozedur <b>SaveEventsToTable <\/b>auf (diese ist nur in Access verf&uuml;gbar und schreibt die Termine in die Tabelle <b>tblEvents<\/b> &#8211; siehe Beispieldatenbank <b>GoogleCalendar2.accdb<\/b>).<\/p>\n<p>Danach tragen wir die Anzahl der gefundenen Termine in die Variable <b>lngItems <\/b>ein. Wenn die Anzahl gr&ouml;&szlig;er als <b>0 <\/b>ist, speichern wir den Wert der Eigenschaft <b>nextPageToken <\/b>in der Variablen <b>strNextPageToken<\/b>. Wenn <b>strNextPageToken <\/b>eine L&auml;nge gr&ouml;&szlig;er als <b>0 <\/b>aufweist, enth&auml;lt die aktuelle Seite Eintr&auml;ge als auch in <b>NextPageToken <\/b>den Hinweis, dass es auf einer weiteren Seite noch mehr Eintr&auml;ge gibt.<\/p>\n<p>In diesem Fall rufen wir die Prozedur <b>GetEventList<\/b> nochmals auf, diesmal mit dem Wert aus <b>strNextPageToken <\/b>als Parameter. Dadurch wird bei diesem Aufruf die zweite Seite der Ergebnisliste abgefragt.<\/p>\n<p>Damit k&ouml;nnen wir nun Ergebnis aus bestimmten Zeitr&auml;umen und auch &uuml;ber mehrere Seiten verteilt abrufen und verarbeiten. Wenn Du etwas anderes machen m&ouml;chtest, als die Termine in eine Access-Datenbank einzutragen, kannst Du die notwendigen Anweisungen oder den Aufruf der ben&ouml;tigten Funktion an Stelle dieser Zeile einf&uuml;gen:<\/p>\n<pre>SaveEventsToTable objJSON<\/pre>\n<h2>Nach Daten und Zeiten filtern<\/h2>\n<p>Damit kommen wir zu einem interessanten Thema: dem Filtern von Events nach Datumsangaben. Dabei m&uuml;ssen wir ber&uuml;cksichtigen, dass der Parameter <b>timeMin <\/b>die Untergrenze f&uuml;r das Ende und <b>timeMax <\/b>die Obergrenze f&uuml;r den Beginn der zu suchenden Ereignisse ist.<\/p>\n<p>Wenn wir also den <b>1.1.2024 <\/b>als <b>timeMin <\/b>und den <b>2.1.2024 <\/b>als <b>timeMax <\/b>angeben, werden alle Termine geliefert, die zwischen diesen beiden Daten enden oder beginnen (oder beides).<\/p>\n<p>Noch interessanter ist das Thema Uhrzeit. Wir schauen uns beispielsweise den folgenden Aufruf an:<\/p>\n<pre>GetEventList GetAppSetting(\"AccessToken\"), \"andre.minhorst@googlemail.com\", True, , CDate(\"1.1.2024 10:00:00\"), CDate(\"1.1.2024 10:30:00\")<\/pre>\n<p>Hier wollen wir eigentlich die Termine suchen, die am 1.1.2024 zwischen 10:00 Uhr und 10:30 Uhr liegen. Allerdings suchen wir tats&auml;chlich nach den Terminen zwischen 10:00 Uhr und 10:30 Uhr in der Zeitzone, f&uuml;r die 0:00 Stunden Zeitverschiebung angegeben sind &#8211; n&auml;mlich GMT, also Greenwich Mean Time.<\/p>\n<p>Zum Suchen von Terminen in Deutschland m&uuml;ssen wir noch den Wert <b>1 <\/b>f&uuml;r den Parameter <b>intTimeZone <\/b>angeben.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>GoogleCalendar2.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/3A24BB0B-F247-4AFE-B2AE-CDE85620CACE\/vbe_416.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kalendereintr&auml;ge einlesen<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[66012024,662024,44000012,44000004,44000025],"tags":[],"yst_prominent_words":[],"class_list":["post-55000416","post","type-post","status-publish","format-standard","hentry","category-66012024","category-662024","category-Interaktiv","category-Loesungen","category-VBAProgrammierung"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000416","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000416"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000416\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000416"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000416"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000416"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000416"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}