0%

Java的日期与时间

1. Date类

1.1 对象创建

可以通过Date的两个构造函数来实现

  1. Date( ) 获取当前日期和时间的Date对象
  2. Date(long millisec) 其中的参数是从1970年1月1日起的毫秒数

代码示例:

1
2
3
4
5
6
7
8
9
10
11
import java.util.Date;

public class DateDemo {
public static void main(String args[]) {
// 初始化 Date 对象
Date date = new Date();

// 使用 toString() 函数显示日期时间
System.out.println(date.toString());
}
}

1.2 常用API

boolean after(Date date);

若当调用此方法的Date对象在指定日期之后返回true,否则返回false。

boolean before(Date date)

若当调用此方法的Date对象在指定日期之前返回true,否则返回false。

int compareTo(Date date)

比较当调用此方法的Date对象和指定日期。两者相等时候返回0。调用对象在指定日期之前则返回负数。调用对象在指定日期之后则返回正数。

boolean equals(Object date)

当调用此方法的Date对象和指定日期相等时候返回true,否则返回false。

long getTime( )

返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。

void setTime(long time)

用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期。

String toString( )

把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。

1.3 日期比较

Java使用以下三种方法来比较两个日期:

  • 使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值。
  • 使用方法 before(),after() 和 equals()。
  • 使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package time;

import java.util.Date;

public class DateDemo {
public static void main(String[] args) throws InterruptedException {
Date d1 = new Date();
Thread.sleep(20);
Date d2 = new Date();
//使用毫秒值来比较大小
boolean flag = (d2.getTime() - d1.getTime())>0 ? true:false;
System.out.println("d2比d1大吗?" + flag); //true
//使用before来比较大小
System.out.println("d2比d1大吗?" + d2.after(d1)); //true
//使用compareTo方法来比较
boolean cFlag = d2.compareTo(d1) > 0 ? true : false;
System.out.println("d2比d1大吗?" + cFlag); //true
}
}

1.4 格式化日期

1.4.1 使用DateFormat类来格式化日期

代码示例:

1
2
3
4
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
String dateStr = dateFormat.format(date);
System.out.println(dateStr); //输出为 2019年1月10日 星期四

使用该类来格式化日期都是JDK自带的日期格式,DateFormat.FULL为其中一种格式化风格,其他风格可以查看API。

1.4.2 使用SimpleDateFormat类来格式化日期

SimpleDateFormat是DateFormat的子类,可以自定义日期的格式化风格

代码示例:

1
2
3
4
5
6
private static void simpleDateFormatDemo() {
Date dNow = new Date( );
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: " + ft.format(dNow));
//输出为 当前时间为: 2019-01-10 10:12:06
}

其中 yyyy 是完整的公元年,MM 是月份,dd 是日期,HH:mm:ss 是时、分、秒。
注意:有的格式大写,有的格式小写,例如 MM 是月份,mm 是分;HH 是 24 小时制,而 hh 是 12 小时制。

具体的日期和时间的格式化编码如下示:
在这里插入图片描述

1.4.3 使用parse方法解析字符串为时间

SimpleDateFormat 类有一些附加的方法,特别是parse(),它试图按照给定的SimpleDateFormat 对象的格式化存储来解析字符串。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static void strToDate() {
try {
//按指定格式将字符串转换为Date对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String strDate = "2019-01-11";
Date date = simpleDateFormat.parse(strDate);
//将Date对象转换其他格式的时间字符串
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL);
String newStrDate = dateFormat.format(date);
System.out.println(newStrDate);

} catch (ParseException e) {
System.out.println("时间转换错误!");
}
}

2. Calendar类

Date有许多弊端和不足之处,无法满足特定需求。相比于Date,Calendar则强大得多。我们可以使用Calendar类设置和获取日期数据的特定部分,比如说小时,日,或者分钟。

2.1 Calendar实例获取

可以使用getInstance方法。

1
2
3
4
//Calendar示例的获取
Calendar calendar = Calendar.getInstance();
String strDate = calendar.getTime().toString();
System.out.println(strDate); //Thu Jan 10 10:55:19 CST 2019

2.2 Calendar类对象字段类型

在这里插入图片描述

特别注意:MONTH月份是从0开始计算的,也就是说0代表一月份,1代表二月份,以此类推;DAY_OF_WEEK规定了一个星期是从星期日开始的,1代表星期日,2代表星期一,以此类推。

