(转)android开发中常见日期管理工具类

原地址:http://blog.csdn.net/lenmoyouzi/article/details/17524779


在日常的开发中,我们常常需要根据各种需要,对日期进行不同形式的格式化。这里我将日常用到的日期格式化加以总结。写出个工具方法,欢迎大家总结:

     1、对时间的加减操作,主要需求为闹钟等题型功能,用到的API为:Calendar  

             

[java]  view plain  copy
  1. /** 
  2.      * 获取增加多少月的时间 
  3.      *  
  4.      * @return addMonth - 增加多少月 
  5.      */  
  6.     public static Date getAddMonthDate(int addMonth) {  
  7.         Calendar calendar = Calendar.getInstance();  
  8.         calendar.add(Calendar.MONTH, addMonth);  
  9.         return calendar.getTime();  
  10.     }  
  11.   
  12.     /** 
  13.      * 获取增加多少天的时间 
  14.      *  
  15.      * @return addDay - 增加多少天 
  16.      */  
  17.     public static Date getAddDayDate(int addDay) {  
  18.         Calendar calendar = Calendar.getInstance();  
  19.         calendar.add(Calendar.DAY_OF_YEAR, addDay);  
  20.         return calendar.getTime();  
  21.     }  
  22.     /** 
  23.      * 获取增加多少小时的时间 
  24.      *  
  25.      * @return addDay - 增加多少消失 
  26.      */  
  27.     public static Date getAddHourDate(int addHour) {  
  28.         Calendar calendar = Calendar.getInstance();  
  29.         calendar.add(Calendar.HOUR, addHour);  
  30.         return calendar.getTime();  
  31.     }  

2、按照  yyyy/MM/dd hh:mm等格式显示日期,主要用到的API为:SimpleDateFormat,Time

             

[java]  view plain  copy
  1. /** 
  2.      * 显示时间格式为 hh:mm 
  3.      *  
  4.      * @param context 
  5.      * @param when 
  6.      * @return String 
  7.      */  
  8.     @SuppressLint("SimpleDateFormat")  
  9.     public static String formatTimeShort(Context context, long when) {  
  10.         String formatStr = "HH:mm";  
  11.         SimpleDateFormat sdf = new SimpleDateFormat(formatStr);  
  12.         String temp = sdf.format(when);  
  13.         if (temp != null && temp.length() == 5 && temp.substring(01).equals("0")) {  
  14.             temp = temp.substring(1);  
  15.         }  
  16.         return temp;  
  17.     }  
3、格式时间,让其输出,今天,昨天,两天前以及日期等。

         

[java]  view plain  copy
  1. /** 
  2.      * 显示时间格式为今天、昨天、yyyy/MM/dd hh:mm 
  3.      *  
  4.      * @param context 
  5.      * @param when 
  6.      * @return String 
  7.      */  
  8.     public static String formatTimeString(Context context, long when) {  
  9.         Time then = new Time();  
  10.         then.set(when);  
  11.         Time now = new Time();  
  12.         now.setToNow();  
  13.   
  14.         String formatStr;  
  15.         if (then.year != now.year) {  
  16.             formatStr = "yyyy/MM/dd";  
  17.         } else if (then.yearDay != now.yearDay) {  
  18.             // If it is from a different day than today, show only the date.  
  19.             formatStr = "MM/dd";  
  20.         } else {  
  21.             // Otherwise, if the message is from today, show the time.  
  22.             formatStr = "HH:MM";  
  23.         }  
  24.   
  25.         if (then.year == now.year && then.yearDay == now.yearDay) {  
  26.             return context.getString(R.string.date_today);  
  27.         } else if ((then.year == now.year) && ((now.yearDay - then.yearDay) == 1)) {  
  28.             return context.getString(R.string.date_yesterday);  
  29.         } else {  
  30.             SimpleDateFormat sdf = new SimpleDateFormat(formatStr);  
  31.             String temp = sdf.format(when);  
  32.             if (temp != null && temp.length() == 5 && temp.substring(01).equals("0")) {  
  33.                 temp = temp.substring(1);  
  34.             }  
  35.             return temp;  
  36.         }  
  37.     }  
4、按照上下午显示时间:主要用到的API,DateUtils

             DateUtils.formatDateTime(this, System.getTimeMillis(), DateUtils.FORMAT_SHOW_TIME);

5、判断两个日期是否是同一个日期:

            

[java]  view plain  copy
  1. /** 
  2.      * 是否同一天 
  3.      *  
  4.      * @param date1 
  5.      * @param date2 
  6.      * @return 
  7.      */  
  8.     public static boolean isSameDate(long date1, long date2) {  
  9.         long days1 = date1 / (1000 * 60 * 60 * 24);  
  10.         long days2 = date2 / (1000 * 60 * 60 * 24);  
  11.         return days1 == days2;  
  12.     }  
6、附上android系统的DateUtils类的代码。

       

