Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recurring yearly appointments created using Google API #853

Closed
ghost opened this issue Oct 16, 2016 · 14 comments
Closed

Recurring yearly appointments created using Google API #853

ghost opened this issue Oct 16, 2016 · 14 comments
Assignees

Comments

@ghost
Copy link

ghost commented Oct 16, 2016

I posted this question to StackOverflow (http://stackoverflow.com/questions/39966524/recurring-yearly-appointments-created-using-google-api) but got no reply with real solution. Additionally I have tested that Google APIs Explorer gives correct results, so something could be wrong with .NET client

I am working on Open Source app to synchronize calendars between Google and Outlook. Some users reported some strange behavior for recurring appointments with start date years ago. Below there is sample code, all asserts are passing. My question is why for appointment starting 1970-10-14T08:00:00.000Z hour is shifted by API to 09:00:00+01:00 and if I shift start date to year 2000 it is shifted to 10:00:00+02:00 ? When I check how both events looks in Google they are starting on different hours, and in my opinion should start the same hour (as only year was changed)

        var e1 = new Google.Apis.Calendar.v3.Data.Event()
        {
            Summary = "Birthday 1",
            Start = new EventDateTime()
            {
                DateTime = new DateTime(1970, 10, 14, 10, 0, 0),
                TimeZone = "Europe/Warsaw"
            },
            End = new EventDateTime()
            {
                DateTime = new DateTime(1970, 10, 14, 11, 0, 0),
                TimeZone = "Europe/Warsaw"
            },
            Recurrence = new String[] { "RRULE:FREQ=YEARLY;BYMONTHDAY=14;BYMONTH=10" }
        };

        Assert.AreEqual("1970-10-14T08:00:00.000Z", e1.Start.DateTimeRaw);
        var c1 = service.Insert(e1, primaryCalendar.Id).Execute();
        Assert.AreEqual("1970-10-14T09:00:00+01:00", c1.Start.DateTimeRaw);

        var e2 = new Google.Apis.Calendar.v3.Data.Event()
        {
            Summary = "Birthday 2",
            Start = new EventDateTime()
            {
                DateTime = new DateTime(2000, 10, 14, 10, 0, 0),
                TimeZone = "Europe/Warsaw"
            },
            End = new EventDateTime()
            {
                DateTime = new DateTime(2000, 10, 14, 11, 0, 0),
                TimeZone = "Europe/Warsaw"
            },
            Recurrence = new String[] { "RRULE:FREQ=YEARLY;BYMONTHDAY=14;BYMONTH=10" }
        };

        Assert.AreEqual("2000-10-14T08:00:00.000Z", e2.Start.DateTimeRaw);
        var c2 = service.Insert(e2, primaryCalendar.Id).Execute();
        Assert.AreEqual("2000-10-14T10:00:00+02:00", c2.Start.DateTimeRaw);

And I would like to know what I need to do to save old recurring appointments correctly to Google? Should I have some separate logic which depends on year?

Many thanks for help!

@jskeet jskeet self-assigned this Oct 17, 2016
@jskeet
Copy link
Collaborator

jskeet commented Oct 17, 2016

The mixture of "Google .NET client libraries" and "date/time issues" is right up my street.

I don't have the cycles to look at this immediately, but I'll try to do so as soon as I can - probably answering the SO question as that'll have a wider audience.

The difference between the year 1970 and the year 2000 is easy to get started on. Looking at https://www.timeanddate.com/time/zone/poland/warsaw, we can see that on October 14th 2000, Warsaw was at UTC+2. On October 14th 1970, Warsaw was at UTC+1. So that's the difference involved, but it's not clear to me why the 1970 event would have been translated to 08:00 UTC. I'll need to dig into what's doing the time zone conversions in the client library.

(Of course, all of this would be better with Noda Time... if I ever do a hand-written Google Calendar client library, I would hope to use Noda Time all the way :)

@LindaLawton
Copy link
Collaborator

LindaLawton commented Oct 17, 2016

October I wonder if they changed when they apply daylight savings time? Warsaw

Daylight Saving Time (DST) changes do not necessarily occur on the same date every year.

Does this mean between those years they didn't actually have daylight savings time?

1970 — 1976 No changes, UTC +1 hour all of the period

@jskeet
Copy link
Collaborator

jskeet commented Oct 17, 2016

@LindaLawton: Yes, exactly. If you look at http://nodatime.org/tzvalidate/generate and search for "Europe/Warsaw" you can find all the time zone transitions until 2035 - you'll see there's a big gap:

1964-05-31 00:00:00Z +02:00:00 daylight CEST
1964-09-27 00:00:00Z +01:00:00 standard CET
1977-04-03 00:00:00Z +02:00:00 daylight CEST
1977-09-25 00:00:00Z +01:00:00 standard CET
1978-04-02 00:00:00Z +02:00:00 daylight CEST

@LindaLawton
Copy link
Collaborator

LindaLawton commented Oct 17, 2016

So if that's what the time offset was then or lack there of one. I am inclined to think that what the API and client library are doing is correct.

Just curious though: Why we are storing events in Google calendar for 1970. Not that i am apposed to finding weird bugs for the fun of it. :octocat:

@ghost
Copy link
Author

ghost commented Oct 17, 2016

Typically those are bithdays ;). Issue was reported by user here: https://sourceforge.net/p/googlesyncmod/bugs/866/ I think he is not from Poland, so problem could be not only for Warsaw time zone.

