Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.contacts.util;
     18 
     19 import android.content.Context;
     20 import android.text.format.DateFormat;
     21 
     22 import java.text.ParsePosition;
     23 import java.text.SimpleDateFormat;
     24 import java.util.Calendar;
     25 import java.util.Date;
     26 import java.util.Locale;
     27 import java.util.TimeZone;
     28 
     29 /**
     30  * Utility methods for processing dates.
     31  */
     32 public class DateUtils {
     33     public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC");
     34 
     35     // All the SimpleDateFormats in this class use the UTC timezone
     36     public static final SimpleDateFormat NO_YEAR_DATE_FORMAT =
     37             new SimpleDateFormat("--MM-dd", Locale.US);
     38     /**
     39      * When parsing a date without a year, the system assumes 1970, which wasn't a leap-year.
     40      * Let's add a one-off hack for that day of the year
     41      */
     42     public static final String NO_YEAR_DATE_FEB29TH = "--02-29";
     43     public static final SimpleDateFormat FULL_DATE_FORMAT =
     44             new SimpleDateFormat("yyyy-MM-dd", Locale.US);
     45     public static final SimpleDateFormat DATE_AND_TIME_FORMAT =
     46             new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
     47     public static final SimpleDateFormat NO_YEAR_DATE_AND_TIME_FORMAT =
     48             new SimpleDateFormat("--MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
     49 
     50     // Variations of ISO 8601 date format.  Do not change the order - it does affect the
     51     // result in ambiguous cases.
     52     private static final SimpleDateFormat[] DATE_FORMATS = {
     53         FULL_DATE_FORMAT,
     54         DATE_AND_TIME_FORMAT,
     55         new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US),
     56         new SimpleDateFormat("yyyyMMdd", Locale.US),
     57         new SimpleDateFormat("yyyyMMdd'T'HHmmssSSS'Z'", Locale.US),
     58         new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US),
     59         new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'", Locale.US),
     60     };
     61 
     62     private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_MONTH_FIRST =
     63             new SimpleDateFormat("MMMM dd");
     64 
     65     private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_DAY_FIRST =
     66             new SimpleDateFormat("dd MMMM");
     67 
     68     static {
     69         for (SimpleDateFormat format : DATE_FORMATS) {
     70             format.setLenient(true);
     71             format.setTimeZone(UTC_TIMEZONE);
     72         }
     73         NO_YEAR_DATE_FORMAT.setTimeZone(UTC_TIMEZONE);
     74         FORMAT_WITHOUT_YEAR_MONTH_FIRST.setTimeZone(UTC_TIMEZONE);
     75         FORMAT_WITHOUT_YEAR_DAY_FIRST.setTimeZone(UTC_TIMEZONE);
     76     }
     77 
     78     /**
     79      * Parses the supplied string to see if it looks like a date. If so,
     80      * returns the date.  Otherwise, returns null.
     81      */
     82     public static Date parseDate(String string) {
     83         ParsePosition parsePosition = new ParsePosition(0);
     84         for (int i = 0; i < DATE_FORMATS.length; i++) {
     85             SimpleDateFormat f = DATE_FORMATS[i];
     86             synchronized (f) {
     87                 parsePosition.setIndex(0);
     88                 Date date = f.parse(string, parsePosition);
     89                 if (parsePosition.getIndex() == string.length()) {
     90                     return date;
     91                 }
     92             }
     93         }
     94         return null;
     95     }
     96 
     97     private static final Date getUtcDate(int year, int month, int dayOfMonth) {
     98         final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US);
     99         calendar.set(Calendar.YEAR, year);
    100         calendar.set(Calendar.MONTH, month);
    101         calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
    102         return calendar.getTime();
    103     }
    104 
    105     /**
    106      * Parses the supplied string to see if it looks like a date. If so,
    107      * returns the same date in a cleaned-up format for the user.  Otherwise, returns
    108      * the supplied string unchanged.
    109      */
    110     public static String formatDate(Context context, String string) {
    111         if (string == null) {
    112             return null;
    113         }
    114 
    115         string = string.trim();
    116         if (string.length() == 0) {
    117             return string;
    118         }
    119 
    120         ParsePosition parsePosition = new ParsePosition(0);
    121 
    122         final boolean noYearParsed;
    123         Date date;
    124 
    125         // Unfortunately, we can't parse Feb 29th correctly, so let's handle this day seperately
    126         if (NO_YEAR_DATE_FEB29TH.equals(string)) {
    127             date = getUtcDate(0, Calendar.FEBRUARY, 29);
    128             noYearParsed = true;
    129         } else {
    130             synchronized (NO_YEAR_DATE_FORMAT) {
    131                 date = NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
    132             }
    133             noYearParsed = parsePosition.getIndex() == string.length();
    134         }
    135 
    136         if (noYearParsed) {
    137             java.text.DateFormat outFormat = isMonthBeforeDay(context)
    138                     ? FORMAT_WITHOUT_YEAR_MONTH_FIRST
    139                     : FORMAT_WITHOUT_YEAR_DAY_FIRST;
    140             synchronized (outFormat) {
    141                 return outFormat.format(date);
    142             }
    143         }
    144 
    145         for (int i = 0; i < DATE_FORMATS.length; i++) {
    146             SimpleDateFormat f = DATE_FORMATS[i];
    147             synchronized (f) {
    148                 parsePosition.setIndex(0);
    149                 date = f.parse(string, parsePosition);
    150                 if (parsePosition.getIndex() == string.length()) {
    151                     java.text.DateFormat outFormat = DateFormat.getDateFormat(context);
    152                     outFormat.setTimeZone(UTC_TIMEZONE);
    153                     return outFormat.format(date);
    154                 }
    155             }
    156         }
    157         return string;
    158     }
    159 
    160     public static boolean isMonthBeforeDay(Context context) {
    161         char[] dateFormatOrder = DateFormat.getDateFormatOrder(context);
    162         for (int i = 0; i < dateFormatOrder.length; i++) {
    163             if (dateFormatOrder[i] == DateFormat.DATE) {
    164                 return false;
    165             }
    166             if (dateFormatOrder[i] == DateFormat.MONTH) {
    167                 return true;
    168             }
    169         }
    170         return false;
    171     }
    172 }
    173