java1.8引入的新特性中,其中就含有对日期时间的处理,下面我们一起学习和了解。
这是java1.8以前的日期时间处理相关类,其存在以下缺点:
1.设计不合理,在java.utl和java.sql的包中都有日期类,java.util.Date同时包含日期和时间的,而java.sqlL.Date仅仅包含日期,此外用于格式化和解析的类在java.text包下。
2.非线程安全,java.util.Date是非线程安全的,所有的日期类都是可变的,这是java日期类最大的问题之一。
3.时区处理麻烦,日期类并不提供国际化,没有时区支持。
下面我们先复习以下它们的基本使用:
public class DateTest {
public static void main(String[] args) throws Exception {
new DateTest().test01();
}
public void test01() throws Exception{
//Date获取当前时间
//Date创建指定时间
Date date = new Date();
Date date2 = new Date(2023,01,01);
System.out.println("Date获取当前时间:"+date);
System.out.println("Date创建指定时间:"+date2);
//SimpleDateFormat.format将日期java对象转换为指定格式日期字符串并输出
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("java对象转字符串"+sdf.format(date));
System.out.println("java对象转字符串"+sdf.format(date2));
//SimpleDateFormat.parse将符合指定格式的日期字符串转换为日期java对象
Date parseDate = sdf.parse("2023-01-01");
System.out.println("日期字符串转java对象"+parseDate);
}
}
执行结果:
Date获取当前时间:Sat Feb 18 16:48:12 CST 2023
Date创建指定时间:Thu Feb 01 00:00:00 CST 3923
java对象转字符串2023-02-18
java对象转字符串3923-02-01
日期字符串转java对象Sun Jan 01 00:00:00 CST 2023
可以看到当使用Date创建指定日期时间时会出现问题。
再看下面一个例子:
public class DateTest {
public static void main(String[] args) throws Exception {
new DateTest().test01();
}
public void test01() throws Exception{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date parseDate = sdf.parse("2023-01-01");
System.out.println("日期字符串转java对象"+parseDate);
for (int i = 0;i<30;i++){
new Thread(()->{
try {
System.out.println(sdf.parse("2023-01-01"));
} catch (ParseException e) {
e.printStackTrace();
}
}).start();
}
}
}
执行结果:
Sun Jan 01 00:00:00 CST 2023
Sun Jan 01 00:00:00 CST 2023
Sun Jan 01 00:00:00 CST 2023
Exception in thread "Thread-5" Exception in thread "Thread-7" Exception in thread "Thread-16" java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.junjie.test3.DateTest.lambda$test01$0(DateTest.java:33)
at java.lang.Thread.run(Thread.java:748)
java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:601)
at java.lang.Long.parseLong(Long.java:631)
at java.text.DigitList.getLong(DigitList.java:195)
at java.text.DecimalFormat.parse(DecimalFormat.java:2051)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
at com.junjie.test3.DateTest.lambda$test01$0(DateTest.java:33)
at java.lang.Thread.run(Thread.java:748)
可以看到SimpleDateFormat是线程不安全的。
由上面的案例,我们可以看到旧版的Date、SimpleDateFormat存在这个一些问题,而且使用起来也及其不方便,所以下面我们学习新的日期时间处理类。
LocalDate:
public void test01(){
//1.创建指定的日期
LocalDate date1 = LocalDate.of(2021,05,06);
System.out .println( "date1 = "+date1);
//2.得到当前的日期
LocalDate now = LocalDate .now();
System.out.println("now = "+now);
// 3.根据LocalDate对象获取对应的日期信息
System.out.println("年:" + now.getYear());
System.out.println("月:" + now.getMonth().getValue());
System.out.println("日:" + now.getDayOfMonth());
System.out.println("星期:" + now.getDayOfWeek().getValue());
}
执行结果:
date1 = 2021-05-06
now = 2023-02-18
年:2023
月:2
日:18
星期:6
LocalTime:
public void test02(){
//1、得到指定时间
LocalTime time = LocalTime.of(5, 26, 33, 2323);
System.out.println("指定时间:"+time);
//2、获取当前的时间
LocalTime now = LocalTime.now();
System.out.println("当前时间:"+now);
//3、获取时间信息
System.out.println("时"+now.getHour());
System.out.println("分"+now.getMinute());
System.out.println("秒"+now.getSecond());
System.out.println("纳秒"+now.getNano());
}
执行结果:
指定时间:05:26:33.000002323
当前时间:17:12:48.909
时17
分12
秒48
纳秒909000000
LocalDateTime:
public void test03(){
//获取指定日期时间
LocalDateTime dateTime = LocalDateTime.of(2023, 01, 01, 12, 12, 33, 1213);
System.out.println("指定日期时间:"+dateTime);
//获取当前的日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前日期时间:"+now);
//获取日期时间信息
System.out.println("年:" + now.getYear());
System.out.println("月:" + now.getMonth().getValue());
System.out.println("日:" + now.getDayOfMonth());
System.out.println("星期:" + now.getDayOfWeek().getValue());
System.out.println("时"+now.getHour());
System.out.println("分"+now.getMinute());
System.out.println("秒"+now.getSecond());
System.out.println("纳秒"+now.getNano());
}
执行结果:
指定日期时间:2023-01-01T12:12:33.000001213
当前日期时间:2023-02-18T17:14:30.547
年:2023
月:2
日:18
星期:6
时17
分14
秒30
纳秒547000000
修改:
public void test01(){
LocalDateTime now = LocalDateTime.now();
System.out.println("now = "+now);
//修改日期时间,每次修改都会重新创建一个新的对象
LocalDateTime localDateTime = now.withYear(1998);
System.out.println("当前时间:"+now);
System.out.println("修改年:"+localDateTime);
System.out.println("修改月:"+now.withMonth(8));
System.out.println("修改天:"+now.withDayOfMonth(3));
System.out.println("修改时:"+now.withHour(8));
System.out.println("修改分:"+now.withMinute(15));
}
执行结果:
now = 2023-02-18T17:20:03.888
当前时间:2023-02-18T17:20:03.888
修改年:1998-02-18T17:20:03.888
修改月:2023-08-18T17:20:03.888
修改天:2023-02-03T17:20:03.888
修改时:2023-02-18T08:20:03.888
修改分:2023-02-18T17:15:03.888
加减:
public void test01(){
LocalDateTime now = LocalDateTime.now();
System.out.println("now = "+now);
//加减法
System.out.println("两天后:"+now.plusDays(2));
System.out.println("两年后:"+now.plusYears(2));
System.out.println("六月后:"+now.plusMonths(6));
System.out.println("三小时后:"+now.plusHours(3));
System.out.println("十年前:"+now.minusYears(10));
System.out.println("半年前:"+now.minusMonths(6));
System.out.println("一周前:"+now.minusDays(7));
}
执行结果:
两天后:2023-02-20T17:20:03.888
两年后:2025-02-18T17:20:03.888
六月后:2023-08-18T17:20:03.888
三小时后:2023-02-18T20:20:03.888
十年前:2013-02-18T17:20:03.888
半年前:2022-08-18T17:20:03.888
一周前:2023-02-11T17:20:03.888
比较:
public void test02(){
LocalDate date = LocalDate.of(2022, 1, 3);
LocalDate now = LocalDate.now();
System.out.println(now.isAfter(date));
System.out.println(now.isBefore(date));
System.out.println(now.isEqual(date));
}
true
false
false
使用TemporalAdjuster进行修改日期时间:
public void test03(){
LocalDateTime now = LocalDateTime.now();
//将当前日期调整到下个月的一号(手动实现)
TemporalAdjuster adjuster = (temporal)->{
LocalDateTime dateTime = (LocalDateTime) temporal;
LocalDateTime nextMonthDay = dateTime.plusMonths(1).withDayOfMonth(1);
return nextMonthDay;
};
LocalDateTime time = now.with(adjuster);
System.out.println(time);
//将当前日期调整到下个月的一号(使用内部封装的)
LocalDateTime time1 = now.with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(time1);
LocalDateTime time2 = now.with(TemporalAdjusters.firstDayOfYear());
System.out.println(time2);
}
执行结果:
2023-03-01T17:51:33.759
2023-03-01T17:51:33.759
2023-01-01T17:51:33.759
注意:在进行日期时间修改和加减的时候,原来的L.ocalDate对象是不会被修改,每次操作都是返回了一个新的LocalDate对象,所以在多线程场景下是数据安全的。
代码:
public void test01(){
LocalDateTime now = LocalDateTime.now();
//DateTimeFormatter.format将日期类型的对象转换为日期字符串(使用系统默认格式)
DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String format = now.format(isoLocalDateTime);
System.out.println(format);
//DateTimeFormatter.format将日期类型的对象转换为日期字符串(使用指定格式)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format1 = now.format(formatter);
System.out.println(format1);
//DateTimeFormatter.parse将日期字符串转换为日期类型java对象(使用默认格式)
LocalDateTime parse2 = LocalDateTime.parse("2023-02-18T17:25:48.267", DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(parse2);
//DateTimeFormatter.parse将日期字符串转换为日期类型java对象(使用指定格式)
LocalDateTime parse = LocalDateTime.parse("1995-04-05 22:33:22", formatter);
System.out.println(parse);
}
执行结果:
2023-02-18T17:30:03.752
2023-02-18 17:30:03
2023-02-18T17:25:48.267
1995-04-05T22:33:22
方式一:
public void test01() throws Exception {
Instant now = Instant.now();
System.out.println("now = "+now);
System.out.println("1970年1月1日00:00:00到现在所耗的纳秒:"+now.getNano());
Thread.sleep(1);
Instant now1 = Instant.now();
System.out.println("沉睡一毫秒后的时间差(单位:纳秒):"+(now1.getNano()-now.getNano()));
}
执行结果:
now = 2023-02-18T09:35:54.939Z
1970年1月1日00:00:00到现在所耗的纳秒:939000000
沉睡一毫秒后的时间差(单位:纳秒):59000000
方式二:使用Duration计算时间的差值
public void test02(){
//计算时间差
LocalTime now = LocalTime.now();
LocalTime time = LocalTime.of(22, 48, 59);
//通过Duration来计算时间差
Duration duration = Duration.between(now, time);
System.out.println(now+"和"+time+"相差了(单位:天)"+duration.toDays());
System.out.println(now+"和"+time+"相差了(单位:时)"+duration.toHours());//取整时,舍去分之后的
System.out.println(now+"和"+time+"相差了(单位:分)"+duration.toMinutes());//取整分,舍去秒之后的
System.out.println(now+"和"+time+"相差了(单位:毫秒)"+duration.toMillis());//同理
}
执行结果:
17:41:12.951和22:48:59相差了(单位:天)0
17:41:12.951和22:48:59相差了(单位:时)5
17:41:12.951和22:48:59相差了(单位:分)307
17:41:12.951和22:48:59相差了(单位:毫秒)18466049
方式三:使用Period计算日期的差值
public void test02(){
LocalDate nowDate = LocalDate.now();
LocalDate date = LocalDate.of(1997, 12, 5);
Period period = Period.between(date, nowDate);
System.out.println(date+"和"+nowDate+"相差了(单位:年):"+period.getYears());//只算了年份的加减,向下取整
System.out.println(date+"和"+nowDate+"相差了(单位:月):"+period.getMonths());//只算了月份的加减
System.out.println(date+"和"+nowDate+"相差了(单位:日):"+period.getDays());//只算了天份的加减
System.out.println(date+"和"+nowDate+"相差了:"+period.getYears()+"年"+period.getMonths()+"个月"+period.getDays()+"天");
}
1997-12-05和2023-02-18相差了(单位:年):25
1997-12-05和2023-02-18相差了(单位:月):2
1997-12-05和2023-02-18相差了(单位:日):13
1997-12-05和2023-02-18相差了:25年2个月13天
这是java1.8的新特性,java1.8之前是没有对时区处理的。
LocalDate、LocalTime、LocalDateTime是不带时区的。
带时区的日期时间类分别为: ZonedDate、ZonedTime、zonedDateTime。其中每个时区都对应着ID,ID的格式为“区域/城市”。例如︰ Asia/Shanghai等。
获取所有时区:
public void test04(){
//获取所有的时区
ZoneId.getAvailableZoneIds().forEach(System.out::println);
}
、、、
、、、
Asia/Anadyr
Australia/Melbourne
Asia/Irkutsk
America/Shiprock
America/Winnipeg
Europe/Vatican
Asia/Amman
Etc/UTC
SystemV/AST4ADT
Asia/Tokyo
America/Toronto
Asia/Singapore
、、、
、、、
时区使用:
public void test04(){
//获取所有的时区
// ZoneId.getAvailableZoneIds().forEach(System.out::println);
//获取当前时间 中国使用的是东八区时区,比标准时间早8个小时
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//获取标准时间
ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC());
System.out.println(now1);
//不指定时区时,默认使用计算机系统的时区
ZonedDateTime now2 = ZonedDateTime.now();
System.out.println(now2);
//使用指定时区创建日期时间
ZonedDateTime now3 = ZonedDateTime.now(ZoneId.of("America/Marigot"));
System.out.println(now3);
}
执行结果:
2023-02-18T17:58:34.833
2023-02-18T09:58:34.834Z
2023-02-18T17:58:34.834+08:00[Asia/Shanghai]
2023-02-18T05:58:34.835-04:00[America/Marigot]
标准时间转换为中国时间(东八区):
public class TimeFormatTest {
public static void main(String args[]) throws ParseException {
ZonedDateTime zdt = ZonedDateTime.parse("2019-07-31T16:00:00.000Z");
LocalDateTime localDateTime = zdt.toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String cst = formatter.format(localDateTime.plusHours(8));
System.out.println("北京时间:" + cst);
}
}