[java]  view plain  copy
  1. public class DateUtils  
  2. {  
  3.     private static final Object sLock = new Object();  
  4.     private static Configuration sLastConfig;  
  5.     private static java.text.DateFormat sStatusTimeFormat;  
  6.     private static String sElapsedFormatMMSS;  
  7.     private static String sElapsedFormatHMMSS;  
  8.   
  9.     private static final String FAST_FORMAT_HMMSS = "%1$d:%2$02d:%3$02d";  
  10.     private static final String FAST_FORMAT_MMSS = "%1$02d:%2$02d";  
  11.     private static final char TIME_SEPARATOR = ':';  
  12.   
  13.   
  14.     public static final long SECOND_IN_MILLIS = 1000;  
  15.     public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;  
  16.     public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;  
  17.     public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;  
  18.     public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;  
  19.     /** 
  20.      * This constant is actually the length of 364 days, not of a year! 
  21.      */  
  22.     public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;  
  23.   
  24.     // The following FORMAT_* symbols are used for specifying the format of  
  25.     // dates and times in the formatDateRange method.  
  26.     public static final int FORMAT_SHOW_TIME = 0x00001;  
  27.     public static final int FORMAT_SHOW_WEEKDAY = 0x00002;  
  28.     public static final int FORMAT_SHOW_YEAR = 0x00004;  
  29.     public static final int FORMAT_NO_YEAR = 0x00008;  
  30.     public static final int FORMAT_SHOW_DATE = 0x00010;  
  31.     public static final int FORMAT_NO_MONTH_DAY = 0x00020;  
  32.     @Deprecated  
  33.     public static final int FORMAT_12HOUR = 0x00040;  
  34.     @Deprecated  
  35.     public static final int FORMAT_24HOUR = 0x00080;  
  36.     @Deprecated  
  37.     public static final int FORMAT_CAP_AMPM = 0x00100;  
  38.     public static final int FORMAT_NO_NOON = 0x00200;  
  39.     @Deprecated  
  40.     public static final int FORMAT_CAP_NOON = 0x00400;  
  41.     public static final int FORMAT_NO_MIDNIGHT = 0x00800;  
  42.     @Deprecated  
  43.     public static final int FORMAT_CAP_MIDNIGHT = 0x01000;  
  44.     /** 
  45.      * @deprecated Use 
  46.      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} 
  47.      * and pass in {@link Time#TIMEZONE_UTC Time.TIMEZONE_UTC} for the timeZone instead. 
  48.      */  
  49.     @Deprecated  
  50.     public static final int FORMAT_UTC = 0x02000;  
  51.     public static final int FORMAT_ABBREV_TIME = 0x04000;  
  52.     public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;  
  53.     public static final int FORMAT_ABBREV_MONTH = 0x10000;  
  54.     public static final int FORMAT_NUMERIC_DATE = 0x20000;  
  55.     public static final int FORMAT_ABBREV_RELATIVE = 0x40000;  
  56.     public static final int FORMAT_ABBREV_ALL = 0x80000;  
  57.     @Deprecated  
  58.     public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);  
  59.     @Deprecated  
  60.     public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT);  
  61.   
  62.     // Date and time format strings that are constant and don't need to be  
  63.     // translated.  
  64.     /** 
  65.      * This is not actually the preferred 24-hour date format in all locales. 
  66.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  67.      */  
  68.     @Deprecated  
  69.     public static final String HOUR_MINUTE_24 = "%H:%M";  
  70.     public static final String MONTH_FORMAT = "%B";  
  71.     /** 
  72.      * This is not actually a useful month name in all locales. 
  73.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  74.      */  
  75.     @Deprecated  
  76.     public static final String ABBREV_MONTH_FORMAT = "%b";  
  77.     public static final String NUMERIC_MONTH_FORMAT = "%m";  
  78.     public static final String MONTH_DAY_FORMAT = "%-d";  
  79.     public static final String YEAR_FORMAT = "%Y";  
  80.     public static final String YEAR_FORMAT_TWO_DIGITS = "%g";  
  81.     public static final String WEEKDAY_FORMAT = "%A";  
  82.     public static final String ABBREV_WEEKDAY_FORMAT = "%a";  
  83.   
  84.     // This table is used to lookup the resource string id of a format string  
  85.     // used for formatting a start and end date that fall in the same year.  
  86.     // The index is constructed from a bit-wise OR of the boolean values:  
  87.     // {showTime, showYear, showWeekDay}.  For example, if showYear and  
  88.     // showWeekDay are both true, then the index would be 3.  
  89.     /** @deprecated do not use. */  
  90.     public static final int sameYearTable[] = {  
  91.         com.android.internal.R.string.same_year_md1_md2,  
  92.         com.android.internal.R.string.same_year_wday1_md1_wday2_md2,  
  93.         com.android.internal.R.string.same_year_mdy1_mdy2,  
  94.         com.android.internal.R.string.same_year_wday1_mdy1_wday2_mdy2,  
  95.         com.android.internal.R.string.same_year_md1_time1_md2_time2,  
  96.         com.android.internal.R.string.same_year_wday1_md1_time1_wday2_md2_time2,  
  97.         com.android.internal.R.string.same_year_mdy1_time1_mdy2_time2,  
  98.         com.android.internal.R.string.same_year_wday1_mdy1_time1_wday2_mdy2_time2,  
  99.   
  100.         // Numeric date strings  
  101.         com.android.internal.R.string.numeric_md1_md2,  
  102.         com.android.internal.R.string.numeric_wday1_md1_wday2_md2,  
  103.         com.android.internal.R.string.numeric_mdy1_mdy2,  
  104.         com.android.internal.R.string.numeric_wday1_mdy1_wday2_mdy2,  
  105.         com.android.internal.R.string.numeric_md1_time1_md2_time2,  
  106.         com.android.internal.R.string.numeric_wday1_md1_time1_wday2_md2_time2,  
  107.         com.android.internal.R.string.numeric_mdy1_time1_mdy2_time2,  
  108.         com.android.internal.R.string.numeric_wday1_mdy1_time1_wday2_mdy2_time2,  
  109.     };  
  110.   
  111.     // This table is used to lookup the resource string id of a format string  
  112.     // used for formatting a start and end date that fall in the same month.  
  113.     // The index is constructed from a bit-wise OR of the boolean values:  
  114.     // {showTime, showYear, showWeekDay}.  For example, if showYear and  
  115.     // showWeekDay are both true, then the index would be 3.  
  116.     /** @deprecated do not use. */  
  117.     public static final int sameMonthTable[] = {  
  118.         com.android.internal.R.string.same_month_md1_md2,  
  119.         com.android.internal.R.string.same_month_wday1_md1_wday2_md2,  
  120.         com.android.internal.R.string.same_month_mdy1_mdy2,  
  121.         com.android.internal.R.string.same_month_wday1_mdy1_wday2_mdy2,  
  122.         com.android.internal.R.string.same_month_md1_time1_md2_time2,  
  123.         com.android.internal.R.string.same_month_wday1_md1_time1_wday2_md2_time2,  
  124.         com.android.internal.R.string.same_month_mdy1_time1_mdy2_time2,  
  125.         com.android.internal.R.string.same_month_wday1_mdy1_time1_wday2_mdy2_time2,  
  126.   
  127.         com.android.internal.R.string.numeric_md1_md2,  
  128.         com.android.internal.R.string.numeric_wday1_md1_wday2_md2,  
  129.         com.android.internal.R.string.numeric_mdy1_mdy2,  
  130.         com.android.internal.R.string.numeric_wday1_mdy1_wday2_mdy2,  
  131.         com.android.internal.R.string.numeric_md1_time1_md2_time2,  
  132.         com.android.internal.R.string.numeric_wday1_md1_time1_wday2_md2_time2,  
  133.         com.android.internal.R.string.numeric_mdy1_time1_mdy2_time2,  
  134.         com.android.internal.R.string.numeric_wday1_mdy1_time1_wday2_mdy2_time2,  
  135.     };  
  136.   
  137.     /** 
  138.      * Request the full spelled-out name. For use with the 'abbrev' parameter of 
  139.      * {@link #getDayOfWeekString} and {@link #getMonthString}. 
  140.      * 
  141.      * @more <p> 
  142.      *       e.g. "Sunday" or "January" 
  143.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  144.      */  
  145.     @Deprecated  
  146.     public static final int LENGTH_LONG = 10;  
  147.   
  148.     /** 
  149.      * Request an abbreviated version of the name. For use with the 'abbrev' 
  150.      * parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. 
  151.      * 
  152.      * @more <p> 
  153.      *       e.g. "Sun" or "Jan" 
  154.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  155.      */  
  156.     @Deprecated  
  157.     public static final int LENGTH_MEDIUM = 20;  
  158.   
  159.     /** 
  160.      * Request a shorter abbreviated version of the name. 
  161.      * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. 
  162.      * @more 
  163.      * <p>e.g. "Su" or "Jan" 
  164.      * <p>In most languages, the results returned for LENGTH_SHORT will be the same as 
  165.      * the results returned for {@link #LENGTH_MEDIUM}. 
  166.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  167.      */  
  168.     @Deprecated  
  169.     public static final int LENGTH_SHORT = 30;  
  170.   
  171.     /** 
  172.      * Request an even shorter abbreviated version of the name. 
  173.      * Do not use this.  Currently this will always return the same result 
  174.      * as {@link #LENGTH_SHORT}. 
  175.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  176.      */  
  177.     @Deprecated  
  178.     public static final int LENGTH_SHORTER = 40;  
  179.   
  180.     /** 
  181.      * Request an even shorter abbreviated version of the name. 
  182.      * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}. 
  183.      * @more 
  184.      * <p>e.g. "S", "T", "T" or "J" 
  185.      * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as 
  186.      * the results returned for {@link #LENGTH_SHORT}. 
  187.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  188.      */  
  189.     @Deprecated  
  190.     public static final int LENGTH_SHORTEST = 50;  
  191.   
  192.     /** 
  193.      * Return a string for the day of the week. 
  194.      * @param dayOfWeek One of {@link Calendar#SUNDAY Calendar.SUNDAY}, 
  195.      *               {@link Calendar#MONDAY Calendar.MONDAY}, etc. 
  196.      * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT}, 
  197.      *               {@link #LENGTH_MEDIUM}, or {@link #LENGTH_SHORTEST}. 
  198.      *               Note that in most languages, {@link #LENGTH_SHORT} 
  199.      *               will return the same as {@link #LENGTH_MEDIUM}. 
  200.      *               Undefined lengths will return {@link #LENGTH_MEDIUM} 
  201.      *               but may return something different in the future. 
  202.      * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds. 
  203.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  204.      */  
  205.     @Deprecated  
  206.     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {  
  207.         LocaleData d = LocaleData.get(Locale.getDefault());  
  208.         String[] names;  
  209.         switch (abbrev) {  
  210.             case LENGTH_LONG:       names = d.longWeekdayNames;  break;  
  211.             case LENGTH_MEDIUM:     names = d.shortWeekdayNames; break;  
  212.             case LENGTH_SHORT:      names = d.shortWeekdayNames; break// TODO  
  213.             case LENGTH_SHORTER:    names = d.shortWeekdayNames; break// TODO  
  214.             case LENGTH_SHORTEST:   names = d.tinyWeekdayNames;  break;  
  215.             default:                names = d.shortWeekdayNames; break;  
  216.         }  
  217.         return names[dayOfWeek];  
  218.     }  
  219.   
  220.     /** 
  221.      * Return a localized string for AM or PM. 
  222.      * @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}. 
  223.      * @throws IndexOutOfBoundsException if the ampm is out of bounds. 
  224.      * @return Localized version of "AM" or "PM". 
  225.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  226.      */  
  227.     @Deprecated  
  228.     public static String getAMPMString(int ampm) {  
  229.         return LocaleData.get(Locale.getDefault()).amPm[ampm - Calendar.AM];  
  230.     }  
  231.   
  232.     /** 
  233.      * Return a localized string for the month of the year. 
  234.      * @param month One of {@link Calendar#JANUARY Calendar.JANUARY}, 
  235.      *               {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc. 
  236.      * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM}, 
  237.      *               or {@link #LENGTH_SHORTEST}. 
  238.      *               Undefined lengths will return {@link #LENGTH_MEDIUM} 
  239.      *               but may return something different in the future. 
  240.      * @return Localized month of the year. 
  241.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  242.      */  
  243.     @Deprecated  
  244.     public static String getMonthString(int month, int abbrev) {  
  245.         // Note that here we use d.shortMonthNames for MEDIUM, SHORT and SHORTER.  
  246.         // This is a shortcut to not spam the translators with too many variations  
  247.         // of the same string.  If we find that in a language the distinction  
  248.         // is necessary, we can can add more without changing this API.  
  249.         LocaleData d = LocaleData.get(Locale.getDefault());  
  250.         String[] names;  
  251.         switch (abbrev) {  
  252.             case LENGTH_LONG:       names = d.longMonthNames;  break;  
  253.             case LENGTH_MEDIUM:     names = d.shortMonthNames; break;  
  254.             case LENGTH_SHORT:      names = d.shortMonthNames; break;  
  255.             case LENGTH_SHORTER:    names = d.shortMonthNames; break;  
  256.             case LENGTH_SHORTEST:   names = d.tinyMonthNames;  break;  
  257.             default:                names = d.shortMonthNames; break;  
  258.         }  
  259.         return names[month];  
  260.     }  
  261.   
  262.     /** 
  263.      * Return a localized string for the month of the year, for 
  264.      * contexts where the month is not formatted together with 
  265.      * a day of the month. 
  266.      * 
  267.      * @param month One of {@link Calendar#JANUARY Calendar.JANUARY}, 
  268.      *               {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc. 
  269.      * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM}, 
  270.      *               or {@link #LENGTH_SHORTEST}. 
  271.      *               Undefined lengths will return {@link #LENGTH_MEDIUM} 
  272.      *               but may return something different in the future. 
  273.      * @return Localized month of the year. 
  274.      * @hide Pending API council approval 
  275.      * @deprecated use {@link java.text.SimpleDateFormat} instead. 
  276.      */  
  277.     @Deprecated  
  278.     public static String getStandaloneMonthString(int month, int abbrev) {  
  279.         // Note that here we use d.shortMonthNames for MEDIUM, SHORT and SHORTER.  
  280.         // This is a shortcut to not spam the translators with too many variations  
  281.         // of the same string.  If we find that in a language the distinction  
  282.         // is necessary, we can can add more without changing this API.  
  283.         LocaleData d = LocaleData.get(Locale.getDefault());  
  284.         String[] names;  
  285.         switch (abbrev) {  
  286.             case LENGTH_LONG:       names = d.longStandAloneMonthNames;  
  287.                                                             break;  
  288.             case LENGTH_MEDIUM:     names = d.shortMonthNames; break;  
  289.             case LENGTH_SHORT:      names = d.shortMonthNames; break;  
  290.             case LENGTH_SHORTER:    names = d.shortMonthNames; break;  
  291.             case LENGTH_SHORTEST:   names = d.tinyMonthNames;  break;  
  292.             default:                names = d.shortMonthNames; break;  
  293.         }  
  294.         return names[month];  
  295.     }  
  296.   
  297.     /** 
  298.      * Returns a string describing the elapsed time since startTime. 
  299.      * @param startTime some time in the past. 
  300.      * @return a String object containing the elapsed time. 
  301.      * @see #getRelativeTimeSpanString(long, long, long) 
  302.      */  
  303.     public static CharSequence getRelativeTimeSpanString(long startTime) {  
  304.         return getRelativeTimeSpanString(startTime, System.currentTimeMillis(), MINUTE_IN_MILLIS);  
  305.     }  
  306.   
  307.     /** 
  308.      * Returns a string describing 'time' as a time relative to 'now'. 
  309.      * <p> 
  310.      * Time spans in the past are formatted like "42 minutes ago". 
  311.      * Time spans in the future are formatted like "in 42 minutes". 
  312.      * 
  313.      * @param time the time to describe, in milliseconds 
  314.      * @param now the current time in milliseconds 
  315.      * @param minResolution the minimum timespan to report. For example, a time 3 seconds in the 
  316.      *     past will be reported as "0 minutes ago" if this is set to MINUTE_IN_MILLIS. Pass one of 
  317.      *     0, MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, WEEK_IN_MILLIS 
  318.      */  
  319.     public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution) {  
  320.         int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH;  
  321.         return getRelativeTimeSpanString(time, now, minResolution, flags);  
  322.     }  
  323.   
  324.     /** 
  325.      * Returns a string describing 'time' as a time relative to 'now'. 
  326.      * <p> 
  327.      * Time spans in the past are formatted like "42 minutes ago". Time spans in 
  328.      * the future are formatted like "in 42 minutes". 
  329.      * <p> 
  330.      * Can use {@link #FORMAT_ABBREV_RELATIVE} flag to use abbreviated relative 
  331.      * times, like "42 mins ago". 
  332.      * 
  333.      * @param time the time to describe, in milliseconds 
  334.      * @param now the current time in milliseconds 
  335.      * @param minResolution the minimum timespan to report. For example, a time 
  336.      *            3 seconds in the past will be reported as "0 minutes ago" if 
  337.      *            this is set to MINUTE_IN_MILLIS. Pass one of 0, 
  338.      *            MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, 
  339.      *            WEEK_IN_MILLIS 
  340.      * @param flags a bit mask of formatting options, such as 
  341.      *            {@link #FORMAT_NUMERIC_DATE} or 
  342.      *            {@link #FORMAT_ABBREV_RELATIVE} 
  343.      */  
  344.     public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution,  
  345.             int flags) {  
  346.         Resources r = Resources.getSystem();  
  347.         boolean abbrevRelative = (flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0;  
  348.   
  349.         boolean past = (now >= time);  
  350.         long duration = Math.abs(now - time);  
  351.   
  352.         int resId;  
  353.         long count;  
  354.         if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {  
  355.             count = duration / SECOND_IN_MILLIS;  
  356.             if (past) {  
  357.                 if (abbrevRelative) {  
  358.                     resId = com.android.internal.R.plurals.abbrev_num_seconds_ago;  
  359.                 } else {  
  360.                     resId = com.android.internal.R.plurals.num_seconds_ago;  
  361.                 }  
  362.             } else {  
  363.                 if (abbrevRelative) {  
  364.                     resId = com.android.internal.R.plurals.abbrev_in_num_seconds;  
  365.                 } else {  
  366.                     resId = com.android.internal.R.plurals.in_num_seconds;  
  367.                 }  
  368.             }  
  369.         } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {  
  370.             count = duration / MINUTE_IN_MILLIS;  
  371.             if (past) {  
  372.                 if (abbrevRelative) {  
  373.                     resId = com.android.internal.R.plurals.abbrev_num_minutes_ago;  
  374.                 } else {  
  375.                     resId = com.android.internal.R.plurals.num_minutes_ago;  
  376.                 }  
  377.             } else {  
  378.                 if (abbrevRelative) {  
  379.                     resId = com.android.internal.R.plurals.abbrev_in_num_minutes;  
  380.                 } else {  
  381.                     resId = com.android.internal.R.plurals.in_num_minutes;  
  382.                 }  
  383.             }  
  384.         } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {  
  385.             count = duration / HOUR_IN_MILLIS;  
  386.             if (past) {  
  387.                 if (abbrevRelative) {  
  388.                     resId = com.android.internal.R.plurals.abbrev_num_hours_ago;  
  389.                 } else {  
  390.                     resId = com.android.internal.R.plurals.num_hours_ago;  
  391.                 }  
  392.             } else {  
  393.                 if (abbrevRelative) {  
  394.                     resId = com.android.internal.R.plurals.abbrev_in_num_hours;  
  395.                 } else {  
  396.                     resId = com.android.internal.R.plurals.in_num_hours;  
  397.                 }  
  398.             }  
  399.         } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {  
  400.             count = getNumberOfDaysPassed(time, now);  
  401.             if (past) {  
  402.                 if (abbrevRelative) {  
  403.                     resId = com.android.internal.R.plurals.abbrev_num_days_ago;  
  404.                 } else {  
  405.                     resId = com.android.internal.R.plurals.num_days_ago;  
  406.                 }  
  407.             } else {  
  408.                 if (abbrevRelative) {  
  409.                     resId = com.android.internal.R.plurals.abbrev_in_num_days;  
  410.                 } else {  
  411.                     resId = com.android.internal.R.plurals.in_num_days;  
  412.                 }  
  413.             }  
  414.         } else {  
  415.             // We know that we won't be showing the time, so it is safe to pass  
  416.             // in a null context.  
  417.             return formatDateRange(null, time, time, flags);  
  418.         }  
  419.   
  420.         String format = r.getQuantityString(resId, (int) count);  
  421.         return String.format(format, count);  
  422.     }  
  423.   
  424.     /** 
  425.      * Returns the number of days passed between two dates. 
  426.      * 
  427.      * @param date1 first date 
  428.      * @param date2 second date 
  429.      * @return number of days passed between to dates. 
  430.      */  
  431.     private synchronized static long getNumberOfDaysPassed(long date1, long date2) {  
  432.         if (sThenTime == null) {  
  433.             sThenTime = new Time();  
  434.         }  
  435.         sThenTime.set(date1);  
  436.         int day1 = Time.getJulianDay(date1, sThenTime.gmtoff);  
  437.         sThenTime.set(date2);  
  438.         int day2 = Time.getJulianDay(date2, sThenTime.gmtoff);  
  439.         return Math.abs(day2 - day1);  
  440.     }  
  441.   
  442.     /** 
  443.      * Return string describing the elapsed time since startTime formatted like 
  444.      * "[relative time/date], [time]". 
  445.      * <p> 
  446.      * Example output strings for the US date format. 
  447.      * <ul> 
  448.      * <li>3 mins ago, 10:15 AM</li> 
  449.      * <li>yesterday, 12:20 PM</li> 
  450.      * <li>Dec 12, 4:12 AM</li> 
  451.      * <li>11/14/2007, 8:20 AM</li> 
  452.      * </ul> 
  453.      * 
  454.      * @param time some time in the past. 
  455.      * @param minResolution the minimum elapsed time (in milliseconds) to report 
  456.      *            when showing relative times. For example, a time 3 seconds in 
  457.      *            the past will be reported as "0 minutes ago" if this is set to 
  458.      *            {@link #MINUTE_IN_MILLIS}. 
  459.      * @param transitionResolution the elapsed time (in milliseconds) at which 
  460.      *            to stop reporting relative measurements. Elapsed times greater 
  461.      *            than this resolution will default to normal date formatting. 
  462.      *            For example, will transition from "6 days ago" to "Dec 12" 
  463.      *            when using {@link #WEEK_IN_MILLIS}. 
  464.      */  
  465.     public static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution,  
  466.             long transitionResolution, int flags) {  
  467.         Resources r = Resources.getSystem();  
  468.   
  469.         long now = System.currentTimeMillis();  
  470.         long duration = Math.abs(now - time);  
  471.   
  472.         // getRelativeTimeSpanString() doesn't correctly format relative dates  
  473.         // above a week or exact dates below a day, so clamp  
  474.         // transitionResolution as needed.  
  475.         if (transitionResolution > WEEK_IN_MILLIS) {  
  476.             transitionResolution = WEEK_IN_MILLIS;  
  477.         } else if (transitionResolution < DAY_IN_MILLIS) {  
  478.             transitionResolution = DAY_IN_MILLIS;  
  479.         }  
  480.   
  481.         CharSequence timeClause = formatDateRange(c, time, time, FORMAT_SHOW_TIME);  
  482.   
  483.         String result;  
  484.         if (duration < transitionResolution) {  
  485.             CharSequence relativeClause = getRelativeTimeSpanString(time, now, minResolution, flags);  
  486.             result = r.getString(com.android.internal.R.string.relative_time, relativeClause, timeClause);  
  487.         } else {  
  488.             CharSequence dateClause = getRelativeTimeSpanString(c, time, false);  
  489.             result = r.getString(com.android.internal.R.string.date_time, dateClause, timeClause);  
  490.         }  
  491.   
  492.         return result;  
  493.     }  
  494.   
  495.     /** 
  496.      * Returns a string describing a day relative to the current day. For example if the day is 
  497.      * today this function returns "Today", if the day was a week ago it returns "7 days ago", and 
  498.      * if the day is in 2 weeks it returns "in 14 days". 
  499.      * 
  500.      * @param r the resources to get the strings from 
  501.      * @param day the relative day to describe in UTC milliseconds 
  502.      * @param today the current time in UTC milliseconds 
  503.      * @return a formatting string 
  504.      */  
  505.     private static final String getRelativeDayString(Resources r, long day, long today) {  
  506.         Time startTime = new Time();  
  507.         startTime.set(day);  
  508.         Time currentTime = new Time();  
  509.         currentTime.set(today);  
  510.   
  511.         int startDay = Time.getJulianDay(day, startTime.gmtoff);  
  512.         int currentDay = Time.getJulianDay(today, currentTime.gmtoff);  
  513.   
  514.         int days = Math.abs(currentDay - startDay);  
  515.         boolean past = (today > day);  
  516.   
  517.         // TODO: some locales name other days too, such as de_DE's "Vorgestern" (today - 2).  
  518.         Locale locale = r.getConfiguration().locale;  
  519.         if (locale == null) {  
  520.             locale = Locale.getDefault();  
  521.         }  
  522.         if (days == 1) {  
  523.             if (past) {  
  524.                 return LocaleData.get(locale).yesterday;  
  525.             } else {  
  526.                 return LocaleData.get(locale).tomorrow;  
  527.             }  
  528.         } else if (days == 0) {  
  529.             return LocaleData.get(locale).today;  
  530.         }  
  531.   
  532.         int resId;  
  533.         if (past) {  
  534.             resId = com.android.internal.R.plurals.num_days_ago;  
  535.         } else {  
  536.             resId = com.android.internal.R.plurals.in_num_days;  
  537.         }  
  538.   
  539.         String format = r.getQuantityString(resId, days);  
  540.         return String.format(format, days);  
  541.     }  
  542.   
  543.     private static void initFormatStrings() {  
  544.         synchronized (sLock) {  
  545.             initFormatStringsLocked();  
  546.         }  
  547.     }  
  548.   
  549.     private static void initFormatStringsLocked() {  
  550.         Resources r = Resources.getSystem();  
  551.         Configuration cfg = r.getConfiguration();  
  552.         if (sLastConfig == null || !sLastConfig.equals(cfg)) {  
  553.             sLastConfig = cfg;  
  554.             sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);  
  555.             sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);  
  556.             sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);  
  557.         }  
  558.     }  
  559.   
  560.     /** 
  561.      * Format a time so it appears like it would in the status bar clock. 
  562.      * @deprecated use {@link #DateFormat.getTimeFormat(Context)} instead. 
  563.      * @hide 
  564.      */  
  565.     public static final CharSequence timeString(long millis) {  
  566.         synchronized (sLock) {  
  567.             initFormatStringsLocked();  
  568.             return sStatusTimeFormat.format(millis);  
  569.         }  
  570.     }  
  571.   
  572.     /** 
  573.      * Formats an elapsed time in the form "MM:SS" or "H:MM:SS" 
  574.      * for display on the call-in-progress screen. 
  575.      * @param elapsedSeconds the elapsed time in seconds. 
  576.      */  
  577.     public static String formatElapsedTime(long elapsedSeconds) {  
  578.         return formatElapsedTime(null, elapsedSeconds);  
  579.     }  
  580.   
  581.     /** 
  582.      * Formats an elapsed time in the form "MM:SS" or "H:MM:SS" 
  583.      * for display on the call-in-progress screen. 
  584.      * 
  585.      * @param recycle {@link StringBuilder} to recycle, if possible 
  586.      * @param elapsedSeconds the elapsed time in seconds. 
  587.      */  
  588.     public static String formatElapsedTime(StringBuilder recycle, long elapsedSeconds) {  
  589.         initFormatStrings();  
  590.   
  591.         long hours = 0;  
  592.         long minutes = 0;  
  593.         long seconds = 0;  
  594.   
  595.         if (elapsedSeconds >= 3600) {  
  596.             hours = elapsedSeconds / 3600;  
  597.             elapsedSeconds -= hours * 3600;  
  598.         }  
  599.         if (elapsedSeconds >= 60) {  
  600.             minutes = elapsedSeconds / 60;  
  601.             elapsedSeconds -= minutes * 60;  
  602.         }  
  603.         seconds = elapsedSeconds;  
  604.   
  605.         String result;  
  606.         if (hours > 0) {  
  607.             return formatElapsedTime(recycle, sElapsedFormatHMMSS, hours, minutes, seconds);  
  608.         } else {  
  609.             return formatElapsedTime(recycle, sElapsedFormatMMSS, minutes, seconds);  
  610.         }  
  611.     }  
  612.   
  613.     private static void append(StringBuilder sb, long value, boolean pad, char zeroDigit) {  
  614.         if (value < 10) {  
  615.             if (pad) {  
  616.                 sb.append(zeroDigit);  
  617.             }  
  618.         } else {  
  619.             sb.append((char) (zeroDigit + (value / 10)));  
  620.         }  
  621.         sb.append((char) (zeroDigit + (value % 10)));  
  622.     }  
  623.   
  624.     /** 
  625.      * Fast formatting of h:mm:ss. 
  626.      */  
  627.     private static String formatElapsedTime(StringBuilder recycle, String format, long hours,  
  628.             long minutes, long seconds) {  
  629.         if (FAST_FORMAT_HMMSS.equals(format)) {  
  630.             char zeroDigit = LocaleData.get(Locale.getDefault()).zeroDigit;  
  631.   
  632.             StringBuilder sb = recycle;  
  633.             if (sb == null) {  
  634.                 sb = new StringBuilder(8);  
  635.             } else {  
  636.                 sb.setLength(0);  
  637.             }  
  638.             append(sb, hours, false, zeroDigit);  
  639.             sb.append(TIME_SEPARATOR);  
  640.             append(sb, minutes, true, zeroDigit);  
  641.             sb.append(TIME_SEPARATOR);  
  642.             append(sb, seconds, true, zeroDigit);  
  643.             return sb.toString();  
  644.         } else {  
  645.             return String.format(format, hours, minutes, seconds);  
  646.         }  
  647.     }  
  648.   
  649.     /** 
  650.      * Fast formatting of mm:ss. 
  651.      */  
  652.     private static String formatElapsedTime(StringBuilder recycle, String format, long minutes,  
  653.             long seconds) {  
  654.         if (FAST_FORMAT_MMSS.equals(format)) {  
  655.             char zeroDigit = LocaleData.get(Locale.getDefault()).zeroDigit;  
  656.   
  657.             StringBuilder sb = recycle;  
  658.             if (sb == null) {  
  659.                 sb = new StringBuilder(8);  
  660.             } else {  
  661.                 sb.setLength(0);  
  662.             }  
  663.             append(sb, minutes, false, zeroDigit);  
  664.             sb.append(TIME_SEPARATOR);  
  665.             append(sb, seconds, true, zeroDigit);  
  666.             return sb.toString();  
  667.         } else {  
  668.             return String.format(format, minutes, seconds);  
  669.         }  
  670.     }  
  671.   
  672.     /** 
  673.      * Format a date / time such that if the then is on the same day as now, it shows 
  674.      * just the time and if it's a different day, it shows just the date. 
  675.      * 
  676.      * <p>The parameters dateFormat and timeFormat should each be one of 
  677.      * {@link java.text.DateFormat#DEFAULT}, 
  678.      * {@link java.text.DateFormat#FULL}, 
  679.      * {@link java.text.DateFormat#LONG}, 
  680.      * {@link java.text.DateFormat#MEDIUM} 
  681.      * or 
  682.      * {@link java.text.DateFormat#SHORT} 
  683.      * 
  684.      * @param then the date to format 
  685.      * @param now the base time 
  686.      * @param dateStyle how to format the date portion. 
  687.      * @param timeStyle how to format the time portion. 
  688.      */  
  689.     public static final CharSequence formatSameDayTime(long then, long now,  
  690.             int dateStyle, int timeStyle) {  
  691.         Calendar thenCal = new GregorianCalendar();  
  692.         thenCal.setTimeInMillis(then);  
  693.         Date thenDate = thenCal.getTime();  
  694.         Calendar nowCal = new GregorianCalendar();  
  695.         nowCal.setTimeInMillis(now);  
  696.   
  697.         java.text.DateFormat f;  
  698.   
  699.         if (thenCal.get(Calendar.YEAR) == nowCal.get(Calendar.YEAR)  
  700.                 && thenCal.get(Calendar.MONTH) == nowCal.get(Calendar.MONTH)  
  701.                 && thenCal.get(Calendar.DAY_OF_MONTH) == nowCal.get(Calendar.DAY_OF_MONTH)) {  
  702.             f = java.text.DateFormat.getTimeInstance(timeStyle);  
  703.         } else {  
  704.             f = java.text.DateFormat.getDateInstance(dateStyle);  
  705.         }  
  706.         return f.format(thenDate);  
  707.     }  
  708.   
  709.     /** 
  710.      * @hide 
  711.      * @deprecated use {@link android.text.format.Time} 
  712.      */  
  713.     public static Calendar newCalendar(boolean zulu)  
  714.     {  
  715.         if (zulu)  
  716.             return Calendar.getInstance(TimeZone.getTimeZone("GMT"));  
  717.   
  718.         return Calendar.getInstance();  
  719.     }  
  720.   
  721.     /** 
  722.      * @return true if the supplied when is today else false 
  723.      */  
  724.     public static boolean isToday(long when) {  
  725.         Time time = new Time();  
  726.         time.set(when);  
  727.   
  728.         int thenYear = time.year;  
  729.         int thenMonth = time.month;  
  730.         int thenMonthDay = time.monthDay;  
  731.   
  732.         time.set(System.currentTimeMillis());  
  733.         return (thenYear == time.year)  
  734.                 && (thenMonth == time.month)  
  735.                 && (thenMonthDay == time.monthDay);  
  736.     }  
  737.   
  738.     /** 
  739.      * @hide 
  740.      * @deprecated use {@link android.text.format.Time} 
  741.      * Return true if this date string is local time 
  742.      */  
  743.     public static boolean isUTC(String s)  
  744.     {  
  745.         if (s.length() == 16 && s.charAt(15) == 'Z') {  
  746.             return true;  
  747.         }  
  748.         if (s.length() == 9 && s.charAt(8) == 'Z') {  
  749.             // XXX not sure if this case possible/valid  
  750.             return true;  
  751.         }  
  752.         return false;  
  753.     }  
  754.   
  755.     /** 
  756.      * Return a string containing the date and time in RFC2445 format. 
  757.      * Ensures that the time is written in UTC.  The Calendar class doesn't 
  758.      * really help out with this, so this is slower than it ought to be. 
  759.      * 
  760.      * @param cal the date and time to write 
  761.      * @hide 
  762.      * @deprecated use {@link android.text.format.Time} 
  763.      */  
  764.     public static String writeDateTime(Calendar cal)  
  765.     {  
  766.         TimeZone tz = TimeZone.getTimeZone("GMT");  
  767.         GregorianCalendar c = new GregorianCalendar(tz);  
  768.         c.setTimeInMillis(cal.getTimeInMillis());  
  769.         return writeDateTime(c, true);  
  770.     }  
  771.   
  772.     /** 
  773.      * Return a string containing the date and time in RFC2445 format. 
  774.      * 
  775.      * @param cal the date and time to write 
  776.      * @param zulu If the calendar is in UTC, pass true, and a Z will 
  777.      * be written at the end as per RFC2445.  Otherwise, the time is 
  778.      * considered in localtime. 
  779.      * @hide 
  780.      * @deprecated use {@link android.text.format.Time} 
  781.      */  
  782.     public static String writeDateTime(Calendar cal, boolean zulu)  
  783.     {  
  784.         StringBuilder sb = new StringBuilder();  
  785.         sb.ensureCapacity(16);  
  786.         if (zulu) {  
  787.             sb.setLength(16);  
  788.             sb.setCharAt(15'Z');  
  789.         } else {  
  790.             sb.setLength(15);  
  791.         }  
  792.         return writeDateTime(cal, sb);  
  793.     }  
  794.   
  795.     /** 
  796.      * Return a string containing the date and time in RFC2445 format. 
  797.      * 
  798.      * @param cal the date and time to write 
  799.      * @param sb a StringBuilder to use.  It is assumed that setLength 
  800.      *           has already been called on sb to the appropriate length 
  801.      *           which is sb.setLength(zulu ? 16 : 15) 
  802.      * @hide 
  803.      * @deprecated use {@link android.text.format.Time} 
  804.      */  
  805.     public static String writeDateTime(Calendar cal, StringBuilder sb)  
  806.     {  
  807.         int n;  
  808.   
  809.         n = cal.get(Calendar.YEAR);  
  810.         sb.setCharAt(3, (char)('0'+n%10));  
  811.         n /= 10;  
  812.         sb.setCharAt(2, (char)('0'+n%10));  
  813.         n /= 10;  
  814.         sb.setCharAt(1, (char)('0'+n%10));  
  815.         n /= 10;  
  816.         sb.setCharAt(0, (char)('0'+n%10));  
  817.   
  818.         n = cal.get(Calendar.MONTH) + 1;  
  819.         sb.setCharAt(5, (char)('0'+n%10));  
  820.         n /= 10;  
  821.         sb.setCharAt(4, (char)('0'+n%10));  
  822.   
  823.         n = cal.get(Calendar.DAY_OF_MONTH);  
  824.         sb.setCharAt(7, (char)('0'+n%10));  
  825.         n /= 10;  
  826.         sb.setCharAt(6, (char)('0'+n%10));  
  827.   
  828.         sb.setCharAt(8'T');  
  829.   
  830.         n = cal.get(Calendar.HOUR_OF_DAY);  
  831.         sb.setCharAt(10, (char)('0'+n%10));  
  832.         n /= 10;  
  833.         sb.setCharAt(9, (char)('0'+n%10));  
  834.   
  835.         n = cal.get(Calendar.MINUTE);  
  836.         sb.setCharAt(12, (char)('0'+n%10));  
  837.         n /= 10;  
  838.         sb.setCharAt(11, (char)('0'+n%10));  
  839.   
  840.         n = cal.get(Calendar.SECOND);  
  841.         sb.setCharAt(14, (char)('0'+n%10));  
  842.         n /= 10;  
  843.         sb.setCharAt(13, (char)('0'+n%10));  
  844.   
  845.         return sb.toString();  
  846.     }  
  847.   
  848.     /** 
  849.      * @hide 
  850.      * @deprecated use {@link android.text.format.Time} 
  851.      */  
  852.     public static void assign(Calendar lval, Calendar rval)  
  853.     {  
  854.         // there should be a faster way.  
  855.         lval.clear();  
  856.         lval.setTimeInMillis(rval.getTimeInMillis());  
  857.     }  
  858.   
  859.     /** 
  860.      * Formats a date or a time range according to the local conventions. 
  861.      * <p> 
  862.      * Note that this is a convenience method. Using it involves creating an 
  863.      * internal {@link java.util.Formatter} instance on-the-fly, which is 
  864.      * somewhat costly in terms of memory and time. This is probably acceptable 
  865.      * if you use the method only rarely, but if you rely on it for formatting a 
  866.      * large number of dates, consider creating and reusing your own 
  867.      * {@link java.util.Formatter} instance and use the version of 
  868.      * {@link #formatDateRange(Context, long, long, int) formatDateRange} 
  869.      * that takes a {@link java.util.Formatter}. 
  870.      * 
  871.      * @param context the context is required only if the time is shown 
  872.      * @param startMillis the start time in UTC milliseconds 
  873.      * @param endMillis the end time in UTC milliseconds 
  874.      * @param flags a bit mask of options See 
  875.      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} 
  876.      * @return a string containing the formatted date/time range. 
  877.      */  
  878.     public static String formatDateRange(Context context, long startMillis,  
  879.             long endMillis, int flags) {  
  880.         Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());  
  881.         return formatDateRange(context, f, startMillis, endMillis, flags).toString();  
  882.     }  
  883.   
  884.     /** 
  885.      * Formats a date or a time range according to the local conventions. 
  886.      * <p> 
  887.      * Note that this is a convenience method for formatting the date or 
  888.      * time range in the local time zone. If you want to specify the time 
  889.      * zone please use 
  890.      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}. 
  891.      * 
  892.      * @param context the context is required only if the time is shown 
  893.      * @param formatter the Formatter used for formatting the date range. 
  894.      * Note: be sure to call setLength(0) on StringBuilder passed to 
  895.      * the Formatter constructor unless you want the results to accumulate. 
  896.      * @param startMillis the start time in UTC milliseconds 
  897.      * @param endMillis the end time in UTC milliseconds 
  898.      * @param flags a bit mask of options See 
  899.      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange} 
  900.      * @return a string containing the formatted date/time range. 
  901.      */  
  902.     public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,  
  903.             long endMillis, int flags) {  
  904.         return formatDateRange(context, formatter, startMillis, endMillis, flags, null);  
  905.     }  
  906.   
  907.     /** 
  908.      * Formats a date or a time range according to the local conventions. 
  909.      * 
  910.      * <p> 
  911.      * Example output strings (date formats in these examples are shown using 
  912.      * the US date format convention but that may change depending on the 
  913.      * local settings): 
  914.      * <ul> 
  915.      *   <li>10:15am</li> 
  916.      *   <li>3:00pm - 4:00pm</li> 
  917.      *   <li>3pm - 4pm</li> 
  918.      *   <li>3PM - 4PM</li> 
  919.      *   <li>08:00 - 17:00</li> 
  920.      *   <li>Oct 9</li> 
  921.      *   <li>Tue, Oct 9</li> 
  922.      *   <li>October 9, 2007</li> 
  923.      *   <li>Oct 9 - 10</li> 
  924.      *   <li>Oct 9 - 10, 2007</li> 
  925.      *   <li>Oct 28 - Nov 3, 2007</li> 
  926.      *   <li>Dec 31, 2007 - Jan 1, 2008</li> 
  927.      *   <li>Oct 9, 8:00am - Oct 10, 5:00pm</li> 
  928.      *   <li>12/31/2007 - 01/01/2008</li> 
  929.      * </ul> 
  930.      * 
  931.      * <p> 
  932.      * The flags argument is a bitmask of options from the following list: 
  933.      * 
  934.      * <ul> 
  935.      *   <li>FORMAT_SHOW_TIME</li> 
  936.      *   <li>FORMAT_SHOW_WEEKDAY</li> 
  937.      *   <li>FORMAT_SHOW_YEAR</li> 
  938.      *   <li>FORMAT_NO_YEAR</li> 
  939.      *   <li>FORMAT_SHOW_DATE</li> 
  940.      *   <li>FORMAT_NO_MONTH_DAY</li> 
  941.      *   <li>FORMAT_12HOUR</li> 
  942.      *   <li>FORMAT_24HOUR</li> 
  943.      *   <li>FORMAT_CAP_AMPM</li> 
  944.      *   <li>FORMAT_NO_NOON</li> 
  945.      *   <li>FORMAT_CAP_NOON</li> 
  946.      *   <li>FORMAT_NO_MIDNIGHT</li> 
  947.      *   <li>FORMAT_CAP_MIDNIGHT</li> 
  948.      *   <li>FORMAT_UTC</li> 
  949.      *   <li>FORMAT_ABBREV_TIME</li> 
  950.      *   <li>FORMAT_ABBREV_WEEKDAY</li> 
  951.      *   <li>FORMAT_ABBREV_MONTH</li> 
  952.      *   <li>FORMAT_ABBREV_ALL</li> 
  953.      *   <li>FORMAT_NUMERIC_DATE</li> 
  954.      * </ul> 
  955.      * 
  956.      * <p> 
  957.      * If FORMAT_SHOW_TIME is set, the time is shown as part of the date range. 
  958.      * If the start and end time are the same, then just the start time is 
  959.      * shown. 
  960.      * 
  961.      * <p> 
  962.      * If FORMAT_SHOW_WEEKDAY is set, then the weekday is shown. 
  963.      * 
  964.      * <p> 
  965.      * If FORMAT_SHOW_YEAR is set, then the year is always shown. 
  966.      * If FORMAT_NO_YEAR is set, then the year is not shown. 
  967.      * If neither FORMAT_SHOW_YEAR nor FORMAT_NO_YEAR are set, then the year 
  968.      * is shown only if it is different from the current year, or if the start 
  969.      * and end dates fall on different years.  If both are set, 
  970.      * FORMAT_SHOW_YEAR takes precedence. 
  971.      * 
  972.      * <p> 
  973.      * Normally the date is shown unless the start and end day are the same. 
  974.      * If FORMAT_SHOW_DATE is set, then the date is always shown, even for 
  975.      * same day ranges. 
  976.      * 
  977.      * <p> 
  978.      * If FORMAT_NO_MONTH_DAY is set, then if the date is shown, just the 
  979.      * month name will be shown, not the day of the month.  For example, 
  980.      * "January, 2008" instead of "January 6 - 12, 2008". 
  981.      * 
  982.      * <p> 
  983.      * If FORMAT_CAP_AMPM is set and 12-hour time is used, then the "AM" 
  984.      * and "PM" are capitalized.  You should not use this flag 
  985.      * because in some locales these terms cannot be capitalized, and in 
  986.      * many others it doesn't make sense to do so even though it is possible. 
  987.      * 
  988.      * <p> 
  989.      * If FORMAT_NO_NOON is set and 12-hour time is used, then "12pm" is 
  990.      * shown instead of "noon". 
  991.      * 
  992.      * <p> 
  993.      * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Noon" is 
  994.      * shown instead of "noon".  You should probably not use this flag 
  995.      * because in many locales it will not make sense to capitalize 
  996.      * the term. 
  997.      * 
  998.      * <p> 
  999.      * If FORMAT_NO_MIDNIGHT is set and 12-hour time is used, then "12am" is 
  1000.      * shown instead of "midnight". 
  1001.      * 
  1002.      * <p> 
  1003.      * If FORMAT_CAP_MIDNIGHT is set and 12-hour time is used, then "Midnight" 
  1004.      * is shown instead of "midnight".  You should probably not use this 
  1005.      * flag because in many locales it will not make sense to capitalize 
  1006.      * the term. 
  1007.      * 
  1008.      * <p> 
  1009.      * If FORMAT_12HOUR is set and the time is shown, then the time is 
  1010.      * shown in the 12-hour time format. You should not normally set this. 
  1011.      * Instead, let the time format be chosen automatically according to the 
  1012.      * system settings. If both FORMAT_12HOUR and FORMAT_24HOUR are set, then 
  1013.      * FORMAT_24HOUR takes precedence. 
  1014.      * 
  1015.      * <p> 
  1016.      * If FORMAT_24HOUR is set and the time is shown, then the time is 
  1017.      * shown in the 24-hour time format. You should not normally set this. 
  1018.      * Instead, let the time format be chosen automatically according to the 
  1019.      * system settings. If both FORMAT_12HOUR and FORMAT_24HOUR are set, then 
  1020.      * FORMAT_24HOUR takes precedence. 
  1021.      * 
  1022.      * <p> 
  1023.      * If FORMAT_UTC is set, then the UTC time zone is used for the start 
  1024.      * and end milliseconds unless a time zone is specified. If a time zone 
  1025.      * is specified it will be used regardless of the FORMAT_UTC flag. 
  1026.      * 
  1027.      * <p> 
  1028.      * If FORMAT_ABBREV_TIME is set and 12-hour time format is used, then the 
  1029.      * start and end times (if shown) are abbreviated by not showing the minutes 
  1030.      * if they are zero.  For example, instead of "3:00pm" the time would be 
  1031.      * abbreviated to "3pm". 
  1032.      * 
  1033.      * <p> 
  1034.      * If FORMAT_ABBREV_WEEKDAY is set, then the weekday (if shown) is 
  1035.      * abbreviated to a 3-letter string. 
  1036.      * 
  1037.      * <p> 
  1038.      * If FORMAT_ABBREV_MONTH is set, then the month (if shown) is abbreviated 
  1039.      * to a 3-letter string. 
  1040.      * 
  1041.      * <p> 
  1042.      * If FORMAT_ABBREV_ALL is set, then the weekday and the month (if shown) 
  1043.      * are abbreviated to 3-letter strings. 
  1044.      * 
  1045.      * <p> 
  1046.      * If FORMAT_NUMERIC_DATE is set, then the date is shown in numeric format 
  1047.      * instead of using the name of the month.  For example, "12/31/2008" 
  1048.      * instead of "December 31, 2008". 
  1049.      * 
  1050.      * <p> 
  1051.      * If the end date ends at 12:00am at the beginning of a day, it is 
  1052.      * formatted as the end of the previous day in two scenarios: 
  1053.      * <ul> 
  1054.      *   <li>For single day events. This results in "8pm - midnight" instead of 
  1055.      *       "Nov 10, 8pm - Nov 11, 12am".</li> 
  1056.      *   <li>When the time is not displayed. This results in "Nov 10 - 11" for 
  1057.      *       an event with a start date of Nov 10 and an end date of Nov 12 at 
  1058.      *       00:00.</li> 
  1059.      * </ul> 
  1060.      * 
  1061.      * @param context the context is required only if the time is shown 
  1062.      * @param formatter the Formatter used for formatting the date range. 
  1063.      * Note: be sure to call setLength(0) on StringBuilder passed to 
  1064.      * the Formatter constructor unless you want the results to accumulate. 
  1065.      * @param startMillis the start time in UTC milliseconds 
  1066.      * @param endMillis the end time in UTC milliseconds 
  1067.      * @param flags a bit mask of options 
  1068.      * @param timeZone the time zone to compute the string in. Use null for local 
  1069.      * or if the FORMAT_UTC flag is being used. 
  1070.      * 
  1071.      * @return the formatter with the formatted date/time range appended to the string buffer. 
  1072.      */  
  1073.     public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,  
  1074.             long endMillis, int flags, String timeZone) {  
  1075.         Resources res = Resources.getSystem();  
  1076.         boolean showTime = (flags & FORMAT_SHOW_TIME) != 0;  
  1077.         boolean showWeekDay = (flags & FORMAT_SHOW_WEEKDAY) != 0;  
  1078.         boolean showYear = (flags & FORMAT_SHOW_YEAR) != 0;  
  1079.         boolean noYear = (flags & FORMAT_NO_YEAR) != 0;  
  1080.         boolean useUTC = (flags & FORMAT_UTC) != 0;  
  1081.         boolean abbrevWeekDay = (flags & (FORMAT_ABBREV_WEEKDAY | FORMAT_ABBREV_ALL)) != 0;  
  1082.         boolean abbrevMonth = (flags & (FORMAT_ABBREV_MONTH | FORMAT_ABBREV_ALL)) != 0;  
  1083.         boolean noMonthDay = (flags & FORMAT_NO_MONTH_DAY) != 0;  
  1084.         boolean numericDate = (flags & FORMAT_NUMERIC_DATE) != 0;  
  1085.   
  1086.         // If we're getting called with a single instant in time (from  
  1087.         // e.g. formatDateTime(), below), then we can skip a lot of  
  1088.         // computation below that'd otherwise be thrown out.  
  1089.         boolean isInstant = (startMillis == endMillis);  
  1090.   
  1091.         Time startDate;  
  1092.         if (timeZone != null) {  
  1093.             startDate = new Time(timeZone);  
  1094.         } else if (useUTC) {  
  1095.             startDate = new Time(Time.TIMEZONE_UTC);  
  1096.         } else {  
  1097.             startDate = new Time();  
  1098.         }  
  1099.         startDate.set(startMillis);  
  1100.   
  1101.         Time endDate;  
  1102.         int dayDistance;  
  1103.         if (isInstant) {  
  1104.             endDate = startDate;  
  1105.             dayDistance = 0;  
  1106.         } else {  
  1107.             if (timeZone != null) {  
  1108.                 endDate = new Time(timeZone);  
  1109.             } else if (useUTC) {  
  1110.                 endDate = new Time(Time.TIMEZONE_UTC);  
  1111.             } else {  
  1112.                 endDate = new Time();  
  1113.             }  
  1114.             endDate.set(endMillis);  
  1115.             int startJulianDay = Time.getJulianDay(startMillis, startDate.gmtoff);  
  1116.             int endJulianDay = Time.getJulianDay(endMillis, endDate.gmtoff);  
  1117.             dayDistance = endJulianDay - startJulianDay;  
  1118.         }  
  1119.   
  1120.         if (!isInstant  
  1121.             && (endDate.hour | endDate.minute | endDate.second) == 0  
  1122.             && (!showTime || dayDistance <= 1)) {  
  1123.             endDate.monthDay -= 1;  
  1124.             endDate.normalize(true /* ignore isDst */);  
  1125.         }  
  1126.   
  1127.         int startDay = startDate.monthDay;  
  1128.         int startMonthNum = startDate.month;  
  1129.         int startYear = startDate.year;  
  1130.   
  1131.         int endDay = endDate.monthDay;  
  1132.         int endMonthNum = endDate.month;  
  1133.         int endYear = endDate.year;  
  1134.   
  1135.         String startWeekDayString = "";  
  1136.         String endWeekDayString = "";  
  1137.         if (showWeekDay) {  
  1138.             String weekDayFormat = "";  
  1139.             if (abbrevWeekDay) {  
  1140.                 weekDayFormat = ABBREV_WEEKDAY_FORMAT;  
  1141.             } else {  
  1142.                 weekDayFormat = WEEKDAY_FORMAT;  
  1143.             }  
  1144.             startWeekDayString = startDate.format(weekDayFormat);  
  1145.             endWeekDayString = isInstant ? startWeekDayString : endDate.format(weekDayFormat);  
  1146.         }  
  1147.   
  1148.         String startTimeString = "";  
  1149.         String endTimeString = "";  
  1150.         if (showTime) {  
  1151.             String startTimeFormat = "";  
  1152.             String endTimeFormat = "";  
  1153.             boolean force24Hour = (flags & FORMAT_24HOUR) != 0;  
  1154.             boolean force12Hour = (flags & FORMAT_12HOUR) != 0;  
  1155.             boolean use24Hour;  
  1156.             if (force24Hour) {  
  1157.                 use24Hour = true;  
  1158.             } else if (force12Hour) {  
  1159.                 use24Hour = false;  
  1160.             } else {  
  1161.                 use24Hour = DateFormat.is24HourFormat(context);  
  1162.             }  
  1163.             if (use24Hour) {  
  1164.                 startTimeFormat = endTimeFormat =  
  1165.                     res.getString(com.android.internal.R.string.hour_minute_24);  
  1166.             } else {  
  1167.                 boolean abbrevTime = (flags & (FORMAT_ABBREV_TIME | FORMAT_ABBREV_ALL)) != 0;  
  1168.                 boolean capAMPM = (flags & FORMAT_CAP_AMPM) != 0;  
  1169.                 boolean noNoon = (flags & FORMAT_NO_NOON) != 0;  
  1170.                 boolean capNoon = (flags & FORMAT_CAP_NOON) != 0;  
  1171.                 boolean noMidnight = (flags & FORMAT_NO_MIDNIGHT) != 0;  
  1172.                 boolean capMidnight = (flags & FORMAT_CAP_MIDNIGHT) != 0;  
  1173.   
  1174.                 boolean startOnTheHour = startDate.minute == 0 && startDate.second == 0;  
  1175.                 boolean endOnTheHour = endDate.minute == 0 && endDate.second == 0;  
  1176.                 if (abbrevTime && startOnTheHour) {  
  1177.                     if (capAMPM) {  
  1178.                         startTimeFormat = res.getString(com.android.internal.R.string.hour_cap_ampm);  
  1179.                     } else {  
  1180.                         startTimeFormat = res.getString(com.android.internal.R.string.hour_ampm);  
  1181.                     }  
  1182.                 } else {  
  1183.                     if (capAMPM) {  
  1184.                         startTimeFormat = res.getString(com.android.internal.R.string.hour_minute_cap_ampm);  
  1185.                     } else {  
  1186.                         startTimeFormat = res.getString(com.android.internal.R.string.hour_minute_ampm);  
  1187.                     }  
  1188.                 }  
  1189.   
  1190.                 // Don't waste time on setting endTimeFormat when  
  1191.                 // we're dealing with an instant, where we'll never  
  1192.                 // need the end point.  (It's the same as the start  
  1193.                 // point)  
  1194.                 if (!isInstant) {  
  1195.                     if (abbrevTime && endOnTheHour) {  
  1196.                         if (capAMPM) {  
  1197.                             endTimeFormat = res.getString(com.android.internal.R.string.hour_cap_ampm);  
  1198.                         } else {  
  1199.                             endTimeFormat = res.getString(com.android.internal.R.string.hour_ampm);  
  1200.                         }  
  1201.                     } else {  
  1202.                         if (capAMPM) {  
  1203.                             endTimeFormat = res.getString(com.android.internal.R.string.hour_minute_cap_ampm);  
  1204.                         } else {  
  1205.                             endTimeFormat = res.getString(com.android.internal.R.string.hour_minute_ampm);  
  1206.                         }  
  1207.                     }  
  1208.   
  1209.                     if (endDate.hour == 12 && endOnTheHour && !noNoon) {  
  1210.                         if (capNoon) {  
  1211.                             endTimeFormat = res.getString(com.android.internal.R.string.Noon);  
  1212.                         } else {  
  1213.                             endTimeFormat = res.getString(com.android.internal.R.string.noon);  
  1214.                         }  
  1215.                     } else if (endDate.hour == 0 && endOnTheHour && !noMidnight) {  
  1216.                         if (capMidnight) {  
  1217.                             endTimeFormat = res.getString(com.android.internal.R.string.Midnight);  
  1218.                         } else {  
  1219.                             endTimeFormat = res.getString(com.android.internal.R.string.midnight);  
  1220.                         }  
  1221.                     }  
  1222.                 }  
  1223.   
  1224.                 if (startDate.hour == 12 && startOnTheHour && !noNoon) {  
  1225.                     if (capNoon) {  
  1226.                         startTimeFormat = res.getString(com.android.internal.R.string.Noon);  
  1227.                     } else {  
  1228.                         startTimeFormat = res.getString(com.android.internal.R.string.noon);  
  1229.                     }  
  1230.                     // Don't show the start time starting at midnight.  Show  
  1231.                     // 12am instead.  
  1232.                 }  
  1233.             }  
  1234.   
  1235.             startTimeString = startDate.format(startTimeFormat);  
  1236.             endTimeString = isInstant ? startTimeString : endDate.format(endTimeFormat);  
  1237.         }  
  1238.   
  1239.         // Show the year if the user specified FORMAT_SHOW_YEAR or if  
  1240.         // the starting and end years are different from each other  
  1241.         // or from the current year.  But don't show the year if the  
  1242.         // user specified FORMAT_NO_YEAR.  
  1243.         if (showYear) {  
  1244.             // No code... just a comment for clarity.  Keep showYear  
  1245.             // on, as they enabled it with FORMAT_SHOW_YEAR.  This  
  1246.             // takes precedence over them setting FORMAT_NO_YEAR.  
  1247.         } else if (noYear) {  
  1248.             // They explicitly didn't want a year.  
  1249.             showYear = false;  
  1250.         } else if (startYear != endYear) {  
  1251.             showYear = true;  
  1252.         } else {  
  1253.             // Show the year if it's not equal to the current year.  
  1254.             Time currentTime = new Time();  
  1255.             currentTime.setToNow();  
  1256.             showYear = startYear != currentTime.year;  
  1257.         }  
  1258.   
  1259.         String defaultDateFormat, fullFormat, dateRange;  
  1260.         if (numericDate) {  
  1261.             defaultDateFormat = res.getString(com.android.internal.R.string.numeric_date);  
  1262.         } else if (showYear) {  
  1263.             if (abbrevMonth) {  
  1264.                 if (noMonthDay) {  
  1265.                     defaultDateFormat = res.getString(com.android.internal.R.string.abbrev_month_year);  
  1266.                 } else {  
  1267.                     defaultDateFormat = res.getString(com.android.internal.R.string.abbrev_month_day_year);  
  1268.                 }  
  1269.             } else {  
  1270.                 if (noMonthDay) {  
  1271.                     defaultDateFormat = res.getString(com.android.internal.R.string.month_year);  
  1272.                 } else {  
  1273.                     defaultDateFormat = res.getString(com.android.internal.R.string.month_day_year);  
  1274.                 }  
  1275.             }  
  1276.         } else {  
  1277.             if (abbrevMonth) {  
  1278.                 if (noMonthDay) {  
  1279.                     defaultDateFormat = res.getString(com.android.internal.R.string.abbrev_month);  
  1280.                 } else {  
  1281.                     defaultDateFormat = res.getString(com.android.internal.R.string.abbrev_month_day);  
  1282.                 }  
  1283.             } else {  
  1284.                 if (noMonthDay) {  
  1285.                     defaultDateFormat = res.getString(com.android.internal.R.string.month);  
  1286.                 } else {  
  1287.                     defaultDateFormat = res.getString(com.android.internal.R.string.month_day);  
  1288.                 }  
  1289.             }  
  1290.         }  
  1291.   
  1292.         if (showWeekDay) {  
  1293.             if (showTime) {  
  1294.                 fullFormat = res.getString(com.android.internal.R.string.wday1_date1_time1_wday2_date2_time2);  
  1295.             } else {  
  1296.                 fullFormat = res.getString(com.android.internal.R.string.wday1_date1_wday2_date2);  
  1297.             }  
  1298.         } else {  
  1299.             if (showTime) {  
  1300.                 fullFormat = res.getString(com.android.internal.R.string.date1_time1_date2_time2);  
  1301.             } else {  
  1302.                 fullFormat = res.getString(com.android.internal.R.string.date1_date2);  
  1303.             }  
  1304.         }  
  1305.   
  1306.         if (noMonthDay && startMonthNum == endMonthNum && startYear == endYear) {  
  1307.             // Example: "January, 2008"  
  1308.             return formatter.format("%s", startDate.format(defaultDateFormat));  
  1309.         }  
  1310.   
  1311.         if (startYear != endYear || noMonthDay) {  
  1312.             // Different year or we are not showing the month day number.  
  1313.             // Example: "December 31, 2007 - January 1, 2008"  
  1314.             // Or: "January - February, 2008"  
  1315.             String startDateString = startDate.format(defaultDateFormat);  
  1316.             String endDateString = endDate.format(defaultDateFormat);  
  1317.   
  1318.             // The values that are used in a fullFormat string are specified  
  1319.             // by position.  
  1320.             return formatter.format(fullFormat,  
  1321.                     startWeekDayString, startDateString, startTimeString,  
  1322.                     endWeekDayString, endDateString, endTimeString);  
  1323.         }  
  1324.   
  1325.         // Get the month, day, and year strings for the start and end dates  
  1326.         String monthFormat;  
  1327.         if (numericDate) {  
  1328.             monthFormat = NUMERIC_MONTH_FORMAT;  
  1329.         } else if (abbrevMonth) {  
  1330.             monthFormat =  
  1331.                 res.getString(com.android.internal.R.string.short_format_month);  
  1332.         } else {  
  1333.             monthFormat = MONTH_FORMAT;  
  1334.         }  
  1335.         String startMonthString = startDate.format(monthFormat);  
  1336.         String startMonthDayString = startDate.format(MONTH_DAY_FORMAT);  
  1337.         String startYearString = startDate.format(YEAR_FORMAT);  
  1338.   
  1339.         String endMonthString = isInstant ? null : endDate.format(monthFormat);  
  1340.         String endMonthDayString = isInstant ? null : endDate.format(MONTH_DAY_FORMAT);  
  1341.         String endYearString = isInstant ? null : endDate.format(YEAR_FORMAT);  
  1342.   
  1343.         String startStandaloneMonthString = startMonthString;  
  1344.         String endStandaloneMonthString = endMonthString;  
  1345.         // We need standalone months for these strings in Persian (fa): http://b/6811327  
  1346.         if (!numericDate && !abbrevMonth && Locale.getDefault().getLanguage().equals("fa")) {  
  1347.             startStandaloneMonthString = startDate.format("%-B");  
  1348.             endStandaloneMonthString = endDate.format("%-B");  
  1349.         }  
  1350.   
  1351.         if (startMonthNum != endMonthNum) {  
  1352.             // Same year, different month.  
  1353.             // Example: "October 28 - November 3"  
  1354.             // or: "Wed, Oct 31 - Sat, Nov 3, 2007"  
  1355.             // or: "Oct 31, 8am - Sat, Nov 3, 2007, 5pm"  
  1356.   
  1357.             int index = 0;  
  1358.             if (showWeekDay) index = 1;  
  1359.             if (showYear) index += 2;  
  1360.             if (showTime) index += 4;  
  1361.             if (numericDate) index += 8;  
  1362.             int resId = sameYearTable[index];  
  1363.             fullFormat = res.getString(resId);  
  1364.   
  1365.             // The values that are used in a fullFormat string are specified  
  1366.             // by position.  
  1367.             return formatter.format(fullFormat,  
  1368.                     startWeekDayString, startMonthString, startMonthDayString,  
  1369.                     startYearString, startTimeString,  
  1370.                     endWeekDayString, endMonthString, endMonthDayString,  
  1371.                     endYearString, endTimeString,  
  1372.                     startStandaloneMonthString, endStandaloneMonthString);  
  1373.         }  
  1374.   
  1375.         if (startDay != endDay) {  
  1376.             // Same month, different day.  
  1377.             int index = 0;  
  1378.             if (showWeekDay) index = 1;  
  1379.             if (showYear) index += 2;  
  1380.             if (showTime) index += 4;  
  1381.             if (numericDate) index += 8;  
  1382.             int resId = sameMonthTable[index];  
  1383.             fullFormat = res.getString(resId);  
  1384.   
  1385.             // The values that are used in a fullFormat string are specified  
  1386.             // by position.  
  1387.             return formatter.format(fullFormat,  
  1388.                     startWeekDayString, startMonthString, startMonthDayString,  
  1389.                     startYearString, startTimeString,  
  1390.                     endWeekDayString, endMonthString, endMonthDayString,  
  1391.                     endYearString, endTimeString,  
  1392.                     startStandaloneMonthString, endStandaloneMonthString);  
  1393.         }  
  1394.   
  1395.         // Same start and end day  
  1396.         boolean showDate = (flags & FORMAT_SHOW_DATE) != 0;  
  1397.   
  1398.         // If nothing was specified, then show the date.  
  1399.         if (!showTime && !showDate && !showWeekDay) showDate = true;  
  1400.   
  1401.         // Compute the time string (example: "10:00 - 11:00 am")  
  1402.         String timeString = "";  
  1403.         if (showTime) {  
  1404.             // If the start and end time are the same, then just show the  
  1405.             // start time.  
  1406.             if (isInstant) {  
  1407.                 // Same start and end time.  
  1408.                 // Example: "10:15 AM"  
  1409.                 timeString = startTimeString;  
  1410.             } else {  
  1411.                 // Example: "10:00 - 11:00 am"  
  1412.                 String timeFormat = res.getString(com.android.internal.R.string.time1_time2);  
  1413.                 // Don't use the user supplied Formatter because the result will pollute the buffer.  
  1414.                 timeString = String.format(timeFormat, startTimeString, endTimeString);  
  1415.             }  
  1416.         }  
  1417.   
  1418.         // Figure out which full format to use.  
  1419.         fullFormat = "";  
  1420.         String dateString = "";  
  1421.         if (showDate) {  
  1422.             dateString = startDate.format(defaultDateFormat);  
  1423.             if (showWeekDay) {  
  1424.                 if (showTime) {  
  1425.                     // Example: "10:00 - 11:00 am, Tue, Oct 9"  
  1426.                     fullFormat = res.getString(com.android.internal.R.string.time_wday_date);  
  1427.                 } else {  
  1428.                     // Example: "Tue, Oct 9"  
  1429.                     fullFormat = res.getString(com.android.internal.R.string.wday_date);  
  1430.                 }  
  1431.             } else {  
  1432.                 if (showTime) {  
  1433.                     // Example: "10:00 - 11:00 am, Oct 9"  
  1434.                     fullFormat = res.getString(com.android.internal.R.string.time_date);  
  1435.                 } else {  
  1436.                     // Example: "Oct 9"  
  1437.                     return formatter.format("%s", dateString);  
  1438.                 }  
  1439.             }  
  1440.         } else if (showWeekDay) {  
  1441.             if (showTime) {  
  1442.                 // Example: "10:00 - 11:00 am, Tue"  
  1443.                 fullFormat = res.getString(com.android.internal.R.string.time_wday);  
  1444.             } else {  
  1445.                 // Example: "Tue"  
  1446.                 return formatter.format("%s", startWeekDayString);  
  1447.             }  
  1448.         } else if (showTime) {  
  1449.             return formatter.format("%s", timeString);  
  1450.         }  
  1451.   
  1452.         // The values that are used in a fullFormat string are specified  
  1453.         // by position.  
  1454.         return formatter.format(fullFormat, timeString, startWeekDayString, dateString);  
  1455.     }  
  1456.   
  1457.     /** 
  1458.      * Formats a date or a time according to the local conventions. There are 
  1459.      * lots of options that allow the caller to control, for example, if the 
  1460.      * time is shown, if the day of the week is shown, if the month name is 
  1461.      * abbreviated, if noon is shown instead of 12pm, and so on. For the 
  1462.      * complete list of options, see the documentation for 
  1463.      * {@link #formatDateRange}. 
  1464.      * <p> 
  1465.      * Example output strings (date formats in these examples are shown using 
  1466.      * the US date format convention but that may change depending on the 
  1467.      * local settings): 
  1468.      * <ul> 
  1469.      *   <li>10:15am</li> 
  1470.      *   <li>3:00pm</li> 
  1471.      *   <li>3pm</li> 
  1472.      *   <li>3PM</li> 
  1473.      *   <li>08:00</li> 
  1474.      *   <li>17:00</li> 
  1475.      *   <li>noon</li> 
  1476.      *   <li>Noon</li> 
  1477.      *   <li>midnight</li> 
  1478.      *   <li>Midnight</li> 
  1479.      *   <li>Oct 31</li> 
  1480.      *   <li>Oct 31, 2007</li> 
  1481.      *   <li>October 31, 2007</li> 
  1482.      *   <li>10am, Oct 31</li> 
  1483.      *   <li>17:00, Oct 31</li> 
  1484.      *   <li>Wed</li> 
  1485.      *   <li>Wednesday</li> 
  1486.      *   <li>10am, Wed, Oct 31</li> 
  1487.      *   <li>Wed, Oct 31</li> 
  1488.      *   <li>Wednesday, Oct 31</li> 
  1489.      *   <li>Wed, Oct 31, 2007</li> 
  1490.      *   <li>Wed, October 31</li> 
  1491.      *   <li>10/31/2007</li> 
  1492.      * </ul> 
  1493.      * 
  1494.      * @param context the context is required only if the time is shown 
  1495.      * @param millis a point in time in UTC milliseconds 
  1496.      * @param flags a bit mask of formatting options 
  1497.      * @return a string containing the formatted date/time. 
  1498.      */  
  1499.     public static String formatDateTime(Context context, long millis, int flags) {  
  1500.         return formatDateRange(context, millis, millis, flags);  
  1501.     }  
  1502.   
  1503.     /** 
  1504.      * @return a relative time string to display the time expressed by millis.  Times 
  1505.      * are counted starting at midnight, which means that assuming that the current 
  1506.      * time is March 31st, 0:30: 
  1507.      * <ul> 
  1508.      *   <li>"millis=0:10 today" will be displayed as "0:10"</li> 
  1509.      *   <li>"millis=11:30pm the day before" will be displayed as "Mar 30"</li> 
  1510.      * </ul> 
  1511.      * If the given millis is in a different year, then the full date is 
  1512.      * returned in numeric format (e.g., "10/12/2008"). 
  1513.      * 
  1514.      * @param withPreposition If true, the string returned will include the correct 
  1515.      * preposition ("at 9:20am", "on 10/12/2008" or "on May 29"). 
  1516.      */  
  1517.     public static CharSequence getRelativeTimeSpanString(Context c, long millis,  
  1518.             boolean withPreposition) {  
  1519.   
  1520.         String result;  
  1521.         long now = System.currentTimeMillis();  
  1522.         long span = Math.abs(now - millis);  
  1523.   
  1524.         synchronized (DateUtils.class) {  
  1525.             if (sNowTime == null) {  
  1526.                 sNowTime = new Time();  
  1527.             }  
  1528.   
  1529.             if (sThenTime == null) {  
  1530.                 sThenTime = new Time();  
  1531.             }  
  1532.   
  1533.             sNowTime.set(now);  
  1534.             sThenTime.set(millis);  
  1535.   
  1536.             int prepositionId;  
  1537.             if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {  
  1538.                 // Same day  
  1539.                 int flags = FORMAT_SHOW_TIME;  
  1540.                 result = formatDateRange(c, millis, millis, flags);  
  1541.                 prepositionId = R.string.preposition_for_time;  
  1542.             } else if (sNowTime.year != sThenTime.year) {  
  1543.                 // Different years  
  1544.                 int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;  
  1545.                 result = formatDateRange(c, millis, millis, flags);  
  1546.   
  1547.                 // This is a date (like "10/31/2008" so use the date preposition)  
  1548.                 prepositionId = R.string.preposition_for_date;  
  1549.             } else {  
  1550.                 // Default  
  1551.                 int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;  
  1552.                 result = formatDateRange(c, millis, millis, flags);  
  1553.                 prepositionId = R.string.preposition_for_date;  
  1554.             }  
  1555.             if (withPreposition) {  
  1556.                 Resources res = c.getResources();  
  1557.                 result = res.getString(prepositionId, result);  
  1558.             }  
  1559.         }  
  1560.         return result;  
  1561.     }  
  1562.   
  1563.     /** 
  1564.      * Convenience function to return relative time string without preposition. 
  1565.      * @param c context for resources 
  1566.      * @param millis time in milliseconds 
  1567.      * @return {@link CharSequence} containing relative time. 
  1568.      * @see #getRelativeTimeSpanString(Context, long, boolean) 
  1569.      */  
  1570.     public static CharSequence getRelativeTimeSpanString(Context c, long millis) {  
  1571.         return getRelativeTimeSpanString(c, millis, false /* no preposition */);  
  1572.     }  
  1573.   
  1574.     private static Time sNowTime;  
  1575.     private static Time sThenTime;  