2.2.1 Calendar对象特定部分的获取

下面是Calendar对象的特定部分进行获取的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Calendar c1 = Calendar.getInstance();
// 获得年份
int year = c1.get(Calendar.YEAR);
System.out.println("year:" + year);
// 获得月份
int month = c1.get(Calendar.MONTH) + 1;
System.out.println("month:" + month);
// 获得日期
int date = c1.get(Calendar.DATE);
System.out.println("date:" + date);
// 获得小时
int hour = c1.get(Calendar.HOUR_OF_DAY);
System.out.println("hour:" + hour);
// 获得分钟
int minute = c1.get(Calendar.MINUTE);
System.out.println("minute:" + minute);
// 获得秒
int second = c1.get(Calendar.SECOND);
System.out.println("second:" + second);
// 获得星期几(注意(这个与Date类是不同的):1代表星期日、2代表星期1、3代表星期二,以此类推)
int day = c1.get(Calendar.DAY_OF_WEEK);
System.out.println("day:" + day);

输出结果:

1
2
3
4
5
6
7
year:2019
month:1
date:10
hour:11
minute:9
second:7
day:5

2.2.2 Calendar类对象信息的设置

set设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void setCalendarTime() {
Calendar c1 = Calendar.getInstance();
//使用set设置日期
c1.set(2019, 5, 25);
myPrintln(c1);//2019-06-25 11:32:01
//使用set设置字段
//设置年份
c1.set(Calendar.YEAR, 2018); //2018-06-25 11:32:01
myPrintln(c1);
//设置月份
c1.set(Calendar.MONTH, 10); //2018-11-25 11:32:01
myPrintln(c1);
//设置日期
c1.set(Calendar.DATE, 31); //2018年11月是没有31号的,时间会重新计算,变成12月1号
myPrintln(c1); //2018-12-01 11:33:32
}

public void myPrintln(Calendar c1) {
Date time = c1.getTime();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(format.format(time));
}

set方法有两种,一种是直接设置日期时间,另一种则是设置日期时间的某一特定部分。
注意:在设置月份的时候,如果设置的月份为负数或者大于12,则会对应的更改当前年份。日期和具体时间同理。

add设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void addCalendarTime() {
Calendar c1 = Calendar.getInstance();
//当前日期时间
myPrintln(c1); //2019-01-10 11:48:53
//设置月份,月份加了13
c1.add(Calendar.MONTH, 13);
myPrintln(c1);//2020-02-10 11:40:21
//设置日期,天数加了23
c1.add(Calendar.DATE, 23);
myPrintln(c1);//2020-03-04 11:47:19
}

public void myPrintln(Calendar c1) {
Date time = c1.getTime();
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(format.format(time));
}

其他字段属性的add的意义以此类推。

3. GregorianCalendar类

Calendar类实现了公历日历,GregorianCalendar是Calendar类的一个具体实现。

3.1 GregorianCalendar对象的创建

在这里插入图片描述

3.2 GregorianCalendar常用方法

GregorianCalendar类的常用方法主要是使用其父类Calendar的方法。

下面讲一下其比较特殊常用的方法:

boolean isLeapYear(int year);

确定给定的年份是否为闰年。如果给定的年份是闰年,则返回 true。如果要指定 BC 年份,必须给定 1 - 年份。例如,指定 -3 为 BC 4 年


Calendar的功能还不够给力,它的实现是易便的,并且它没有处理诸如闰秒这样的缺。所以Java SE 8中引入的java.time API,它修正了过去的缺陷。

下面开始讲述关于Java SE 8的一些关于时间日期的新特性,包括java.time.*;包里的Instant,LocalDate,LocalTime 和 LocalDateTime等类。

4. Instant类

所谓的 Instant 类代表的是时间线上的某个时间点(有点像 java.util.Date),它是精确到纳秒(而不是像旧版本的Date精确到毫秒),是计算机相关的时间。
起点是格林威治时间(GMT)1970-01-01:00:00(实际上Instant 取代原有的 Date 类)。
时间从起点开始向前移动,每天为86,400秒。

4.1 对象创建

