前言
日常开发中,日期和时间是我们经常遇到并且需要处理的。由于日期时间的复杂性,随着Java的发展,也诞生了三代日期处理API。接下来就让我们一起来探究下Java日期时间的前世今生。
一、基本概念
1.1 日期与时间
日期是指某一天,它不是连续变化的;而时间分为带日期的时间和不带日期的时间
带日期的时间能唯一确定某个时刻,不带日期的时间是无法确定一个唯一时刻的
日期如下
时间如下
- 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());
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
日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间
创建 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秒
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 日期字符串互转
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
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("********************************************************************");
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("-------------------------------------------------------------------");
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("-------------------------------------------------------------------");
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("-------------------------------------------------------------------");
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));
System.out.println("获取一天中的小时(24小时制):"+c.get(Calendar.HOUR_OF_DAY));
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 c = Calendar.getInstance();
c.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 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.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 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 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 c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
Calendar c3 = Calendar.getInstance();
c1.clear();
c2.clear();
c3.clear();
c1.set(1992,8,13 ,23,35,59);
c2.set(1992,9,8 ,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("******************************************************************");
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("------------------------------------------------------------------");
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("------------------------------------------------------------------");
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("------------------------------------------------------------------");
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 {
LocalDateTime dateTime1 = LocalDateTime.of(1992, 9, 13, 23, 35, 59);
LocalDateTime dateTime2 = LocalDateTime.of(1992, 10, 8, 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);
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));
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));
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));
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);
LocalDateTime localDateTime4 = now.plusHours(4);
System.out.println("4小时后:"+localDateTime4);
LocalDateTime localDateTime5 = now.plusMinutes(30);
System.out.println("30分钟后:" + localDateTime5);
LocalDateTime localDateTime6 = now.plusSeconds(80);
System.out.println("80秒后:" + localDateTime6);
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);
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);
LocalDateTime localDateTime4 = now.minusHours(4);
System.out.println("4小时前:"+localDateTime4);
LocalDateTime localDateTime5 = now.minusMinutes(30);
System.out.println("30分钟前:" + localDateTime5);
LocalDateTime localDateTime6 = now.plusSeconds(80);
System.out.println("80秒前:" + localDateTime6);
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);
LocalDateTime localDateTime1 = now.withYear(1992);
System.out.println("指定1992年:" + localDateTime1);
LocalDateTime localDateTime2 = now.withMonth(11);
System.out.println("指定11月份:" + localDateTime2);
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 {
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);
long ll = now.toEpochMilli();
System.out.println("1970-01-01 00:00:00到当前时间间隔的毫秒值:" + ll);
long epochSecond = now.getEpochSecond();
System.out.println("1970-01-01 00:00:00 到当前时间间隔的秒数:" + epochSecond);
Instant instant = Instant.ofEpochMilli(1000 * 60 * 60 * 24);
System.out.println("计算机元年增加1天(86400000毫秒):"+instant);
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 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 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 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 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 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 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()。
说明:如果像获取更加精确的纳秒级时间值,则使用System.nanoTime()
的方法。在JDK8中,针对时间等场景,推荐使用Instant类
④ 【强制】不允许在程序的任何地方使用:
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();
//反列
int[] dayArray = new int[365]
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等指代月份,进行传参或比较
本期内容就到这了,我们下期再见(●’◡’●)