除了 String 这个类在日常的项目中比较常用之外,有关时间和日期的操作也是经常遇到的,本篇就讲详细介绍下 Java API 中对时间和日期的支持。其实在 Java 8 之前时间日期的 API 并不是很好用,以至于人们在项目中大多使用的是一个第三方库 Joda-Time,当然 Java 8 吸收了该库的大部分优点,改进了相关 API,现在的时间日期处理接口相对以前来说是好用很多,本篇也将学习下这个优秀的第三方库。下面是本篇主要涉及内容:
一、古老的 Date 类 Date 这个类自 jdk1.0 开始就被设计出来, 从它的源代码中我们也是可以看出来,Date 类曾经扮演过很重要的角色,jdk 早期的版本中有关日期和时间的操作几乎都是由 Date 类完成的,下面我们一起看看它的源码:
- private transient long fastTime;
首先 Date 中有封装一个 long 类型的变量,这个变量是整个时间日期操作的对象,也就是我们使用该变量代表时间和日期。下面说明它是如何表示时间和日期的。所有计算机中的时间都是用一个整数表示的,该整数的值代表的是距离格林尼治标准时间(1970 年 1 月 1 日 0 时 0 分 0 秒)的毫秒数,也就是说 fastTime 值为 1000 的时候代表时间为 1970 年 1 月 1 日 0 时 0 分 1 秒。至于为什么是这个时间,由于种种历史原因大家也可以去了解下,此处不再赘述。
由于该类中大部分方法都被注解了 @Deprecated,已经不再推荐使用了,所以接下来我们主要还是看看其中还保留着的方法。只剩下两个构造方法:
- public Date(long date) {
- fastTime = date;
- }
- public Date() {
- this(System.currentTimeMillis());
- }
只推荐使用上述两个构造方法来构造我们的 Date 对象,一个是默认无参构造器(内部调用本地函数获取系统当前时间计算与标准时间的毫秒差值),另一个则需要手动传入一个毫秒值构造 Date 对象。
剩下的则主要是一些获取和设置 fastTime 的函数,以及比较日期大小的函数,其他的都被注解了,至于上述这些函数,代码相对简单此处不再赘述。
二、处理年月日的年历类 Calendar 以前我们是可以使用 Date 来处理日期年月日的,但是由于该类不支持国际化等原因,现在其中大部分方法被注解,不再推荐使用,现在的 Date 类更像是代表着某一个时刻的对象,而处理年月日的这种转换则完全交给了 Calendar 类处理。所以 Calendar 目前是日期时间处理中的核心类,接下来我们看看其中源码:
- //和Date一样封装了毫秒属性
- protected long time;
- protected int fields[];
- //封装了十七个静态常量
- public final static int ERA = 0;
- public final static int YEAR = 1;
- public final static int MONTH = 2;
- public final static int WEEK_OF_YEAR = 3;
- .........
- public final static int DST_OFFSET = 16;
在 Calendar 的内部封装了 17 个静态常量,这些常量将会作为索引用来检索 fields 属性,例如:fields[YEAR] 将返回当前毫秒值对应的日期时间的年份部分,fields[MONTH] 将返回的是月份部分的值等等。至于这些值是哪里来的,等我们介绍到后续源码的时候再说明,此处只需要理解这些常量的作用即可。
该类是抽象类,我们使用工厂方法获取该类实例:
- public static Calendar getInstance()
- {
- return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
- }
- public static Calendar getInstance(TimeZone zone)
- {
- return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
- }
- public static Calendar getInstance(Locale aLocale)
- {
- return createCalendar(TimeZone.getDefault(), aLocale);
- }
- public static Calendar getInstance(TimeZone zone,
- Locale aLocale)
- {
- return createCalendar(zone, aLocale);
- }
主要有四个方法用于创建 Calendar 实例,其实内部调用的是同一的方法只是传入的参数的值不同。创建一个 Calend 实例需要两个参数,一个是 TimeZone 时区,另一个是 Locale 语言国家。因为每个国家或地区他们表示时间的形式是不一样的,所以我们需要通过这两个参数确定具体需要使用的格式,当然是以本地时间作为 fastTime 的值的,如果我们没有指定时区和国家语言,那么将会默认使用本机系统信息。接下来我们看如何通过获取到 Calendar 实例完成对日期时间进行计算。
我们有获取和设置内部代表毫秒的 time 属性:
- public final Date getTime() {
- return new Date(getTimeInMillis());
- }
- public void setTimeInMillis(long millis){}
也有获取上述介绍的 17 中属性的方法:
- public int get(int field)
- {
- complete();
- return internalGet(field);
- }
其中 complete 方法就是调用了本地函数完成对 fields 属性中没有值的元素赋值。 调用 internalGet 方法其实就是调用的 fields[field],为我们返回指定属性的结果值。我们可以看个例子:
- public static void main(String[] args){
- Calendar calendar = Calendar.getInstance();
- System.out.println(calendar.get(Calendar.YEAR));
- System.out.println(calendar.get(Calendar.MONTH));
- System.out.println(calendar.get(Calendar.AM_PM))
- }
结果如下:
上述代码运行在不同的时候的结果都是不一样的,写作时的时间:2017/5/29 14:02。需要注意一点的是,month 属性是从 0 开始的,也就是 0 表示一月,4 表示 5 月,星期也是一样。此外,上述中的 AM_PM 表示的是上下午的概念,上午为 0,下午为 1。
除了获取有关日期时间的信息,我们也是有可以用来设置他们的方法的:
- //为指定属性设置值
- public void set(int field, int value)
- //设置年月日等,很多重载
- public final void set(int year, int month, int date)
- ......
- //清空所有该Calendar实例的属性值
- public final void clear()
除此之外,还有一些通过计算来设置 Calendar 属性的方法:
- //为指定属性添加值
- abstract public void add(int field, int amount);
例如:
- public static void main(String[] args){
- Calendar calendar = Calendar.getInstance();
- System.out.print(calendar.get(Calendar.YEAR));
- calendar.add(Calendar.YEAR,10);
- System.out.print(calendar.get(Calendar.YEAR));
- }
改程序将输出:2017 2027。还有一个 roll 方法也很有意思:
- abstract public void roll(int field, boolean up);
- //重载
- public void roll(int field, int amount)
- {
- while (amount > 0) {
- roll(field, true);
- amount--;
- }
- while (amount < 0) {
- roll(field, false);
- amount++;
- }
- }
我们需要记住的是,roll 方法完成的工作是和 add 一样的,只是 add 方法处理了越界的特殊情况(越界会向上进一位),而 roll 方法会重新回到初始值再加。例如:
- public static void main(String[] args){
- Calendar calendar = Calendar.getInstance();
- calendar.set(Calendar.MONTH, 11);//十二月
- System.out.println(calendar.getTime());
- //calendar.add(Calendar.MONTH,5);
- //calendar.roll(Calendar.MONTH,5);
- System.out.println(calendar.getTime());
- }
上述程序我们设置 Calendar 日期为 2017/12,针对上述两种方式 add 和 roll,输出结果如下:
对于 12 月,add 方法加 5 之后,month 为 5 月但是已经是 2018 年,而 roll 则没有向上进位,这就是区别,实际使用的时候还需加以区分。当然,如果你对某个属性的范围不是很明确,可以使用下面两个方法获取:
- abstract public int getMinimum(int field);
- abstract public int getMaximum(int field);
还有一些有关比较的函数,和 Date 是类似的:
- public boolean equals(Object obj)
- public int compareTo(Calendar anotherCalendar)
- public boolean after(Object when)
- public boolean before(Object when)
三、DateFormat 处理格式转换 DateFormat 是一个抽象类,该类主要用于实现 Date 对象和字符串之间相互转换, 涉及到两个转换的方法:
- //将Date类型转换为String类型
- public final String format(Date date)
- //将String类型转换Date类型
- public Date parse(String source)
除此之外,DateFormat 还提供了四个静态常量,代表着四种不同的风格。不同的风格输出信息的内容详尽程度不同,默认的风格是 MEDIUM。(折中)
- public static final int FULL = 0;
- public static final int LONG = 1;
- public static final int MEDIUM = 2;
- public static final int SHORT = 3;
- public static final int DEFAULT = MEDIUM;
该类是抽象类,一样需要使用静态工厂获取实例对象。
- public final static DateFormat getTimeInstance()
- public final static DateFormat getTimeInstance(int style)
- public final static DateFormat getTimeInstance(int style,Locale aLocale)
- public final static DateFormat getDateInstance()
- public final static DateFormat getDateInstance(int style)
- public final static DateFormat getDateInstance(int style,Locale aLocale)
- public final static DateFormat getDateTimeInstance()
- public final static DateFormat getDateTimeInstance(int dateStyle,int timeStyle)
- public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
很明显,有三种不同的方式来获取 DateFormat 实例,每种方式有三个重载,getDateInstance 用来处理日期,getTimeInstance 用来处理时间,getDateTimeInstance 既可以处理日期,也可以处理时间。我们通过一个例子看看他们之间的区别:
- public static void main(String[] args) {
- Calendar c = Calendar.getInstance();
- System.out.println(DateFormat.getDateInstance().format(c.getTime()));
- System.out.println(DateFormat.getTimeInstance().format(c.getTime()));
- System.out.println(DateFormat.getDateTimeInstance().format(c.getTime()));
- }
输出结果:
- 2017-5-29
- 17:18:26
- 2017-5-29 17:18:26
很显然,三者之间的区别也是不言而喻。对于他们另外两个重载来说,一个重载提供修改输出风格,另一个提供修改 locale。无论是上述的哪一种工厂方法,在他们内部都调用的是同一个函数
- private static DateFormat get(int timeStyle, int dateStyle,int flags, Locale loc)
四个参数,所有我们在调用工厂方法的时候没有提供的参数值都会使用默认值。至于该方法具体是如何实现创建一个实例返回的我们就暂时不深究了。至于其他的一些方法,我们将在其子类 SimpleDateFormat 中学习。
四、优秀的实现类 SimpleDateFormat SimpleDateFormat 是 DateFormat 的一个优秀的实现类,它增强了一个重要的性质。它允许自定义格式输出模板。构造 SimpleDateFormat 实例的时候,可以传入一个 pattern 作为输出模板。看个例子:
- public static void main(String[] args) {
- Calendar c = Calendar.getInstance();
- SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH时mm分ss秒");
- System.out.println(sm.format(c.getTime()));
- }
输出结果:
- 2017年05月29日 星期一 20时25分31秒
上述的代码中,字符串 yyyy 年 MM 月 dd 日 E HH 时 mm 分 ss 秒就是一个模板 pattern,其中:
其中需要注意一点的是,m 这个字母大写状态被用作表示月份,小写状态被用作表示分钟,不能混用二者。除了可以使用 HH 表示小时以外,hh 也可以表示小时,只是它是 12 的(上午和下午)。当然我们也可以逆向操作:
- public static void main(String[] args) throws ParseException {
- Calendar c = Calendar.getInstance();
- SimpleDateFormat sm = new SimpleDateFormat("yyyy年MM月dd日 E HH时mm分ss秒");
- String s = "2016年11月11日 星期五 00时00分00秒";
- System.out.println(sm.parse(s));
- }
输出结果:
- Fri Nov 11 00:00:00 CST 2016
五、开源第三方库 Joda-Time Joda-Time 库中的内容还是很多的,我们简单了解下基本的使用即可,至于深入学习该库,大家可以自行尝试,此处限于篇幅,不再赘述。在该库中 DateTime 相当于 jdk 中 Calendar,主要完成对日期年月日的计算操作。首先我们通过简单易理解的方式创建 DateTime 的实例对象:
- //2017-05-29 21:40
- DateTime dt = new DateTime(2017,5,29,21,40);
- //2017-05-29 21:40 50秒
- DateTime dt2 = new DateTime(2017,5,29,21,40,50);
创建一个日期实例比 Calendar 中为每个属性 set 值方便多了。在该库中获取日期的操作被分解了,不像 Calendar 中共享一个 int 数组。
- DateTime dt = new DateTime(2017,5,29,21,40);
- System.out.println("year: "+dt.getYear());
- System.out.println("month: "+dt.getMonthOfYear());
- System.out.println("day: "+dt.getDayOfMonth());
- System.out.println("hour: "+dt.getHourOfDay());
- System.out.println("minute: "+dt.getMinuteOfHour());
- System.out.println("second: "+dt.getSecondOfMinute());
- System.out.println("millisecond: " +dt.getMillisOfSecond());
- System.out.println("day_of_week: " +dt.getDayOfWeek());
我们也可以直接使用 DateTime 的 tostring 方法来实现将日期转换成指定 pattern 的字符串,例如:
- DateTime dt = new DateTime(2017,5,29,21,40);
- System.out.println(dt.toString("yyyy-MM-dd HH:mm:ss"));
上述代码将会把日期类型按照指定的模板输出,该 Joda-Time 库中内容很多,此处就简单介绍到这, 感兴趣的同学可以自行研究,该库的核心优势就在于它将很多复杂的操作分解为单个简单操作,这也是我们程序设计中核心的思维方式。
有关 Java 中日期和时间的内容本篇已经简单介绍完了,有理解不到之处,望大家指出,相互学习!
来源: http://www.cnblogs.com/yangming1996/p/6919191.html