1
2
3
4
5
//获取当前时间 默认获取 UTC 时区
Instant instant = Instant.now(); //2019-01-10T05:41:33.411Z
//给定秒值或毫秒值构建Instant对象
Instant instant = Instant.ofEpochSecond(1547102726);
Instant instant = Instant.ofEpochMilli(1564564121354544);

注意:Instant表示的时间是UTC时区的时间,即世界标准时间(比中国时差为-8个小时)

4.2 获取时间

Instant 对象中包含两个内部属性用于表示当前的时间:即距离初始时间的秒钟数+在当前一秒内的第几纳秒,他们的组合表达了当前时间点。

1
2
3
Instant instant = Instant.now();
long second = instant.getEpochSecond();//获取秒值
int nano = instant.getNano(); //获取纳秒值

4.3 Instant的操作

Instant类与许多用于时间的算术运算方法,比如plusNanos,plusMillis等等。下面仅仅演示plusMillis(long millisToAdd)方法。其他方法可以查看API文档。

1
2
3
4
5
6
7
8
Instant startInstant = Instant.now();
Thread.sleep(200);
Instant endInstant = Instant.now();

Instant plusMillis = startInstant.plusMillis(200);

System.out.println(plusMillis);
System.out.println(endInstant);

输出结果:

1
2
2019-01-10T07:09:00.763Z
2019-01-10T07:09:00.764Z

5. Duration类

Duration类用来计算两个时刻之间的时间量

5.1 常用方法

static Duration between(Temporal startInclusive,Temporal endExclusive);

计算两个时间的间隔,默认的单位是秒

示例:

1
2
3
4
5
6
Instant startInstant = Instant.now();
Thread.sleep(200);
Instant endInstant = Instant.now();
//两次时间戳之间的时间量
Duration duration = Duration.between(startInstant, endInstant);
System.out.println(duration.toMillis()); //输出200毫秒

其他常用方法基本与Instant类一样。

注意:Instant和Duration类都是不可修改类,所以诸如multipliedBy和munus这样的方法都会返回一个新的实例。

6. LocalDate类

LocalDate类指的是本地时间,它包含年、月、日的日期,也就是说它不包含时间,只是单纯的年月日。

6.1 对象的创建

主要使用now方法和of方法

1
2
3
4
5
6
7
8
9
10
11
12
//获取当前的日期
LocalDate today = LocalDate.now();//2019-01-11

//根据年月日取日期
//注意:日期不存在时会发生 java.time.DateTimeException
LocalDate crischristmas = LocalDate.of(2017, 12, 25);//2017-12-25

// 根据字符串取:
// 严格按照ISO yyyy-MM-dd验证,02写成2都不行,当然也有一个重载方法允许自己定义格式
LocalDate endOfFeb = LocalDate.parse("2014-02-28"); //2014-02-28

// LocalDate.parse("2014-02-29"); // 无效日期无法通过:DateTimeParseException: Invalid date

6.2 常用日期操作

6.2.1 加法运算

plusDays,plusWeeks,plusMonths,plusYears,plus等方法
即在当前的LocalDate上加上一定量的天、星期、月或年等。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dateCalculate() {
//使用加法运算
//程序员日是每年的第256天,可以通过加上255天来求得当天的日期
LocalDate programmingDay = LocalDate.of(2014, 1, 1).plusDays(255);
System.out.println(programmingDay); //2014-09-13

//年份加5
LocalDate newYearDay = programmingDay.plusYears(5);
System.out.println(newYearDay); //2019-09-13

//月份加6
LocalDate newMonthDay = newYearDay.plusMonths(6);
System.out.println(newMonthDay); //2020-03-13

}

我们知道在两个Insatnt之间的时长是Duration,而用于本地时期的等价物是Period,它表示的是流逝的年、月或日的数量。plus方法涉及到该类,下面以代码的方式说明。

1
2
3
4
LocalDate birthday = LocalDate.of(1997, 4, 19);
//得到下一年的生日
LocalDate nextYearBirthday = birthday.plus(Period.ofYears(1)); //和plusYears(1)一样的
System.out.println(nextYearBirthday); //1998-04-19

日期的减法运算和加法运算基本运算,不过多说明。

6.2.2 util方法

代码示例:

1
2
3
4
5
6
7
8
//获取今天具体我出生日期的时长
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1997, 4, 19);

System.out.println("距离出生日期的日期时长为:" + birthday.until(today));

