Date parser utility helpers throw parse exceptions

Update to reflect API change to parseDateTimeToMillis and
parseEmailDateTimeToMillis, which now throw ParseExceptions on
malformed date strings.

Bug:14279251
Change-Id: I74e015b81079b127ddba15f34c8b7e555099bd1e
diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java
index 45191a6..d8902ce 100644
--- a/src/com/android/exchange/EasSyncService.java
+++ b/src/com/android/exchange/EasSyncService.java
@@ -104,6 +104,7 @@
 import java.lang.Thread.State;
 import java.net.URI;
 import java.security.cert.CertificateException;
+import java.text.ParseException;
 
 public class EasSyncService extends AbstractSyncService {
     // DO NOT CHECK IN SET TO TRUE
@@ -986,6 +987,7 @@
         }
         return null;
     }
+
     /**
      * Send an email responding to a Message that has been marked as a meeting request.  The message
      * will consist a little bit of event information and an iCalendar attachment
@@ -1006,6 +1008,10 @@
         String dtStamp = meetingInfo.get(MeetingInfo.MEETING_DTSTAMP);
         String dtStart = meetingInfo.get(MeetingInfo.MEETING_DTSTART);
         String dtEnd = meetingInfo.get(MeetingInfo.MEETING_DTEND);
+        if (TextUtils.isEmpty(dtStamp) || TextUtils.isEmpty(dtStart) ||
+                TextUtils.isEmpty(dtEnd)) {
+            return;
+        }
 
         // What we're doing here is to create an Entity that looks like an Event as it would be
         // stored by CalendarProvider
@@ -1015,8 +1021,13 @@
         // Fill in times, location, title, and organizer
         entityValues.put("DTSTAMP",
                 CalendarUtilities.convertEmailDateTimeToCalendarDateTime(dtStamp));
-        entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart));
-        entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd));
+        try {
+            entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart));
+            entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd));
+        } catch (ParseException e) {
+            LogUtils.w(TAG, "Parse error for DTSTART/DTEND tags.", e);
+            return;
+        }
         entityValues.put(Events.EVENT_LOCATION, meetingInfo.get(MeetingInfo.MEETING_LOCATION));
         entityValues.put(Events.TITLE, meetingInfo.get(MeetingInfo.MEETING_TITLE));
         entityValues.put(Events.ORGANIZER, organizerEmail);
diff --git a/src/com/android/exchange/adapter/CalendarSyncParser.java b/src/com/android/exchange/adapter/CalendarSyncParser.java
index 111b510..a9bb3ea 100644
--- a/src/com/android/exchange/adapter/CalendarSyncParser.java
+++ b/src/com/android/exchange/adapter/CalendarSyncParser.java
@@ -33,6 +33,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.GregorianCalendar;
 import java.util.Map.Entry;
@@ -445,10 +446,18 @@
                     cv.put(Events.EVENT_TIMEZONE, timeZone.getID());
                     break;
                 case Tags.CALENDAR_START_TIME:
-                    startTime = Utility.parseDateTimeToMillis(getValue());
+                    try {
+                        startTime = Utility.parseDateTimeToMillis(getValue());
+                    } catch (ParseException e) {
+                        LogUtils.w(TAG, "Parse error for CALENDAR_START_TIME tag.", e);
+                    }
                     break;
                 case Tags.CALENDAR_END_TIME:
-                    endTime = Utility.parseDateTimeToMillis(getValue());
+                    try {
+                        endTime = Utility.parseDateTimeToMillis(getValue());
+                    } catch (ParseException e) {
+                        LogUtils.w(TAG, "Parse error for CALENDAR_END_TIME tag.", e);
+                    }
                     break;
                 case Tags.CALENDAR_EXCEPTIONS:
                     // For exceptions to show the organizer, the organizer must be added before
@@ -787,9 +796,14 @@
                     attachmentsParser();
                     break;
                 case Tags.CALENDAR_EXCEPTION_START_TIME:
-                    exceptionStartTime = getValue();
-                    cv.put(Events.ORIGINAL_INSTANCE_TIME,
-                            Utility.parseDateTimeToMillis(exceptionStartTime));
+                    final String valueStr = getValue();
+                    try {
+                        cv.put(Events.ORIGINAL_INSTANCE_TIME,
+                                Utility.parseDateTimeToMillis(valueStr));
+                        exceptionStartTime = valueStr;
+                    } catch (ParseException e) {
+                        LogUtils.w(TAG, "Parse error for CALENDAR_EXCEPTION_START_TIME tag.", e);
+                    }
                     break;
                 case Tags.CALENDAR_EXCEPTION_IS_DELETED:
                     if (getValueInt() == 1) {
@@ -807,10 +821,18 @@
                     cv.put(Events.DESCRIPTION, getValue());
                     break;
                 case Tags.CALENDAR_START_TIME:
-                    startTime = Utility.parseDateTimeToMillis(getValue());
+                    try {
+                        startTime = Utility.parseDateTimeToMillis(getValue());
+                    } catch (ParseException e) {
+                        LogUtils.w(TAG, "Parse error for CALENDAR_START_TIME tag.", e);
+                    }
                     break;
                 case Tags.CALENDAR_END_TIME:
-                    endTime = Utility.parseDateTimeToMillis(getValue());
+                    try {
+                        endTime = Utility.parseDateTimeToMillis(getValue());
+                    } catch (ParseException e) {
+                        LogUtils.w(TAG, "Parse error for CALENDAR_END_TIME tag.", e);
+                    }
                     break;
                 case Tags.CALENDAR_LOCATION:
                     cv.put(Events.EVENT_LOCATION, getValue());
diff --git a/src/com/android/exchange/adapter/ContactsSyncParser.java b/src/com/android/exchange/adapter/ContactsSyncParser.java
index 75ee50e..e9aa7c7 100644
--- a/src/com/android/exchange/adapter/ContactsSyncParser.java
+++ b/src/com/android/exchange/adapter/ContactsSyncParser.java
@@ -47,6 +47,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.GregorianCalendar;
 import java.util.TimeZone;
@@ -1047,7 +1048,13 @@
                 return;
             }
             // TODO: Store the date in the format expected by EAS servers.
-            long millis = Utility.parseEmailDateTimeToMillis(birthday);
+            final long millis;
+            try {
+                millis = Utility.parseEmailDateTimeToMillis(birthday);
+            } catch (ParseException e) {
+                LogUtils.w(TAG, "Parse error for birthday date field.", e);
+                return;
+            }
             GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
             cal.setTimeInMillis(millis);
             if (cal.get(GregorianCalendar.HOUR_OF_DAY) >= 12) {
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index b8676ea..6a9680f 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -77,6 +77,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
@@ -541,7 +542,11 @@
                         msg.mReplyTo = Address.toString(Address.parse(getValue()));
                         break;
                     case Tags.EMAIL_DATE_RECEIVED:
-                        msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue());
+                        try {
+                            msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue());
+                        } catch (ParseException e) {
+                            LogUtils.w(TAG, "Parse error for EMAIL_DATE_RECEIVED tag.", e);
+                        }
                         break;
                     case Tags.EMAIL_SUBJECT:
                         msg.mSubject = getValue();
@@ -635,8 +640,13 @@
                                 Events.EVENT_LOCATION);
                         String dtstart = ps.get(MeetingInfo.MEETING_DTSTART);
                         if (!TextUtils.isEmpty(dtstart)) {
-                            long startTime = Utility.parseEmailDateTimeToMillis(dtstart);
-                            values.put(Events.DTSTART, startTime);
+                            try {
+                                final long startTime =
+                                    Utility.parseEmailDateTimeToMillis(dtstart);
+                                values.put(Events.DTSTART, startTime);
+                            } catch (ParseException e) {
+                                LogUtils.w(TAG, "Parse error for MEETING_DTSTART tag.", e);
+                            }
                         }
                         putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values,
                                 Events.ALL_DAY);
diff --git a/src/com/android/exchange/adapter/EmailSyncParser.java b/src/com/android/exchange/adapter/EmailSyncParser.java
index 787dcdb..d1c9453 100644
--- a/src/com/android/exchange/adapter/EmailSyncParser.java
+++ b/src/com/android/exchange/adapter/EmailSyncParser.java
@@ -45,6 +45,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
@@ -156,7 +157,11 @@
                     msg.mReplyTo = Address.toString(Address.parse(getValue()));
                     break;
                 case Tags.EMAIL_DATE_RECEIVED:
-                    msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue());
+                    try {
+                        msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue());
+                    } catch (ParseException e) {
+                        LogUtils.w(TAG, "Parse error for EMAIL_DATE_RECEIVED tag.", e);
+                    }
                     break;
                 case Tags.EMAIL_SUBJECT:
                     msg.mSubject = getValue();
@@ -250,8 +255,13 @@
                             CalendarContract.Events.EVENT_LOCATION);
                     String dtstart = ps.get(MeetingInfo.MEETING_DTSTART);
                     if (!TextUtils.isEmpty(dtstart)) {
-                        long startTime = Utility.parseEmailDateTimeToMillis(dtstart);
-                        values.put(CalendarContract.Events.DTSTART, startTime);
+                        try {
+                            final long startTime =
+                                Utility.parseEmailDateTimeToMillis(dtstart);
+                            values.put(CalendarContract.Events.DTSTART, startTime);
+                        } catch (ParseException e) {
+                            LogUtils.w(TAG, "Parse error for MEETING_DTSTART tag.", e);
+                        }
                     }
                     putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values,
                             CalendarContract.Events.ALL_DAY);
diff --git a/src/com/android/exchange/service/EasMeetingResponder.java b/src/com/android/exchange/service/EasMeetingResponder.java
index 28ef2c6..f42f583 100644
--- a/src/com/android/exchange/service/EasMeetingResponder.java
+++ b/src/com/android/exchange/service/EasMeetingResponder.java
@@ -6,6 +6,7 @@
 import android.content.Entity;
 import android.provider.CalendarContract.Attendees;
 import android.provider.CalendarContract.Events;
+import android.text.TextUtils;
 
 import com.android.emailcommon.mail.Address;
 import com.android.emailcommon.mail.MeetingInfo;
@@ -29,6 +30,7 @@
 
 import java.io.IOException;
 import java.security.cert.CertificateException;
+import java.text.ParseException;
 
 /**
  * Responds to a meeting request, both notifying the EAS server and sending email.
@@ -128,6 +130,10 @@
         final String dtStamp = meetingInfo.get(MeetingInfo.MEETING_DTSTAMP);
         final String dtStart = meetingInfo.get(MeetingInfo.MEETING_DTSTART);
         final String dtEnd = meetingInfo.get(MeetingInfo.MEETING_DTEND);
+        if (TextUtils.isEmpty(dtStamp) || TextUtils.isEmpty(dtStart) ||
+                TextUtils.isEmpty(dtEnd)) {
+            return;
+        }
 
         // What we're doing here is to create an Entity that looks like an Event as it would be
         // stored by CalendarProvider
@@ -137,8 +143,13 @@
         // Fill in times, location, title, and organizer
         entityValues.put("DTSTAMP",
                 CalendarUtilities.convertEmailDateTimeToCalendarDateTime(dtStamp));
-        entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart));
-        entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd));
+        try {
+            entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart));
+            entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd));
+        } catch (ParseException e) {
+            LogUtils.w(TAG, "Parse error for DTSTART/DTEND tags.", e);
+            return;
+        }
         entityValues.put(Events.EVENT_LOCATION, meetingInfo.get(MeetingInfo.MEETING_LOCATION));
         entityValues.put(Events.TITLE, meetingInfo.get(MeetingInfo.MEETING_TITLE));
         entityValues.put(Events.ORGANIZER, organizerEmail);
diff --git a/src/com/android/exchange/utility/CalendarUtilities.java b/src/com/android/exchange/utility/CalendarUtilities.java
index 5661bb1..3cd4183 100644
--- a/src/com/android/exchange/utility/CalendarUtilities.java
+++ b/src/com/android/exchange/utility/CalendarUtilities.java
@@ -54,6 +54,7 @@
 
 import java.io.IOException;
 import java.text.DateFormat;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
@@ -1146,7 +1147,7 @@
      * Reformat an RRULE style UNTIL to an EAS style until
      */
     @VisibleForTesting
