前言
JDK中提供了好用的SimpleDateFormat工具来帮我们把格式化时间,并且制定了格式化的规范,只要我们按照规范传入字符串,就能随意定制我们想要的时间格式,例如:
- 传入"yyyy.MM.dd G 'at' HH:mm:ss z",就能得到2001.07.04 AD at 12:08:56 PDT
- 传入"EEE, MMM d, ''yy",就能得到Wed, Jul 4, '01
- 传入"h:mm a",就能得到12:08 PM
等等,具体用法 可以查阅官方文档,用起来也非常方便。
但是近些年移动互联网发展迅速,用户体验也被越来越多的重视起来,对于时间的显示也越来越人性化。比如说聊天记录的时间,人们更愿意看到"两分钟前","一小时前","昨天"等样式,而不是冷冰冰的几月几号几点几分。
当产品给出了如上的需求时,很多开发者都是去判断要显示的时间与当前时间的相对间隔,然后再查表得出需要显示的时间。殊不知,安卓已经给我们提供好了相应的工具,只需一个方法调用统统搞定。该工具就是今天的主角DateUtils,他位于android.text.format包下。
作为系统提供的工具,当然也会发挥本地化的优势。格式化出来的时间也会跟随系统系统自动变化,比如说如果系统默认语言是中文,格式化出来的时间是"一分钟前",而如果系统语言是英语,格式化的时间就变成"one minute ago",省去了翻译字符串的麻烦。
小试牛刀
按照上面描述的需求,我们来看看用DateUtils如何实现:
long currentTimeMillis = System.currentTimeMillis();
//两分钟前
Log.d(TAG, "getRelativeTimeSpanString " + DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 2 * 60 * 1000));
//三个小时前
Log.d(TAG, "getRelativeTimeSpanString " + DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 3 * 60 * 60 * 1000));
//一天前
Log.d(TAG, "getRelativeTimeSpanString " + DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 28 * 60 * 60 * 1000));
打印出来的结果如下:
08-12 15:43:30.454 13935-13935/com.wangng.myapplication D/DateUtilsTAG: getRelativeTimeSpanString 2 分钟前
08-12 15:43:30.454 13935-13935/com.wangng.myapplication D/DateUtilsTAG: getRelativeTimeSpanString 3 小时前
08-12 15:43:30.464 13935-13935/com.wangng.myapplication D/DateUtilsTAG: getRelativeTimeSpanString 昨天
getRelativeTimeSpanString有几个重载方法:
public static CharSequence getRelativeTimeSpanString(long startTime)
public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution)
public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution, int flags)
我们来看看参数最多的最后一个方法,了解下这么多参数都有什么用。
- long time : 即你需要格式化的时间;
- long now : 即你需要格式化的时间的相对时间,一般都是相对与当前时间:System.currentTimeMillis();
- long minResolution : 忽略的最小时间,有五个固定的常量取值,下面会开一个段落专门讲解;
- int flags : 格式化的选项,有几个固定的取值,也可以是这几个固定取值的组合,下面会开一个段落专门讲解。
其实看源码就知道参数少的方法内部最终都是调用的参数最多的方法。
public static CharSequence getRelativeTimeSpanString(long startTime) {
return getRelativeTimeSpanString(startTime, System.currentTimeMillis(), MINUTE_IN_MILLIS);
}
public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution) {
int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH;
return getRelativeTimeSpanString(time, now, minResolution, flags);
}
第三个参数minResolution
那个方法的注释已经写的很清楚
the minimum timespan to report. For example, a time3 seconds in the past will be reported as "0 minutes ago" if this is set to MINUTE_IN_MILLIS. Pass one of 0,MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, WEEK_IN_MILLIS
大致意思就是:格式化的时候忽略的最小时间,比如说:如果该参数被设置成MINUTE_IN_MILLIS,小于一分钟的时间就会被忽略,传入的时间刚好是3秒前,由于小于一分钟就会被忽略而格式化成"0分钟前";
该参数的取值有:
- 0 :即再小的时间间隔也不会被忽略;
- MINUTE_IN_MILLIS :其值为60X1000,如果间隔小于一分钟就会被忽略,最常用;
- HOUR_IN_MILLIS :其值为60X60X1000,如果时间间隔小于一个小时就会被忽略,详见下面代码打印结果;
- DAY_IN_MILLIS :其值为24X60X60X1000,如果时间间隔小于一天就会被忽略,详见下面代码打印结果;
-
WEEK_IN_MILLIS :其值为7X24X60X60X1000,如果时间间隔小于一个周就会被忽略,详见下面代码打印结果;
为了更容易理解,我们分别把函数的第三个参数设置成如上的几个值,然后打印出来结果,一目了然:
DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 2 * 60 * 1000,
currentTimeMillis,
DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_SHOW_DATE)
DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 3 * 60 * 60 * 1000,
currentTimeMillis,
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_DATE)
DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 28 * 60 * 60 * 1000,
currentTimeMillis,
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_DATE)
DateUtils.getRelativeTimeSpanString(
currentTimeMillis - 180 * 60 * 60 * 1000,
currentTimeMillis,
DateUtils.DAY_IN_MILLIS,
DateUtils.FORMAT_SHOW_DATE)
为了直观,整理成表格如下:
时刻 | 时刻描述 | MINUTE | HOUR | DAY | WEEK |
---|---|---|---|---|---|
currentTimeMillis - 2 * 60 * 1000 | 两分钟前 | 2分钟前 | 0小时前 | 今天 | 8月12日 |
currentTimeMillis - 3 * 60 * 60 * 1000 | 三小时前 | 3小时前 | 3小时前 | 今天 | 8月12日 |
currentTimeMillis - 28 * 60 * 60 * 1000 | 一天前 | 昨天 | 昨天 | 昨天 | 8月11日 |
currentTimeMillis - 180 * 60 * 60 * 1000 | 一周前 | 8月5日 | 8月5日 | 8月5日 | 8月5日 |
注1: 试验的当前日期是8月12日
注2: 为了表格的简洁,表格中的"MINUTE","HOUR","DAY","WEEK"均省略了后面的"IN_MILLIS"
注3 : markdown的表格真难用
如果看完上面的表格还有什么不清楚的,麻烦你去撞墙清醒一下脑袋。
第四个参数flags
我们知道,一个完整的时间包括年份,月份,日期,星期,时,分,秒,当然时分又有12小时制和24小时制。flag的值就是限制显示时间的哪些元素。
该参数可选的值包括:
- FORMAT_SHOW_TIME:单独使用只显示时和分,例如“下午12:16”,或者“23:22”,具体是12小时制还是24小时制,取决于系统设置的样式,当然也可以配合下面的FORMAT_12HOUR,FORMAT_24HOUR使用;
- FORMAT_SHOW_WEEKDAY:单独使用只显示星期几,例如“星期五”;
- FORMAT_SHOW_YEAR:如果该值被设置了,则年份始终会显示,如果没有设置,年份只有在格式化的时间跟当前时间不在同一年的时候显示。例如现在是2017年8月12日,而要格式化的时间是2017年7月22日,如果flag设置了FORMAT_SHOW_YEAR,则格式化出来的时间为“2017年7月12日”,如果没有设置FORMAT_SHOW_YEAR,则格式化出来的时间为“7月12日”;
- FORMAT_SHOW_DATE:显示月份和日期;
- FORMAT_NO_MONTH_DAY:显示月份和日期当中的月份,而不是日期,例如会显示“八月”,而不是“8月18日”;
- FORMAT_12HOUR:按照12小时制显示,通常不要设置这个值,因为系统会自动根据系统设置的显示样式来显示,如果FORMAT_12HOUR和FORMAT_24HOUR同时被设置,则按照FORMAT_24HOUR显示;
- FORMAT_24HOUR:类似于FORMAT_12HOUR;
- FORMAT_CAP_AMPM:如果这个值和FORMAT_12HOUR同时被设置,则“AM”和“PM"会显示成大写;
- FORMAT_NO_NOON:正常情况下中午12点会被显示成“noon”,但是如果这个值和FORMAT_12HOUR同时被设置,则会显示成“12pm”
- FORMAT_CAP_NOON:正常情况下中午12点会被显示成“noon”,但是如果这个值被设置,则会显示成“Noon”;
- FORMAT_NO_MIDNIGHT:类似于FORMAT_NO_NOON,正常情况下物业12点会被显示成“midnight”,如果设置了该值,则显示成“12am”;
- FORMAT_CAP_MIDNIGHT:类似于FORMAT_CAP_NOON,“midnight”大写显示成“Midnight”;
- FORMAT_ABBREV_TIME:如果该值和FORMAT_12HOUR同时被设置,且时间刚好是整点,则分“00”就会被省略,例如“3:00pm”将会简写成“3pm”;
- FORMAT_ABBREV_WEEKDAY:星期几会被简写成三个字母的字符串,当然这是在系统语言是英语的情况下,例如“Sunday”会简写成“Sun”;
- FORMAT_ABBREV_MONTH:类似于FORMAT_ABBREV_WEEKDAY,简写月份成三个字母的字符串;
- FORMAT_ABBREV_ALL:时间,星期,月份都会按照对应的简写规则简写;
- FORMAT_NUMERIC_DATE:如果该值被设置,则直接格式化成“12/13”,而不是显示月份的名称,如“December 31”
参数的名字叫“flags”,是复数形式,所以意味着我们可以叠加这些flag使用。举个例子,大家就明白怎么用了:
DateUtils.formatDateTime(getActivity(),
System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_ABBREV_ALL)
分析上面的代码,flag用到了FORMAT_SHOW_TIME,FORMAT_SHOW_DATE,根据我们上面的介绍,可以推断会显示出时间和日期,又用到了FORMAT_ABBREV_ALL,所以月份和时间都会采用简写形式,打印一下:8月12日 下午8点15,FORMAT_ABBREV_ALL貌似没起作用,但是如果把系统语言切换成英语,就能看到差别了:“Aug 12, 8:15 PM”。
DateUtils的其他方法
public boolean isToday(long when)
从方法名就能看出来,判断传入时间是否跟今天是同一天,在发送某个页面的UV统计的时候非常好用。
public static String formatDateRange(Context context, long startMillis,
long endMillis, int flags)
public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
long endMillis, int flags)
public static Formatter formatDateRange(Context context, Formatter formatter,
long startMillis,long endMillis, int flags, String timeZone)
格式化一个时间区间,比如说电商的一个活动的活动时间就是由开始时间和结束时间组成,用上面的方法格式化就很方便。
小结
其实要是弄懂了flag的用法,就基本上已经掌握了这个工具类了。