//获取天数
System.out.println("距离出生日期的天数为:" + birthday.until(today, ChronoUnit.DAYS));

运行结果:

1
2
距离出生日期的日期时长为:P21Y8M23D
距离出生日期的天数为:7937

6.2.3 其他方法

  • withDayOfMonth,WithDayOfYear,withMonth,withYear

    返回一个新的LocalDate,其月的日期、年的日期、月或年修改为给定的值,相当于设置日期。

  • getMonth,getMonthValue

    获取月份的枚举值,或者是1 ~ 12之间的数字 。注意,不同于Calendar类那样月份从0开始。

  • getDayOfWeek

    获取星期的日期,返回DayOfWeek枚举值。

注意:DayOfWeek.MONDAY的枚举值为1,而DayOfWeek.SUNDAY的枚举值为7。这与Calendar类周日为1,周一为2有所不同。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
private void weekDayDemo() {
LocalDate today = LocalDate.now();
System.out.println(today); //2019-01-11

DayOfWeek dayOfWeek = today.getDayOfWeek();
//输出枚举值
System.out.println(dayOfWeek); //FRIDAY

//输出枚举值对应的数
System.out.println(dayOfWeek.getValue()); //5
}

其他方法查看API文档。

7. TemporalAdjusters类

该类为日期调整器,提供大量的用于常见调整的静态方法。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  void temporalAdjustersDemo() {
LocalDate today = LocalDate.now(); //201901-11
// 取本月第1天:
LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
System.out.println(firstDayOfThisMonth); //2019-01-01

// 取本月最后一天,再也不用计算是28,29,30还是31:
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDayOfThisMonth); //2019-01-31

// 取2017年1月第一个周一
LocalDate firstMondayOf2017 = LocalDate.parse("2017-01-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstMondayOf2017); //2017-01-02

//取下个周一
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
System.out.println(nextMonday); //2019-01-14

//取这个月的第三个周一
LocalDate theThirdMonday = today.with(TemporalAdjusters.dayOfWeekInMonth(3, DayOfWeek.MONDAY));
System.out.println(theThirdMonday); //2019-01-21
}

8. LocalTime类

与LocalDate仅仅包含日期不同是,LocalTime仅包含时间部分。LocalTime表示当前时刻,例如 15:30:21。

8.1 对象的创建

  • 使用now方法创建当前时间的实例
    1
    2
    3
    4
    5
    LocalTime rightTime = LocalTime.now();
    System.out.println("当前时间为:" + rightTime); //当前时间为:10:41:22.334

    LocalTime bedTime = LocalTime.of(23, 12, 30, 4560 << 9);
    System.out.println("睡觉时间为:" + bedTime); //睡觉时间为:23:12:30.002334720

8.2 常见操作

LocalTime的许多时间的操作方法基本与LocalDate相同,这里不再赘述。详细参见API文档。

9. LocalDateTime类

LocalDateTime类相当于LocalDate类与LocalTime的结合,它可以表示日期和时间,适合存储固定时区的时间点。

9.1 对象的创建

1
2
3
4
5
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime); //2019-01-11T10:52:13.434

LocalDateTime myDateTime = LocalDateTime.of(2018, 9, 23, 12, 45);
System.out.println(myDateTime); //2018-09-23T12:45

9.2 常见操作

9.2.1 间隔运算

使用Duration进行 day,hour,minute,second等的计算
使用Period进行Year,Month的计算

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void tiemCalculate() {
LocalDateTime dateTime = LocalDateTime.now();
LocalDateTime birthdateTime = LocalDateTime.of(1997, 4, 19, 4, 21);

//使用Duration进行 day,hour,minute,second等的计算
Duration duration = Duration.between(birthdateTime,dateTime);
long days = duration.toDays();
long hours = duration.toHours();
long minutes = duration.toMinutes();
System.out.println("days:" + days + " | hours:" + hours + " | minutes:" + minutes);

//使用Period进行Year,Month的计算
Period period = Period.between(birthdateTime.toLocalDate(), dateTime.toLocalDate());
int years = period.getYears();
long totalMonths = period.toTotalMonths();

System.out.println("years:" + years + " | totalMonths:" + totalMonths);

}

运行结果:

1
2
days:7937 | hours:190495 | minutes:11429737
years:21 | totalMonths:260

9.2.2 其他方法

