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