Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List events for specific day in Android 4+ (ALL_DAY issue)

I'm attempting to get all events happening on selected day in Android 4+.

As far as I understand, CalendarContract.Instances is the easiest way of getting events, since it handles both recurring events and single time events.

However, I have some problems with its way of handling timestamps. To be specific, it seems to treat ALL_DAY events differently.

I've started with the following code (start is the day for which I'm fetching events, millisecond "maths" is to avoid unnecessary events beginning on the 12 am of the next day, Calendar uses default timezone).

public Cursor fetchCursor(Calendar start) {
    Calendar end = (Calendar) start.clone();
    end.add(Calendar.DATE, 1);

    long startTime = start.getTimeInMillis() + 1;
    long endTime = end.getTimeInMillis() - 1;

    ContentResolver resolver = mContext.getContentResolver();
    return CalendarContract.Instances.query(resolver, projection, startTime, endTime);
}

This code seems to work well with many edge cases, different timezones, events that span multiple days... everything except all day events.

When I examined returned event with ALL_DAY flag:

    Cursor c = fetchCursor(date);
    Log.e("SIZE", String.valueOf(c.getCount()));
    while (c.moveToNext()) {
        long begin = c.getLong(2); // CalendarContract.Instances.BEGIN
        long end = c.getLong(3);   // CalendarContract.Instances.END
        Date beginDate = new Date(begin);
        Date endDate = new Date(end);
        Log.e("EVENT", String.valueOf(begin) + " - " + String.valueOf(end));
        Log.e("EVENT", beginDate.toString() + " - " + endDate.toString());
    }

I got: EVENT(10075): Thu Sep 13 02:00:00 CEST 2012 - Sat Sep 15 02:00:00 CEST 2012.

For me it looks like query expects range begin and end in millis since epoch in MY TIMEZONE (yet API states it needs UTC based timestamp). But events marked as ALL_DAY use millis since epoch in UTC (so I get 2 additional hours because of +2 timezone).

I also tried calculating and adding timezone offset. Then all day events worked fine but everything else... not.

private long getTimezoneOffset(TimeZone zone, long date) {
    long delta = zone.getOffset(date);
    return delta;
}

private long getTimeInUTC(Calendar date) {
    long time = date.getTimeInMillis();
    time += getTimezoneOffset(date.getTimeZone(), time);
    return time;
}

public Cursor fetchCursor(Calendar start) {
    Calendar end = (Calendar) start.clone();
    end.add(Calendar.DATE, 1);

    long startTime = getTimeInUTC(start) + 1;
    long endTime = getTimeInUTC(end) - 1;

    ContentResolver resolver = mContext.getContentResolver();
    return CalendarContract.Instances.query(resolver, projection, startTime, endTime);
}

While there is quite easy way to fix this issue by iterating over cursor, altering ALL_DAY events and removing unnecessary events, I would like to know if there is a better way. Or maybe this is Android bug? But if it is a bug then why System Calendar works OK? This inconsistent behavior seems odd to me.

Thanks.

like image 239
Grzegorz Świrski Avatar asked Oct 05 '12 18:10

Grzegorz Świrski


1 Answers

Calendar api is also a some what device dependent, your code is working perfectly in Android - 4.4.4 KitKat but not in other devices. The following code is working perfectly all devices in which i have tested.

Try this:

public Cursor fetchCursor(Calendar start) {
    Calendar end = (Calendar) start.clone();
    end.add(Calendar.DATE, 1);
    ContentResolver resolver = this.getContentResolver();
    return CalendarContract.Instances.query(resolver,  new String[] { "calendar_id", "title", "description",
                "dtstart", "dtend", "eventLocation" }, start.getTimeInMillis(),
                end.getTimeInMillis());
}
like image 188
DjP Avatar answered Oct 20 '22 08:10

DjP