Android技能树 — 时间相关总体小结

3,197 阅读11分钟

Android基础知识

Android技能树 — Fragment总体小结

Android技能树 — 动画小结

Android技能树 — View小结

Android技能树 — Activity小结

Android技能树 — View事件体系小结

Android技能树 — Android存储路径及IO操作小结

Android技能树 — 多进程相关小结

Android技能树 — Drawable小结

Android技能树 — 屏幕适配小结



前言:

发现很多开发人员做在时间相关的问题的时候,各有各的方法,然后对时间相关的处理,各种Java自带的时间相关类的知识面都不是很清楚,有时候看见他们在开发时候,都是一边使用到了,然后临时百度去处理时间相关的问题。

说个简单的,比如现在要问一首歌的时长,后台返回给你00:03:06,然后你代码中要用到总秒数时长,这时候问你怎么把这个变成具体的秒数。在现有的项目中,我看这个的处理代码是这样的:

if (min.contains(":")) {
    String[] items = min.split(":");
    duration += (Integer.valueOf(items[0]) * 60 * 60 * 1000 + Integer.valueOf(items[1]) * 60 * 1000 + Integer.valueOf(items[2]) * 1000);
}

上面代码当然没有任何问题,但是我们其实可以用时间相关类来处理。


正文:

1. 时间获取

既然说到了时间相关知识,最主要的肯定先要获取到时间。

1.1 Date:

其实看脑图,就已经很详细了,而且Date大家应该熟悉的不要太熟悉了,所以不会超级详细说明,我们只挑几个点说一下:

构造函数:

Date date = new Date();
// 使用 toString() 函数显示当前日期时间
date.toString();  //Sat Aug 31 15:20:08 CST 2019

Date date1 = new Date(1000); 
//1970.01.01 00:00:00开始加上你填入的数值后的时间点
date1.toString(); //Thu Jan 01 08:00:01 CST 1970

get/set函数:

Date date = new Date();
//获取当前Date代表的时间距离1970.01.01 08:00:00的差值
//'实际的Date代表的是1970.01.01 00:00:00的时间差,因为当前是东八区,所以获取到的是08:00:00'
date.getTime(); //1567576587763

Date date1 = new Date(1000); 
date1.getTime(); //1000

Date date2 = new Date();
date2.setTime(1000);
date2.getTime(); //1000

二个Date比较函数:

Date aaaaa = new Date(1000);
Date bbbbb = new Date(2000);

aaaaa.after(bbbbb);//false
aaaaa.before(bbbbb);//true
aaaaa.equals(bbbbb);//false
aaaaa.compareTo(bbbbb);//-1(a<b返回-1,a==b返回0,a>b返回1)

1.2 Calendar:

我们知道了Date可以代表时间值(因为有了Date就可以获取距离1970-01-01 08:00:00 的差值,也就知道了当前Date代表的时间)

所以Calendar在获取时间的方法上,不仅提供了获取时间返回long类型的方法,还提供了返回Date对象的方法

获取时间:

Calendar cl  = Calendar.getInstance();
Date date = cl.getTime();//获取Date对象
cl.getTimeInMillis();//直接获取long值

date.getTime() 与 cl.getTimeInMillis()的结果是一样的

当然Calendar 也可以具体的拿年月日时分秒等具体某个想要的值:

比如获取当前时间的小时:
Calendar cl  = Calendar.getInstance();

//比如现在是下午四点,获取到的就
cl.get(Calendar.HOUR_OF_DAY));  
是16

//'注意一点,拿到的Mouth是从0开始的,比如现在是9月,你获取的是8'
cl.get(Calendar.MONTH);  //8

具体的参数有很多,可以自己去选:

设置时间:

上面说了获取时间,设置时间也是一样的,可以直接设置long类型,也可以设置Date类型,也可以直接设置具体的年月日字段值。

public final void setTime(Date date)
public void setTimeInMillis(long millis)

public void set(int field, int value)
public final void set(int year, int month, int date)
public final void set(int year, int month, int date, int hourOfDay, int minute)
public final void set(int year, int month, int date, int hourOfDay, int minute,int second)

二个Calendar对象比较: 完全可以参考前面的二个Date对象进行比较,这里也不细说了:


其实我们前面说的DateCalendar都是大家很熟悉的类,用的也是最多的,但是因为使用方式不友好,所以后面java8之后,都是使用新的·InstantLocalxxxx系列用来替换DateCalendar 的类了。

1.3 Instant:

很多人说,Date不是挺好用的,感觉够用了。假如我现在需要你获取当前时间,然后在该基础上加上6小时,再求时间,你就会发现有点不方便了。

Date dd1 = new Date();
dd1.setTime(dd1.getTime()+6*60*60*1000);

//'当前有些人会说Date不是有个setHours()方法吗?但实际已经标记抛弃了'

我们看下Instant的用法:

Instant i = Instant.now();

i.toString(); //2019-09-04T09:44:07.169Z

