DateUtils.java revision 428f008513d1591cc08fcfe2cf0c9237fb313241
145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov/*
245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project
345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov *
445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License");
545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * you may not use this file except in compliance with the License.
645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * You may obtain a copy of the License at
745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov *
845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov *      http://www.apache.org/licenses/LICENSE-2.0
945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov *
1045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * Unless required by applicable law or agreed to in writing, software
1145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS,
1245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * See the License for the specific language governing permissions and
1445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * limitations under the License.
1545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov */
1645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
1745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovpackage com.android.contacts.util;
1845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
1945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport android.content.Context;
2045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport android.text.format.DateFormat;
2145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
22428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Chengimport com.android.contacts.common.util.CommonDateUtils;
23428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Cheng
2445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport java.text.ParsePosition;
2545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport java.text.SimpleDateFormat;
265a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmannimport java.util.Calendar;
2745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport java.util.Date;
284b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmannimport java.util.Locale;
294b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmannimport java.util.TimeZone;
3045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
3145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov/**
3245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * Utility methods for processing dates.
3345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov */
3445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovpublic class DateUtils {
354b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann    public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC");
364b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann
375a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann    /**
385a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann     * When parsing a date without a year, the system assumes 1970, which wasn't a leap-year.
395a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann     * Let's add a one-off hack for that day of the year
405a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann     */
415a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann    public static final String NO_YEAR_DATE_FEB29TH = "--02-29";
4245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
432b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov    // Variations of ISO 8601 date format.  Do not change the order - it does affect the
442b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov    // result in ambiguous cases.
4545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    private static final SimpleDateFormat[] DATE_FORMATS = {
46428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Cheng        CommonDateUtils.FULL_DATE_FORMAT,
47428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Cheng        CommonDateUtils.DATE_AND_TIME_FORMAT,
484b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US),
494b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        new SimpleDateFormat("yyyyMMdd", Locale.US),
504b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        new SimpleDateFormat("yyyyMMdd'T'HHmmssSSS'Z'", Locale.US),
514b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US),
524b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'", Locale.US),
5345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    };
542b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov
5545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_MONTH_FIRST =
5645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            new SimpleDateFormat("MMMM dd");
5745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
5811812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann    private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_DAY_FIRST =
5945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            new SimpleDateFormat("dd MMMM");
6045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
614b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann    static {
624b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        for (SimpleDateFormat format : DATE_FORMATS) {
634b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann            format.setLenient(true);
644b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann            format.setTimeZone(UTC_TIMEZONE);
654b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        }
66428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Cheng        CommonDateUtils.NO_YEAR_DATE_FORMAT.setTimeZone(UTC_TIMEZONE);
674b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann        FORMAT_WITHOUT_YEAR_MONTH_FIRST.setTimeZone(UTC_TIMEZONE);
6811812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann        FORMAT_WITHOUT_YEAR_DAY_FIRST.setTimeZone(UTC_TIMEZONE);
694b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann    }
704b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann
7145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    /**
7245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov     * Parses the supplied string to see if it looks like a date. If so,
732b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov     * returns the date.  Otherwise, returns null.
742b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov     */
752b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov    public static Date parseDate(String string) {
762b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        ParsePosition parsePosition = new ParsePosition(0);
772b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        for (int i = 0; i < DATE_FORMATS.length; i++) {
782b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov            SimpleDateFormat f = DATE_FORMATS[i];
792b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov            synchronized (f) {
802b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                parsePosition.setIndex(0);
812b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                Date date = f.parse(string, parsePosition);
822b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                if (parsePosition.getIndex() == string.length()) {
832b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                    return date;
842b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                }
852b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov            }
862b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        }
872b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        return null;
882b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov    }
892b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov
905a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann    private static final Date getUtcDate(int year, int month, int dayOfMonth) {
915a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US);
925a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        calendar.set(Calendar.YEAR, year);
935a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        calendar.set(Calendar.MONTH, month);
945a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
955a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        return calendar.getTime();
965a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann    }
975a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann
982b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov    /**
992b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov     * Parses the supplied string to see if it looks like a date. If so,
1004b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann     * returns the same date in a cleaned-up format for the user.  Otherwise, returns
10145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov     * the supplied string unchanged.
10245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov     */
10345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    public static String formatDate(Context context, String string) {
10445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        if (string == null) {
10545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            return null;
10645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        }
10745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
10845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        string = string.trim();
10945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        if (string.length() == 0) {
11045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            return string;
11145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        }
11245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
11345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        ParsePosition parsePosition = new ParsePosition(0);
11445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
1155a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        final boolean noYearParsed;
1162b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        Date date;
1172b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov
1185a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        // Unfortunately, we can't parse Feb 29th correctly, so let's handle this day seperately
1195a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        if (NO_YEAR_DATE_FEB29TH.equals(string)) {
1205a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann            date = getUtcDate(0, Calendar.FEBRUARY, 29);
1215a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann            noYearParsed = true;
1225a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        } else {
123428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Cheng            synchronized (CommonDateUtils.NO_YEAR_DATE_FORMAT) {
124428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Cheng                date = CommonDateUtils.NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
1255a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann            }
1265a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann            noYearParsed = parsePosition.getIndex() == string.length();
1272b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        }
1282b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov
1295a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann        if (noYearParsed) {
13011812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann            java.text.DateFormat outFormat = isMonthBeforeDay(context)
1312b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                    ? FORMAT_WITHOUT_YEAR_MONTH_FIRST
13211812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann                    : FORMAT_WITHOUT_YEAR_DAY_FIRST;
1332b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov            synchronized (outFormat) {
1342b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                return outFormat.format(date);
1352b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov            }
1362b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov        }
1372b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov
13845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        for (int i = 0; i < DATE_FORMATS.length; i++) {
13945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            SimpleDateFormat f = DATE_FORMATS[i];
14045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            synchronized (f) {
14145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov                parsePosition.setIndex(0);
1422b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                date = f.parse(string, parsePosition);
14345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov                if (parsePosition.getIndex() == string.length()) {
1442b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov                    java.text.DateFormat outFormat = DateFormat.getDateFormat(context);
1454b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann                    outFormat.setTimeZone(UTC_TIMEZONE);
1464b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann                    return outFormat.format(date);
14745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov                }
14845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            }
14945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        }
15045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        return string;
15145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    }
15245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov
15311812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann    public static boolean isMonthBeforeDay(Context context) {
15445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        char[] dateFormatOrder = DateFormat.getDateFormatOrder(context);
15545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        for (int i = 0; i < dateFormatOrder.length; i++) {
15645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            if (dateFormatOrder[i] == DateFormat.DATE) {
15745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov                return false;
15845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            }
15945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            if (dateFormatOrder[i] == DateFormat.MONTH) {
16045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov                return true;
16145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov            }
16245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        }
16345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov        return false;
16445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov    }
16545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov}
166