-    static String recurrenceUntilToEasUntil(String until) {
+    static String recurrenceUntilToEasUntil(String until) throws ParseException {
         // Get a calendar in our local time zone
         GregorianCalendar localCalendar = new GregorianCalendar(TimeZone.getDefault());
         // Set the time per GMT time in the 'until'
@@ -1176,7 +1177,11 @@
         }
         String until = tokenFromRrule(rrule, "UNTIL=");
         if (until != null) {
-            s.data(Tags.CALENDAR_RECURRENCE_UNTIL, recurrenceUntilToEasUntil(until));
+            try {
+                s.data(Tags.CALENDAR_RECURRENCE_UNTIL, recurrenceUntilToEasUntil(until));
+            } catch (ParseException e) {
+                LogUtils.w(TAG, "Parse error for CALENDAR_RECURRENCE_UNTIL tag.", e);
+            }
         }
     }
 
diff --git a/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java b/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
index 4501bff..68e57e0 100644
--- a/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
+++ b/tests/src/com/android/exchange/utility/CalendarUtilitiesTests.java
@@ -42,6 +42,7 @@
 import java.io.IOException;
 import java.io.StringReader;
 import java.text.DateFormat;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
@@ -203,7 +204,7 @@
         assertNull(CalendarUtilities.tokenFromRrule(rrule, "UNTIL="));
     }
 