Instant i2 = i.plus(6,ChronoUnit.HOURS);//加了6个小时
i2.toString(); //2019-09-04T15:44:07.169Z

//也可以通过Duration类辅助 ('Duration类后面会讲到')
Instant i3 =  i.plus(Duration.ofHours(6));
i3.toString(); //2019-09-04T15:44:07.169Z

//也可以通过TimeUnit辅助类('TimeUnit类后面会讲到')
Instant i4 = i.plusMillis(TimeUnit.HOURS.toMillis(6));
i4.toString(); ////2019-09-04T15:44:07.169Z

//'减少则使用minus方法,使用方式同plus'

二个Instant对象的相关比较方法,与Date一样:

这里主要讲一个until方法:

Instant i = Instant.now();
Instant i2 = i.plus(6,ChronoUnit.HOURS);

i2.until(i,ChronoUnit.HOURS); //-6
i.until(i2,ChronoUnit.HOURS); //6

'(i2.until(i,ChronoUnit.HOURS);
i2到i1,说明要回到以前,当然是要减少6个小时,反之亦然)'

PS: Instant.now(); 拿到的时间是比我们当前中国时区慢8小时,大家要注意下

1.4 LocalXXXXX系列

Local系列主要有: LocalTimeLocalDateLocalDateTime

从单纯的字面意思就可以看出来:LocalTime是指时间(时分秒),LocalDate是指日期(年月日),LocalDateTime是整个完整日期(年-月-日-时-分-秒)

1.4.1 LocalDate :

基本的方法就如上面脑图,常用的方法也都有了。

这里提个点:

Calendar获取的月份是从0开始的,比如现在是9月份,你获取到的数字是8。但是localDate.getMonthValue()获取到的是9。更贴合实际。

1.4.2 LocalTime :

LocalTime的方法也入上面脑图,常用的方法也都有了。

1.4.3 LocalDateTime :

LocalDateTime = LocalDate + LocalTime

LocalDate.atTime()方法添加时间
返回的结果对象同时变为LocalDateTime对象

LocalTime.atDate()方法添加日期
返回的结果对象同时变为LocalDateTime对象

LocalDateTime的使用和LocalTimeLocalDate的使用方法基本的一样。

1.4.4 ZoneDateTime :

和LocalDateTime也基本相同,从字面意思看我们也知道是带有时区相关的:

LocalDateTime.now(); 
//2019-09-05T22:49:06.369

ZoneDateTime.now();
//2019-09-05T22:49:06.371+08:00[Asia/Shanghai]

我们可以看到当前的时区,其他方法使用方式也基本都一模一样。


2.时间格式化

前面我们已经说了怎么获取到时间。接下去我们讲时间和各种格式的字符串等各种相互转换。

  1. 但我们知道界面上我们想要显示的文字不同: 2018-08-07 09:12:12,2018#08#07#09#12#12各种方式,这个是时间转成字符串显示。

  2. 然后假如别人传给你2018-08-07 09:12:122018#08#07#09#12#12,想要把字符串转换成时间对象。

2.1 DateFormat:

DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。日期/时间格式化子类(如SimpleDateFormat)允许进行格式化(也就是日期 -> 文本)、解析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年 1 月 1 日 00:00:00这一刻开始的毫秒数。

2.1.1时间和字符串的互转:

DateFormat 可帮助进行格式化并解析任何语言环境的日期。对于月、星期,甚至日历格式(阴历和阳历),其代码可完全与语言环境的约定无关。

要格式化一个当前语言环境下的日期,可使用某个静态工厂方法:

myString = DateFormat.getDateInstance().format(myDate);

如果格式化多个日期,那么获取该格式并多次使用它是更为高效的做法,这样系统就不必多次获取有关环境语言和国家/地区约定的信息了。

DateFormat df = DateFormat.getDateInstance();
for (int i = 0; i < myDate.length; ++i) {
    output.println(df.format(myDate[i]) + "; ");
}

要格式化不同语言环境的日期,可在 getDateInstance() 的调用中指定它。

DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);

还可使用 DateFormat 进行解析。

myDate = df.parse(myString);

2.1.2获取DateFormat对象:

使用getDateInstance 来获取该国家/地区的标准日期格式。另外还提供了一些其他静态工厂方法。使用 getTimeInstance 可获取该国家/地区的时间格式。使用 getDateTimeInstance可获取日期和时间格式。可以将不同选项传入这些工厂方法,以控制结果的长度(从 SHORTMEDIUMLONG 再到 FULL)。确切的结果取决于语言环境,但是通常:

SHORT 完全为数字,如 12.13.52 或 3:30pm
MEDIUM 较长,如 Jan 12, 1952
LONG 更长,如 January 12, 1952 或 3:30:32pm
FULL 是完全指定,如 Tuesday、April 12、1952 AD 或 3:30:42pm PST。

