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 2245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport java.text.ParsePosition; 2345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport java.text.SimpleDateFormat; 245a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmannimport java.util.Calendar; 2545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovimport java.util.Date; 264b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmannimport java.util.Locale; 274b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmannimport java.util.TimeZone; 2845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 2945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov/** 3045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * Utility methods for processing dates. 3145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov */ 3245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikovpublic class DateUtils { 334b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); 344b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann 354b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann // All the SimpleDateFormats in this class use the UTC timezone 364b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann public static final SimpleDateFormat NO_YEAR_DATE_FORMAT = 374b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("--MM-dd", Locale.US); 385a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann /** 395a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann * When parsing a date without a year, the system assumes 1970, which wasn't a leap-year. 405a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann * Let's add a one-off hack for that day of the year 415a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann */ 425a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann public static final String NO_YEAR_DATE_FEB29TH = "--02-29"; 434b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann public static final SimpleDateFormat FULL_DATE_FORMAT = 444b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyy-MM-dd", Locale.US); 45392ccec3b56e8074a5a028af28106134b39f64bcDaniel Lehmann public static final SimpleDateFormat DATE_AND_TIME_FORMAT = 464b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); 47dcf751d75ad5d31620a4216d8c53fd59525e8bd8Makoto Onuki public static final SimpleDateFormat NO_YEAR_DATE_AND_TIME_FORMAT = 48dcf751d75ad5d31620a4216d8c53fd59525e8bd8Makoto Onuki new SimpleDateFormat("--MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); 4945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 502b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov // Variations of ISO 8601 date format. Do not change the order - it does affect the 512b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov // result in ambiguous cases. 5245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov private static final SimpleDateFormat[] DATE_FORMATS = { 53392ccec3b56e8074a5a028af28106134b39f64bcDaniel Lehmann FULL_DATE_FORMAT, 54392ccec3b56e8074a5a028af28106134b39f64bcDaniel Lehmann DATE_AND_TIME_FORMAT, 554b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US), 564b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyyMMdd", Locale.US), 574b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyyMMdd'T'HHmmssSSS'Z'", Locale.US), 584b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US), 594b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'", Locale.US), 6045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov }; 612b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov 6245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_MONTH_FIRST = 6345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov new SimpleDateFormat("MMMM dd"); 6445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 6511812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_DAY_FIRST = 6645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov new SimpleDateFormat("dd MMMM"); 6745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 684b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann static { 694b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann for (SimpleDateFormat format : DATE_FORMATS) { 704b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann format.setLenient(true); 714b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann format.setTimeZone(UTC_TIMEZONE); 724b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann } 734b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann NO_YEAR_DATE_FORMAT.setTimeZone(UTC_TIMEZONE); 744b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann FORMAT_WITHOUT_YEAR_MONTH_FIRST.setTimeZone(UTC_TIMEZONE); 7511812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann FORMAT_WITHOUT_YEAR_DAY_FIRST.setTimeZone(UTC_TIMEZONE); 764b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann } 774b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann 7845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov /** 7945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * Parses the supplied string to see if it looks like a date. If so, 802b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov * returns the date. Otherwise, returns null. 812b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov */ 822b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov public static Date parseDate(String string) { 832b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov ParsePosition parsePosition = new ParsePosition(0); 842b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov for (int i = 0; i < DATE_FORMATS.length; i++) { 852b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov SimpleDateFormat f = DATE_FORMATS[i]; 862b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov synchronized (f) { 872b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov parsePosition.setIndex(0); 882b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov Date date = f.parse(string, parsePosition); 892b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov if (parsePosition.getIndex() == string.length()) { 902b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov return date; 912b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 922b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 932b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 942b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov return null; 952b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 962b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov 975a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann private static final Date getUtcDate(int year, int month, int dayOfMonth) { 985a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US); 995a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann calendar.set(Calendar.YEAR, year); 1005a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann calendar.set(Calendar.MONTH, month); 1015a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); 1025a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann return calendar.getTime(); 1035a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann } 1045a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann 1052b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov /** 1062b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov * Parses the supplied string to see if it looks like a date. If so, 1074b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann * returns the same date in a cleaned-up format for the user. Otherwise, returns 10845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov * the supplied string unchanged. 10945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov */ 11045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov public static String formatDate(Context context, String string) { 11145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov if (string == null) { 11245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov return null; 11345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 11445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 11545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov string = string.trim(); 11645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov if (string.length() == 0) { 11745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov return string; 11845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 11945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 12045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov ParsePosition parsePosition = new ParsePosition(0); 12145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 1225a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann final boolean noYearParsed; 1232b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov Date date; 1242b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov 1255a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann // Unfortunately, we can't parse Feb 29th correctly, so let's handle this day seperately 1265a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann if (NO_YEAR_DATE_FEB29TH.equals(string)) { 1275a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann date = getUtcDate(0, Calendar.FEBRUARY, 29); 1285a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann noYearParsed = true; 1295a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann } else { 1305a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann synchronized (NO_YEAR_DATE_FORMAT) { 1315a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann date = NO_YEAR_DATE_FORMAT.parse(string, parsePosition); 1325a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann } 1335a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann noYearParsed = parsePosition.getIndex() == string.length(); 1342b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 1352b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov 1365a7a26962cc781d675280c790789e2dea28a7b34Daniel Lehmann if (noYearParsed) { 13711812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann java.text.DateFormat outFormat = isMonthBeforeDay(context) 1382b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov ? FORMAT_WITHOUT_YEAR_MONTH_FIRST 13911812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann : FORMAT_WITHOUT_YEAR_DAY_FIRST; 1402b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov synchronized (outFormat) { 1412b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov return outFormat.format(date); 1422b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 1432b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov } 1442b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov 14545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov for (int i = 0; i < DATE_FORMATS.length; i++) { 14645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov SimpleDateFormat f = DATE_FORMATS[i]; 14745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov synchronized (f) { 14845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov parsePosition.setIndex(0); 1492b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov date = f.parse(string, parsePosition); 15045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov if (parsePosition.getIndex() == string.length()) { 1512b3f95cc12b76523410782d4178562ce241410efDmitri Plotnikov java.text.DateFormat outFormat = DateFormat.getDateFormat(context); 1524b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann outFormat.setTimeZone(UTC_TIMEZONE); 1534b648485346462dc26c3a469caeae8589f5fbd58Daniel Lehmann return outFormat.format(date); 15445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 15545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 15645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 15745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov return string; 15845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 15945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov 16011812c59ee140d62eaf3a8d826d2018767d490c5Daniel Lehmann public static boolean isMonthBeforeDay(Context context) { 16145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov char[] dateFormatOrder = DateFormat.getDateFormatOrder(context); 16245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov for (int i = 0; i < dateFormatOrder.length; i++) { 16345b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov if (dateFormatOrder[i] == DateFormat.DATE) { 16445b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov return false; 16545b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 16645b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov if (dateFormatOrder[i] == DateFormat.MONTH) { 16745b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov return true; 16845b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 16945b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 17045b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov return false; 17145b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov } 17245b4402f0c05b21312dd8d647ae549d43057537dDmitri Plotnikov} 173