-    public void testRecurrenceUntilToEasUntil() {
+    public void testRecurrenceUntilToEasUntil() throws ParseException {
         TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
         // Case where local time crosses into next day in GMT
         assertEquals("20110730T000000Z",
@@ -211,9 +212,18 @@
         // Case where local time does not cross into next day in GMT
         assertEquals("20110730T000000Z",
                 CalendarUtilities.recurrenceUntilToEasUntil("20110730T235959Z"));
+        // Abbreviated date format
+        assertEquals("20110729T000000Z",
+                CalendarUtilities.recurrenceUntilToEasUntil("20110730"));
+        try {
+            CalendarUtilities.recurrenceUntilToEasUntil("201107");
+            fail("Expected ParseException");
+        } catch (ParseException e) {
+            // expected
+        }
     }
 
-    public void testParseEmailDateTimeToMillis(String date) {
+    public void testParseEmailDateTimeToMillis(String date) throws ParseException {
         // Format for email date strings is 2010-02-23T16:00:00.000Z
         String dateString = "2010-02-23T15:16:17.000Z";
         long dateTime = Utility.parseEmailDateTimeToMillis(dateString);
@@ -228,7 +238,7 @@
         assertEquals(cal.get(Calendar.SECOND), 17);
     }
 
-    public void testParseDateTimeToMillis(String date) {
+    public void testParseDateTimeToMillis(String date) throws ParseException {
         // Format for calendar date strings is 20100223T160000000Z
         String dateString = "20100223T151617000Z";
         long dateTime = Utility.parseDateTimeToMillis(dateString);
@@ -254,10 +264,14 @@
         // Fill in times, location, title, and organizer
         entityValues.put("DTSTAMP",
                 CalendarUtilities.convertEmailDateTimeToCalendarDateTime("2010-04-05T14:30:51Z"));
-        entityValues.put(Events.DTSTART,
-                Utility.parseEmailDateTimeToMillis("2010-04-12T18:30:00Z"));
-        entityValues.put(Events.DTEND,
-                Utility.parseEmailDateTimeToMillis("2010-04-12T19:30:00Z"));
+        try {
+            entityValues.put(Events.DTSTART,
+                    Utility.parseEmailDateTimeToMillis("2010-04-12T18:30:00Z"));
+            entityValues.put(Events.DTEND,
+                    Utility.parseEmailDateTimeToMillis("2010-04-12T19:30:00Z"));
+        } catch (ParseException e) {
+            // ignore
+        }
         entityValues.put(Events.EVENT_LOCATION, location);
         entityValues.put(Events.TITLE, title);
         entityValues.put(Events.ORGANIZER, organizer);
@@ -282,8 +296,12 @@
         ContentValues entityValues = entity.getEntityValues();
         entityValues.put(Events.ORIGINAL_SYNC_ID, 69);
         // The exception will be on April 26th
-        entityValues.put(Events.ORIGINAL_INSTANCE_TIME,
-                Utility.parseEmailDateTimeToMillis("2010-04-26T18:30:00Z"));
+        try {
+            entityValues.put(Events.ORIGINAL_INSTANCE_TIME,
+                    Utility.parseEmailDateTimeToMillis("2010-04-26T18:30:00Z"));
+        } catch (ParseException e) {
+            // ignore
+        }
         return entity;
     }
 
@@ -540,7 +558,7 @@
         // Set up the "exception"...
         String title = "Discuss Unit Tests";
         Entity entity = setupTestExceptionEntity(ORGANIZER, ATTENDEE, title);
-        
+
         ContentValues entityValues = entity.getEntityValues();
         // Mark the Exception as dirty
         entityValues.put(Events.DIRTY, 1);