安卓中简单好用的时间格式化工具DateUtils

前言

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的用法,就基本上已经掌握了这个工具类了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容