And just to repeat, If I create the same events in "Google API Explorer" everything works as it should....

@LindaLawton
Copy link
Collaborator

LindaLawton commented Oct 17, 2016

Start date and end date need to be RFC3339 dates.

Code says:

Must be an RFC3339 timestamp with mandatory time zone offset.

Documentation says:

The time, as a combined date-time value (formatted according to RFC3339). A time zone offset is required unless a time zone is explicitly specified in timeZone.

Note: this is independent of the fact that you are sending the timezone parameter.

@jskeet can correct me if i am wrong but by doing this

DateTime = new DateTime(1970, 10, 14, 10, 0, 0),

Its just creating a date variable it in the default timezone or system timezone. Its not actually in Warsaw time.

Still digging trying to figure out how we parse these to send them to the API.

@jskeet found this ConvertToRFC3339

@jskeet
Copy link
Collaborator

jskeet commented Oct 17, 2016

No, if the API is converting new DateTime(1970, 10, 14, 10, 0, 0) to "1970-10-14T08:00:00.000Z", that's not right - it should be "1970-10-14T09:00:00.000Z" to be 10am local time.

That DateTime constructor will construct a DateTime with a kind of Unspecified. It shouldn't be assumed to be in the system local time zone, IMO. My guess is that it is being assumed to be in the system local time zone, unfortunately. It would be better represented in the raw API as "1970-10-14T10:00:00.000" (note the lack of Z). I'll look when I get a chance...

EDIT: Yes, judging by that ConvertToRfc3339, it's assumed to be system local time zone. That's a pain, but probably too late to fix.

From a "change to client code" perspective, it would be best to pass in a DateTime with a Kind of Utc to remove any ambiguity.

@LindaLawton
Copy link
Collaborator

LindaLawton commented Oct 17, 2016

Looks to me like if its unspecified we are saying its universal casting it as a string and then sending it to the API.

if (date.Kind == DateTimeKind.Unspecified)
   {
   date = date.ToUniversalTime();
   }
return date.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", DateTimeFormatInfo.InvariantInfo);

Which makes me think if he "specifies" the time zone when he creates the date it will cast correctly and work. Then we can argue if we should be casting this to Universal in the first place.

@jskeet I blame you for getting me hooked on this datetime fun.

@jskeet
Copy link
Collaborator

jskeet commented Oct 17, 2016

No, if it's unspecified, we're assuming it's system local and converting it to UTC with ToUniversalTime. That's ugly, IMO, but I don't think it's feasible to change that now.

@LindaLawton
Copy link
Collaborator

@obelix30 any chance you know what time zone the calendar in question is set to i want to test this with some raw HTTP calls, bothers me that Explorer works and client library doesn't.

@ghost
Copy link
Author

ghost commented Oct 17, 2016

My Google calendar time zone is set "(GMT+02:00) Berlin"

@ghost
Copy link
Author

ghost commented Nov 14, 2016

@jskeet any progress on this one? I have learned there are other TZ impacted (for example Europe/Prague) they have probably the same gaps as Europe/Warsaw

@jskeet
Copy link
Collaborator

jskeet commented Nov 14, 2016

@obelix30: As I mentioned earlier, basically you should pass in a DateTime with a Kind of UTC - work out the UTC instant you care about, and specify that. We won't be able to change the existing behaviour without breaking backward compatibility... so we'll consider it for v2.0, but won't do anything in the v1.X series.

@chrisdunelm
Copy link
Contributor

Looks like a using UTC DateTime's is the solution; so closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants