6

I'm using CalendarContract.Instances to get a set of calendar events. In general my queries work fine. However, the begin and end times for events in the "holidays" calendar come back in a wrong time zone. Events in one of my personal calendars come with correct times.

For example:

New Year's day "begins" at 04:00 PM, 31 Dec 2014.

where as

Opera "begins" at 02:00 PM, 11 Jan 2015.

I'm using exactly the same code to display both:

  SimpleDateFormat formatter = new SimpleDateFormat ("hh:mm a, d MMM yyyy", Locale.US);
  logD (prefix + i + ": " + formatter.format (data.startTime) + "; " + data.note);

where data.startTime maps to Instances.BEGIN and data.note maps to Instances.TITLE. The Opera is showing at the correct time, New Year's day is obviously 8 hours off (I'm in the US Pacific time zone).

If I view these in the Android calendar app, both show with the correct time.

Obviously, I can look at which calendar the event comes from and set the time zone accordingly to make it show with the correct time. However, I'm hoping there's a more proper solution that I'm unaware of.

Here's a snip of code that gets the event values from the cursor:

@Override
public View getView (int position, View convertView, ViewGroup parent)
{
  ...
  EventFields fields = new EventFields();
  cursor.moveToPosition (position);
  fields.title = cursor.getString (cursor.getColumnIndex (Instances.TITLE));
  fields.dtStart = cursor.getLong (cursor.getColumnIndex (Instances.BEGIN));
  fields.dtEnd = cursor.getLong (cursor.getColumnIndex (Instances.END));
  fields.iCalDuration = cursor.getString (cursor.getColumnIndex (Instances.DURATION));
  fields.rrule = cursor.getString (cursor.getColumnIndex (Instances.RRULE));
  ...
}

Here's the query:

@Override
public void refreshData (String constraint)
{
  long begin = ... some date ...
  long end = ... another date ...

  final Uri uri = Uri.parse(CalendarContract.Instances.CONTENT_URI + "/" + 
                            Long.toString(begin) + "/" + 
                            Long.toString(end));

  // Setup query  - projection ordering must match statics above.
  final String[] projection = new String[] {
    Instances._ID,
    Instances.EVENT_ID,
    Instances.TITLE,
    Instances.BEGIN,
    Instances.END,
    Instances.DURATION,
    Instances.RRULE,
    Instances.DESCRIPTION,
  };
  final String sortOrder = Instances.BEGIN;

  String selection = null;
  if (constraint != null)
    selection = Instances.TITLE + " like '%" + constraint.toString() + "%'"; 

  cursor = getActivity().getContentResolver().query (
    uri,
    projection, 
    selection,
    null,
    sortOrder);
}

For the example above, New Year's Day

New Year's day dtStart = 1419984000000 and

And another event which really starts at 4pm has

Roger          dtStart = 1420052400000
Peri Hartman
  • 19,314
  • 18
  • 55
  • 101
  • Can you add more code to show how you create the calendars. What is the value of data.StartTime before you put it through the SimpledateFormat? Instances.BEGIN should be in UTC so its worth checking to find out where its going wrong – EdmundYeung99 May 14 '15 at 02:13
  • I added some code snips and some debug output. Have a look. – Peri Hartman May 14 '15 at 02:43
  • Actually, wait! The 2nd event I cited starts at 11am 31 dec 2014. I do have another event which starts at 4pm, though, and guess what? It has the same UTC milliseconds value as NYD. That means something is going on in the formatting? – Peri Hartman May 14 '15 at 02:47
  • Are holiday calendar dates in UTC? If I'm getting the same value for NYD and my actual 4pm event, how can they have the same millisecond value? – Peri Hartman May 14 '15 at 03:02
  • you can try to set the timezone on your SimpleDateFormat using 'simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));' – EdmundYeung99 May 14 '15 at 03:30
  • As a last resort, I can do that for just the holiday calendar. But there's surely something else wrong. Just for more study, I logged the values from START_DAY - it's supposed to be the julian date in the local time zone. I got a 6 digit number! – Peri Hartman May 14 '15 at 03:50
  • as @Tse Ka Leong mentioned can you check the timezone of the event and set the timezone on the formatter appropriately? – EdmundYeung99 May 14 '15 at 04:41
  • I *think* Start_Day is in Julian format which means number of days since January 1, 4713 B.C (or something like that) – EdmundYeung99 May 14 '15 at 04:47
  • I found the android calendar source code. It definitely does something to all day events, regarding the time zone ... even though the documentation says everything is in UTC. – Peri Hartman May 14 '15 at 16:23
  • Well, I hacked it. If the event is all_day, I set the formatter to the UTC time zone. The bounty is still open if anyone has a better solution! – Peri Hartman May 15 '15 at 00:50
  • but you don't want to adjust times for all day events on your personal calendar right? only those on the holiday calendar – EdmundYeung99 May 15 '15 at 04:41
  • Both calendars - the problem is related to the all_day condition, not the calendar. – Peri Hartman May 15 '15 at 04:44
  • there is also EVENT_TIMEZONE (an inherited field) you can check – EdmundYeung99 May 15 '15 at 04:46
  • Yep. So apparently, even though they say BEGIN, END are in UTC, they apparently are not. They seem to concur with EVENT_TIMEZONE. – Peri Hartman May 15 '15 at 13:52

3 Answers3

3

As discussed in the comments, we determined that this is a problem related to All Day events displaying times in UTC.

It looks like this is just how Android handles it.

Looking at the Format Time class in the Android Docs

public boolean allDay
True if this is an allDay event. The hour, minute, second fields are all zero, and the date is displayed the same in all time zones.

And finally on the CalendarContract.Events docs

If allDay is set to 1 eventTimezone must be TIMEZONE_UTC and the time must correspond to a midnight boundary.

So what you have done by formatting all day events is correct

simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

Update

By default, if you don't explicitly set the timezone of your SimpleDateFormat, it will default to your device's local timezone.

Your BEGIN and END times should come back in UTC time.

All Day Events are always set to midnight in the UTC timezone, so formatting them in anything but the UTC time zone will give you a non-midnight time.

You can check the Calendar Event Instance's timezone using the inherited field:

EVENT_TIMEZONE

Community
  • 1
  • 1
EdmundYeung99
  • 2,461
  • 4
  • 27
  • 43
  • No, what I've done does *not* match the documentation. Here http://developer.android.com/reference/android/provider/CalendarContract.Instances.html it says that BEGIN, END are retrieved in UTC. Unconditionally. That means the same timezone in the formatter should work for all_day vs regular events. I think there's something else wrong. – Peri Hartman May 15 '15 at 13:48
  • 1
    I think your point about the EVENT_TIMEZONE is the key. It appears that the Instance query does return all normal events in UTC and, possibly at some outer level of the query, it mistakenly adjusts the all_day event times to UTC *even though they are already in UTC*. So, my solution is this simple code: if (event_is_all_day) formatter.setTimeZone (TimeZone.getTimeZone ("UTC")). – Peri Hartman May 15 '15 at 13:58
  • I'd like to offer you the bounty since you stuck with me through this - it really helped. If you can modify your answer to show using EVENT_TIMEZONE to determine how to display the event times, I'll accept it. – Peri Hartman May 18 '15 at 20:41
  • Edmund, I'm not sure what you mean by xxx will come back in UTC time but might have a timezone offset. Either the values are UTC or they are in some other time zone. It's a single value - there's no provision for an offset. The documentation says they are in UTC. I think what's happening is that some code buried in the service is inadvertently adding a timezone shift to the all_day events which must then be corrected before displaying. – Peri Hartman May 20 '15 at 04:59
  • try creating a non-all-day event, that starts at midnight on new years day and compare the BEGIN value with an all-day event – EdmundYeung99 May 20 '15 at 05:56
  • They're different - in my case off by 7 hours. That indicates that the normal event is being returned in UTC and the all_day event is adjusted backwards. – Peri Hartman May 20 '15 at 14:02
  • I think they are supposed to be different. The all-day event is stored at midnight UTC, while the non-all-day event is stored at midnight in the timezone of your calendar, which is 7 hours behind utc, hence you get different values for Instance.BEGIN which returns the time in milliseconds in UTC – EdmundYeung99 May 20 '15 at 20:24
  • I performed a query with start and end time (UTC). After performing the query I can see that the calendar returns all day events with start time before the end time I used BUT I think that @PeriHartman is right because if I convert the all day event my timezone I see 03/26/2016 19PM with GMT-5. I performed the query with end time 03/26/2016 23:59 PM. If I open the calendar I see the event 03/27/2016 0:00AM, so it means that after you performed the query you need to check the all day timezone, adjust the offset and then check if it's still in the time range you want. – greywolf82 Mar 27 '16 at 17:02
  • @EdmundYeung99 You are wrong. According to the docs all day and non all day event BEGIN field return value in ***UTC***. So they shouldn't be different at all. – greywolf82 Mar 27 '16 at 17:06
0

Try adding: formatter.setTimeZone(TimeZone.getTimeZone("US/Pacific"));

scottt
  • 8,301
  • 1
  • 31
  • 41
  • How would this help unless I filter on which calendar the event is coming from? See comments in O.P. – Peri Hartman May 14 '15 at 03:51
  • I misunderstood; I thought it was the display of the date that was incorrect. But dates are supposed to be timezone agnostic internally. My holiday calendar is configured to already use my timezone; is yours? – scottt May 14 '15 at 18:17
  • Yes, I think so. I went to my browser and opened the setting for "holidays in the US Details". It says "Calendar Time Zone" is "Pacific Time", which is correct. – Peri Hartman May 14 '15 at 21:47
  • My issue is with the display of the date, ultimately. As I noted in the comments with Edmund, the problem turns out to be related to the all-day setting, not the holiday calendar. I determined that by looking at the android calendar source code. However, I've had my attention on some other things for the last few hours and need to finish understanding their code. Have you looked at it? – Peri Hartman May 14 '15 at 21:49
0

You can actually set time zones on your calendars. Double check that the time zones of the two calendars you compare are set to the same value.

DKIT
  • 3,471
  • 2
  • 20
  • 24