一、基本概念
1、本地时间
- 不同的时区,在同一时刻,本地时间是不同的。
- 全球一共分为24个时区,伦敦所在的时区称为标准时区,其他时区按东/西偏移的小时区分,北京所在的时区是
东八区
。
2、时区
- GMT
- GMT+08:00:东八区
- 格林威治时区/GMT+00:00
- UTC
- UTC+08:00:东八区
GMT
和UTC
可以认为基本是等价的,只是UTC
使用更精确的原子钟计时,每隔几年会有一个闰秒,我们在开发程序的时候可以忽略两者的误差,因为计算机的时钟在联网的时候会自动与时间服务器同步时间。
- CST
- China Standard Time:中国标准时间
- Central Standard Time USA:美国中部时间
- 缩写容易产生混淆,尽量不要使用缩写
- 洲/城市
Asia/Shanghai
表示上海所在地的时区- 特别注意城市名称不是任意的城市,而是由国际标准组织规定的城市
3、夏令时
- 所谓夏令时,就是夏天开始的时候,把时间往后拨1小时,夏天结束的时候,再把时间往前拨1小时
- 我们国家实行过一段时间夏令时,1992年就废除了,但是美国人到现在还在使用,所以时间换算更加复杂
- 计算夏令时请使用标准库提供的相关类,不要试图自己计算夏令时。
4、本地化
- 在计算机中,通常使用
Locale
表示一个国家或地区的日期、时间、数字、货币等格式 Locale
由语言_国家
的字母缩写构成,例如,zh_CN
表示中文+中国,en_US
表示英文+美国。语言使用小写,国家使用大写
如:
地区 | 中国 (zh_CN) | 美国 (en_US) |
---|---|---|
购买价格 | 12000.00 | 12,000.00 |
购买日期 | 2016-11-30 | 11/30/2016 |
二、新旧API概述
API | 所在包 | 主要类 |
---|---|---|
旧 | java.util | Date、Calendar、TimeZone |
新(Java1.8) | java.time | LocalDateTime、ZonedDateTime、ZoneId |
三、旧 java.util
1、Epoch_Time
Epoch Time
是计算从1970年1月1日零点(格林威治时区/GMT+00:00)到现在所经历的秒数,例如:
1574208900
表示从1970年1月1日零点GMT时区到该时刻一共经历了1574208900秒,换算成伦敦、北京和纽约时间分别是:
1574208900
= 北京时间2019-11-20 8:15:00
= 伦敦时间2019-11-20 0:15:00
= 纽约时间2019-11-19 19:15:00
- 在计算机中,只需要存储一个整数表示某一时刻,如1574208900。当需要显示为某一地区的当地时间时,就把它格式化为一个字符串:
String displayDateTime(int n, String timezone) { ... }
Epoch Time
又称为时间戳,在不同的编程语言中,会有几种存储方式:- 以秒为单位的整数:1574208900,缺点是精度只能到秒;
- 以毫秒为单位的整数:1574208900123,最后3位表示毫秒数;
- 以秒为单位的浮点数:1574208900.123,小数点后面表示零点几秒。
它们之间转换非常简单。而在Java程序中,时间戳通常是用long
表示的毫秒数,即:
long t = 1574208900123L;
2019-11-20T8:15:00.123//北京时间
//获取时间戳最常用的方法
System.currentTimeMillis();
2、Date
java.util.Date
是用于表示一个日期和时间的对象- 注意与
java.sql.Date
区分,后者用在数据库中 - 如果观察Date的源码,可以发现它实际上存储了一个long类型的以毫秒表示的时间戳
public class Date implements Serializable, Cloneable, Comparable<Date> {
private transient long fastTime;
...
}
// 获取当前时间:
Date date = new Date();
System.out.println(date.getYear() + 1900); // 必须加上1900
System.out.println(date.getMonth() + 1); // 0~11,必须加上1
System.out.println(date.getDate()); // 1~31,不能加1
// 转换为String:
System.out.println(date.toString());
// 转换为GMT时区:
System.out.println(date.toGMTString());
// 转换为本地时区:
System.out.println(date.toLocaleString());
-
注意
getYear()
返回的年份必须加上1900
-
getMonth()
返回的月份是0-11,分别表示1~12月,所以要加1 -
getDate()
返回的日期范围是1-31,又不能加1
格式化输出SimpleDateFormat
// 获取当前时间:
Date date = new Date();
//yyyy:年//MM:月//dd: 日//HH: 小时//mm: 分钟//ss: 秒
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(date));
- Data对象的严重问题
- 它不能转换时区,除了
toGMTString()
可以按GMT+0:00
输出外,Date总是以当前计算机系统的默认时区为基础进行输出 - 很难对日期和时间进行加减,计算两个日期相差多少天,计算某个月第一个星期一的日期等
- 它不能转换时区,除了
3、Calendar
可以用于获取并设置年、月、日、时、分、秒,它和Date
比,主要多了一个可以做简单的日期和时间运算的功能
// 获取当前时间:
Calendar c = Calendar.getInstance();
int y = c.get(Calendar.YEAR);
int m = 1 + c.get(Calendar.MONTH);
int d = c.get(Calendar.DAY_OF_MONTH);
int w = c.get(Calendar.DAY_OF_WEEK);
int hh = c.get(Calendar.HOUR_OF_DAY);
int mm = c.get(Calendar.MINUTE);
int ss = c.get(Calendar.SECOND);
int ms = c.get(Calendar.MILLISECOND);
System.out.println(y + "-" + m + "-" + d + " " + w + " " + hh + ":" + mm + ":" + ss + "." + ms);
Calendar
获取年月日为get(int field)
- 返回的年份不必转换
- 返回的月份仍然要加1
- 返回的星期要特别注意,1-7分别表示周日,周一,……,周六
Calendar
只有一种方式获取,即Calendar.getInstance()
,而且一获取到就是当前时间- 如果想给它设置成特定的一个日期和时间,就必须先清除所有字段:
// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置2019年:
c.set(Calendar.YEAR, 2019);
// 设置9月:注意8表示9月:
c.set(Calendar.MONTH, 8);
// 设置2日:
c.set(Calendar.DATE, 2);
// 设置时间:
c.set(Calendar.HOUR_OF_DAY, 21);
c.set(Calendar.MINUTE, 22);
c.set(Calendar.SECOND, 23);
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));// 2019-09-02 21:22:23
- 利用
Calendar.getTime()
可以将一个Calendar
对象转换成Date
对象,然后就可以用SimpleDateFormat
进行格式化了
4、TimeZone
Calendar
和Date
相比,它提供了时区转换的功能。时区用TimeZone
对象表示
TimeZone tzDefault = TimeZone.getDefault(); // 当前时区
TimeZone tzGMT9 = TimeZone.getTimeZone("GMT+09:00"); // GMT+9:00时区
TimeZone tzNY = TimeZone.getTimeZone("America/New_York"); // 纽约时区
System.out.println(tzDefault.getID()); // Asia/Shanghai
System.out.println(tzGMT9.getID()); // GMT+09:00
System.out.println(tzNY.getID()); // America/New_York
- 时区的唯一标识是以
字符串
表示的ID,获取指定TimeZone
对象也是以这个ID为参数获取,GMT+09:00
、Asia/Shanghai
都是有效的时区ID。 - 要列出系统支持的所有ID,请使用
TimeZone.getAvailableIDs()
有了时区,我们就可以对指定时间进行转换。例如,下面的例子演示了如何将北京时间2019-11-20 8:15:00
转换为纽约时间:
// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置为北京时区:
c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
// 设置年月日时分秒:
c.set(2019, 10 /* 11月 */, 20, 8, 15, 0);
// 显示时间:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println(sdf.format(c.getTime()));// 2019-11-19 19:15:00
可见,利用Calendar
进行时区转换的步骤是:
- 清除所有字段;
- 设定指定时区;
- 设定日期和时间;
- 创建
SimpleDateFormat
并设定目标时区; - 格式化获取的
Date
对象(注意Date
对象无时区信息,时区信息存储在SimpleDateFormat
中)
因此,本质上时区转换只能通过SimpleDateFormat在显示的时候完成
Calendar
也可以对日期和时间进行简单的加减
// 当前时间:
Calendar c = Calendar.getInstance();
// 清除所有:
c.clear();
// 设置年月日时分秒:
c.set(2019, 10 /* 11月 */, 20, 8, 15, 0);
// 加5天并减去2小时:
c.add(Calendar.DAY_OF_MONTH, 5);
c.add(Calendar.HOUR_OF_DAY, -2);
// 显示时间:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = c.getTime();
System.out.println(sdf.format(d));// 2019-11-25 6:15:00
四、新 java.time
1、新API概述
从Java 8开始,java.time
包提供了新的日期和时间API,主要涉及的类型有:
-
本地日期和时间:
LocalDateTime
,LocalDate
,LocalTime
; -
带时区的日期和时间:
ZonedDateTime
; -
时刻:
Instant
; -
时区:
ZoneId
,ZoneOffset
; -
时间间隔:
Duration
-
天数间隔:
Period
-
格式化类型:
DateTimeFormatter
- 可以取代旧的 SimpleDateFormat
区别 DateTimeFormatter SimpleDateFormat 可变性 final 非final 多线程 线程安全 线程非安全
新特性
- 新API严格区分了时刻、本地日期、本地时间和带时区的日期时间
- 对日期和时间进行运算更加方便
- 新API修正了旧API不合理的常量设计
- Month的范围用1~12,表示1月到12月
- Week的范围用1~7,表示周一到周日
- 新API的类型几乎全部是不变类型(和String类似),可以放心使用不必担心被修改。线程安全。
2、本地日期时间
LocalDateTime、LocalDate、LocalTime
2.1、得到本地日期和时间
1)now:直接获取
LocalDate d = LocalDate.now(); // 当前日期
LocalTime t = LocalTime.now(); // 当前时间
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
System.out.println(d); // 严格按照ISO 8601格式打印
System.out.println(t); // 严格按照ISO 8601格式打印
System.out.println(dt); // 严格按照ISO 8601格式打印
本地日期和时间通过now()获取到的总是以当前默认时区返回的,和旧API不同,
LocalDateTime
、LocalDate
和LocalTime
默认严格按照ISO 8601规定的日期和时间格式进行打印
- 优化:上述代码其实有一个小问题,在获取3个类型的时候,由于执行一行代码总会消耗一点时间,因此,3个类型的日期和时间很可能对不上(时间的毫秒数基本上不同)。为了保证获取到同一时刻的日期和时间,可以改写如下:
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间
2)of:指定
// 指定日期和时间:
LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
LocalDateTime dt3 = LocalDateTime.of(d2, t2);
3)parse:解析
- 参数为字符串。严格按标准格式。
- 如果某项是一位数,须在前面补0。如:“2019-5-19"不符合,应为"2019-05-19”
LocalDateTime dt = LocalDateTime.parse("2019-11-19T15:16:17");
LocalDate d = LocalDate.parse("2019-05-19");
LocalTime t = LocalTime.parse("15:06:17");
4)toXXX:转换
从 LocalDateTime 转换为 LocalDate、LocalTime
LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
LocalDate d = dt.toLocalDate(); // 转换到当前日期
LocalTime t = dt.toLocalTime(); // 转换到当前时间
2.2、标准格式
- 日期:yyyy-MM-dd
- 时间:HH:mm:ss
- 带毫秒的时间:HH:mm:ss.SSS
- 日期和时间:yyyy-MM-dd
T
HH:mm:ss - 带毫秒的日期和时间:yyyy-MM-dd
T
HH:mm:ss.SSS
注意ISO 8601规定的日期和时间分隔符是
T
2.3、格式化 DateTimeFormatter
// 自定义格式化:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dtf.format(LocalDateTime.now()));
// 用自定义格式解析:
// 字符串和dtf格式一致
// 如果不加dtf,字符串需按标准格式:"2019-11-30T15:16:17"
LocalDateTime dt2 = LocalDateTime.parse("2019/11/30 15:16:17", dtf);
System.out.println(dt2);
2.4、日期加减
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// 加5天减3小时:
LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
System.out.println(dt2); // 2019-10-31T17:30:59
// 减1月:
LocalDateTime dt3 = dt2.minusMonths(1);
System.out.println(dt3); // 2019-09-30T17:30:59
月份加减会自动调整日期,例如从
2019-10-31
减去1个月得到的结果是2019-09-30
,因为9月没有31日
2.5、日期调整 withXXX()
- 调整年:withYear()
- 调整月:withMonth()
- 调整日:withDayOfMonth()
- 调整时:withHour()
- 调整分:withMinute()
- 调整秒:withSecond()
LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
System.out.println(dt);
// 日期变为31日:
LocalDateTime dt2 = dt.withDayOfMonth(31);
System.out.println(dt2); // 2019-10-31T20:30:59
// 月份变为9:
LocalDateTime dt3 = dt2.withMonth(9);
System.out.println(dt3); // 2019-09-30T20:30:59
调整月份时,会相应地调整日期,即把
2019-10-31
的月份调整为9
时,日期也自动变为30
2.6、通用with()
方法
- 用LocalDate,没有具体的时间
- TemporalAdjusters
- dayOfWeekInMonth() :一周中的某一天,例如,三月中第二个星期二
- firstDayOfMonth() :当前月的第一天
- firstDayOfNextMonth() : 下一个月的第一天
- firstDayOfNextYear() : 下一年的第一天
- firstDayOfYear() : 当年的第一天
- lastDayOfMonth() : 当月的最后一天
- nextOrSame() : 下一次或当天发生的一周中的某天
// 本月第一天0:00时刻:
LocalDateTime firstDay = LocalDate.now().withDayOfMonth(1).atStartOfDay();
System.out.println(firstDay);
// 本月最后1天:
LocalDate lastDay = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth());
System.out.println(lastDay);
// 下月第1天:
LocalDate nextMonthFirstDay = LocalDate.now().with(TemporalAdjusters.firstDayOfNextMonth());
System.out.println(nextMonthFirstDay);
// 本月第1个周一:
LocalDate firstWeekday = LocalDate.now().with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println(firstWeekday);
2.7、比较先后
- isBefore()
- isAfter()
LocalDateTime now = LocalDateTime.now();
LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
System.out.println(now.isBefore(target));
System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19)));
System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00")));
2.8、获取时间戳
LocalDateTime
无法与时间戳进行转换,因为LocalDateTime
没有时区,无法确定某一时刻。ZonedDateTime
相当于LocalDateTime
加时区的组合,具有时区,可以与long
表示的时间戳进行转换- 获取时间戳
Long milliSecond = LocalDateTime.now()
.toInstant(ZoneOffset.of("+8")).toEpochMilli();
2.9、Duration和Period
Duration
表示两个时刻之间的时间间隔。精确到分秒Period
表示两个日期之间的天数
LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
Duration d = Duration.between(start, end);
System.out.println(d); // PT1235H10M30S
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
System.out.println(p); // P1M21D
- 两个
LocalDateTime
之间的差值使用Duration
表示,类似PT1235H10M30S
,表示1235小时10分钟30秒 - 两个
LocalDate
之间的差值用Period
表示,类似P1M21D
,表示1个月21天 Duration
和Period
的表示方法也符合ISO 8601的格式,它以P...T...
的形式表示,P...T
之间表示日期间隔,T
后面表示时间间隔。如果是PT...
的格式表示仅有时间间隔- 利用
ofXxx()
或者parse()
方法也可以直接创建Duration
Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes
2.10、历史
因为开源的Joda Time设计很好,应用广泛,所以JDK团队邀请Joda Time的作者Stephen Colebourne共同设计了java.time
API
3、ZonedDateTime
3.1、简介
LocalDateTime
表示本地日期和时间,要表示一个带时区的日期和时间,就需要ZonedDateTime
- 可以简单地把
ZonedDateTime
理解成LocalDateTime
加ZoneId
ZoneId
是java.time
引入的新的时区类,注意和旧的java.util.TimeZone
区别
3.2、创建ZonedDateTime对象
- now()
ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区Zoned
DateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
System.out.println(zbj);
System.out.println(zny);
//输出
2019-09-15T20:58:18.786182+08:00[Asia/Shanghai]
2019-09-15T08:58:18.788860-04:00[America/New_York]
观察打印的两个
ZonedDateTime
,发现它们时区不同,但表示的时间都是同一时刻(毫秒数不同是执行语句时的时间差)
- 给LocalDateTime附加ZoneId
LocalDateTime ldt = LocalDateTime.of(2019, 9, 15, 15, 16, 17);
ZonedDateTime zbj = ldt.atZone(ZoneId.systemDefault());
ZonedDateTime zny = ldt.atZone(ZoneId.of("America/New_York"));
System.out.println(zbj);System.out.println(zny);
//输出
2019-09-15T15:16:17+08:00[Asia/Shanghai]
2019-09-15T15:16:17-04:00[America/New_York]
以这种方式创建的
ZonedDateTime
,它的日期和时间与LocalDateTime
相同,但附加的时区不同,因此是两个不同的时刻
3.3、时区转换
- 需要有一个
ZonedDateTime
对象 - 通过
withZoneSameInstant()
将关联时区转换到另一个时区,转换后日期和时间都会相应调整
// 以中国时区获取当前时间:
ZonedDateTime zbj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
// 转换为纽约时间:
ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println(zbj);
System.out.println(zny);
要特别注意,时区转换的时候,由于夏令时的存在,不同的日期转换的结果很可能是不同的
涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时
3.4、转为LocalDateTime
转换为
LocalDateTime
时,直接丢弃时区信息
ZonedDateTime zdt = ...
LocalDateTime ldt = zdt.toLocalDateTime();
3.5、加减等操作
和LocalDateTime一样
4、DateTimeFormatter
- 线程安全
- 可以只创建一个实例,到处引用
//创建方式一
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
//创建方式二
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.US);
5、Instant 时刻 时间戳
java.time
中以Instant
类型表示时间戳,即时刻- 用
Instant.now()
获取当前时间戳,效果和System.currentTimeMillis()
类似
Instant now = Instant.now();//当前时刻
System.out.println(now);
System.out.println(now.getEpochSecond());//秒数
System.out.println(now.toEpochMilli());//毫秒
//类似于
System.out.println(System.currentTimeMillis());
// 输出
2023-05-19T01:33:41.578Z
1684460021
1684460021578
1684460021633
//最后三位毫秒值不同是因为两条语句的运行时差
Instant
内部只有两个核心字段:
public final class Instant implements ... {
private final long seconds;
private final int nanos;
}
6、相互转换
-
LocalDateTime 转 ZonedDateTime
LocalDateTime ldt = LocalDateTime.now(); //给LocalDateTime添加时区 ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()); ZonedDateTime zdt1 = ldt.atZone(ZoneId.of("America/New_York"));//转换后不是同一时刻
-
LocalDateTime 转 Instant
LocalDateTime ldt = LocalDateTime.now(); //给LocalDateTime添加时区,转为ZonedDateTime,再调用toInstant() Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant();
-
LocalDateTime 转 long
LocalDateTime ldt = LocalDateTime.now(); //给LocalDateTime添加时区,转为ZonedDateTime,再调用toInstant() Instant instant = ldt.atZone(ZoneId.systemDefault()).toInstant(); //由instant 转为 long long second = instant.getEpochSecond(); long milli = instant.toEpochMilli();
-
ZonedDateTime 转 LocalDateTime
ZonedDateTime zdt = ZonedDateTime.now(); LocalDateTime ldt = zdt.toLocalDateTime();//转换为LocalDateTime后,直接丢弃时区信息
-
ZonedDateTime 转 Instant
Instant instant = ZonedDateTime.now().toInstant();
-
ZonedDateTime 转 long
long second = ZonedDateTime.now().toInstant().getEpochSecond(); long milli = ZonedDateTime.now().toInstant().toEpochMilli();
-
Instant 转 LocalDateTime
LocalDateTime ldt = Instant.now().atZone(ZoneId.systemDefault()).toLocalDateTime();
-
Instant 转 ZonedDateTime
// atZone() ZonedDateTime zdt = Instant.now().atZone(ZoneId.systemDefault()); // ofInstant() ZonedDateTime zdt = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
-
Instant 转 long
long second = Instant.now().getEpochSecond(); long milli = Instant.now().toEpochMilli();
-
long 转 LocalDateTime
long s = 1623729859; LocalDateTime ldt = Instant.ofEpochSecond(s).atZone(ZoneId.systemDefault()).toLocalDateTime();
-
long 转 ZonedDateTime
long s = 1623729859; ZonedDateTime zdt = Instant.ofEpochSecond(s).atZone(ZoneId.systemDefault());
-
long 转 Instant
long s = 1623729859; Instant instant = Instant.ofEpochSecond(s);
五、最佳实践
-
由于Java提供了新旧两套日期和时间的API,除非涉及到遗留代码,否则我们应该坚持使用新的API
-
旧API转新API
- 如果要把旧式的
Date
或Calendar
转换为新API对象,可以通过toInstant()
方法转换为Instant
对象,再继续转换为ZonedDateTime
:
// Date -> Instant: Instant ins1 = new Date().toInstant(); // Calendar -> Instant -> ZonedDateTime: Calendar calendar = Calendar.getInstance(); Instant ins2 = calendar.toInstant(); ZonedDateTime zdt = ins2.atZone(calendar.getTimeZone().toZoneId());
- 如果要把旧式的
-
新API转旧API
- 如果要把新的
ZonedDateTime
转换为旧的API对象,只能借助long
型时间戳做一个“中转”:
// ZonedDateTime -> long: ZonedDateTime zdt = ZonedDateTime.now(); long ts = zdt.toEpochSecond() * 1000; // long -> Date: Date date = new Date(ts); // long -> Calendar: Calendar calendar = Calendar.getInstance(); calendar.clear(); calendar.setTimeZone(TimeZone.getTimeZone(zdt.getZone().getId())); calendar.setTimeInMillis(zdt.toEpochSecond() * 1000);
- 如果要把新的
-
在数据库中存储日期和时间
数据库 | 对应Java类(旧) | 对应Java类(新) |
---|---|---|
DATETIME | java.util.Date | LocalDateTime |
DATE | java.sql.Date | LocalDate |
TIME | java.sql.Time | LocalTime |
TIMESTAMP | java.sql.Timestamp | LocalDateTime |
实际上,在数据库中,我们需要存储的最常用的是时刻(
Instant
),因为有了时刻信息,就可以根据用户自己选择的时区,显示出正确的本地时间。所以,最好的方法是直接用长整数long表示,在数据库中存储为BIGINT类型
六、获取日期时间方法汇总
编号 | 途经 | 代码 | 说明 | 建议 |
---|---|---|---|---|
1 | java.util.Date | new Date() | Date对象 | 过时 |
2 | System | System.currentTimeMillis() | 时间戳 | 常用 |
3 | java.util.Calendar | Calendar.getInstance().getTime() | Date升级版 | 较常用 |
4 | java.time.LocalDate | LocalDate.now() | 只有日期 | 推荐 (JDK1.8新API) |
5 | java.time.LocalTime | LocalTime.now() | 只有时间 | 推荐 (JDK1.8新API) |
6 | java.time.LocalDateTime | LocalDateTime.now() | 日期时间 | 推荐 (JDK1.8新API) |
7 | java.time.ZonedDateTime | ZonedDateTime.now() | 有时区 | 推荐 (JDK1.8新API) |
8 | Instant | Instant.now() | 时刻 | 推荐 (JDK1.8新API) |
import java.text.SimpleDateFormat;
import java.time.*;
import java.util.Calendar;
import java.util.Date;
/**
* @author ajun
* Date 2021/7/9
* @version 1.0
* 获取当前时间
*/
public class CurrentTime {
public static void main(String[] args) {
//getByDate();
//getBySystem();
//getByCalendar();
//getByLocalDate();
//getByLocalTime();
//getByLocalDateTime();
//getByZonedDateTime();
getByInstant();
}
// 使用java.util.Date类
public static void getByDate(){
Date date = new Date();
//SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//System.out.println(format.format(date));
System.out.println(date);
}
// 使用System类
public static void getBySystem(){
long l = System.currentTimeMillis();
Date date = new Date(l);
System.out.println(date);
}
// 使用Calendar类
public static void getByCalendar(){
Date time = Calendar.getInstance().getTime();
System.out.println(time);
}
// 使用LocalDate类:只有日期,无时间
public static void getByLocalDate(){
LocalDate now = LocalDate.now();
System.out.println(now);
}
// 使用LocalTime类:只有时间,无日期
public static void getByLocalTime(){
LocalTime now = LocalTime.now();
System.out.println(now);
}
// 使用LocalDateTime类
public static void getByLocalDateTime(){
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
}
// 使用ZonedDateTime类
public static void getByZonedDateTime(){
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
}
// 使用Instant类
public static void getByInstant(){
Instant now = Instant.now();
System.out.println(now);
}
}