7、附上android系统 Calender类的源代码

      

[java]  view plain  copy
  1. public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> {  
  2.   
  3.     private static final long serialVersionUID = -1807547505821590642L;  
  4.   
  5.     /** 
  6.      * True iff the values in {@code fields[]} correspond to {@code time}. Despite the name, this 
  7.      * is effectively "are the values in fields[] up-to-date?" --- {@code fields[]} may contain 
  8.      * non-zero values and {@code isSet[]} may contain {@code true} values even when 
  9.      * {@code areFieldsSet} is false. 
  10.      * Accessing the fields via {@code get} will ensure the fields are up-to-date. 
  11.      */  
  12.     protected boolean areFieldsSet;  
  13.   
  14.     /** 
  15.      * Contains broken-down field values for the current value of {@code time} if 
  16.      * {@code areFieldsSet} is true, or stale data corresponding to some previous value otherwise. 
  17.      * Accessing the fields via {@code get} will ensure the fields are up-to-date. 
  18.      * The array length is always {@code FIELD_COUNT}. 
  19.      */  
  20.     protected int[] fields;  
  21.   
  22.     /** 
  23.      * Whether the corresponding element in {@code field[]} has been set. Initially, these are all 
  24.      * false. The first time the fields are computed, these are set to true and remain set even if 
  25.      * the data becomes stale: you <i>must</i> check {@code areFieldsSet} if you want to know 
  26.      * whether the value is up-to-date. 
  27.      * Note that {@code isSet} is <i>not</i> a safe alternative to accessing this array directly, 
  28.      * and will likewise return stale data! 
  29.      * The array length is always {@code FIELD_COUNT}. 
  30.      */  
  31.     protected boolean[] isSet;  
  32.   
  33.     /** 
  34.      * Whether {@code time} corresponds to the values in {@code fields[]}. If false, {@code time} 
  35.      * is out-of-date with respect to changes {@code fields[]}. 
  36.      * Accessing the time via {@code getTimeInMillis} will always return the correct value. 
  37.      */  
  38.     protected boolean isTimeSet;  
  39.   
  40.     /** 
  41.      * A time in milliseconds since January 1, 1970. See {@code isTimeSet}. 
  42.      * Accessing the time via {@code getTimeInMillis} will always return the correct value. 
  43.      */  
  44.     protected long time;  
  45.   
  46.     transient int lastTimeFieldSet;  
  47.   
  48.     transient int lastDateFieldSet;  
  49.   
  50.     private boolean lenient;  
  51.   
  52.     private int firstDayOfWeek;  
  53.   
  54.     private int minimalDaysInFirstWeek;  
  55.   
  56.     private TimeZone zone;  
  57.   
  58.     /** 
  59.      * Value of the {@code MONTH} field indicating the first month of the 
  60.      * year. 
  61.      */  
  62.     public static final int JANUARY = 0;  
  63.   
  64.     /** 
  65.      * Value of the {@code MONTH} field indicating the second month of 
  66.      * the year. 
  67.      */  
  68.     public static final int FEBRUARY = 1;  
  69.   
  70.     /** 
  71.      * Value of the {@code MONTH} field indicating the third month of the 
  72.      * year. 
  73.      */  
  74.     public static final int MARCH = 2;  
  75.   
  76.     /** 
  77.      * Value of the {@code MONTH} field indicating the fourth month of 
  78.      * the year. 
  79.      */  
  80.     public static final int APRIL = 3;  
  81.   
  82.     /** 
  83.      * Value of the {@code MONTH} field indicating the fifth month of the 
  84.      * year. 
  85.      */  
  86.     public static final int MAY = 4;  
  87.   
  88.     /** 
  89.      * Value of the {@code MONTH} field indicating the sixth month of the 
  90.      * year. 
  91.      */  
  92.     public static final int JUNE = 5;  
  93.   
  94.     /** 
  95.      * Value of the {@code MONTH} field indicating the seventh month of 
  96.      * the year. 
  97.      */  
  98.     public static final int JULY = 6;  
  99.   
  100.     /** 
  101.      * Value of the {@code MONTH} field indicating the eighth month of 
  102.      * the year. 
  103.      */  
  104.     public static final int AUGUST = 7;  
  105.   
  106.     /** 
  107.      * Value of the {@code MONTH} field indicating the ninth month of the 
  108.      * year. 
  109.      */  
  110.     public static final int SEPTEMBER = 8;  
  111.   
  112.     /** 
  113.      * Value of the {@code MONTH} field indicating the tenth month of the 
  114.      * year. 
  115.      */  
  116.     public static final int OCTOBER = 9;  
  117.   
  118.     /** 
  119.      * Value of the {@code MONTH} field indicating the eleventh month of 
  120.      * the year. 
  121.      */  
  122.     public static final int NOVEMBER = 10;  
  123.   
  124.     /** 
  125.      * Value of the {@code MONTH} field indicating the twelfth month of 
  126.      * the year. 
  127.      */  
  128.     public static final int DECEMBER = 11;  
  129.   
  130.     /** 
  131.      * Value of the {@code MONTH} field indicating the thirteenth month 
  132.      * of the year. Although {@code GregorianCalendar} does not use this 
  133.      * value, lunar calendars do. 
  134.      */  
  135.     public static final int UNDECIMBER = 12;  
  136.   
  137.     /** 
  138.      * Value of the {@code DAY_OF_WEEK} field indicating Sunday. 
  139.      */  
  140.     public static final int SUNDAY = 1;  
  141.   
  142.     /** 
  143.      * Value of the {@code DAY_OF_WEEK} field indicating Monday. 
  144.      */  
  145.     public static final int MONDAY = 2;  
  146.   
  147.     /** 
  148.      * Value of the {@code DAY_OF_WEEK} field indicating Tuesday. 
  149.      */  
  150.     public static final int TUESDAY = 3;  
  151.   
  152.     /** 
  153.      * Value of the {@code DAY_OF_WEEK} field indicating Wednesday. 
  154.      */  
  155.     public static final int WEDNESDAY = 4;  
  156.   
  157.     /** 
  158.      * Value of the {@code DAY_OF_WEEK} field indicating Thursday. 
  159.      */  
  160.     public static final int THURSDAY = 5;  
  161.   
  162.     /** 
  163.      * Value of the {@code DAY_OF_WEEK} field indicating Friday. 
  164.      */  
  165.     public static final int FRIDAY = 6;  
  166.   
  167.     /** 
  168.      * Value of the {@code DAY_OF_WEEK} field indicating Saturday. 
  169.      */  
  170.     public static final int SATURDAY = 7;  
  171.   
  172.     /** 
  173.      * Field number for {@code get} and {@code set} indicating the 
  174.      * era, e.g., AD or BC in the Julian calendar. This is a calendar-specific 
  175.      * value; see subclass documentation. 
  176.      * 
  177.      * @see GregorianCalendar#AD 
  178.      * @see GregorianCalendar#BC 
  179.      */  
  180.     public static final int ERA = 0;  
  181.   
  182.     /** 
  183.      * Field number for {@code get} and {@code set} indicating the 
  184.      * year. This is a calendar-specific value; see subclass documentation. 
  185.      */  
  186.     public static final int YEAR = 1;  
  187.   
  188.     /** 
  189.      * Field number for {@code get} and {@code set} indicating the 
  190.      * month. This is a calendar-specific value. The first month of the year is 
  191.      * {@code JANUARY}; the last depends on the number of months in a 
  192.      * year. 
  193.      * 
  194.      * @see #JANUARY 
  195.      * @see #FEBRUARY 
  196.      * @see #MARCH 
  197.      * @see #APRIL 
  198.      * @see #MAY 
  199.      * @see #JUNE 
  200.      * @see #JULY 
  201.      * @see #AUGUST 
  202.      * @see #SEPTEMBER 
  203.      * @see #OCTOBER 
  204.      * @see #NOVEMBER 
  205.      * @see #DECEMBER 
  206.      * @see #UNDECIMBER 
  207.      */  
  208.     public static final int MONTH = 2;  
  209.   
  210.     /** 
  211.      * Field number for {@code get} and {@code set} indicating the 
  212.      * week number within the current year. The first week of the year, as 
  213.      * defined by {@code getFirstDayOfWeek()} and 
  214.      * {@code getMinimalDaysInFirstWeek()}, has value 1. Subclasses 
  215.      * define the value of {@code WEEK_OF_YEAR} for days before the first 
  216.      * week of the year. 
  217.      * 
  218.      * @see #getFirstDayOfWeek 
  219.      * @see #getMinimalDaysInFirstWeek 
  220.      */  
  221.     public static final int WEEK_OF_YEAR = 3;  
  222.   
  223.     /** 
  224.      * Field number for {@code get} and {@code set} indicating the 
  225.      * week number within the current month. The first week of the month, as 
  226.      * defined by {@code getFirstDayOfWeek()} and 
  227.      * {@code getMinimalDaysInFirstWeek()}, has value 1. Subclasses 
  228.      * define the value of {@code WEEK_OF_MONTH} for days before the 
  229.      * first week of the month. 
  230.      * 
  231.      * @see #getFirstDayOfWeek 
  232.      * @see #getMinimalDaysInFirstWeek 
  233.      */  
  234.     public static final int WEEK_OF_MONTH = 4;  
  235.   
  236.     /** 
  237.      * Field number for {@code get} and {@code set} indicating the 
  238.      * day of the month. This is a synonym for {@code DAY_OF_MONTH}. The 
  239.      * first day of the month has value 1. 
  240.      * 
  241.      * @see #DAY_OF_MONTH 
  242.      */  
  243.     public static final int DATE = 5;  
  244.   
  245.     /** 
  246.      * Field number for {@code get} and {@code set} indicating the 
  247.      * day of the month. This is a synonym for {@code DATE}. The first 
  248.      * day of the month has value 1. 
  249.      * 
  250.      * @see #DATE 
  251.      */  
  252.     public static final int DAY_OF_MONTH = 5;  
  253.   
  254.     /** 
  255.      * Field number for {@code get} and {@code set} indicating the 
  256.      * day number within the current year. The first day of the year has value 
  257.      * 1. 
  258.      */  
  259.     public static final int DAY_OF_YEAR = 6;  
  260.   
  261.     /** 
  262.      * Field number for {@code get} and {@code set} indicating the 
  263.      * day of the week. This field takes values {@code SUNDAY}, 
  264.      * {@code MONDAY}, {@code TUESDAY}, {@code WEDNESDAY}, 
  265.      * {@code THURSDAY}, {@code FRIDAY}, and 
  266.      * {@code SATURDAY}. 
  267.      * 
  268.      * @see #SUNDAY 
  269.      * @see #MONDAY 
  270.      * @see #TUESDAY 
  271.      * @see #WEDNESDAY 
  272.      * @see #THURSDAY 
  273.      * @see #FRIDAY 
  274.      * @see #SATURDAY 
  275.      */  
  276.     public static final int DAY_OF_WEEK = 7;  
  277.   
  278.     /** 
  279.      * Field number for {@code get} and {@code set} indicating the 
  280.      * ordinal number of the day of the week within the current month. Together 
  281.      * with the {@code DAY_OF_WEEK} field, this uniquely specifies a day 
  282.      * within a month. Unlike {@code WEEK_OF_MONTH} and 
  283.      * {@code WEEK_OF_YEAR}, this field's value does <em>not</em> 
  284.      * depend on {@code getFirstDayOfWeek()} or 
  285.      * {@code getMinimalDaysInFirstWeek()}. {@code DAY_OF_MONTH 1} 
  286.      * through {@code 7} always correspond to <code>DAY_OF_WEEK_IN_MONTH 
  287.      * 1</code>; 
  288.      * {@code 8} through {@code 15} correspond to 
  289.      * {@code DAY_OF_WEEK_IN_MONTH 2}, and so on. 
  290.      * {@code DAY_OF_WEEK_IN_MONTH 0} indicates the week before 
  291.      * {@code DAY_OF_WEEK_IN_MONTH 1}. Negative values count back from 
  292.      * the end of the month, so the last Sunday of a month is specified as 
  293.      * {@code DAY_OF_WEEK = SUNDAY, DAY_OF_WEEK_IN_MONTH = -1}. Because 
  294.      * negative values count backward they will usually be aligned differently 
  295.      * within the month than positive values. For example, if a month has 31 
  296.      * days, {@code DAY_OF_WEEK_IN_MONTH -1} will overlap 
  297.      * {@code DAY_OF_WEEK_IN_MONTH 5} and the end of {@code 4}. 
  298.      * 
  299.      * @see #DAY_OF_WEEK 
  300.      * @see #WEEK_OF_MONTH 
  301.      */  
  302.     public static final int DAY_OF_WEEK_IN_MONTH = 8;  
  303.   
  304.     /** 
  305.      * Field number for {@code get} and {@code set} indicating 
  306.      * whether the {@code HOUR} is before or after noon. E.g., at 
  307.      * 10:04:15.250 PM the {@code AM_PM} is {@code PM}. 
  308.      * 
  309.      * @see #AM 
  310.      * @see #PM 
  311.      * @see #HOUR 
  312.      */  
  313.     public static final int AM_PM = 9;  
  314.   
  315.     /** 
  316.      * Field number for {@code get} and {@code set} indicating the 
  317.      * hour of the morning or afternoon. {@code HOUR} is used for the 
  318.      * 12-hour clock. E.g., at 10:04:15.250 PM the {@code HOUR} is 10. 
  319.      * 
  320.      * @see #AM_PM 
  321.      * @see #HOUR_OF_DAY 
  322.      */  
  323.     public static final int HOUR = 10;  
  324.   
  325.     /** 
  326.      * Field number for {@code get} and {@code set} indicating the 
  327.      * hour of the day. {@code HOUR_OF_DAY} is used for the 24-hour 
  328.      * clock. E.g., at 10:04:15.250 PM the {@code HOUR_OF_DAY} is 22. 
  329.      * 
  330.      * @see #HOUR 
  331.      */  
  332.     public static final int HOUR_OF_DAY = 11;  
  333.   
  334.     /** 
  335.      * Field number for {@code get} and {@code set} indicating the 
  336.      * minute within the hour. E.g., at 10:04:15.250 PM the {@code MINUTE} 
  337.      * is 4. 
  338.      */  
  339.     public static final int MINUTE = 12;  
  340.   
  341.     /** 
  342.      * Field number for {@code get} and {@code set} indicating the 
  343.      * second within the minute. E.g., at 10:04:15.250 PM the 
  344.      * {@code SECOND} is 15. 
  345.      */  
  346.     public static final int SECOND = 13;  
  347.   
  348.     /** 
  349.      * Field number for {@code get} and {@code set} indicating the 
  350.      * millisecond within the second. E.g., at 10:04:15.250 PM the 
  351.      * {@code MILLISECOND} is 250. 
  352.      */  
  353.     public static final int MILLISECOND = 14;  
  354.   
  355.     /** 
  356.      * Field number for {@code get} and {@code set} indicating the 
  357.      * raw offset from GMT in milliseconds. 
  358.      */  
  359.     public static final int ZONE_OFFSET = 15;  
  360.   
  361.     /** 
  362.      * Field number for {@code get} and {@code set} indicating the 
  363.      * daylight savings offset in milliseconds. 
  364.      */  
  365.     public static final int DST_OFFSET = 16;  
  366.   
  367.     /** 
  368.      * This is the total number of fields in this calendar. 
  369.      */  
  370.     public static final int FIELD_COUNT = 17;  
  371.   
  372.     /** 
  373.      * Value of the {@code AM_PM} field indicating the period of the day 
  374.      * from midnight to just before noon. 
  375.      */  
  376.     public static final int AM = 0;  
  377.   
  378.     /** 
  379.      * Value of the {@code AM_PM} field indicating the period of the day 
  380.      * from noon to just before midnight. 
  381.      */  
  382.     public static final int PM = 1;  
  383.   
  384.     /** 
  385.      * Requests both {@code SHORT} and {@code LONG} styles in the map returned by 
  386.      * {@link #getDisplayNames}. 
  387.      * @since 1.6 
  388.      */  
  389.     public static final int ALL_STYLES = 0;  
  390.   
  391.     /** 
  392.      * Requests short names (such as "Jan") from 
  393.      * {@link #getDisplayName} or {@link #getDisplayNames}. 
  394.      * @since 1.6 
  395.      */  
  396.     public static final int SHORT = 1;  
  397.   
  398.     /** 
  399.      * Requests long names (such as "January") from 
  400.      * {@link #getDisplayName} or {@link #getDisplayNames}. 
  401.      * @since 1.6 
  402.      */  
  403.     public static final int LONG = 2;  
  404.   
  405.     private static final String[] FIELD_NAMES = { "ERA""YEAR""MONTH",  
  406.             "WEEK_OF_YEAR""WEEK_OF_MONTH""DAY_OF_MONTH""DAY_OF_YEAR",  
  407.             "DAY_OF_WEEK""DAY_OF_WEEK_IN_MONTH""AM_PM""HOUR",  
  408.             "HOUR_OF_DAY""MINUTE""SECOND""MILLISECOND",  
  409.             "ZONE_OFFSET""DST_OFFSET" };  
  410.   
  411.     /** 
  412.      * Constructs a {@code Calendar} instance using the default {@code TimeZone} and {@code Locale}. 
  413.      */  
  414.     protected Calendar() {  
  415.         this(TimeZone.getDefault(), Locale.getDefault());  
  416.     }  
  417.   
  418.     Calendar(TimeZone timezone) {  
  419.         fields = new int[FIELD_COUNT];  
  420.         isSet = new boolean[FIELD_COUNT];  
  421.         areFieldsSet = isTimeSet = false;  
  422.         setLenient(true);  
  423.         setTimeZone(timezone);  
  424.     }  
  425.   
  426.     /** 
  427.      * Constructs a {@code Calendar} instance using the specified {@code TimeZone} and {@code Locale}. 
  428.      * 
  429.      * @param timezone 
  430.      *            the timezone. 
  431.      * @param locale 
  432.      *            the locale. 
  433.      */  
  434.     protected Calendar(TimeZone timezone, Locale locale) {  
  435.         this(timezone);  
  436.         LocaleData localeData = LocaleData.get(locale);  
  437.         setFirstDayOfWeek(localeData.firstDayOfWeek.intValue());  
  438.         setMinimalDaysInFirstWeek(localeData.minimalDaysInFirstWeek.intValue());  
  439.     }  
  440.   
  441.   
  442.     /** 
  443.      * Adds the specified amount to a {@code Calendar} field. 
  444.      * 
  445.      * @param field 
  446.      *            the {@code Calendar} field to modify. 
  447.      * @param value 
  448.      *            the amount to add to the field. 
  449.      * @throws IllegalArgumentException 
  450.      *                if {@code field} is {@code DST_OFFSET} or {@code 
  451.      *                ZONE_OFFSET}. 
  452.      */  
  453.     public abstract void add(int field, int value);  
  454.   
  455.     /** 
  456.      * Returns whether the {@code Date} specified by this {@code Calendar} instance is after the {@code Date} 
  457.      * specified by the parameter. The comparison is not dependent on the time 
  458.      * zones of the {@code Calendar}. 
  459.      * 
  460.      * @param calendar 
  461.      *            the {@code Calendar} instance to compare. 
  462.      * @return {@code true} when this Calendar is after calendar, {@code false} otherwise. 
  463.      * @throws IllegalArgumentException 
  464.      *                if the time is not set and the time cannot be computed 
  465.      *                from the current field values. 
  466.      */  
  467.     public boolean after(Object calendar) {  
  468.         if (!(calendar instanceof Calendar)) {  
  469.             return false;  
  470.         }  
  471.         return getTimeInMillis() > ((Calendar) calendar).getTimeInMillis();  
  472.     }  
  473.   
  474.     /** 
  475.      * Returns whether the {@code Date} specified by this {@code Calendar} instance is before the 
  476.      * {@code Date} specified by the parameter. The comparison is not dependent on the 
  477.      * time zones of the {@code Calendar}. 
  478.      * 
  479.      * @param calendar 
  480.      *            the {@code Calendar} instance to compare. 
  481.      * @return {@code true} when this Calendar is before calendar, {@code false} otherwise. 
  482.      * @throws IllegalArgumentException 
  483.      *                if the time is not set and the time cannot be computed 
  484.      *                from the current field values. 
  485.      */  
  486.     public boolean before(Object calendar) {  
  487.         if (!(calendar instanceof Calendar)) {  
  488.             return false;  
  489.         }  
  490.         return getTimeInMillis() < ((Calendar) calendar).getTimeInMillis();  
  491.     }  
  492.   
  493.     /** 
  494.      * Clears all of the fields of this {@code Calendar}. All fields are initialized to 
  495.      * zero. 
  496.      */  
  497.     public final void clear() {  
  498.         for (int i = 0; i < FIELD_COUNT; i++) {  
  499.             fields[i] = 0;  
  500.             isSet[i] = false;  
  501.         }  
  502.         areFieldsSet = isTimeSet = false;  
  503.     }  
  504.   
  505.     /** 
  506.      * Clears the specified field to zero and sets the isSet flag to {@code false}. 
  507.      * 
  508.      * @param field 
  509.      *            the field to clear. 
  510.      */  
  511.     public final void clear(int field) {  
  512.         fields[field] = 0;  
  513.         isSet[field] = false;  
  514.         areFieldsSet = isTimeSet = false;  
  515.     }  
  516.   
  517.     /** 
  518.      * Returns a new {@code Calendar} with the same properties. 
  519.      * 
  520.      * @return a shallow copy of this {@code Calendar}. 
  521.      * 
  522.      * @see java.lang.Cloneable 
  523.      */  
  524.     @Override  
  525.     public Object clone() {  
  526.         try {  
  527.             Calendar clone = (Calendar) super.clone();  
  528.             clone.fields = fields.clone();  
  529.             clone.isSet = isSet.clone();  
  530.             clone.zone = (TimeZone) zone.clone();  
  531.             return clone;  
  532.         } catch (CloneNotSupportedException e) {  
  533.             throw new AssertionError(e);  
  534.         }  
  535.     }  
  536.   
  537.     /** 
  538.      * Computes the time from the fields if the time has not already been set. 
  539.      * Computes the fields from the time if the fields are not already set. 
  540.      * 
  541.      * @throws IllegalArgumentException 
  542.      *                if the time is not set and the time cannot be computed 
  543.      *                from the current field values. 
  544.      */  
  545.     protected void complete() {  
  546.         if (!isTimeSet) {  
  547.             computeTime();  
  548.             isTimeSet = true;  
  549.         }  
  550.         if (!areFieldsSet) {  
  551.             computeFields();  
  552.             areFieldsSet = true;  
  553.         }  
  554.     }  
  555.   
  556.     /** 
  557.      * Computes the {@code Calendar} fields from {@code time}. 
  558.      */  
  559.     protected abstract void computeFields();  
  560.   
  561.     /** 
  562.      * Computes {@code time} from the Calendar fields. 
  563.      * 
  564.      * @throws IllegalArgumentException 
  565.      *                if the time cannot be computed from the current field 
  566.      *                values. 
  567.      */  
  568.     protected abstract void computeTime();  
  569.   
  570.     /** 
  571.      * Compares the specified object to this {@code Calendar} and returns whether they are 
  572.      * equal. The object must be an instance of {@code Calendar} and have the same 
  573.      * properties. 
  574.      * 
  575.      * @param object 
  576.      *            the object to compare with this object. 
  577.      * @return {@code true} if the specified object is equal to this {@code Calendar}, {@code false} 
  578.      *         otherwise. 
  579.      */  
  580.     @Override  
  581.     public boolean equals(Object object) {  
  582.         if (this == object) {  
  583.             return true;  
  584.         }  
  585.         if (!(object instanceof Calendar)) {  
  586.             return false;  
  587.         }  
  588.         Calendar cal = (Calendar) object;  
  589.         return getTimeInMillis() == cal.getTimeInMillis()  
  590.                 && isLenient() == cal.isLenient()  
  591.                 && getFirstDayOfWeek() == cal.getFirstDayOfWeek()  
  592.                 && getMinimalDaysInFirstWeek() == cal.getMinimalDaysInFirstWeek()  
  593.                 && getTimeZone().equals(cal.getTimeZone());  
  594.     }  
  595.   
  596.     /** 
  597.      * Gets the value of the specified field after computing the field values by 
  598.      * calling {@code complete()} first. 
  599.      * 
  600.      * @param field 
  601.      *            the field to get. 
  602.      * @return the value of the specified field. 
  603.      * 
  604.      * @throws IllegalArgumentException 
  605.      *                if the fields are not set, the time is not set, and the 
  606.      *                time cannot be computed from the current field values. 
  607.      * @throws ArrayIndexOutOfBoundsException 
  608.      *                if the field is not inside the range of possible fields. 
  609.      *                The range is starting at 0 up to {@code FIELD_COUNT}. 
  610.      */  
  611.     public int get(int field) {  
  612.         complete();  
  613.         return fields[field];  
  614.     }  
  615.   
  616.     /** 
  617.      * Returns the maximum value of the specified field for the current date. 
  618.      * For example, the maximum number of days in the current month. 
  619.      */  
  620.     public int getActualMaximum(int field) {  
  621.         int value, next;  
  622.         if (getMaximum(field) == (next = getLeastMaximum(field))) {  
  623.             return next;  
  624.         }  
  625.         complete();  
  626.         long orgTime = time;  
  627.         set(field, next);  
  628.         do {  
  629.             value = next;  
  630.             roll(field, true);  
  631.             next = get(field);  
  632.         } while (next > value);  
  633.         time = orgTime;  
  634.         areFieldsSet = false;  
  635.         return value;  
  636.     }  
  637.   
  638.     /** 
  639.      * Gets the minimum value of the specified field for the current date. 
  640.      * 
  641.      * @param field 
  642.      *            the field. 
  643.      * @return the minimum value of the specified field. 
  644.      */  
  645.     public int getActualMinimum(int field) {  
  646.         int value, next;  
  647.         if (getMinimum(field) == (next = getGreatestMinimum(field))) {  
  648.             return next;  
  649.         }  
  650.         complete();  
  651.         long orgTime = time;  
  652.         set(field, next);  
  653.         do {  
  654.             value = next;  
  655.             roll(field, false);  
  656.             next = get(field);  
  657.         } while (next < value);  
  658.         time = orgTime;  
  659.         areFieldsSet = false;  
  660.         return value;  
  661.     }  
  662.   
  663.     /** 
  664.      * Returns an array of locales for which custom {@code Calendar} instances 
  665.      * are available. 
  666.      * <p>Note that Android does not support user-supplied locale service providers. 
  667.      */  
  668.     public static synchronized Locale[] getAvailableLocales() {  
  669.         return ICU.getAvailableCalendarLocales();  
  670.     }  
  671.   
  672.     /** 
  673.      * Gets the first day of the week for this {@code Calendar}. 
  674.      * 
  675.      * @return the first day of the week. 
  676.      */  
  677.     public int getFirstDayOfWeek() {  
  678.         return firstDayOfWeek;  
  679.     }  
  680.   
  681.     /** 
  682.      * Gets the greatest minimum value of the specified field. This is the 
  683.      * biggest value that {@code getActualMinimum} can return for any possible 
  684.      * time. 
  685.      * 
  686.      * @param field 
  687.      *            the field. 
  688.      * @return the greatest minimum value of the specified field. 
  689.      */  
  690.     public abstract int getGreatestMinimum(int field);  
  691.   
  692.     /** 
  693.      * Constructs a new instance of the {@code Calendar} subclass appropriate for the 
  694.      * default {@code Locale}. 
  695.      * 
  696.      * @return a {@code Calendar} subclass instance set to the current date and time in 
  697.      *         the default {@code Timezone}. 
  698.      */  
  699.     public static synchronized Calendar getInstance() {  
  700.         return new GregorianCalendar();  
  701.     }  
  702.   
  703.     /** 
  704.      * Constructs a new instance of the {@code Calendar} subclass appropriate for the 
  705.      * specified {@code Locale}. 
  706.      * 
  707.      * @param locale 
  708.      *            the locale to use. 
  709.      * @return a {@code Calendar} subclass instance set to the current date and time. 
  710.      */  
  711.     public static synchronized Calendar getInstance(Locale locale) {  
  712.         return new GregorianCalendar(locale);  
  713.     }  
  714.   
  715.     /** 
  716.      * Constructs a new instance of the {@code Calendar} subclass appropriate for the 
  717.      * default {@code Locale}, using the specified {@code TimeZone}. 
  718.      * 
  719.      * @param timezone 
  720.      *            the {@code TimeZone} to use. 
  721.      * @return a {@code Calendar} subclass instance set to the current date and time in 
  722.      *         the specified timezone. 
  723.      */  
  724.     public static synchronized Calendar getInstance(TimeZone timezone) {  
  725.         return new GregorianCalendar(timezone);  
  726.     }  
  727.   
  728.     /** 
  729.      * Constructs a new instance of the {@code Calendar} subclass appropriate for the 
  730.      * specified {@code Locale}. 
  731.      * 
  732.      * @param timezone 
  733.      *            the {@code TimeZone} to use. 
  734.      * @param locale 
  735.      *            the {@code Locale} to use. 
  736.      * @return a {@code Calendar} subclass instance set to the current date and time in 
  737.      *         the specified timezone. 
  738.      */  
  739.     public static synchronized Calendar getInstance(TimeZone timezone, Locale locale) {  
  740.         return new GregorianCalendar(timezone, locale);  
  741.     }  
  742.   
  743.     /** 
  744.      * Gets the smallest maximum value of the specified field. This is the 
  745.      * smallest value that {@code getActualMaximum()} can return for any 
  746.      * possible time. 
  747.      * 
  748.      * @param field 
  749.      *            the field number. 
  750.      * @return the smallest maximum value of the specified field. 
  751.      */  
  752.     public abstract int getLeastMaximum(int field);  
  753.   
  754.     /** 
  755.      * Gets the greatest maximum value of the specified field. This returns the 
  756.      * biggest value that {@code get} can return for the specified field. 
  757.      * 
  758.      * @param field 
  759.      *            the field. 
  760.      * @return the greatest maximum value of the specified field. 
  761.      */  
  762.     public abstract int getMaximum(int field);  
  763.   
  764.     /** 
  765.      * Gets the minimal days in the first week of the year. 
  766.      * 
  767.      * @return the minimal days in the first week of the year. 
  768.      */  
  769.     public int getMinimalDaysInFirstWeek() {  
  770.         return minimalDaysInFirstWeek;  
  771.     }  
  772.   
  773.     /** 
  774.      * Gets the smallest minimum value of the specified field. this returns the 
  775.      * smallest value thet {@code get} can return for the specified field. 
  776.      * 
  777.      * @param field 
  778.      *            the field number. 
  779.      * @return the smallest minimum value of the specified field. 
  780.      */  
  781.     public abstract int getMinimum(int field);  
  782.   
  783.     /** 
  784.      * Gets the time of this {@code Calendar} as a {@code Date} object. 
  785.      * 
  786.      * @return a new {@code Date} initialized to the time of this {@code Calendar}. 
  787.      * 
  788.      * @throws IllegalArgumentException 
  789.      *                if the time is not set and the time cannot be computed 
  790.      *                from the current field values. 
  791.      */  
  792.     public final Date getTime() {  
  793.         return new Date(getTimeInMillis());  
  794.     }  
  795.   
  796.     /** 
  797.      * Computes the time from the fields if required and returns the time. 
  798.      * 
  799.      * @return the time of this {@code Calendar}. 
  800.      * 
  801.      * @throws IllegalArgumentException 
  802.      *                if the time is not set and the time cannot be computed 
  803.      *                from the current field values. 
  804.      */  
  805.     public long getTimeInMillis() {  
  806.         if (!isTimeSet) {  
  807.             computeTime();  
  808.             isTimeSet = true;  
  809.         }  
  810.         return time;  
  811.     }  
  812.   
  813.     /** 
  814.      * Gets the timezone of this {@code Calendar}. 
  815.      * 
  816.      * @return the {@code TimeZone} used by this {@code Calendar}. 
  817.      */  
  818.     public TimeZone getTimeZone() {  
  819.         return zone;  
  820.     }  
  821.   
  822.     /** 
  823.      * Returns an integer hash code for the receiver. Objects which are equal 
  824.      * return the same value for this method. 
  825.      * 
  826.      * @return the receiver's hash. 
  827.      * 
  828.      * @see #equals 
  829.      */  
  830.     @Override  
  831.     public int hashCode() {  
  832.         return (isLenient() ? 1237 : 1231) + getFirstDayOfWeek()  
  833.                 + getMinimalDaysInFirstWeek() + getTimeZone().hashCode();  
  834.     }  
  835.   
  836.     /** 
  837.      * Gets the value of the specified field without recomputing. 
  838.      * 
  839.      * @param field 
  840.      *            the field. 
  841.      * @return the value of the specified field. 
  842.      */  
  843.     protected final int internalGet(int field) {  
  844.         return fields[field];  
  845.     }  
  846.   
  847.     /** 
  848.      * Returns if this {@code Calendar} accepts field values which are outside the valid 
  849.      * range for the field. 
  850.      * 
  851.      * @return {@code true} if this {@code Calendar} is lenient, {@code false} otherwise. 
  852.      */  
  853.     public boolean isLenient() {  
  854.         return lenient;  
  855.     }  
  856.   
  857.     /** 
  858.      * Returns whether the specified field is set. Note that the interpretation of "is set" is 
  859.      * somewhat technical. In particular, it does <i>not</i> mean that the field's value is up 
  860.      * to date. If you want to know whether a field contains an up-to-date value, you must also 
  861.      * check {@code areFieldsSet}, making this method somewhat useless unless you're a subclass, 
  862.      * in which case you can access the {@code isSet} array directly. 
  863.      * <p> 
  864.      * A field remains "set" from the first time its value is computed until it's cleared by one 
  865.      * of the {@code clear} methods. Thus "set" does not mean "valid". You probably want to call 
  866.      * {@code get} -- which will update fields as necessary -- rather than try to make use of 
  867.      * this method. 
  868.      * 
  869.      * @param field 
  870.      *            a {@code Calendar} field number. 
  871.      * @return {@code true} if the specified field is set, {@code false} otherwise. 
  872.      */  
  873.     public final boolean isSet(int field) {  
  874.         return isSet[field];  
  875.     }  
  876.   
  877.     /** 
  878.      * Adds the specified amount to the specified field and wraps the value of 
  879.      * the field when it goes beyond the maximum or minimum value for the 
  880.      * current date. Other fields will be adjusted as required to maintain a 
  881.      * consistent date. 
  882.      * 
  883.      * @param field 
  884.      *            the field to roll. 
  885.      * @param value 
  886.      *            the amount to add. 
  887.      */  
  888.     public void roll(int field, int value) {  
  889.         boolean increment = value >= 0;  
  890.         int count = increment ? value : -value;  
  891.         for (int i = 0; i < count; i++) {  
  892.             roll(field, increment);  
  893.         }  
  894.     }  
  895.   
  896.     /** 
  897.      * Increment or decrement the specified field and wrap the value of the 
  898.      * field when it goes beyond the maximum or minimum value for the current 
  899.      * date. Other fields will be adjusted as required to maintain a consistent 
  900.      * date. 
  901.      * 
  902.      * @param field 
  903.      *            the number indicating the field to roll. 
  904.      * @param increment 
  905.      *            {@code true} to increment the field, {@code false} to decrement. 
  906.      */  
  907.     public abstract void roll(int field, boolean increment);  
  908.   
  909.     /** 
  910.      * Sets a field to the specified value. 
  911.      * 
  912.      * @param field 
  913.      *            the code indicating the {@code Calendar} field to modify. 
  914.      * @param value 
  915.      *            the value. 
  916.      */  
  917.     public void set(int field, int value) {  
  918.         fields[field] = value;  
  919.         isSet[field] = true;  
  920.         areFieldsSet = isTimeSet = false;  
  921.         if (field > MONTH && field < AM_PM) {  
  922.             lastDateFieldSet = field;  
  923.         }  
  924.         if (field == HOUR || field == HOUR_OF_DAY) {  
  925.             lastTimeFieldSet = field;  
  926.         }  
  927.         if (field == AM_PM) {  
  928.             lastTimeFieldSet = HOUR;  
  929.         }  
  930.     }  
  931.   
  932.     /** 
  933.      * Sets the year, month and day of the month fields. Other fields are not 
  934.      * changed. 
  935.      * 
  936.      * @param year 
  937.      *            the year. 
  938.      * @param month 
  939.      *            the month. 
  940.      * @param day 
  941.      *            the day of the month. 
  942.      */  
  943.     public final void set(int year, int month, int day) {  
  944.         set(YEAR, year);  
  945.         set(MONTH, month);  
  946.         set(DATE, day);  
  947.     }  
  948.   
  949.     /** 
  950.      * Sets the year, month, day of the month, hour of day and minute fields. 
  951.      * Other fields are not changed. 
  952.      * 
  953.      * @param year 
  954.      *            the year. 
  955.      * @param month 
  956.      *            the month. 
  957.      * @param day 
  958.      *            the day of the month. 
  959.      * @param hourOfDay 
  960.      *            the hour of day. 
  961.      * @param minute 
  962.      *            the minute. 
  963.      */  
  964.     public final void set(int year, int month, int day, int hourOfDay,  
  965.             int minute) {  
  966.         set(year, month, day);  
  967.         set(HOUR_OF_DAY, hourOfDay);  
  968.         set(MINUTE, minute);  
  969.     }  
  970.   
  971.     /** 
  972.      * Sets the year, month, day of the month, hour of day, minute and second 
  973.      * fields. Other fields are not changed. 
  974.      * 
  975.      * @param year 
  976.      *            the year. 
  977.      * @param month 
  978.      *            the month. 
  979.      * @param day 
  980.      *            the day of the month. 
  981.      * @param hourOfDay 
  982.      *            the hour of day. 
  983.      * @param minute 
  984.      *            the minute. 
  985.      * @param second 
  986.      *            the second. 
  987.      */  
  988.     public final void set(int year, int month, int day, int hourOfDay,  
  989.             int minute, int second) {  
  990.         set(year, month, day, hourOfDay, minute);  
  991.         set(SECOND, second);  
  992.     }  
  993.   
  994.     /** 
  995.      * Sets the first day of the week for this {@code Calendar}. 
  996.      * 
  997.      * @param value 
  998.      *            a {@code Calendar} day of the week. 
  999.      */  
  1000.     public void setFirstDayOfWeek(int value) {  
  1001.         firstDayOfWeek = value;  
  1002.     }  
  1003.   
  1004.     /** 
  1005.      * Sets this {@code Calendar} to accept field values which are outside the valid 
  1006.      * range for the field. 
  1007.      * 
  1008.      * @param value 
  1009.      *            a boolean value. 
  1010.      */  
  1011.     public void setLenient(boolean value) {  
  1012.         lenient = value;  
  1013.     }  
  1014.   
  1015.     /** 
  1016.      * Sets the minimal days in the first week of the year. 
  1017.      * 
  1018.      * @param value 
  1019.      *            the minimal days in the first week of the year. 
  1020.      */  
  1021.     public void setMinimalDaysInFirstWeek(int value) {  
  1022.         minimalDaysInFirstWeek = value;  
  1023.     }  
  1024.   
  1025.     /** 
  1026.      * Sets the time of this {@code Calendar}. 
  1027.      * 
  1028.      * @param date 
  1029.      *            a {@code Date} object. 
  1030.      */  
  1031.     public final void setTime(Date date) {  
  1032.         setTimeInMillis(date.getTime());  
  1033.     }  
  1034.   
  1035.     /** 
  1036.      * Sets the time of this {@code Calendar}. 
  1037.      * 
  1038.      * @param milliseconds 
  1039.      *            the time as the number of milliseconds since Jan. 1, 1970. 
  1040.      */  
  1041.     public void setTimeInMillis(long milliseconds) {  
  1042.         if (!isTimeSet || !areFieldsSet || time != milliseconds) {  
  1043.             time = milliseconds;  
  1044.             isTimeSet = true;  
  1045.             areFieldsSet = false;  
  1046.             complete();  
  1047.         }  
  1048.     }  
  1049.   
  1050.     /** 
  1051.      * Sets the {@code TimeZone} used by this Calendar. 
  1052.      * 
  1053.      * @param timezone 
  1054.      *            a {@code TimeZone}. 
  1055.      */  
  1056.     public void setTimeZone(TimeZone timezone) {  
  1057.         zone = timezone;  
  1058.         areFieldsSet = false;  
  1059.     }  
  1060.   
  1061.     /** 
  1062.      * Returns the string representation of this {@code Calendar}. 
  1063.      */  
  1064.     @Override  
  1065.     public String toString() {  
  1066.         StringBuilder result = new StringBuilder(getClass().getName() +  
  1067.                 "[time=" + (isTimeSet ? String.valueOf(time) : "?") +  
  1068.                 ",areFieldsSet=" + areFieldsSet +  
  1069.                 ",lenient=" + lenient +  
  1070.                 ",zone=" + zone.getID() +  
  1071.                 ",firstDayOfWeek=" + firstDayOfWeek +  
  1072.                 ",minimalDaysInFirstWeek=" + minimalDaysInFirstWeek);  
  1073.         for (int i = 0; i < FIELD_COUNT; i++) {  
  1074.             result.append(',');  
  1075.             result.append(FIELD_NAMES[i]);  
  1076.             result.append('=');  
  1077.             if (isSet[i]) {  
  1078.                 result.append(fields[i]);  
  1079.             } else {  
  1080.                 result.append('?');  
  1081.             }  
  1082.         }  
  1083.         result.append(']');  
  1084.         return result.toString();  
  1085.     }  
  1086.   
  1087.     /** 
  1088.      * Compares the times of the two {@code Calendar}, which represent the milliseconds 
  1089.      * from the January 1, 1970 00:00:00.000 GMT (Gregorian). 
  1090.      * 
  1091.      * @param anotherCalendar 
  1092.      *            another calendar that this one is compared with. 
  1093.      * @return 0 if the times of the two {@code Calendar}s are equal, -1 if the time of 
  1094.      *         this {@code Calendar} is before the other one, 1 if the time of this 
  1095.      *         {@code Calendar} is after the other one. 
  1096.      * @throws NullPointerException 
  1097.      *             if the argument is null. 
  1098.      * @throws IllegalArgumentException 
  1099.      *             if the argument does not include a valid time 
  1100.      *             value. 
  1101.      */  
  1102.     public int compareTo(Calendar anotherCalendar) {  
  1103.         if (anotherCalendar == null) {  
  1104.             throw new NullPointerException("anotherCalendar == null");  
  1105.         }  
  1106.         long timeInMillis = getTimeInMillis();  
  1107.         long anotherTimeInMillis = anotherCalendar.getTimeInMillis();  
  1108.         if (timeInMillis > anotherTimeInMillis) {  
  1109.             return 1;  
  1110.         }  
  1111.         if (timeInMillis == anotherTimeInMillis) {  
  1112.             return 0;  
  1113.         }  
  1114.         return -1;  
  1115.     }  
  1116.   
  1117.     /** 
  1118.      * Returns a human-readable string for the value of {@code field} 
  1119.      * using the given style and locale. If no string is available, returns null. 
  1120.      * The value is retrieved by invoking {@code get(field)}. 
  1121.      * 
  1122.      * <p>For example, {@code getDisplayName(MONTH, SHORT, Locale.US)} will return "Jan" 
  1123.      * while {@code getDisplayName(MONTH, LONG, Locale.US)} will return "January". 
  1124.      * 
  1125.      * @param field the field 
  1126.      * @param style {@code SHORT} or {@code LONG} 
  1127.      * @param locale the locale 
  1128.      * @return the display name, or null 
  1129.      * @throws NullPointerException if {@code locale == null} 
  1130.      * @throws IllegalArgumentException if {@code field} or {@code style} is invalid 
  1131.      * @since 1.6 
  1132.      */  
  1133.     public String getDisplayName(int field, int style, Locale locale) {  
  1134.         // TODO: the RI's documentation says ALL_STYLES is invalid, but actually treats it as SHORT.  
  1135.         if (style == ALL_STYLES) {  
  1136.             style = SHORT;  
  1137.         }  
  1138.         String[] array = getDisplayNameArray(field, style, locale);  
  1139.         int value = get(field);  
  1140.         return (array != null) ? array[value] : null;  
  1141.     }  
  1142.   
  1143.     private String[] getDisplayNameArray(int field, int style, Locale locale) {  
  1144.         if (field < 0 || field >= FIELD_COUNT) {  
  1145.             throw new IllegalArgumentException("bad field " + field);  
  1146.         }  
  1147.         checkStyle(style);  
  1148.         DateFormatSymbols dfs = DateFormatSymbols.getInstance(locale);  
  1149.         switch (field) {  
  1150.         case AM_PM:  
  1151.             return dfs.getAmPmStrings();  
  1152.         case DAY_OF_WEEK:  
  1153.             return (style == LONG) ? dfs.getWeekdays() : dfs.getShortWeekdays();  
  1154.         case ERA:  
  1155.             return dfs.getEras();  
  1156.         case MONTH:  
  1157.             return (style == LONG) ? dfs.getMonths() : dfs.getShortMonths();  
  1158.         }  
  1159.         return null;  
  1160.     }  
  1161.   
  1162.     private static void checkStyle(int style) {  
  1163.         if (style != ALL_STYLES && style != SHORT && style != LONG) {  
  1164.             throw new IllegalArgumentException("bad style " + style);  
  1165.         }  
  1166.     }  
  1167.   
  1168.     /** 
  1169.      * Returns a map of human-readable strings to corresponding values, 
  1170.      * for the given field, style, and locale. 
  1171.      * Returns null if no strings are available. 
  1172.      * 
  1173.      * <p>For example, {@code getDisplayNames(MONTH, ALL_STYLES, Locale.US)} would 
  1174.      * contain mappings from "Jan" and "January" to {@link #JANUARY}, and so on. 
  1175.      * 
  1176.      * @param field the field 
  1177.      * @param style {@code SHORT}, {@code LONG}, or {@code ALL_STYLES} 
  1178.      * @param locale the locale 
  1179.      * @return the display name, or null 
  1180.      * @throws NullPointerException if {@code locale == null} 
  1181.      * @throws IllegalArgumentException if {@code field} or {@code style} is invalid 
  1182.      * @since 1.6 
  1183.      */  
  1184.     public Map<String, Integer> getDisplayNames(int field, int style, Locale locale) {  
  1185.         checkStyle(style);  
  1186.         complete();  
  1187.         Map<String, Integer> result = new HashMap<String, Integer>();  
  1188.         if (style == SHORT || style == ALL_STYLES) {  
  1189.             insertValuesInMap(result, getDisplayNameArray(field, SHORT, locale));  
  1190.         }  
  1191.         if (style == LONG || style == ALL_STYLES) {  
  1192.             insertValuesInMap(result, getDisplayNameArray(field, LONG, locale));  
  1193.         }  
  1194.         return result.isEmpty() ? null : result;  
  1195.     }  
  1196.   
  1197.     private static void insertValuesInMap(Map<String, Integer> map, String[] values) {  
  1198.         if (values == null) {  
  1199.             return;  
  1200.         }  
  1201.         for (int i = 0; i < values.length; ++i) {  
  1202.             if (values[i] != null && !values[i].isEmpty()) {  
  1203.                 map.put(values[i], i);  
  1204.             }  
  1205.         }  
  1206.     }  
  1207.   
  1208.     private static final ObjectStreamField[] serialPersistentFields = {  
  1209.         new ObjectStreamField("areFieldsSet"boolean.class),  
  1210.         new ObjectStreamField("fields"int[].class),  
  1211.         new ObjectStreamField("firstDayOfWeek"int.class),  
  1212.         new ObjectStreamField("isSet"boolean[].class),  
  1213.         new ObjectStreamField("isTimeSet"boolean.class),  
  1214.         new ObjectStreamField("lenient"boolean.class),  
  1215.         new ObjectStreamField("minimalDaysInFirstWeek"int.class),  
  1216.         new ObjectStreamField("nextStamp"int.class),  
  1217.         new ObjectStreamField("serialVersionOnStream"int.class),  
  1218.         new ObjectStreamField("time"long.class),  
  1219.         new ObjectStreamField("zone", TimeZone.class),  
  1220.     };  
  1221.   
  1222.     private void writeObject(ObjectOutputStream stream) throws IOException {  
  1223.         complete();  
  1224.         ObjectOutputStream.PutField putFields = stream.putFields();  
  1225.         putFields.put("areFieldsSet", areFieldsSet);  
  1226.         putFields.put("fields"this.fields);  
  1227.         putFields.put("firstDayOfWeek", firstDayOfWeek);  
  1228.         putFields.put("isSet", isSet);  
  1229.         putFields.put("isTimeSet", isTimeSet);  
  1230.         putFields.put("lenient", lenient);  
  1231.         putFields.put("minimalDaysInFirstWeek", minimalDaysInFirstWeek);  
  1232.         putFields.put("nextStamp"2 /* MINIMUM_USER_STAMP */);  
  1233.         putFields.put("serialVersionOnStream"1);  
  1234.         putFields.put("time", time);  
  1235.         putFields.put("zone", zone);  
  1236.         stream.writeFields();  
  1237.     }  
  1238.   
  1239.     private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {  
  1240.         ObjectInputStream.GetField readFields = stream.readFields();  
  1241.         areFieldsSet = readFields.get("areFieldsSet"false);  
  1242.         this.fields = (int[]) readFields.get("fields"null);  
  1243.         firstDayOfWeek = readFields.get("firstDayOfWeek", Calendar.SUNDAY);  
  1244.         isSet = (boolean[]) readFields.get("isSet"null);  
  1245.         isTimeSet = readFields.get("isTimeSet"false);  
  1246.         lenient = readFields.get("lenient"true);  
  1247.         minimalDaysInFirstWeek = readFields.get("minimalDaysInFirstWeek"1);  
  1248.         time = readFields.get("time", 0L);  
  1249.         zone = (TimeZone) readFields.get("zone"null);  
  1250.     }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值