前言
日常开发中,日期和时间是我们经常遇到并且需要处理的。由于日期时间的复杂性,随着Java的发展,也诞生了三代日期处理API。接下来就让我们一起来探究下Java日期时间的前世今生。
一、基本概念
1.1 日期与时间
日期是指某一天,它不是连续变化的;而时间分为带日期的时间和不带日期的时间
带日期的时间能唯一确定某个时刻,不带日期的时间是无法确定一个唯一时刻的
日期如下
- 2022-05-20
- 1992-09-13
时间如下
- 2022-11-20 08:01:59
- 08:01:59
1.2 本地时间
当前时刻是2022年11月20日早上8:08,我们实际上说的是本地时间,也就是北京时间。
有点常识的我们都知道,如果这一个时间点,地球是不同地区的小伙伴看手表,看到的本地时间是不同的。
不同的时区,在同一时刻,本地时间是不同的。
1.3 时区
由于本地时间没法确定一个准确时刻,所以时区的概念就出现了
全球一共分为24个时区,伦敦所在的时区称为标准时区,
其他时区按东/西偏移的小时区分,北京所在的时区是东八区
时区的三种表示方式
- 以GMT或者UTC加时区偏移表示
- GMT是前世界标准时,UTC是现世界标准时,UTC 比 GMT更精准,以原子时计时,每隔几年会有一个闰秒,我们在开发程序的时候可以忽略两者的误差
例如:GMT+08:00
或者UTC+08:00
表示东八区 UTC/GMT +9:00
表示东九区
- 以缩写表示
- 例如:
CST
表示China Standard Time
,也就是中国标准时间 - 以洲/城市表示
例如:Asia/Shanghai,表示上海所在地的时区。城市名称不是任意的城市,而是由国际标准组织规定的城市。 - 这里好学的小伙伴就会问了,中国的时区为什么是Asia/Shanghai,而不是Asia/Beijing 呢?
北京和上海处于同一时区(东八区),只能保留一个。而作为时区代表上海已经足够具有代表性。
**注:**虽然身处不同时区的两个小伙伴,同一时刻表上显示的本地时间不同,但两个表示的时刻是相同的
1.4 夏令时
是夏天开始的时候,把时间往后拨1小时,夏天结束的时候,再把时间往前拨1小时
夏令时是比时区更为复杂的计算方式,很多地区都是不执行夏令时的,我国也在1992年就废除了
二、Java三代日期时间API
第一代日期时间类定义在java.util包下,主要包含Date 、SimpleDateFormat类
第二代日期时间类也是定义在java.util包下,主要是Calendar、TimeZone 类
第三代日期时间类是在Java 8引入的,定义在java.time 包下,主要包含LocalDateTime 、ZonedDateTime 、ZoneId 、DateTimeFormatter类
此时,小伙伴可能会问了
① 为什么会出现三代日期时间类呢?
答:历史遗留问题,早期的API存在着很多问题,所以java在不断迭代更新,引入了新的API。更加方便我们处理日期时间
②既然早期API存在诸多问题,我们能不能直接使用最新API呢?
答:如果是新项目,就建议使用最新API。因为新的API中的类是不变对象,并且是线程安全的;
如果读者和我一样苦逼,还得维护JDK1.6 这样的遗留代码,就必须对一二代API有所了解。
③如果要维护历史遗留代码,新旧API可以相互转换吗?
答:当然了,新旧API之间也是可以相互转换的。
三、 第一代日期时间Date
3.1 继承关系
3.2 获取实例对象
从上图可以看出,已经弃用了好多。这里我们说一说常用的两种
Date()
创建的对象可以获取本地当前时间,精确到毫秒- -
Date(long date)
以从1970 年 1 月 1 日 0 时 0 分 0 秒
开始的毫秒数初始化时间对象
import java.util.Date; public class DateTest { public static void main(String[] args) { Date date1 = new Date(); System.out.println(date1); Date date2 = new Date(15000); System.out.println(date2); } } //输出 Sun Nov 20 09:55:58 CST 2022 Thu Jan 01 08:00:15 CST 1970
3.3 常用方法
我们发现好多也是已弃用了,这里面的很多方法将会被第二代日期时间中的方法取代
3.3 获取当前年月日时分秒
getYear() 、getMonth()、date.getDate()、getHours()、getMinutes()、getSeconds()、
toString()、toGMTString()、toLocaleString()
注:
getYear()返回的年份必须加上1900,
getMonth() 返回的月份是0-11 分别表示1-12月,所以要+1
getDate()
返回的日期范围是1~31,不能加1
import java.util.Date; public class DateTest { public static void main(String[] args) { //获取当前时间 Date date = new Date(); //获取当前年份 System.out.println((date.getYear() + 1900)); //获取当前月份 System.out.println((date.getMonth() + 1)); //获取当前日 System.out.println(date.getDate()); //获取小时 System.out.println(date.getHours()); //获取分钟 System.out.println(date.getMinutes()); //获取秒 System.out.println(date.getSeconds()); } } //输出 2022 11 20 10 17 1
3.4 日期时间转换
toString()
、toGMTString()
、toLocaleString()
import java.util.Date; public class DateTest { public static void main(String[] args) { //获取当前时间 Date date = new Date(); //转换为字符串 System.out.println(date.toString()); //转换为GMT时区 System.out.println(date.toGMTString()); //转换为本地时区 System.out.println(date.toLocaleString()); } } //输出 Sun Nov 20 10:19:25 CST 2022 20 Nov 2022 02:19:25 GMT 2022-11-20 10:19:25
3.5 日期时间格式化
格式化日期表示将日期/时间格式转换为预先定义的日期/时间格式;
例如将
Sun Nov 20 13:48:40 CST 2022
格式化为2022-11-20 13:48:40
3.5.1 DateFormat
类 进行格式化
日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间
创建 DateFormat
对象时不能使用 new 关键字,而应该使用 DateFormat 类中的静态方法
getDateInstance()
。
getDateInstance()
的变种有以下形式
方法 | 描述 |
static DateFormat getDateInstance() | 获取具有默认格式化风格和默认语言环境的日期格式 |
static DateFormat getDateInstance(int style) | 获取具有指定格式化风格和默认语言环境的日期格式 |
static DateFormat getDateInstance(int style, Locale locale) | 获取具有指定格式化风格和指定语言环境的日期格式 |
static DateFormat getDateTimeInstance() | 获取具有默认格式化风格和默认语言环境的日期/时间 格式 |
static DateFormat getDateTimeInstance(int dateStyle,int timeStyle) | 获取具有指定日期/时间格式化风格和默认语言环境的 日期/时间格式 |
static DateFormat getDateTimeInstance(int dateStyle,int timeStyle,Locale locale) | 获取具有指定日期/时间格式化风格和指定语言环境的 日期/时间格式 |
static DateFormat getTimeInstance() | 获取具有默认格式化风格和默认语言环境的时间格式 |
static DateFormat getTimeInstance(int style) | 获取具有指定格式化风格和默认语言环境的时间格式 |
static DateFormat getTimeInstance(int style, Locale locale) | 获取具有指定格式化风格和指定语言环境的时间格式 |
格式化样式主要通过 DateFormat 常量设置。将不同的常量传入getDateInstance()
的方法中,以控制结果的长度。
DateFormat 类的常量有以下几种
SHORT:完全为数字,如 12.5.10 或 5:30pm。
MEDIUM:较长,如 May 10,2016。
LONG:更长,如 May 12,2016 或 11:15:32am。
FULL:是完全指定,如 Tuesday、May 10、2012 AD 或 11:l5:42am CST
import java.text.DateFormat; import java.util.Date; import java.util.Locale; public class DateTest { public static void main(String[] args) { Date date = new Date(); // 获取不同格式化风格和中国环境的日期 DateFormat df1 = DateFormat.getDateInstance(DateFormat.SHORT, Locale.CHINA); DateFormat df2 = DateFormat.getDateInstance(DateFormat.FULL, Locale.CHINA); DateFormat df3 = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.CHINA); DateFormat df4 = DateFormat.getDateInstance(DateFormat.LONG, Locale.CHINA); // 获取不同格式化风格和中国环境的时间 DateFormat df5 = DateFormat.getTimeInstance(DateFormat.SHORT, Locale.ENGLISH); DateFormat df6 = DateFormat.getTimeInstance(DateFormat.FULL, Locale.CHINA); DateFormat df7 = DateFormat.getTimeInstance(DateFormat.MEDIUM, Locale.CHINA); DateFormat df8 = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA); // 将不同格式化风格的日期格式化为日期字符串 String date1 = df1.format(date); String date2 = df2.format(date); String date3 = df3.format(date); String date4 = df4.format(date); // 将不同格式化风格的时间格式化为时间字符串 String time1 = df5.format(date); String time2 = df6.format(date); String time3 = df7.format(date); String time4 = df8.format(date); // 输出日期 System.out.println("SHORT格式:" + date1 + " " + time1); System.out.println("FULL格式:" + date2 + " " + time2); System.out.println("MEDIUM格式:" + date3 + " " + time3); System.out.println("LONG格式:" + date4 + " " + time4); } } //输出 SHORT格式:22-11-20 2:24 PM FULL格式:2022年11月20日 星期日 下午02时24分59秒 CST MEDIUM格式:2022-11-20 14:24:59 LONG格式:2022年11月20日 下午02时24分59秒
3.5.2 SimpleDateFormat类进行格式化
DateFormat 类的子类,具有更加强大的日期格式化功能;DateFormat只能单独对日期或时间格式化,而 SimpleDateFormat可以选择任何用户定义的日期/时间格式的模式
SimpleDateFormat
类主要有如下 3 种构造方法。
SimpleDateFormat()
:用默认的格式和默认的语言环境构造 SimpleDateFormat。SimpleDateFormat(String pattern)
:用指定的格式和默认的语言环境构造 SimpleDateFormat。SimpleDateFormat(String pattern,Locale locale)
:用指定的格式和指定的语言环境构造 SimpleDateF ormat。
字母 | 描述 | 示例 |
G |
纪元标记 根据语言环境显示 | Locale.CHINA 语言环境下,如:公元;Locale.US语言环境下,如:AD |
y |
年份。yy表示2位年份,yyyy表示4位年份 | 使用yy 表示年份,如22;使用yyyy表示年份,例如2022 |
M |
月份 一般用 MM 表示月份,如果使用 MMM,则会根据语言环境显示不同语言的月份 | 使用 MM 表示的月份,如 11; 使用 MMM 表示月份,在 Locale.CHINA语言环境下,如“十月”;在 Locale.US环境下 如Nov |
d |
月份中的天数。一般用dd表示天数 | 使用dd表示天数 例如:20 |
h |
一天中的小时(1~12)。 一般使用hh表示小时 | 如 10 (注意 10 有可能是 10 点,也可能是 22 点) |
H |
一天中的小时 (0~23)。 | 如:22 |
m |
分钟数 。一般用mm表示分钟数 | 使用mm表示分钟数,如:59 |
s |
秒数。一般使用ss表示秒数 | 使用ss表示秒数,如:55 |
S |
毫秒数 。一般使用SSS 表示毫秒数 | 使用SSS表示毫秒数,如:234 |
E |
星期几 。 会根据语言环境的不同,显示不同语言的星期几 | Locale.CHINA 语言环境下,如:星期日;在 Locale.US 语言下,如:Sun |
D |
一年中的日子。 | 324 一年中的第324天 |
F |
一个月中第几周。 | 3 表示一个月中的第三周 |
w |
一年中第几周 。 | 48 表示一年中的第48周 |
W |
一个月中第几周 | 1 |
a |
A.M./P.M. 或上午/下午标记。根据语言环境不同,显示不同 | Locale.CHINA 语言环境下,如:下午 Locale.US语言环境下,如:PM |
k |
一天中的小时(1~24) | 15 一天中的第15小时 |
K |
一天中的小时(1~12) | 3 下午3小时 |
z |
时区 | 中国的东八区 如:+0800 |
import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class DateTest { public static void main(String[] args) { SimpleDateFormat sdf1 = new SimpleDateFormat("今天是:"+"yyyy年MM月dd日 HH点mm分ss秒 SSS 毫秒 E ", Locale.CHINA); System.out.println(sdf1.format(new Date())); SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss", Locale.CHINA); System.out.println(sdf2.format(new Date())); } } //输出 今天是:2022年11月20日 15点27分37秒 091 毫秒 星期日 2022-27-20 03:27:37
3.6 日期字符串互转
3.6.1 DateFormat
类实现
import java.text.DateFormat; import java.text.ParseException; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //将日期转为字符串 Date date1 = new Date(); DateFormat df1 = DateFormat.getDateInstance(DateFormat.MEDIUM); String strdate = df1.format(date1); System.out.println(strdate); //将字符串转日期 DateFormat df2 = DateFormat.getDateInstance(DateFormat.MEDIUM); Date date2 = df2.parse("1998-19-13"); System.out.println(date2); } } //输出 2022-11-20 Tue Jul 13 00:00:00 CST 1999
3.6.2 SimpleDateFormat
类实现
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; public class DateTest { public static void main(String[] args) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //日期转字符串 Date date1 = new Date(); String strdate1 = sdf.format(date1); System.out.println(strdate1); //字符串转日期 Date date2 = sdf.parse("1992-09-13 08:34:45"); System.out.println(date2.toString()); } } //输出 2022-11-20 03:32:21 Sun Sep 13 08:34:45 CST 1992
3.7 日期时间比较
boolean before(Date when)
、boolean after(Date when)
、boolean equals(Object obj)
、int compareTo(Date anotherDate)
public class DateTest { public static void main(String[] args) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); Date date1 = sdf.parse("1992-10-08 11:23:59"); Date date2 = sdf.parse("1992-09-13 08:34:45"); Date date3 = sdf.parse("1992-09-13 08:34:45"); System.out.println("date1:" + sdf.format(date1)); System.out.println("date2:" + sdf.format(date2)); System.out.println("date3:" + sdf.format(date3)); System.out.println("********************************************************************"); //compareTo方法比较 System.out.println("compareTo方法比较结果1:"+date1.compareTo(date2)); System.out.println("compareTo方法比较结果2:"+date2.compareTo(date3)); System.out.println("compareTo方法比较结果2:"+date2.compareTo(date1)); System.out.println("-------------------------------------------------------------------"); //equals方法比较 System.out.println("equals方法比较结果1:"+date1.equals(date2)); System.out.println("equals方法比较结果2:"+date2.equals(date3)); System.out.println("equals方法比较结果2:"+date2.equals(date1)); System.out.println("-------------------------------------------------------------------"); //before方法比较 System.out.println("before方法比较结果1:"+date1.before(date2)); System.out.println("before方法比较结果2:"+date2.before(date3)); System.out.println("before方法比较结果2:"+date2.before(date1)); System.out.println("-------------------------------------------------------------------"); //after方法比较 System.out.println("after方法比较结果1:"+date1.after(date2)); System.out.println("after方法比较结果2:"+date2.after(date3)); System.out.println("after方法比较结果2:"+date2.after(date1)); } } //输出 date1:1992-10-08 11:23:59 date2:1992-09-13 08:34:45 date3:1992-09-13 08:34:45 ******************************************************************** compareTo方法比较结果1:1 compareTo方法比较结果2:0 compareTo方法比较结果2:-1 ------------------------------------------------------------------- equals方法比较结果1:false equals方法比较结果2:true equals方法比较结果2:false ------------------------------------------------------------------- before方法比较结果1:false before方法比较结果2:false before方法比较结果2:true ------------------------------------------------------------------- after方法比较结果1:true after方法比较结果2:false after方法比较结果2:false
四、 第二代日期时间类
由于第一代日期时间类Date
存在几个严重问题:
- 不能转换时区。除了
toGMTString()
可以按GMT+0:00
输出外, - Date总是以当前计算机系统的默认时区为基础进行输出。
- 我们也很难对日期和时间进行加减,计算两个日期相差多少天,计算某个月第一个星期一的日期等。
由于以上诸多问题,第二代日期时间类Calendar
便来了。上一小节中我们发现Date 类下的很多方法都废弃了,当然了在Calendar
中有新的方法可以取代
4.1 继承关系
4.2 获取实例对象
因为 Calendar 类是一个抽象类,创建 Calendar 对象不能使用 new 关键字,但是它提供了一个 getInstance() 方法来获得 Calendar类的对象
//默认是当前日期 Calendar c = Calendar.getInstance();
4.3 TimeZone 时区
Calendar
和Date
相比,提供了时区类TimeZone
import java.text.ParseException; import java.util.TimeZone; public class DateTest { public static void main(String[] args) throws ParseException { // 当前时区 TimeZone tzDefault = TimeZone.getDefault(); System.out.println("当前时区:" + tzDefault.getID()); //获取纽约时区 TimeZone tzNY = TimeZone.getTimeZone("America/New_York"); System.out.println("纽约时区:" + tzNY.getID()); } } //输出 当前时区:Asia/Shanghai 纽约时区:America/New_York
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.TimeZone; public class DateTest { public static void main(String[] args) throws ParseException { // 当前时间: Calendar c = Calendar.getInstance(); // 设置为纽约时区: c.setTimeZone(TimeZone.getTimeZone("America/New_York")); // 格式化显示时间: SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf1.setTimeZone(TimeZone.getTimeZone("America/New_York")); System.out.println("纽约时间:"+sdf1.format(c.getTime())); // 设置为北京时区: c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 格式化显示时间: SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf2.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); System.out.println("北京时间:"+sdf2.format(c.getTime())); } } //输出 纽约时间:2022-11-22 18:49:08 北京时间:2022-11-23 07:49:08
4.4 常用时间点的获取
注:
- 与中国人习惯不同,一年中1月的值为0。
- 与中国人的习惯不同, 一周中的第一天为 周日. 一周的顺序依次为: 周日(1), 周一(2), 周二(3), 周三(4), 周四(5), 周五(6), 周六(7)
Calendar类中各个常量含义
常量 | 含义 |
Calendar.YEAR | 年份 |
Calendar.MONTH | 月份 |
Calendar.DAY_OF_MONTH | 日期 |
Calendar.DATE | 日期,和上面的字段意义完全相同 |
Calendar.HOUR | 12小时制的小时 |
Calendar.HOUR_OF_DAY | 24小时制的小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.MILLISECOND | 毫秒 |
Calendar.DAY_OF_WEEK | 星期几 周日(1), 周一(2), 周二(3), 周三(4), 周四(5), 周五(6), 周六(7) |
Calendar.WEEK_OF_YEAR | 一年中第几周 |
Calendar.WEEK_OF_MONTH | 一月中第几周 |
//获取时间点 import java.text.ParseException; import java.util.Calendar; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前时间 Calendar c = Calendar.getInstance(); //获取年份 System.out.println("获取年份:"+c.get(Calendar.YEAR)); //获取月份 System.out.println("获取月份:"+(c.get(Calendar.MONTH) + 1)); //获取一月中的天 System.out.println("获取一月中的天:"+c.get(Calendar.DAY_OF_MONTH)); System.out.println("获取一月中的天:"+c.get(Calendar.DATE)); //获取一天中的小时 24小时制 System.out.println("获取一天中的小时(24小时制):"+c.get(Calendar.HOUR_OF_DAY)); //获取一天中的小时 12小时制 System.out.println("获取一天中的小时(12小时制):"+c.get(Calendar.HOUR)); //获取分钟 System.out.println("获取分钟:"+c.get(Calendar.MINUTE)); //获取秒 System.out.println("获取秒:" + c.get(Calendar.SECOND)); //获取毫秒 System.out.println("获取毫秒:" + c.get(Calendar.MILLISECOND)); //获取一周中星期几 System.out.println("获取一周中星期几:"+ c.get(Calendar.DAY_OF_WEEK)); //获取一年中星期几 System.out.println("获取一年中第几周:"+c.get(Calendar.WEEK_OF_YEAR)); //获取一月中第几周 System.out.println("获取一月中第几周:"+c.get(Calendar.WEEK_OF_MONTH)); System.out.println("当前时间:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); } } //输出 获取年份:2022 获取月份:11 获取一月中的天:20 获取一月中的天:20 获取一天中的小时(24小时制):16 获取一天中的小时(12小时制):4 获取分钟:39 获取秒:37 获取毫秒:238 获取一周中星期几:1 获取一年中第几周:48 获取一月中第几周:4 当前时间:2022-11-20 16:39:37:238
4.5 常用时间点设置
注:在使用set方法之前,必须先clear一下,否则很多信息会继承自系统当前时间
4.5.1 分别对年、月、日、时、分、秒每个值进行设置
//设置时间点 import java.text.ParseException; import java.util.Calendar; public class DateTest { public static void main(String[] args) throws ParseException { //初始化Calendar类 Calendar c = Calendar.getInstance(); //将时间设置成1992-09-13 23:35:59:123 c.clear(); //先clear一下,否则很多信息会继承自系统当前时间 c.set(Calendar.YEAR,1992); c.set(Calendar.MONTH,9); c.set(Calendar.DAY_OF_MONTH,13); c.set(Calendar.HOUR_OF_DAY,23); c.set(Calendar.MINUTE,35); c.set(Calendar.SECOND,59); c.set(Calendar.MILLISECOND,123); System.out.println("设置好的时间为:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); } } //输出 设置好的时间为:1992-10-13 23:35:59:123
4.5.2 一起设置年月日、年月日时分、年月日时分秒
set(int year ,int month,int date)
set(int year ,int month,int date,int hour,int minute)
set(int year ,int month,int date,int hour,int minute,int second)
import java.text.ParseException; import java.util.Calendar; public class DateTest { public static void main(String[] args) throws ParseException { //初始化Calendar类 Calendar c = Calendar.getInstance(); System.out.println("当前时间为:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); //将时间设置成1992-09-13 23:35:59 c.clear(); c.set(1992,9,13,23,35,59); System.out.println("设置好的时间为:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); } } //输出 当前时间为:2022-11-20 17:29:27:98 设置好的时间为:1992-10-13 23:35:59:0
4.6 时间计算
Calendar.add()
对时间进行加减运算
import java.text.ParseException; import java.util.Calendar; public class DateTest { public static void main(String[] args) throws ParseException { //初始化Calendar类 Calendar c = Calendar.getInstance(); System.out.println("当前时间是:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); c.add(Calendar.DATE,5); System.out.println("当前日期+5天后:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); c.add(Calendar.DATE,-15); System.out.println("当前日期-15天后:"+(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"+ c.get(Calendar.DAY_OF_MONTH) + " "+ c.get(Calendar.HOUR_OF_DAY) + ':' + c.get(Calendar.MINUTE) + ':' + c.get(Calendar.SECOND) + ':' + c.get(Calendar.MILLISECOND) )); } } //输出 当前时间是:2022-11-20 17:23:57:406 当前日期+5天后:2022-11-25 17:23:57:406 当前日期-15天后:2022-11-10 17:23:57:406
4.7 获取时间戳( 获取当前毫秒数)
import java.text.ParseException; import java.util.Calendar; public class DateTest { public static void main(String[] args) throws ParseException { //初始化Calendar类 Calendar c = Calendar.getInstance(); //获取毫秒数 System.out.println(c.getTimeInMillis()); } } //输出 1668936885795
4.8 时间日期比较
Calender.before()
、Calender.after()
、Calender.compareTo()
和 Calender.equals()
import java.text.ParseException; import java.util.Calendar; public class DateTest { public static void main(String[] args) throws ParseException { //初始化Calendar类 Calendar c1 = Calendar.getInstance(); Calendar c2 = Calendar.getInstance(); Calendar c3 = Calendar.getInstance(); c1.clear(); c2.clear(); c3.clear(); //c1设置为1992-09-13 23:35:59 c1.set(1992,8,13 ,23,35,59); //c2设置为1992-10-08 21:21:55 c2.set(1992,9,8 ,21,21,55); //c3设置为1992-10-08 21:21:55 c3.set(1992,9,8 ,21,21,55); System.out.println("c1日期值:"+(c1.get(Calendar.YEAR) + "-" + (c1.get(Calendar.MONTH) + 1) + "-"+ c1.get(Calendar.DAY_OF_MONTH) + " "+ c1.get(Calendar.HOUR_OF_DAY) + ':' + c1.get(Calendar.MINUTE) + ':' + c1.get(Calendar.SECOND) + ':' + c1.get(Calendar.MILLISECOND) )); System.out.println("c2日期值:"+(c2.get(Calendar.YEAR) + "-" + (c2.get(Calendar.MONTH) + 1) + "-"+ c2.get(Calendar.DAY_OF_MONTH) + " "+ c2.get(Calendar.HOUR_OF_DAY) + ':' + c2.get(Calendar.MINUTE) + ':' + c2.get(Calendar.SECOND) + ':' + c2.get(Calendar.MILLISECOND) )); System.out.println("c3日期值:"+(c3.get(Calendar.YEAR) + "-" + (c3.get(Calendar.MONTH) + 1) + "-"+ c3.get(Calendar.DAY_OF_MONTH) + " "+ c3.get(Calendar.HOUR_OF_DAY) + ':' + c3.get(Calendar.MINUTE) + ':' + c3.get(Calendar.SECOND) + ':' + c3.get(Calendar.MILLISECOND) )); System.out.println("******************************************************************"); //Calender.compareTo() 方法比较 System.out.println("compareTo方法比较结果1:"+c1.compareTo(c2)); System.out.println("compareTo方法比较结果2:"+c2.compareTo(c3)); System.out.println("compareTo方法比较结果3:"+c2.compareTo(c1)); System.out.println("------------------------------------------------------------------"); //Calender.equals() 方法比较 System.out.println("equals方法比较结果1:"+c1.equals(c2)); System.out.println("equals方法比较结果2:"+c2.equals(c3)); System.out.println("equals方法比较结果3:"+c2.equals(c1)); System.out.println("------------------------------------------------------------------"); //Calender.before() 方法比较 System.out.println("before方法比较结果1:"+c1.before(c2)); System.out.println("before方法比较结果2:"+c2.before(c3)); System.out.println("before方法比较结果3:"+c2.before(c1)); System.out.println("------------------------------------------------------------------"); //Calender.after() 方法比较 System.out.println("after方法比较结果1:"+c1.after(c2)); System.out.println("after方法比较结果2:"+c2.after(c3)); System.out.println("after方法比较结果3:"+c2.after(c1)); } } //输出 c1日期值:1992-9-13 23:35:59:0 c2日期值:1992-10-8 21:21:55:0 c3日期值:1992-10-8 21:21:55:0 ****************************************************************** compareTo方法比较结果1:-1 compareTo方法比较结果2:0 compareTo方法比较结果3:1 ------------------------------------------------------------------ equals方法比较结果1:false equals方法比较结果2:true equals方法比较结果3:false ------------------------------------------------------------------ before方法比较结果1:true before方法比较结果2:false before方法比较结果3:false ------------------------------------------------------------------ after方法比较结果1:false after方法比较结果2:false after方法比较结果3:true
五、 第三代日期时间
由于第二代日期类也存在着以下问题
- 可变性:像日期这样的类应该时不可变的
- 偏移性:年份是从1900开始的,而月份从0开始
- 格式化:格式化只对Date类有用,Calendar则不行,要格式化只能将Calendar 转换为Date
- 线程安全性:都不是线程安全的
- 时区问题:时区处理麻烦
- 闰秒问题:不能处理闰秒(每隔2天,多出1s)
所以在JDK8中出现了第三代日期时间类,第三代日期时间API具有以下特点 - 线程安全性: 是线程安全的。不仅没有 setter 方法,而且任何对实例的变更都会返回一个新的实例,保证原来的实例不变
- 方便性: − 提供了大量的方法,简化了日期时间的处理
- 时区问题:引入了 域 ( domain ) 这个概念对时区进行处理
5.1 继承关系
java.time.LocalDate
用于表示 “本地日期”,无 “时间”。LocalDate
不承载时区信息。java.time.LocalTime
用于表示 “本地时间”,无 “日期”。LocalTime
不承载时区信息java.time.LocalDateTime
用于表示 “本地日期与时间”。LocalDateTime
不承载时区信息。java.time.ZonedDateTime
用于表示承载时区信息的时间java.time.ZoneId
是引入的新的时区类LocalDate
实例与LocalTime
实例能够共同构建LocalDateTime
实例;- 由
LocalDateTime
实例能够获取LocalDate
实例与LocalTime
实例。 - 可以简单地把
ZonedDateTime
理解成LocalDateTime
加ZoneId
5.2 获取实例对象
- 通过静态方法 :
now()
(获取的时间是系统当前的时间) - 通过静态方法:
of()
(方法参数可以指定时间)
import java.text.ParseException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前日期时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前日期时间:"+now); //获取当前日期 LocalDate nowdate = LocalDate.now(); System.out.println("当前日期:" + nowdate); //获取当前时间 LocalTime time = LocalTime.now(); System.out.println("当前时间:" + time); System.out.println("********************************************"); //设置日期时间 LocalDateTime localdatetime = LocalDateTime.of(1992, 9, 13, 23, 56, 59, 333); System.out.println("日期时间设置为:" + localdatetime); //设置日期 LocalDate localdate = LocalDate.of(1992, 9, 13); System.out.println("日期设置为:" + localdate); //设置时间 LocalTime localtime = LocalTime.of(23, 56, 59, 333); System.out.println("时间设置为:" + localtime); } } //输出 当前日期时间:2022-11-22T06:34:13.280 当前日期:2022-11-22 当前时间:06:34:13.280 ******************************************** 日期时间设置为:1992-09-13T23:56:59.000000333 日期设置为:1992-09-13 时间设置为:23:56:59.000000333
5.3 获取年月日时分秒
getYear()
:获取年getMonth()
:获得月份(返回一个 Month 枚举值)getMonthValue()
:获得月份(1-12)getDayOfMonth()
:获得月份天数(1-31)getDayOfYear()
:获得年份天数(1-366)getHour()
:获取小时getMinute()
:获取分钟getSecond()
:获取秒值now.getNano()
:获取纳秒getDayOfWeek()
:获得星期几(返回一个 DayOfWeek枚举值)
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前日期时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前日期时间:"+now); //获取年份 int year = now.getYear(); System.out.println("当前年份:" + year); //获取月份 Month month = now.getMonth(); int monthValue = now.getMonthValue(); System.out.println("当前月份枚举值获取:" + month); System.out.println("当前月份值:" + monthValue); //获取日 int dayOfMonth = now.getDayOfMonth(); int dayOfYear = now.getDayOfYear(); DayOfWeek dayOfWeek = now.getDayOfWeek(); System.out.println("一个月中的天:" + dayOfMonth); System.out.println("一年中的天:" + dayOfYear); System.out.println("一周中的天:" + dayOfWeek); //获取小时 int hour = now.getHour(); System.out.println("小时:" + hour); //获取分钟 int minute = now.getMinute(); System.out.println("分钟:" + minute); //获取秒 int second = now.getSecond(); System.out.println("秒:" + second); //获取纳秒 int nano = now.getNano(); System.out.println("纳秒:" + nano); } } //输出 当前日期时间:2022-11-22T06:53:29.172 当前年份:2022 当前月份枚举值获取:NOVEMBER 当前月份值:11 一个月中的天:22 一年中的天:326 一周中的天:TUESDAY 小时:6 分钟:53 秒:29 纳秒:172000000
5.4 日期时间比较
isAfter()
:判断一个日期是否在指定日期之后isBefore()
:判断一个日期是否在指定日期之前isEqual()
:判断两个日期是否相同isLeapYear()
:判断是否是闰年(注意是LocalDate类 和 LocalDateTime类特有的方法)compareTo()
: 两个日期比较返回0,1,-1
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { //dateTime1时间初始化为1992-09-13 23:35:59 LocalDateTime dateTime1 = LocalDateTime.of(1992, 9, 13, 23, 35, 59); //dateTime2时间初始化为1992-10-08 23:35:50 LocalDateTime dateTime2 = LocalDateTime.of(1992, 10, 8, 23, 35, 50); //dateTime3时间初始化为1992-10-08 23:35:50 LocalDateTime dateTime3 = LocalDateTime.of(1992, 10, 8, 23, 35, 50); System.out.println("******************输出三个时间*****************************"); System.out.println("dateTime1时间1:"+dateTime1); System.out.println("dateTime2时间2:"+dateTime2); System.out.println("dateTime3时间3:"+dateTime3); //isAfter()方法比较 System.out.println("------------isAfter()方法比较-------------------"); System.out.println("dateTime1在dateTime2之后?"+dateTime1.isAfter(dateTime2)); System.out.println("dateTime2在dateTime3之后?"+dateTime2.isAfter(dateTime3)); System.out.println("dateTime2在dateTime1之后?"+dateTime2.isAfter(dateTime1)); //isBefore()方法比较 System.out.println("------------isBefore()方法比较-------------------"); System.out.println("dateTime1在dateTime2之前?"+dateTime1.isBefore(dateTime2)); System.out.println("dateTime2在dateTime3之前?"+dateTime2.isBefore(dateTime3)); System.out.println("dateTime2在dateTime1之前?"+dateTime2.isBefore(dateTime1)); //isEqual()方法比较 System.out.println("------------isEqual()方法比较-------------------"); System.out.println("dateTime1与dateTime2相等?"+dateTime1.isEqual(dateTime2)); System.out.println("dateTime2与dateTime3相等?"+dateTime2.isEqual(dateTime3)); System.out.println("dateTime2与dateTime1相等?"+dateTime2.isEqual(dateTime1)); //compareTo()方法比较 System.out.println("------------compareTo()方法比较-------------------"); System.out.println("dateTime1与dateTime2比较?"+dateTime1.compareTo(dateTime2)); System.out.println("dateTime2与dateTime3比较?"+dateTime2.compareTo(dateTime3)); System.out.println("dateTime2与dateTime1比较?"+dateTime2.compareTo(dateTime1)); } } //输出 ******************输出三个时间***************************** dateTime1时间1:1992-09-13T23:35:59 dateTime2时间2:1992-10-08T23:35:50 dateTime3时间3:1992-10-08T23:35:50 ------------isAfter()方法比较------------------- dateTime1在dateTime2之后?false dateTime2在dateTime3之后?false dateTime2在dateTime1之后?true ------------isBefore()方法比较------------------- dateTime1在dateTime2之前?true dateTime2在dateTime3之前?false dateTime2在dateTime1之前?false ------------isEqual()方法比较------------------- dateTime1与dateTime2相等?false dateTime2与dateTime3相等?true dateTime2与dateTime1相等?false ------------compareTo()方法比较------------------- dateTime1与dateTime2比较?-1 dateTime2与dateTime3比较?0 dateTime2与dateTime1比较?1
5.5 日期时间计算
5.5.1 增加年月日时分秒
plusYears(int offset)
:增加指定年份plusMonths(int offset)
:增加指定月份plusDates(int offset)
:增加指定日plusHours(int offset)
:增加指定时plusMinuets(int offset)
:增加指定分plusSeconds(int offset)
:增加指定秒plusNanos(int offset)
:增加指定纳秒plusWeeks(int offset)
:增加指定周
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前时间:" + now); //两年后 LocalDateTime localDateTime1 = now.plusYears(2); System.out.println("两年以后:" + localDateTime1); //三个月后 LocalDateTime localDateTime2 = now.plusMonths(3); System.out.println("三个月后:" + localDateTime2); //五天后 LocalDateTime localDateTime3 = now.plusDays(5); System.out.println("五天后:" + localDateTime3); //4小时后 LocalDateTime localDateTime4 = now.plusHours(4); System.out.println("4小时后:"+localDateTime4); //30分钟后 LocalDateTime localDateTime5 = now.plusMinutes(30); System.out.println("30分钟后:" + localDateTime5); //80秒后 LocalDateTime localDateTime6 = now.plusSeconds(80); System.out.println("80秒后:" + localDateTime6); //3周后 LocalDateTime localDateTime7 = now.plusWeeks(3); System.out.println("3周后:"+localDateTime7); } } //输出 当前时间:2022-11-22T07:28:55.129 两年以后:2024-11-22T07:28:55.129 三个月后:2023-02-22T07:28:55.129 五天后:2022-11-27T07:28:55.129 4小时后:2022-11-22T11:28:55.129 30分钟后:2022-11-22T07:58:55.129 80秒后:2022-11-22T07:30:15.129 3周后:2022-12-13T07:28:55.129
5.5.1 减少年月日时分秒
minusYears(int offset)
:减少指定年minusMonths(int offset)
:减少指定月minusDates(int offset)
:减少指定日minusHours(int offset)
:减少指定时minusMinuets(int offset)
:减少指定分minusSeconds(int offset)
:减少指定秒minusNanos(int offset)
:减少指定纳秒minusWeeks(int offset)
:减少指定周
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前时间:" + now); //10年以前 LocalDateTime localDateTime1 = now.minusYears(10); System.out.println("10年以前:" + localDateTime1); //三个月以前 LocalDateTime localDateTime2 = now.minusMonths(3); System.out.println("三个月以前:" + localDateTime2); //五天前 LocalDateTime localDateTime3 = now.minusDays(5); System.out.println("五天前:" + localDateTime3); //4小时前 LocalDateTime localDateTime4 = now.minusHours(4); System.out.println("4小时前:"+localDateTime4); //30分钟前 LocalDateTime localDateTime5 = now.minusMinutes(30); System.out.println("30分钟前:" + localDateTime5); //80秒前 LocalDateTime localDateTime6 = now.plusSeconds(80); System.out.println("80秒前:" + localDateTime6); //3周后 LocalDateTime localDateTime7 = now.minusWeeks(3); System.out.println("3周前:"+localDateTime7); } } //输出 当前时间:2022-11-22T07:33:14.203 10年以前:2012-11-22T07:33:14.203 三个月以前:2022-08-22T07:33:14.203 五天前:2022-11-17T07:33:14.203 4小时前:2022-11-22T03:33:14.203 30分钟前:2022-11-22T07:03:14.203 80秒前:2022-11-22T07:34:34.203 3周前:2022-11-01T07:33:14.203
5.5.3 时间调节器
可以更加灵活地处理一些复杂的日期,例如:实现日期调整到下个周日、下个工作日,或者是下个月的第一天等等,也可以增加自己定制功能
import java.text.ParseException; import java.time.*; import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAdjusters; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前时间:" + now); System.out.println("------------------下个月第一天--------------------------"); //下个月第一天 TemporalAdjuster temporalAdjuster = TemporalAdjusters.firstDayOfNextMonth(); LocalDateTime localDateTime1 = now.with(temporalAdjuster); System.out.println(localDateTime1); System.out.println("------------------下周周五--------------------------"); //下周周五 TemporalAdjuster next = TemporalAdjusters.next(DayOfWeek.FRIDAY); LocalDateTime localDateTime12 = now.with(next); System.out.println(localDateTime12); } } //输出 当前时间:2022-11-22T07:56:56.906 ------------------下个月第一天-------------------------- 2022-12-01T07:56:56.906 ------------------下周周五-------------------------- 2022-11-25T07:56:56.906
5.5.4 with方法使用
withYear(int year)
:指定年withMonth(int month)
:指定月withDayOfMonth(int dayOfMonth)
:指定日withDayOfYear(int dayOfYear)
:指定日with(TemporalAdjuster adjuster)
:指定特殊时间
import java.text.ParseException; import java.time.*; import java.time.temporal.TemporalAdjusters; public class DateTest { public static void main(String[] args) throws ParseException { //获取当前时间 LocalDateTime now = LocalDateTime.now(); System.out.println("当前时间:" + now); //指定1992年 LocalDateTime localDateTime1 = now.withYear(1992); System.out.println("指定1992年:" + localDateTime1); //指定11月 LocalDateTime localDateTime2 = now.withMonth(11); System.out.println("指定11月份:" + localDateTime2); //指定11日 LocalDateTime localDateTime3 = now.withDayOfMonth(11); System.out.println("指定11日:" + localDateTime3); //指定特殊日期 LocalDateTime localDateTime4 = now.with(TemporalAdjusters.firstDayOfMonth()); System.out.println("指定当月第一天:"+localDateTime4); } } //输出 当前时间:2022-11-22T07:50:02.386 指定1992年:1992-11-22T07:50:02.386 指定11月份:2022-11-22T07:50:02.386 指定11日:2022-11-11T07:50:02.386 指定当月第一天:2022-11-01T07:50:02.386
5.5.5 计算两个“日期”间隔
Period 用于计算两个日期间隔, between() 方法只能接收 LocalDate 类型的参数
- 静态方法
between()
:计算两个日期之间的间隔 getYears()
:获取年份getMonths()
:获取月份getDays()
:获取天数
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { //设置生日 LocalDate birth = LocalDate.of(2004, 10, 8); //获取当前时间 LocalDate now = LocalDate.now(); //计算两个时间间隔 Period between = Period.between(birth, now); int years = between.getYears(); int months = between.getMonths(); int days = between.getDays(); System.out.println("从出生到现在" + years + "年" + months + "月" + days + "天了"); } } //输出 从出生到现在18年1月15天了
5.5.6 计算两个“时间”间隔
Duration 表示一个时间段,Duration 包含两部分:seconds 表示秒,nanos 表示纳秒,它们的组合表达了时间长度。
因为 Duration 表示时间段,所以 Duration 类中不包含 now() 静态方法。注意,Duration 不包含毫秒这个属性。
Duration只能处理两个LocalTime, LocalDateTime, ZonedDateTime; 如果传入的是LocalDate,将会抛出异常
- 静态方法 between():计算两个时间的间隔,默认是秒
toDays()
:将时间转换为以天为单位的toHours()
:将时间转换为以时为单位的toMinutes()
:将时间转换为以分钟为单位的toMillis()
:将时间转换为以毫秒为单位的toNanos()
:将时间转换为以纳秒为单位的
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { LocalTime beginTime = LocalTime.of(0, 1, 2); LocalTime now = LocalTime.now(); Duration between = Duration.between(beginTime, now); long days = between.toDays(); long hours = between.toHours(); long minutes = between.toMinutes(); long millis = between.toMillis(); long nanos = between.toNanos(); System.out.println("从0点1分1秒到现在一共" + days + "天" ); System.out.println("从0点1分1秒到现在一共" + hours + "小时" ); System.out.println("从0点1分1秒到现在一共" + minutes + "分" ); System.out.println("从0点1分1秒到现在一共" + millis/1000 + "秒" ); System.out.println("从0点1分1秒到现在一共" + nanos + "纳秒" ); } } //输出 从0点1分1秒到现在一共0天 从0点1分1秒到现在一共6小时 从0点1分1秒到现在一共381分 从0点1分1秒到现在一共22888秒 从0点1分1秒到现在一共22888801000000纳秒
5.6 获取时间戳
java.time.Instant
可以获取时间线上的一个瞬时点,精确到纳秒级
now()
: 获取实例化对象。默认获取出来的是默认时区,和我们(东八区)相差8小时atOffset()
:设置偏移量atZone()
:获取系统默认时区时间,参数是一个时区的编号(可以通过时区编号类获取ZonedDateTime类的对象)getEpochSecond()
:获取从1970-01-01 00:00:00到当前时间的秒数toEpochMilli()
:获取从1970-01-01 00:00:00到当前时间的毫秒数getNano()
:获取从1970-01-01 00:00:00到当前时间的纳秒数ofEpochSecond()
:给计算机元年增加秒数ofEpochMilli()
:给计算机元年增加毫秒数
import java.text.ParseException; import java.time.*; public class DateTest { public static void main(String[] args) throws ParseException { //从1970-01-01 00:00:00 截止到当前时间的毫秒值(时区不是中国的) Instant now = Instant.now(); System.out.println("1970-01-01 00:00:00到默认时区当前日期:" + now); //获取当前时区的,我们可以添加偏移量,返回偏移过后的日期 OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8)); System.out.println("1970-01-01 00:00:00到当前时区日期:"+offsetDateTime); //1970-01-01 00:00:00 截止到当前时间间隔的毫秒值 long ll = now.toEpochMilli(); System.out.println("1970-01-01 00:00:00到当前时间间隔的毫秒值:" + ll); //1970-01-01 00:00:00 截止到当前时间间隔的秒数 long epochSecond = now.getEpochSecond(); System.out.println("1970-01-01 00:00:00 到当前时间间隔的秒数:" + epochSecond); //ofEpochMilli() 给计算机元年增加毫秒数 Instant instant = Instant.ofEpochMilli(1000 * 60 * 60 * 24); System.out.println("计算机元年增加1天(86400000毫秒):"+instant); //ofEpochSecond() 方法 给计算机元年增加秒数 Instant instant1 = Instant.ofEpochSecond(60 * 60 * 24); System.out.println("计算机元年增加1天(86400秒):"+instant1); } } //输出 1970-01-01 00:00:00到默认时区当前日期:2022-11-22T23:12:40.988Z 1970-01-01 00:00:00到当前时区日期:2022-11-23T07:12:40.988+08:00 1970-01-01 00:00:00到当前时间间隔的毫秒值:1669158760988 1970-01-01 00:00:00 到当前时间间隔的秒数:1669158760 计算机元年增加1天(86400000毫秒):1970-01-02T00:00:00Z 计算机元年增加1天(86400秒):1970-01-02T00:00:00Z
5.7 带时区的日期时间
ZonedDateTime
是带有带有特定时区的日期时间类,类的方法和用法和LocalDateTime
基本一样。可以简单地把ZonedDateTime
理解成LocalDateTime
加ZoneId
ZoneId
是java.time
引入的新的时区类
5.7.1 获取不同时区当前时间
import java.text.ParseException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Set; public class DateTest { public static void main(String[] args) throws ParseException { //获取世界各个地方的时区编号 Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); for (String availableZoneId : availableZoneIds) { System.out.println(availableZoneId); } System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++"); //获取系统默认时区编号 ZoneId zoneId = ZoneId.systemDefault(); System.out.println("系统默认时区编号:" + zoneId); System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++"); ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区 System.out.println("获取默认时区时间:"+zbj); System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++"); ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间 System.out.println("获取指定时区时间:"+zny); } } **注:时区不同,但表示的时间都是同一时刻** //输出 Asia/Aden America/Cuiaba Etc/GMT+9 ....... 由于时区比较多,这里就不一一例举了 Europe/Athens US/Pacific Europe/Monaco +++++++++++++++++++++++++++++++++++++++++++++++ 系统默认时区编号:Asia/Shanghai +++++++++++++++++++++++++++++++++++++++++++++++ 获取默认时区时间:2022-11-23T21:24:03.304+08:00[Asia/Shanghai] +++++++++++++++++++++++++++++++++++++++++++++++ 获取指定时区时间:2022-11-23T08:24:03.305-05:00[America/New_York]
5.7.2 设置一个时区时间
import java.text.ParseException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; public class DateTest { public static void main(String[] args) throws ParseException { LocalDateTime localDateTime = LocalDateTime.of(1992, 9, 13, 23, 35, 59); ZonedDateTime zonedDateTime1 = localDateTime.atZone(ZoneId.systemDefault()); ZonedDateTime zonedDateTime2 = localDateTime.atZone(ZoneId.of("America/New_York")); System.out.println("默认时区时间:"+zonedDateTime1); System.out.println("America/New_York时区时间:"+zonedDateTime2); } } //输出 默认时区时间:1992-09-13T23:35:59+08:00[Asia/Shanghai] America/New_York时区时间:1992-09-13T23:35:59-04:00[America/New_York]
注:它的日期和时间与LocalDateTime
相同,但附加的时区不同,因此是两个不同的时刻
5.7.3 时区转换
将关联时区转换到另一个时区,转换后日期和时间都会相应调整
import java.text.ParseException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; public class DateTest { public static void main(String[] args) throws ParseException { //以中国时区获取当前时间 ZonedDateTime now1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); //将中国时间转换为美国纽约时间 ZonedDateTime zonedDateTime = now1.withZoneSameInstant(ZoneId.of("America/New_York")); System.out.println("中国时区当前时间:" + now1); System.out.println("将中国时间转换为美国纽约时间:"+zonedDateTime); } } //输出 中国时区当前时间:2022-11-23T21:43:31.065+08:00[Asia/Shanghai] 将中国时间转换为美国纽约时间:2022-11-23T08:43:31.065-05:00[America/New_York]
5.7.4 日期时间格式化
第一代
Date
对象用SimpleDateFormat
进行格式化显示,而DateTimeFormatter
用于解析第三代日期字符串和格式化日期输出
import java.text.ParseException; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class DateTest { public static void main(String[] args) throws ParseException { //指定日期格式 DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); LocalDateTime now = LocalDateTime.now(); String format = dateTimeFormatter.format(now); System.out.println("未格式化日期显示:" + now); System.out.println("当前日期格式化后:" + format); } } //输出 未格式化日期显示:2022-11-23T21:51:00.197 当前日期格式化后:2022-11-23 09:51:00
5.7.5 日期字符串转日期
import java.text.ParseException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; public class DateTest { public static void main(String[] args) throws ParseException { //指定日期格式 DateTimeFormatter dateTimeFormatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd"); DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDate localDate = LocalDate.parse("1992-09-13", dateTimeFormatter1); System.out.println("字符串转LocalDate:" + localDate); LocalDateTime localDateTime = LocalDateTime.parse("1992-09-13 23:35:59", dateTimeFormatter2); System.out.println("字符串转LocalDateTime:" + localDateTime); } } //输出 字符串转LocalDate:1992-09-13 字符串转LocalDateTime:1992-09-13T23:35:59
六、三代日期之间相互转换
由于历史遗留问题,有时候我们不得不与遗留代码打交道。这时就涉及了三代日期时间之间的相互转换
注: 在转换中我们需要特别注意,java8之前Date
是包含日期和时间的,而LocalDate
只包含日期,LocalTime
只包含时间,所以与Date
与之互转过程中,会丢失日期或者时间。最好是转换成LocalDateTime
,这样就不存在日期或时间丢失问题
6.1 Calendar 与 Date 互转
6.1.1 Date 转换为Calendar
import java.text.ParseException; import java.util.Calendar; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //Date 转换为Calendar Date date = new Date(); System.out.println("当前Date时间为:"+date); Calendar cal= Calendar.getInstance(); cal.setTime(date); System.out.println("Date转换为Calendar后:"+(cal.get(Calendar.YEAR) + "-" + (cal.get(Calendar.MONTH) + 1) + "-"+ cal.get(Calendar.DAY_OF_MONTH) + " "+ cal.get(Calendar.HOUR_OF_DAY) + ':' + cal.get(Calendar.MINUTE) + ':' + cal.get(Calendar.SECOND))); } } //输出 当前Date时间为:Fri Nov 25 07:17:06 CST 2022 Date转换为Calendar后:2022-11-25 7:17:6
6.1.2 Calendar 转换为Date
import java.text.ParseException; import java.util.Calendar; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //Calendar 转换为Date Calendar cal=Calendar.getInstance(); System.out.println("Calendar获取的当前时间是:"+(cal.get(Calendar.YEAR) + "-" + (cal.get(Calendar.MONTH) + 1) + "-"+ cal.get(Calendar.DAY_OF_MONTH) + " "+ cal.get(Calendar.HOUR_OF_DAY) + ':' + cal.get(Calendar.MINUTE) + ':' + cal.get(Calendar.SECOND))); Date date=cal.getTime(); System.out.println("当前Date时间为:"+date); } } //输出 Calendar获取的当前时间是:2022-11-25 7:17:29 当前Date时间为:Fri Nov 25 07:17:29 CST 2022
6.2 Date和Instant互换
6.2.1 Date转为Instant
import java.text.ParseException; import java.time.Instant; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //Date转为Instant Date date = new Date(); System.out.println("当前Date获取的时间是:"+date); Instant instant = date.toInstant(); System.out.println("转为Instant后的时间是:"+instant); } } //输出 当前Date获取的时间是:Fri Nov 25 07:27:13 CST 2022 转为Instant后的时间是:2022-11-24T23:27:13.860Z
6.2.2 Instant转为Date
import java.text.ParseException; import java.time.Instant; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //Instant转为Date Instant instant = Instant.now(); System.out.println("Instant获取的时间是:"+instant); Date date = Date.from(instant); System.out.println("转换为Date后:"+date); } } //输出 Instant获取的时间是:2022-11-24T23:29:18.857Z 转换为Date后:Fri Nov 25 07:29:18 CST 2022
6.3 Date与LocalDateTime互转
6.3.1 Date转LocalDateTime
import java.text.ParseException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //Date 转LocalDateTime Date date = new Date(); System.out.println("Date获取的当前时间" + date); LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); System.out.println("第一种方式转为LocalDateTime后: " + localDateTime); LocalDateTime localDateTime1 = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); System.out.println("第二种方式转为localDateTime2: " + localDateTime1); } } //输出 Date获取的当前时间Fri Nov 25 07:35:04 CST 2022 第一种方式转为LocalDateTime后: 2022-11-25T07:35:04.379 第二种方式转为localDateTime2: 2022-11-25T07:35:04.379
6.3.2 LocalDateTime 转Date
import java.text.ParseException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; public class DateTest { public static void main(String[] args) throws ParseException { //LocalDateTime 转 Date LocalDateTime localDateTime = LocalDateTime.now(); System.out.println("localDateTime获取的当前时间: " + localDateTime); Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); System.out.println("转为Date后的时间: " + date); } } //输出 localDateTime获取的当前时间: 2022-11-25T07:36:58.216 转为Date后的时间: Fri Nov 25 07:36:58 CST 2022
七、阿里巴巴关于日期时间编程规约
阿里巴巴java开发手册,第一章1.5小结对日期时间做了相关规约。这里的规约也值得我们每个开发者遵守
①【强制】在日期格式化时,传入pattern中表示年份统一使用小写y
日期格式化时,yyyy表示当天所在的年,大写的YYYY表示当天所在的周所属的年份。一周以周日开始,周六结束,只要本周跨年,返回的YYYY就是下一年。
//正例 new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
②【强制】在日期格式中,分清楚大写的M和小写的m、大写的H和小写的h分别代表的含义
- 表示月份,用大写M
- 表示分钟,用小写m
- 表示24小时制,用大写H
- 表示12小时制,用小写h
- ③ 【强制】获取当前毫秒数,是System.currentTimeMillis();而不是new Date().getTime()。
④ 【强制】不允许在程序的任何地方使用:
1)java.sql.Date;
2) java.sql.Time;
3) java.sql.Timestamp;
说明:第1个不记录时间,getHours()
抛出异常;
第2个不记录日期,getYear()
抛出异常
第3个在构造方法supper((time/1000)*1000)
时,在Timestamp属性henanos中分别存贮秒和纳秒信息
⑤【强制】不要在程序中写死一年未365天,避免在闰年时出现日期转换错误或程序逻辑错误。
//正列
//获取几年的天数 int days =localDate.now().lengthOfYear(); //获取某年的天数 Localdate.of(2022,1,2).lengthOfYear();
//反列
//第一种情况:在闰年(366天)时,出现数组越界异常 int[] dayArray = new int[365] //第二种情况:一年有效期会员制,今年1月26日注册, //一年未365天,返回的却是1月25日 Calendar calendar = Calendar.getInstance(); calendar.set(2022,0,26); calendar.add(Calendar.DATE,365);
⑥ 【推荐】避免出现闰年2月问题。闰年2月有29天,一年后的那一天不可能是2月29日。
⑦ 【推荐】使用枚举值指代月份(month)取值在0~11之间
说明:参考JDK原生注释:Month value is 0-based.e.g.0 for January
//正例
用Calendar.JANUARY、Calendar.FEBRUARY、Calendar.MARCH等指代月份,进行传参或比较
本期内容就到这了,我们下期再见(●’◡’●)