如果愿意,还可以在格式上设置时区。如果想对格式化或解析施加更多的控制(或者给予用户更多的控制),可以尝试将从工厂方法所获取的 DateFormat 强制转换为 SimpleDateFormat。这适用于大多数国家/地区;只是要记住将其放入一个 try 代码块中,以防遇到特殊的格式。还可以使用借助 ParsePosition 和 FieldPosition 的解析和格式化方法形式来逐步地解析字符串的各部分。 对齐任意特定的字段,或者找出字符串在屏幕上的选择位置。 同步日期格式不是同步的。建议为每个线程创建独立的格式实例。如果多个线程同时访问一个格式,则它必须保持外部同步。

2.2 SimpleDateFormat:

SimpleDateFormatDateFormat的子类,所以前面的DateFormat的基本的知识介绍基本都是一模一样的。这里就不说明了。

这个类估计大家百分之99.9999都使用,并且知道怎么使用了。

下面这段代码,基本是使用率最高的:


Date date = new Date();
String strDateFormat = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);

//时间转换为字符串
sdf.format(date); //2019-09-06 21:13:23


String str = "2019-09-06 21:13:23";
//字符串转换为时间
Date date = sdf.parse(str);  

主要的不熟悉的反而不是方法的使用,而是格式的定义老是记不清楚:

字母	日期或时间元素	表示	示例
G	Era 标志符	Text	AD
y	年	Year	1996; 96
M	年中的月份	Month	July; Jul; 07
w	年中的周数	Number	27
W	月份中的周数	Number	2
D	年中的天数	Number	189
d	月份中的天数	Number	10
F	月份中的星期	Number	2
E	星期中的天数	Text	Tuesday; Tue
a	Am/pm 标记	Text	PM
H	一天中的小时数(0-23)	Number	0
k	一天中的小时数(1-24)	Number	24
K	am/pm 中的小时数(0-11)	Number	0
h	am/pm 中的小时数(1-12)	Number	12
m	小时中的分钟数	Number	30
s	分钟中的秒数	Number	55
S	毫秒数	Number	978
z	时区	General time zone	Pacific Standard Time; PST; GMT-08:00
Z	时区	RFC 822 time zone	-0800

2.3 DateTimeFormatter:

这个是Java8 后的推荐转换使用类。

2.3.1 字符串转换时间对象:

// 定义一个任意格式的日期时间字符串
String str1 = "2014==04==12 01时06分09秒";
// 根据需要解析的日期、时间字符串定义解析所用的格式器
DateTimeFormatter fomatter1 = DateTimeFormatter.ofPattern("yyyy==MM==dd HH时mm分ss秒");
// 执行解析
LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1);

// ---下面代码再次解析另一个字符串---
String str2 = "2014?$四月?$13 20小时";
DateTimeFormatter fomatter2 = DateTimeFormatter.ofPattern("yyy?$MMM?$dd HH小时");
LocalDateTime dt2 = LocalDateTime.parse(str2, fomatter2);

这里的使用方式和SimpleDateFormat基本一致。

2.3.2 :时间对象转换字符串:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy==MM==dd HH时mm分ss秒");

//'1. 和SimpleDateFormat的使用方法一样,把时间对象作为参数传入'
formatter.format(LocalDateTime.now());

//'2. 把DateTimeFormatter作为参数传入'
LocalDateTime.now().format(formatter);

3.时间相关类

3.1 比较时间:

我们可以看到获取某二个日期直接差值的对象,主要是:年月日的Period类和秒的Duration类.

3.2 时间辅助类:

3.2.1 TimeUnit:

时间颗粒度转换 :

比如我想算N个小时M分钟有几秒,很多人可能是这么写的

N* 60 * 60 + M * 60;

其实有了TimeUnit 这个类,很多不同时间单位的相互转换非常方便。

TimeUnit.HOURS.toSeconds(N) + TimeUnit.MINUTES.toSeconds(M);

特别是换算成其他毫秒等,使用一般的乘法,那么多个0,很容易弄错,用TimeUnit就很方便了:

TimeUnit.HOURS.toMillis(N) + TimeUnit.MINUTES.toMillis(M);
//把3天转化成小时
TimeUnit.HOURS.convert( 3 , TimeUnit.DAYS );
//结果是:72

延时:

除了这个,TimeUnit还可以用在线程Sleep:

一般人写线程休眠:

Thread.sleep( 5 * 1000 );

使用TimeUnit:

TimeUnit.SECONDS.sleep( 5 );

还有timeJoin/timeWait等,都是同理。


结语:

回到刚开始的问题,我们刚开始提的歌曲时长获取:

我们也可以通过这些自带的时间工具类(我随便举二个写法,当然还有其他写法:)

String str = "00:03:06";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
LocalTime localTime = LocalTime.parse(str, formatter);
localTime.toSecondOfDay();//186


String str1 = "00:00:00";
LocalTime time1 = LocalTime.parse(str1);
String str2 = "00:03:06";
LocalTime time2 = LocalTime.parse(str2);
Duration duration =  Duration.between(time1,time2);
duration.getSeconds(); //186

又是很懒的不更新文章.........有错误的请指出哦。