LocalDateTime与LocalDate和LocalTime的方法有许多相似之处,如时间日期的运算、比较、获取和设置等,在这里不再赘述。

10. 格式化与解析

格式化与解析所涉及到的类主要是DateTimeFormater类,它是用来替代java.util.DateFormat类的。

10.1 格式化

DateTimeFormater类提供了三种用于打印日期/时间的格式器:

  • 预定义的格式器(具体参见API文档)
  • Locale相关的格式器
  • 带有定制模式的格式器

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
DateTimeFormatter[] formatters = new DateTimeFormatter[]{
// 预定义的格式器,即直接使用常量创建DateTimeFormatter格式器
DateTimeFormatter.ISO_LOCAL_DATE,
DateTimeFormatter.ISO_LOCAL_TIME,
DateTimeFormatter.ISO_LOCAL_DATE_TIME,
//注意:DateTimeFormatter.ISO_OFFSET_DATE是有时区偏移的,即适用时区时间的格式化

// 使用Locale相关的格式器的不同风格来创建DateTimeFormatter格式器
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.MEDIUM),
DateTimeFormatter.ofLocalizedTime(FormatStyle.LONG),
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG),

// 带有定制模式的格式器,即根据模式字符串来创建DateTimeFormatter格式器
DateTimeFormatter.ofPattern("Gyyyy%%MMM%%dd HH:mm:ss")
};

LocalDateTime date = LocalDateTime.now();
// 依次使用不同的格式器对LocalDateTime进行格式化
for(int i = 0 ; i < formatters.length ; i++)
{
// 下面两行代码的作用相同
System.out.println(date.format(formatters[i]));
System.out.println(formatters[i].format(date));
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2019-01-11
2019-01-11
11:40:29.895
11:40:29.895
2019-01-11T11:40:29.895
2019-01-11T11:40:29.895
2019年1月11日 星期五 11:40:29
2019年1月11日 星期五 11:40:29
上午11时40分29秒
上午11时40分29秒
2019年1月11日
2019年1月11日
公元2019%%一月%%11 11:40:29
公元2019%%一月%%11 11:40:29

10.2 字符串解析

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义一个任意格式的日期时间字符串
String str1 = "2014==04==12 01时06分09秒";
// 根据需要解析的日期、时间字符串定义解析所用的格式器
DateTimeFormatter fomatter1 = DateTimeFormatter
.ofPattern("yyyy==MM==dd HH时mm分ss秒");
// 执行解析
LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1);
System.out.println(dt1); // 输出 2014-04-12T01:06:09

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

11. Java 8 日期/时间设计原则

Java 8 日期/时间 API 是 JSR-310 的实现,它的实现目标是克服旧的日期时间实现中所有的缺陷,新的日期/时间API 的一些设计原则是:

  • 不变性:新的日期/时间 API 中,所有的类都是不可变的,这对多线程环境有好处。
  • 关注点分离:新的 API 将人可读的日期时间和机器时间(unix timestamp)明确分离,它为日期(Date)、时间(Time)、日期时间(DateTime)、时间戳(unix timestamp)以及时区定义了不同的类。
  • 清晰:在所有的类中,方法都被明确定义用以完成相同的行为。举个例子,要拿到当前实例我们可以使用 now()方法,在所有的类中都定义了 format()和 parse()方法,而不是像以前那样专门有一个独立的类。为了更好的处理问题,所有的类都使用了工厂模式和策略模式,一旦你使用了其中某个类的方法,与其他类协同工作并不困难。
  • 实用操作:所有新的日期/时间 API 类都实现了一系列方法用以完成通用的任务,如:加、减、格式化、解析、从日期/时间中提取单独部分,等等。
  • 可扩展性:新的日期/时间 API 是工作在 ISO-8601 日历系统上的,但我们也可以将其应用在非 ISO 的日历上。

12. 总结

java关于时间日期的内容还有很多,比如不同类之间的转换问题以及相互应用的问题等等。另外,还有关于时区时间的内容等还没有讲到。

这部分内容错综复杂,暂时就总结到这了。应该多在具体的实践中加深这部分内容的理解和应用。

参考文章:
https://blog.csdn.net/lamp113/article/details/79049528
http://www.runoob.com/java/java-date-time.html
http://www.runoob.com/java/java8-datetime-api.html

------ 本文结束------