[california] Support DURATION when DTEND not present: Bug #737709



commit be88a2f6d2ad9f2d0402fafd2e15aa306721d370
Author: Jim Nelson <jim yorba org>
Date:   Wed Oct 29 16:33:39 2014 -0700

    Support DURATION when DTEND not present: Bug #737709
    
    DTEND is optional in VEVENTs.  If not present, iCal specifies rules
    for determining it, including using DURATION.

 src/component/component-date-time.vala |   51 +++++++++++++++++++++++++++++++-
 src/component/component-event.vala     |   34 ++++++++++++++++++++-
 vapi/libical.vapi                      |   28 +++++++++---------
 3 files changed, 96 insertions(+), 17 deletions(-)
---
diff --git a/src/component/component-date-time.vala b/src/component/component-date-time.vala
index d7a0d66..a4cf2b8 100644
--- a/src/component/component-date-time.vala
+++ b/src/component/component-date-time.vala
@@ -80,8 +80,19 @@ public class DateTime : BaseObject, Gee.Hashable<DateTime>, Gee.Comparable<DateT
         init_from_property(prop);
     }
     
+    private DateTime.from_icaltimetype(iCal.icaltimetype dt, iCal.icalproperty_kind kind) {
+        this.dt = dt;
+        this.kind = kind;
+        this.value = "";
+    }
+    
     private void init_from_property(iCal.icalproperty prop) throws ComponentError {
-        unowned iCal.icalvalue prop_value = prop.get_value();
+        unowned iCal.icalvalue? prop_value = prop.get_value();
+        if (prop_value == null) {
+            throw new ComponentError.UNAVAILABLE("Property of kind %s has no associated value",
+                prop.isa().to_string());
+        }
+        
         switch (prop_value.isa()) {
             case iCal.icalvalue_kind.DATE_VALUE:
                 dt = prop_value.get_date();
@@ -167,6 +178,44 @@ public class DateTime : BaseObject, Gee.Hashable<DateTime>, Gee.Comparable<DateT
     }
     
     /**
+     * Return a { link DateTime} adjusted with the supplied iCal component's DURATION and the
+     * supplied property kind.
+     *
+     * The returned DateTime will have an empty string for its value.
+     *
+     * @throws ComponentError.UNAVAILABLE if DURATION not found
+     * @throws ComponentError.INVALID if not a valid DURATION (including a null DURATION)
+     */
+    public DateTime adjust_duration(iCal.icalcomponent ical_component, iCal.icalproperty_kind new_kind)
+        throws ComponentError {
+        unowned iCal.icalproperty? prop = ical_component.get_first_property(
+            iCal.icalproperty_kind.DURATION_PROPERTY);
+        if (prop == null)
+            throw new ComponentError.UNAVAILABLE("No DURATION property found");
+        
+        unowned iCal.icalvalue? value = prop.get_value();
+        if (value == null)
+            throw new ComponentError.UNAVAILABLE("No value for DURATION property");
+        
+        if (value.isa() != iCal.icalvalue_kind.DURATION_VALUE)
+            throw new ComponentError.INVALID("DURATION property does not have a DURATION value");
+        
+        iCal.icaldurationtype duration = value.get_duration();
+        if (duration.is_bad_duration() != 0 || duration.is_null_duration() != 0)
+            throw new ComponentError.INVALID("DURATION value is bad or null");
+        
+        // if adjusting a DATE DTSTART, only days and weeks are to be respected in the adjustment:
+        // https://tools.ietf.org/html/rfc5545#section-3.8.2.5
+        if (kind == iCal.icalproperty_kind.DTSTART_PROPERTY && is_date) {
+            duration.hours = 0;
+            duration.minutes = 0;
+            duration.seconds = 0;
+        }
+        
+        return new DateTime.from_icaltimetype(iCal.icaltime_add(dt, duration), new_kind);
+    }
+    
+    /**
      * Converts the stored iCal DATE-TIME to an { link Calendar.ExactTime}.
      *
      * Returns null if { link is_date} is true.
diff --git a/src/component/component-event.vala b/src/component/component-event.vala
index 1ead71e..00f13c8 100644
--- a/src/component/component-event.vala
+++ b/src/component/component-event.vala
@@ -122,12 +122,42 @@ public class Event : Instance, Gee.Comparable<Event> {
         description = ical_component.get_description();
         
         DateTime dt_start = new DateTime(ical_component, iCal.icalproperty_kind.DTSTART_PROPERTY);
-        DateTime dt_end = new DateTime(ical_component, iCal.icalproperty_kind.DTEND_PROPERTY);
+        
+        // DTSTART is required for a valid VEVENT but DTEND is not.  See
+        // https://tools.ietf.org/html/rfc5545#section-3.6.1 for how a missing DTEND is treated
+        // when interpreting a VEVENT.
+        DateTime? dt_end = null;
+        try {
+            dt_end = new DateTime(ical_component, iCal.icalproperty_kind.DTEND_PROPERTY);
+        } catch (ComponentError comperr) {
+            // if UNAVAILABLE, fall through and follow interpretation rules in iCal spec
+            if (!(comperr is ComponentError.UNAVAILABLE))
+                throw comperr;
+        }
+        
+        // If no DTEND, look for a DURATION
+        if (dt_end == null) {
+            try {
+                dt_end = dt_start.adjust_duration(ical_component, iCal.icalproperty_kind.DTEND_PROPERTY);
+            } catch (ComponentError comperr) {
+                // fall through
+            }
+        }
+        
+        bool dtend_inclusive = false;
+        if (dt_end == null) {
+            // For DTSTART w/ DATE, treat DTEND as one-day event; for DATETIME, use DTSTART for DTEND.
+            // Because DTEND is non-inclusive in VEVENTs, that means use the same value in both cases
+            // and just don't convert as non-inclusive
+            dt_end = dt_start;
+            dtend_inclusive = true;
+        }
+        
         // convert start and end DATE/DATE-TIMEs to internal values ... note that VEVENT dtend
         // is non-inclusive (see https://tools.ietf.org/html/rfc5545#section-3.6.1)
         Calendar.DateSpan? date_span;
         Calendar.ExactTimeSpan? exact_time_span;
-        DateTime.to_span(dt_start, dt_end, false, out date_span, out exact_time_span);
+        DateTime.to_span(dt_start, dt_end, dtend_inclusive, out date_span, out exact_time_span);
         if (exact_time_span != null) {
             set_event_exact_time_span(exact_time_span);
         } else {
diff --git a/vapi/libical.vapi b/vapi/libical.vapi
index 9b50119..5ad3c1a 100644
--- a/vapi/libical.vapi
+++ b/vapi/libical.vapi
@@ -99,7 +99,7 @@ namespace iCal {
                [CCode (cname = "icalcomponent_get_due")]
                public iCal.icaltimetype get_due ();
                [CCode (cname = "icalcomponent_get_duration")]
-               public unowned iCal.icaldurationtype get_duration ();
+               public iCal.icaldurationtype get_duration ();
                [CCode (cname = "icalcomponent_get_first_component")]
                public unowned iCal.icalcomponent? get_first_component (iCal.icalcomponent_kind kind);
                [CCode (cname = "icalcomponent_get_first_property")]
@@ -213,9 +213,9 @@ namespace iCal {
                [CCode (cname = "icalcomponent_new_xstandard", has_construct_function = false)]
                public icalcomponent.xstandard ();
        }
-       [CCode (cheader_filename = "libical/ical.h")]
-       [Compact]
-       public class icaldurationtype {
+       [CCode (cheader_filename = "libical/ical.h", cname="struct icaldurationtype")]
+       [SimpleType]
+       public struct icaldurationtype {
                public uint days;
                public uint hours;
                public int is_neg;
@@ -229,17 +229,17 @@ namespace iCal {
                [CCode (cname = "icaldurationtype_as_int")]
                public int as_int ();
                [CCode (cname = "icaldurationtype_bad_duration")]
-               public static unowned iCal.icaldurationtype bad_duration ();
+               public static iCal.icaldurationtype bad_duration ();
                [CCode (cname = "icaldurationtype_from_int")]
-               public static unowned iCal.icaldurationtype from_int (int t);
+               public static iCal.icaldurationtype from_int (int t);
                [CCode (cname = "icaldurationtype_from_string")]
-               public static unowned iCal.icaldurationtype from_string (string p1);
+               public static iCal.icaldurationtype from_string (string p1);
                [CCode (cname = "icaldurationtype_is_bad_duration")]
                public int is_bad_duration ();
                [CCode (cname = "icaldurationtype_is_null_duration")]
                public int is_null_duration ();
                [CCode (cname = "icaldurationtype_null_duration")]
-               public static unowned iCal.icaldurationtype null_duration ();
+               public static iCal.icaldurationtype null_duration ();
        }
        [CCode (cheader_filename = "libical/ical.h", free_function = "icalparameter_free")]
        [Compact]
@@ -657,7 +657,7 @@ namespace iCal {
                [CCode (cname = "icalproperty_get_due")]
                public iCal.icaltimetype get_due ();
                [CCode (cname = "icalproperty_get_duration")]
-               public unowned iCal.icaldurationtype get_duration ();
+               public iCal.icaldurationtype get_duration ();
                [CCode (cname = "icalproperty_get_exdate")]
                public iCal.icaltimetype get_exdate ();
                [CCode (cname = "icalproperty_get_expand")]
@@ -779,7 +779,7 @@ namespace iCal {
                [CCode (cname = "icalproperty_get_url")]
                public unowned string get_url ();
                [CCode (cname = "icalproperty_get_value")]
-               public unowned iCal.icalvalue get_value ();
+               public unowned iCal.icalvalue? get_value ();
                [CCode (cname = "icalproperty_get_value_as_string")]
                public unowned string get_value_as_string ();
                [CCode (cname = "icalproperty_get_value_as_string_r")]
@@ -1502,7 +1502,7 @@ namespace iCal {
                [CCode (cname = "icalvalue_get_datetimeperiod")]
                public iCal.icaldatetimeperiodtype get_datetimeperiod ();
                [CCode (cname = "icalvalue_get_duration")]
-               public unowned iCal.icaldurationtype get_duration ();
+               public iCal.icaldurationtype get_duration ();
                [CCode (cname = "icalvalue_get_float")]
                public global::float get_float ();
                [CCode (cname = "icalvalue_get_geo")]
@@ -1694,7 +1694,7 @@ namespace iCal {
        public struct icalperiodtype {
                public iCal.icaltimetype start;
                public iCal.icaltimetype end;
-               public weak iCal.icaldurationtype duration;
+               public iCal.icaldurationtype duration;
                [CCode (cname = "icalperiodtype_as_ical_string")]
                public unowned string as_ical_string ();
                [CCode (cname = "icalperiodtype_as_ical_string_r")]
@@ -1789,7 +1789,7 @@ namespace iCal {
        [CCode (cheader_filename = "libical/ical.h")]
        public struct icaltriggertype {
                public iCal.icaltimetype time;
-               public weak iCal.icaldurationtype duration;
+               public iCal.icaldurationtype duration;
                [CCode (cname = "icaltriggertype_from_int")]
                public static iCal.icaltriggertype from_int (int reltime);
                [CCode (cname = "icaltriggertype_from_string")]
@@ -2640,7 +2640,7 @@ namespace iCal {
        [CCode (cheader_filename = "libical/ical.h", cname = "icaltime_start_doy_week")]
        public static int icaltime_start_doy_week (iCal.icaltimetype t, int fdow);
        [CCode (cheader_filename = "libical/ical.h", cname = "icaltime_subtract")]
-       public static unowned iCal.icaldurationtype icaltime_subtract (iCal.icaltimetype t1, 
iCal.icaltimetype t2);
+       public static iCal.icaldurationtype icaltime_subtract (iCal.icaltimetype t1, iCal.icaltimetype t2);
        [CCode (cheader_filename = "libical/ical.h", cname = "icaltime_today")]
        public static iCal.icaltimetype icaltime_today ();
        [CCode (cheader_filename = "libical/ical.h", cname = "icaltime_week_number")]


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]