介绍
随着Java版本的更新,处理日期和时间的API也经历了显著的变化。在Java 8之前,主要使用的是 java.util.Date、java.util.Calendar 和 java.text.SimpleDateFormat 等类来处理日期和时间,但这些类存在很多缺点,比如设计不合理、线程不安全、不可变性差等。该类已经有很多方法已被注销不再使用,所以我们不推荐使用此类
从Java 8开始,引入了全新的日期时间API,位于java.time包下,这个新的API旨在解决旧有API的问题,并提供更加直观和强大的日期时间处理能力。
第一代日期类
Date类
我们可以使用 Date 类的无参构造器,它的功能就是获取当前时间。
但需注意,我们使用该方法需要引入的包为 java.util.Date ;而该方法在 java.sql.Date; 包中也存在,但并非我们所需的包,所以需要注意不要误引。
该类常用的构造方法有两个,分为无参和有参构造,无参构造即获取当前时间,有参构造允许接收一个 long 型参数,该参数表示自1970年1月1日 00:00:00 GMT以来的过了毫秒数之后的时间。
我们还可使用 getTime() 返回自1970年1月1日00:00:00 GMT以来的毫秒数(即时间戳),我们可以使用两个由该方法所得的参数来判断程序运行所需时长。我们可以先创建Date对象,再调用该对象的.getTime()就可获取时间戳。
其次,该方法输出的日期格式并不便于理解,因此通常需要使用 SimpleDateFormat 类对其格式进行转换。
import java.util.Date;
//不要误导入 java.sql.Date 包
public class Go {
public static void main(String[] args) {
Date x = new Date();//调用无参构造器
System.out.println(x);
Date now = new Date(99999999);//调用有参构造器传入long型变量99999999
System.out.println(now);
}//输出结果:Thu Aug 08 23:47:22 CST 2024
// Fri Jan 02 11:46:39 CST 1970
}
SimpleDateFormat类
格式化日期
SimpleDateFormat类是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。然而,由于它依赖于 Date 类,因此也受到其设计缺陷的影响。
public class Go {
public static void main(String[] args) {
Date x = new Date();//调用无参构造器
Date now = new Date(99999999);//调用有参构造器传入long型变量99999999
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(x));
System.out.println(sdf.format(now));
}//输出2024-08-11 04:14:12
}// 1970-01-02 11:46:39
这个例子中,模式字符串 "yyyy-MM-dd HH:mm:ss" 表示日期时间将按照“年-月-日 时:分:秒”的格式进行格式化和解析。
字符串转换为日期
此外,该类还可以将指定格式的字符串转换为对应的Date,但须注意此时的格式和使用 Date() 方法构造的格式一样不便理解,还需使用 sdf.format() 方法转换其格式再进行输出。
注意所给的字符串格式必须与使用的 sdf 格式相同,否则会抛出异常,在此我们为避免抛出异常,可使用 throws 关键字事先声明该异常。
public class Go {
public static void main(String[] args) throws ParseException {
//事先使用throws关键字抛出异常
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//创建 SimpleDateFormat 类实例
String str = "2222-22-22 22:22:22";
//创建字符串,格式与定义的sdf相同
System.out.println(sdf.format(sdf.parse(str)));
//使用sdf.parse()方法转换为Date类再调用sdf.format()方法改变日期格式
}
}
第二代日期类
第二代日期类指的是 java.util.Calendar 类。它是一个抽象类,提供了方法和字段(如年、月、日、时、分、秒等)的方法。它允许进行日期和时间的计算,如添加或减去特定的时间单位。
由于 Calendar 是抽象类,并且他的构造器也是私有化的,因此不能直接实例化,而需通过调用其静态方法 getInstance() 来获取一个 Calendar 对象,这个方法会根据默认时区和语言环境返回一个子类对象。
public class Go {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar);
}
}
输出结果:java.util.GregorianCalendar[time=1723405314881,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2024,MONTH=7,WEEK_OF_YEAR=33,WEEK_OF_MONTH=3,DAY_OF_MONTH=12,DAY_OF_YEAR=225,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=2,AM_PM=0,HOUR=3,HOUR_OF_DAY=3,MINUTE=41,SECOND=54,MILLISECOND=881,ZONE_OFFSET=28800000,DST_OFFSET=0]
这种输出方法会输出大量字段信息,我们难以获取所需字段,所以我们一般会通过 get 方法获取指定字段:
public class Go {
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
System.out.print("今天是:\n"+c.get(Calendar.YEAR)+"年");
System.out.print(c.get(Calendar.MONTH)+1+"月");//需+1
System.out.println(c.get(Calendar.DAY_OF_MONTH)+"日");//为12小时制
System.out.print(c.get(Calendar.HOUR)+"点");
System.out.print(c.get(Calendar.MINUTE)+"分");
System.out.print(c.get(Calendar.SECOND)+"秒");
}
}
输出结果:
今天是:
2024年8月12日
3点50分44秒
注意:因为这里的月份是从零开始算的,所以需要+1才是正确的月份
这里输出的小时为12小时制,如需使用24小时制只需将 Calendar.HOUR 替换为 Calendar.HOUR_OF_DAY 即可
因为 Calendar 并未提供指定的格式化输出方法,所以具体需要截取哪一字段的信息需要自行编写组合。
相对于 Date 类 来说 Calendar 类已经完善了很多,但仍存在一定问题:
可变性:像日期和时间这样的类应该是不可变的。
偏移性:月份并不是从 1 开始,而是从 0 开始计算。
格式化:不包含格式化输出方法。
线程安全:它不是线程安全的,例如不能处理闰秒(每隔两天,多出一秒)
第三代日期类
第三代日期类主要指的是Java 8及以后版本中引入的 java.time 包下的一系列日期和时间API。这些新的日期和时间类旨在解决早前两代日期类存在的问题,如设计缺陷、易用性差、线程安全性问题等。
java.time包提供了一套全新的日期和时间API,包括日期(LocalDate
)、时间(LocalTime
)、日期时间(LocalDateTime
)、时区日期时间(ZonedDateTime
)等类,以及用于日期时间运算的Duration
和Period
类。这些类基于ISO-8601日历系统,提供了更好的设计、更清晰的API和更好的时区支持。
主要方法
-
LocalDate:表示一个具体的日期,不包含时间信息(只有年月日,不包含时分秒)。可以通过年、月、日创建实例,支持日期的加减、比较等操作。
-
LocalTime:表示一个具体的时间,不包含日期信息(只有时分秒,不包含年月日))。可以通过时、分、秒(以及纳秒)创建实例,支持时间的加减、比较等操作。
-
LocalDateTime:表示一个具体的日期和时间(既包含年月日,也包含时分秒),不包含时区信息。可以看作
LocalDate
和LocalTime
的组合,支持日期和时间的加减、比较等操作。 -
ZonedDateTime:表示一个具体的时区日期时间。在
LocalDateTime
的基础上增加了时区信息,可以精确表示世界上任何时区的日期和时间。 -
Duration:用于表示两个时间点之间的时间间隔,以秒和纳秒为基准。可以用于计算两个
LocalTime
或LocalDateTime
之间的时间差。 -
Period:用于表示两个日期之间的时间间隔,以年、月、日为基准。可以用于计算两个
LocalDate
之间的日期差。 - now:通过该方法返回当前日期类的对象。
public class Go {
public static void main(String[] args) {
LocalDateTime x=LocalDateTime.now();//获取年月日时分秒
//也可替换为LocalDate now= LocalDate.now();或LocalTime now= LocalTime.now();
System.out.println(x);
}
}
输出结果:2024-08-12T04:21:02.450
如需获取单独的字段,也可使用实例所包含的方法,其中x.getYear()表获取年、x.getMonth()表获取英文月份、x.getMonthValue()表获取中文月份、x.getDayOfMonth()表获取日、x.getHour()表获取时、x.getMinute()表获取分、x.getSecond()表获取秒。
DateTimeFormatter类
同时第三代日期类也包含一个类似于第一代日期类中 SimpleDateFormat 功能(即格式化日期格式)的类:DateTimeFormatter类,功能也类似
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Go {
public static void main(String[] args) {
// 创建DateTimeFormatter对象,指定自定义格式
DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 格式化LocalDateTime对象
LocalDateTime x = LocalDateTime.now();
String aim = DTF.format(x);
System.out.println("格式化后的日期: " + aim);
// 解析字符串为LocalDateTime对象
LocalDateTime DTF1 = LocalDateTime.parse("2023-10-01 12:00:00", DTF);
System.out.println("解析后的日期: " + DTF1);
}
}
输出结果
格式化后的日期: 2024-08-12 04:47:26
解析后的日期: 2023-10-01T12:00
Instant类
该类类似于 Date,并提供了一系列和 Date 类转换的方式:
public class Go {
public static void main(String[] args) {
//通过静态方法now获取当前时间戳的对象
Instant ins =Instant.now();
System.out.println(ins);
//通过from()方法将Instant类转换为Date类
Date d=Date.from(ins);
System.out.println(d);
//通过toInstant()方法将Date类转换成Instant类
ins=d.toInstant();
}
}
输出结果:
2024-08-11T21:00:15.085Z
Mon Aug 12 05:00:15 CST 2024
还可以进行时间计算:可以使用plus
和minus
系列的方法来计算时间偏移,如plusSeconds(long seconds)
、minusMillis(long millis)
等。
public class Go {
public static void main(String[] args) {
// 创建DateTimeFormatter对象,指定自定义格式
DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt=LocalDateTime.now();
System.out.println("当前日期:"+DTF.format(ldt));
ldt=ldt.plusDays(8);
System.out.println("8天后的日期:"+DTF.format(ldt));
ldt=ldt.minusDays(16);
System.out.println("8天前的日期:"+DTF.format(ldt));
}
}
执行结果:
当前日期:2024-08-12 05:12:44
8天后的日期:2024-08-20 05:12:44
8天前的日期:2024-08-04 05:12:44