Java日期时间API
日期时间API
参考:https://lw900925.github.io/java/java8-newtime-api.html
旧日期时间API
System
java.lang.System类提供一个long currentTimeMillis() 静态方法:
获取当前时间与1970年1月1日0时0分0秒之间的UTC时间差 (ms),也叫做时间戳。
常用的时间标准主要有:
UTC (Coordinated Universal Time):世界标准时间,各国都基于UTC进行协调同步
GMT (Greenwich Mean Time):格林威治时间 (伦敦时间),等于UTC+0
CST (China Standard Time):中国标准时间 (北京时间),等于UTC+8
@Test
public void test01() throws InterruptedException {long start = System.currentTimeMillis();Thread.sleep(5000);long end = System.currentTimeMillis();System.out.println("程序执行时间 (ms): " + (end - start)); //5000
}
java.util.Date
表示特定的时刻,精确到毫秒,不包含时区信息。
构造器:
Date() :创建一个当前时间的Date对象
Date(long date) :创建一个指定时间戳的Date对象
常用方法:
long getTime() : 返回GMT时间戳 (ms),等同于
System.currentTimeMillis()
String toString() : 转换成字符串形式,格式:dow mon dd hh:mm:ss zzz yyyy
dow指星期几 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
mon指月份 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)。
dd指本月的第几天,两位十进制数。
hh指小时,两位十进制数。
mm指分钟,两位十进制数。
ss指秒,两位十进制数。
zzz指时区,如CST、GMT等,如果时区不可用,则值为空。
yyyy指年份,四位十进制数。
- 其他很多方法都过时了
@Test
public void test01() {//创建一个当前时间的Date对象Date date1 = new Date();//创建一个指定时间戳的Date对象Date date2 = new Date(System.currentTimeMillis());//获得时间戳, 等同于System.currentTimeMillis()System.out.println(date1.getTime()); //1623740033611//打印Date, 格式: EEE MMM dd HH:mm:ss zzz yyyySystem.out.println(date1); //Tue Jun 15 14:50:33 CST 2021
}
java.sql.Date
还有一个java.sql.Date类,它是java.util.Date的子类,两者的区别:java.util.Date
** 包含日期和时间,而 ** java.sql.Date
** 只包含日期,正好对应于MySQL的** date
类型。
java.sql.Date只能通过时间戳创建:
@Test
public void test02() {//根据时间戳创建java.sql.Date对象java.sql.Date date1 = new java.sql.Date(System.currentTimeMillis());System.out.println(date1); //2021-06-15
}
java.sql.Date和java.util.Date的转换
需要借助getTime()
方法:
@Test
public void test03() {//java.sql.Date转java.util.Datejava.sql.Date date1 = new java.sql.Date(System.currentTimeMillis());java.util.Date date2 = new java.util.Date(date1.getTime());System.out.println(date2);//java.util.Date转java.sql.Datejava.util.Date date3 = new java.util.Date();java.sql.Date date4 = new java.sql.Date(date3.getTime());System.out.println(date4);
}
String转java.sql.Date
需要借助SimpleDateFormat
对象的parse()
方法先根据String构造java.util.Date对象,再调用 date.getTime()
获得时间戳,然后根据时间戳构造java.sql.Date对象。
/** 字符串转java.util.Date */
public static Date stringToSqlDate(String date) {try {java.util.Date utilDate = new SimpleDateFormat("yyyyMMdd").parse(date);return new Date(utilDate.getTime());} catch (ParseException e) {throw new RuntimeException("日期格式错误, 期望格式yyyyMMdd, 而实际是[" + date + "]");}
}
SimpleDateFormat
Date类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat类用一种与语言环境无关的方式来格式化具体的Date对象,和解析具体的日期时间字符串。SimpleDateFormat不是线程安全的,在使用时,只能在方法内部创建新的局部变量。
创建SimpleDateFormat对象:
SimpleDateFormat() :使用默认的日期格式和语言环境来创建
SimpleDateFormat(String pattern) :根据给定的日期格式pattern,默认的语言环境来创建
SimpleDateFormat(String pattern, Locale locale) :根据给定的日期格式pattern和语言环境locale来创建
格式化:Date → String
- String format(Date date) :将给定的date对象格式化为字符串
解析:String → Date
- Date parse(String source) :将给定的source字符串解析成Date对象
@Test
public void test01() throws ParseException {Date date = new Date();//使用默认的日期格式和语言环境来创建SimpleDateFormat对象SimpleDateFormat dateFormat1 = new SimpleDateFormat();//格式化成字符串String dateStr1 = dateFormat1.format(date);System.out.println(dateStr1);//解析成Date对象Date toDate1 = dateFormat1.parse("20-1-1 上午00:00"); //21-6-15 下午5:32System.out.println(toDate1); //Wed Jan 01 00:00:00 CST 2020System.out.println("------------------------------------------------");//根据给定的日期格式pattern,默认的语言环境来创建SimpleDateFormat对象SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//格式化成字符串String dateStr2 = dateFormat2.format(date);System.out.println(dateStr2); //2021-06-15 17:32:09//解析成Date对象Date toDate2 = dateFormat2.parse("2020-01-01 00:00:00");System.out.println(toDate2); //Wed Jan 01 00:00:00 CST 2020
}
常用的日期格式:
“yyyy-MM-dd”:只显示日期
“yyyy-MM-dd HH:mm:ss”:日期+时间,24小时制
“yyyy-MM-dd hh:mm:ss”:日期+时间,12小时制
Calendar
Calendar (日历) 是一个抽象的基类,包含时区信息。
主要用于获取和设置各个日期字段的值,以及基本的加法计算。
创建Calendar实例:
调用Calendar.getInstance() 静态方法,创建一个当前时间的Calendar实例
调用子类GregorianCalendar 的构造器,创建一个指定时间的Calendar实例
常用方法:
int get(int field) :获取指定日期字段的值,field的取值来自于Calendar内部定义的一些常量,包括
YEAR
、MONTH
、DAY_OF_WEEK
、DAY_OF_MONTH
、HOUR_OF_DAY
、MINUTE
、SECOND
等。void set(int field, int value) :设置指定日期字段的值
void add(int field, int amount) :对指定的日期字段进行加法运算
void setTime(Date date) :根据Date对象来设置各个字段的值
Date getTime() :转换成Date对象
在获取月份时,一月是0,二月是1, 三月是2 … 十二月是11;
在获取星期时,周日是1,周一是2,周二是3 … 周六是7。
@Test
public void test04() {//创建当前时间的Calendar对象Calendar calendar = Calendar.getInstance();//get(): 取指定字段的值int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH);int day = calendar.get(Calendar.DAY_OF_MONTH);int hour = calendar.get(Calendar.HOUR_OF_DAY);int minute = calendar.get(Calendar.MINUTE);int second = calendar.get(Calendar.SECOND);//set(): 设置指定字段的值calendar.set(Calendar.YEAR, 2021);calendar.set(Calendar.MONTH, 0); //设置为一月份, 注意0代表一月份, 1代表二月份...calendar.set(Calendar.WEEK_OF_MONTH, 2); //设置为星期一, 注意1代表周日, 2代表周一...calendar.set(Calendar.DAY_OF_MONTH, 1);//add(): 对指定字段的值进行加法运算calendar.add(Calendar.MONTH, 3);calendar.add(Calendar.MONTH, 3);calendar.add(Calendar.DAY_OF_MONTH, 1);//getTime(): Calendar → DateDate date1 = calendar.getTime();//setTime(): Date → CalenderDate date2 = new Date();calendar.setTime(date2);
}
JDK 8新日期时间API
JDK 1.0中的java.util.Date类的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了,但Calendar并不比Date好用多少。它们面临的问题是:
可变性:像日期和时间这样的类应该是不可变的。
偏移性:Date中的年份是从1900开始的,而月份都从0开始,容易出错。
格式化:SimpleDateFormat只能格式化Date,不能格式化Calendar。
此外,这些旧API都是线程不安全的,也不能适应闰秒等问题。
Java 8 推出了新的新日期时间API,在java.time包中包含了所有关于 本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)等类。另外还为历史悠久的 Date 类新增了 toInstant() 方法,用来把 Date 转换成新的Instant类型。
这些新API有一个共同点:它们都是 不可变的对象 ,每次修改都会返回一个新的实例。
LocalDateTime
LocalDate、LocalTime、LocalDateTime 类都是不可变的,分别表示日期、时间、日期+时间。它们仅仅存储年月日、时分秒这样的数字,不包含时区信息。这意味着在不同的时区下,这样的数字其实代表着不同的时间。也支持获取和设置指定字段的值,以及进行加减运算。
LocalDate:表示
yyyy-MM-dd
格式的日期。LocalTime:表示
HH:mm:ss.SSS
格式的时间。LocalDateTime:表示
yyyy-MM-ddTHH:mm:ss.SSS
格式的日期和时间,这是最常用的类。
常用方法:
创建对象 | 描述 |
---|---|
now()、now(ZoneId zone) | 静态方法,根据当前时间创建对象(可以指定时区) |
of(…) | 静态方法,根据给定的日期时间创建对象 |
parse(CharSequence text, DateTimeFormatter formatter) | 静态方法,根据给定的字符串和格式化器创建对象 |
修改值,返回新对象 | 描述 |
withDayOfMonth()、withDayOfYear()、 withMonth()、withYear() |
设置 月第几天、年第几天、月份、年份 |
plusDays()、plusWeeks()、 plusMonths()、plusYears()、plusHours() |
加上 几天、几周、几个月、几年、几小时 |
minusMonths()、minusWeeks()、 minusDays()、minusYears()、minusHours() |
减去 几月、几周、几天、几年、几小时 |
获取值 | 描述 |
getDayOfMonth()、getDayOfYear() | 获取月第几天 (1-31)、获取年第几天 (1-366) |
getDayOfWeek() | 获取星期几,返回一个DayOfWeek枚举值 |
getMonth() | 获得月份,返回一个 Month 枚举值 |
getHour()、getMinute()、getSecond() | 获得 时、分、秒 |
转换为其他类型 | 描述 |
LocalTime toInstant(ZoneOffset offset) | 转为Instant对象 |
LocalTime toLocalTime() | 转为LocalTime对象 |
LocalDate toLocalDate() | 转为LocalDate对象 |
long toEpochSecond(ZoneOffset offset) | 转为秒级时间戳 |
OffsetDateTime atOffset(ZoneOffset offset) | 结合指定的时区偏移量,转为OffsetDateTime对象 |
在用法上,LocalDateTime类似于Calender,不过它不包含时区信息,且是不可变的。
@Test
public void test15() {LocalDateTime dateTime = LocalDateTime.now();// 转为Instant对象Instant instant = dateTime.toInstant(ZoneOffset.ofHours(8));// 转为LocalTime对象LocalTime localTime = dateTime.toLocalTime();// 转为LocalDate对象LocalDate localDate = dateTime.toLocalDate();// 转为秒级时间戳long seconds = dateTime.toEpochSecond(ZoneOffset.ofHours(8));// 结合指定的时区偏移量返回一个OffsetDateTime对象OffsetDateTime offsetDateTime = dateTime.atOffset(ZoneOffset.ofHours(-8));
}
@Test
public void localDateTimeTest() {// now(): 获取当前的日期、时间、日期+时间LocalDate date1 = LocalDate.now();LocalTime time1 = LocalTime.now();LocalDateTime dateTime1 = LocalDateTime.now();System.out.println(date1); //2021-06-16System.out.println(time1); //18:39:58.198System.out.println(dateTime1); //2021-06-16T18:39:58.198// of(): 获取指定的日期、时间、日期+时间LocalDate date2 = LocalDate.of(2020, 10, 1);LocalTime time2 = LocalTime.of(12, 0, 0);LocalDateTime dateTime2 = LocalDateTime.of(2020, 10, 1, 12, 0, 0);System.out.println(date2); // 2020-10-01System.out.println(time2); // 12:00System.out.println(dateTime2); // 2020-10-01T12:00// parse(): 根据给定的字符串和格式化器创建对象LocalDate date3 = LocalDate.parse("2020-10-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"));LocalTime time3 = LocalTime.parse("12:00:00", DateTimeFormatter.ofPattern("HH:mm:ss"));LocalDateTime dateTime = LocalDateTime.parse("2020-10-01 12:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));// getXxx(): 获取指定字段的值LocalDateTime dateTime3 = LocalDateTime.now();System.out.println(dateTime3.getYear());System.out.println(dateTime3.getMonth()); //Month枚举类System.out.println(dateTime3.getDayOfMonth());System.out.println(dateTime3.getHour());System.out.println(dateTime3.getMinute());System.out.println(dateTime3.getSecond());// WithXxx(): 设置指定字段的值, 注意LocalDateTime是不可变的LocalDateTime dateTime4 = LocalDateTime.now();System.out.println(dateTime4.withYear(2020)); //2020-06-16T18:48:15.795System.out.println(dateTime4.withMonth(10)); //2021-10-16T18:48:15.795System.out.println(dateTime4.withDayOfMonth(1)); //2021-06-01T18:48:15.795System.out.println(dateTime4); //不变, 仍是2021-06-16T18:48:15.795// plusXxx(), minusXxx(): 加法和减法运算, 注意LocalDateTime是不可变的LocalDateTime dateTime5 = LocalDateTime.now();System.out.println(dateTime5.plusMonths(4)); //2021-10-16T18:48:15.795System.out.println(dateTime5.plusDays(3)); //2021-06-19T18:48:15.795System.out.println(dateTime5.minusMonths(4)); //2021-02-16T18:48:15.795System.out.println(dateTime5.minusDays(3)); //2021-06-13T18:48:15.795
}
@Test
public void test15() {LocalDateTime dateTime = LocalDateTime.now();// 转为Instant对象Instant instant = dateTime.toInstant(ZoneOffset.ofHours(8));// 转为LocalTime对象LocalTime localTime = dateTime.toLocalTime();// 转为LocalDate对象LocalDate localDate = dateTime.toLocalDate();// 转为秒级时间戳long seconds = dateTime.toEpochSecond(ZoneOffset.ofHours(8));// 结合指定的时区偏移量返回一个OffsetDateTime对象OffsetDateTime offsetDateTime = dateTime.atOffset(ZoneOffset.ofHours(-8));
}
Instant
Instant代表时间线上的一个瞬时点,也就是时间戳,不包含时区信息。
从概念上讲,Instant只是简单地表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。
1s = 1000ms =10^6us = 10^9ns
常用方法:
创建对象 | 描述 |
---|---|
now() | 静态方法,创建当前时间的UTC时区的Instant对象 |
ofEpochMilli(long epochMilli) | 静态方法,根据指定的毫秒时间戳创建Instant对象 |
获取值 | 描述 |
long toEpochMilli() | 获取毫秒时间戳 |
atOffset(ZoneOffset offset) | 结合指定的时区偏移量返回一个OffsetDateTime对象 |
在用法上,Instant类似于Date,两者都不包含时区信息,但 Instant 是不可变的。
@Test
public void test01() {//now(): 创建当前时间的UTC时区的Instant对象Instant instant1 = Instant.now();//of(): 根据指定的时间戳创建Instant对象Instant instant2 = Instant.ofEpochMilli(1623843136891L);//直接打印的话, 因为是以UTC+0为基准, 所以结果比北京时间少8小时System.out.println(instant1); //2021-06-16T11:28:25.397Z//atOffset(): 根据给定ZoneOffset时区偏移量, 返回一个OffsetDateTime对象, 但仍不包含时区信息//转换为UTC+8时区的OffsetDateTime对象OffsetDateTime dateTime = instant1.atOffset(ZoneOffset.ofHours(8));System.out.println(dateTime); //2021-06-16T19:28:25.397+08:00//toEpochMill(): 获得从1970-1-1开始的毫秒数, 即时间戳long milli = instant1.toEpochMilli();System.out.println(milli); //1623843136891
}
DateTimeFormatter
使用旧的Date对象时,我们用SimpleDateFormat进行格式化显示。使用新的LocalDateTime或ZonedLocalDateTime时,我们要进行格式化显示,就要使用DateTimeFormatter。
与SimpleDateFormat不同的是,DateTimeFormatter不但是不可变的对象,还是线程安全的。可以只创建一个DateTimeFormatter实例,然后到处引用。
创建DateTimeFormatter实例:
预定义的标准格式。如:
.ISO_LOCAL_DATE_TIME
、.ISO_LOCAL_DATE
、.ISO_LOCAL_TIME
本地化相关的格式。如:
.ofLocalizedDateTime(FormatStyle.LONG)
,可选值:FormatStyle.SHORT
、FormatStyle.MEDIUM
、FormatStyle.LONG
、FormatStyle.FULL
自定义的格式,最常用。如:
.ofPattern("yyyy-MM-dd HH:mm:ss")
常用方法:
String format(TemporalAccessor t) :将TemporalAccessor格式化成字符串。这个TemporalAccessor可以是LocalDate、LocalTime、LocalDateTime等。
TemporalAccessor parse(CharSequence text) :将字符串解析成一个TemporalAccessor的实现类对象java.time.format.Parsed,并不好用。更推荐使用LocalDateTime.parse() 方法,根据给定的字符串和DateTimeFormatter来解析出LocalDateTime对象。
@Test
public void test03() {LocalDateTime now = LocalDateTime.now();//创建DateTimeFormatter对象:// 预定义的标准格式。如:ISO_LOCAL_DATE_TIME、ISO_LOCAL_DATE、ISO_LOCAL_TIMEDateTimeFormatter formatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;// 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.MEDIUM)//可选值:FormatStyle.SHORT、FormatStyle.MEDIUM、FormatStyle.LONG、FormatStyle.FULLDateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);DateTimeFormatter formatter3 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);DateTimeFormatter formatter4 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);// 自定义的格式,最常用。如:ofPattern("yyyy-MM-dd HH:mm:ss E")DateTimeFormatter formatter5 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss E");//格式化: 日期 -> 字符串System.out.println(formatter1.format(now)); //2021-06-16T20:14:03.584System.out.println(formatter2.format(now)); //21-6-16 下午8:14System.out.println(formatter3.format(now)); //2021-6-16 20:14:03System.out.println(formatter4.format(now)); //2021年6月16日 下午08时14分03秒System.out.println(formatter5.format(now)); //2021-06-16 20:14:03 星期三//解析: 字符串 -> 日期TemporalAccessor temporalAccessor1 = formatter1.parse("2021-06-16T20:00:32.175");TemporalAccessor temporalAccessor2 = formatter5.parse("2021-06-16 20:12:28 星期三");System.out.println(temporalAccessor1); //{},ISO resolved to 2021-06-16T20:00:32.175System.out.println(temporalAccessor2); //{},ISO resolved to 2021-06-16T20:12:28
}
TemporalAdjuster
为了保证线程安全,Java 8 中的日期/时间类都是不可变的。任何修改都会返回一个新的实例:
LocalDate date = LocalDate.of(2017, 1, 5); // 2017-01-05LocalDate date1 = date.withYear(2016); // 修改为 2016-01-05
LocalDate date2 = date.withMonth(2); // 修改为 2017-02-05
LocalDate date3 = date.withDayOfMonth(1); // 修改为 2017-01-01LocalDate date4 = date.plusYears(1); // 增加一年 2018-01-05
LocalDate date5 = date.minusMonths(2); // 减少两个月 2016-11-05
LocalDate date6 = date.plus(5, ChronoUnit.DAYS); // 增加5天 2017-01-10
上面例子中对于日期的操作比较简单,但是有些时候我们要面临更复杂的时间操作,比如将时间调到下一个工作日,或者是下个月的最后一天。这时候我们可以将一个TemporalAdjuster
传给LocalDateTime
实例的with()
方法,来获取一个调整后的时间。
LocalDate date7 = date.with(nextOrSame(DayOfWeek.SUNDAY)); // 返回下一个距离当前时间最近的星期日
LocalDate date9 = date.with(lastInMonth(DayOfWeek.SATURDAY)); // 返回本月最后一个星期六
要使上面的代码正确编译,你需要使用静态导入TemporalAdjusters
对象:
import static java.time.temporal.TemporalAdjusters.*;
TemporalAdjuster
是一个时间调整器, 指代一种时间调整逻辑。**
**
TemporalAdjusters
是一个工具类,提供了大量常用的TemporalAdjuster
实现:
方法名 | 描述 |
---|---|
dayOfWeekInMonth
|
返回同一个月中每周的第几天 |
firstDayOfMonth
|
返回当月的第一天 |
firstDayOfNextMonth
|
返回下月的第一天 |
firstDayOfNextYear
|
返回下一年的第一天 |
firstDayOfYear
|
返回本年的第一天 |
firstInMonth
|
返回同一个月中第一个星期几 |
lastDayOfMonth
|
返回当月的最后一天 |
lastDayOfNextMonth
|
返回下月的最后一天 |
lastDayOfNextYear
|
返回下一年的最后一天 |
lastDayOfYear
|
返回本年的最后一天 |
lastInMonth
|
返回同一个月中最后一个星期几 |
next / previous
|
返回下一个 / 前一个给定的星期几 |
nextOrSame / previousOrSame
|
返回下一个 / 前一个给定的星期几, 如果这个值满足条件,直接返回 |
如果上面表格中列出的方法不能满足你的需求,你还可以创建自定义的TemporalAdjuster
接口的实现,TemporalAdjuster
也是一个函数式接口,所以我们可以使用 Lambda 表达式:
@FunctionalInterface
public interface TemporalAdjuster {Temporal adjustInto(Temporal temporal);
}
比如给定一个日期,计算该日期的下一个工作日:
LocalDate date = LocalDate.of(2017, 1, 5);
date.with(temporal -> {// 当前日期DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));// 正常情况下,每次增加一天int dayToAdd = 1;// 如果是星期五,增加三天if (dayOfWeek == DayOfWeek.FRIDAY) {dayToAdd = 3;}// 如果是星期六,增加两天if (dayOfWeek == DayOfWeek.SATURDAY) {dayToAdd = 2;}return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
ZoneId和ZoneOffset
ZoneId
代表一个时区,内部格式是 “地区/城市”,如 “Asia/Shanghai”。
新的时区类java.time.ZoneId
是原有的java.util.TimeZone
类的替代品。
@Test
public void test07() {// ZoneId: 代表一个时区,内部格式是 "地区/城市",如 "Asia/Shanghai"// ZoneId.systemDefault(): 获取当前系统默认时区的ZoneId对象ZoneId zoneId = ZoneId.systemDefault();System.out.println(zoneId); //Asia/Shanghai// ZoneId.of(zoneStr): 获取指定时区的ZoneId对象, 需传入一个 "地区/城市" 格式的字符串ZoneId shanghai = ZoneId.of("Asia/Shanghai");LocalDateTime dateTime = LocalDateTime.now(shanghai);// ZoneId.getAvailableZoneIds(): 获取所有时区的ZoneIdSet<String> zoneIds = ZoneId.getAvailableZoneIds();for (String zoneId : zoneIds) {System.out.println(zoneId);}
}
对于老的时区类TimeZone
,Java 8 也提供了转化方法:
ZoneId oldToNewZoneId = TimeZone.getDefault().toZoneId();
借助于ZoneId
,我们就可以将一个LocalDateTime
对象转化为带时区的ZonedDateTime
对象:
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, shanghai);
System.out.println(zonedDateTime);
打印结果:
2017-01-05T15:26:56.147+08:00[Asia/Shanghai]
ZonedDateTime
对象由两部分构成:LocalDateTime
+ZoneId
,其中“2017-01-05T15:26:56.147”部分为LocalDateTime
,“+08:00[Asia/Shanghai]”部分为ZoneId
。
另一种表示时区的方式是使用ZoneOffset
,它表示当前时间和GMT时间的偏移量,例如:
ZoneOffset zoneOffset = ZoneOffset.of("+09:00");
LocalDateTime localDateTime = LocalDateTime.now();
//这个offsetDateTime代表的是系统默认时区的时间+9小时后的时间
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, zoneOffset);
其他API
- ZonedDateTime :带时区的日期时间,如 2021-06-19T13:28:34.906+08:00[Asia/Shanghai]。
@Test
public void test08() {// ZonedDateTime: 带时区的日期时间// ZonedDateTime.now(): 获得当前系统的默认时区的日期时间ZonedDateTime dateTime = ZonedDateTime.now();System.out.println(dateTime); //2021-06-19T13:28:34.906+08:00[Asia/Shanghai]// getZone(): 获取ZonedDateTime里的时区ZoneId zoneId = dateTime.getZone();System.out.println(zoneId); //Asia/Shanghai// ZonedDateTime.now(ZoneId zoneId): 获取指定时区的ZonedDateTime对象ZonedDateTime dateTime2 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));System.out.println(dateTime2); //2021-06-19T13:28:34.906+08:00[Asia/Shanghai]
}
Clock :使用时区提供对当前即时、日期和时间的访问的时钟。
Duration :持续时间,表示两个时间的间隔时长,以【秒数+纳秒数】衡量。
@Test
public void test09() {// Duration: 表示两个时间的间隔时长, 以 秒数+纳秒数 衡量LocalTime time1 = LocalTime.of(0, 0, 0);LocalTime time2 = LocalTime.now();// Duration.between(time1, time2): 返回一个Duration对象, 表示两个时间的间隔时长Duration duration = Duration.between(time1, time2);System.out.println(duration); //PT14H58M9.822S// getSeconds(): 获得秒数部分System.out.println(duration.getSeconds()); //53889// getNano(): 获得纳秒部分System.out.println(duration.getNano()); //822000000// toMillis(): 转为对应的毫秒数System.out.println(duration.toMillis()); //53889822// toDays(): 换算成天数表示, 即 秒数/86400, 不足一天的直接被忽略LocalDateTime dateTime1 = LocalDateTime.of(2016, 1, 1, 15, 23, 32);LocalDateTime datetime2 = LocalDateTime.of(2017, 1, 1, 15, 23, 32);Duration duration2 = Duration.between(dateTime1, datetime2);System.out.println(duration2.toDays()); //366
}
- Period :日期间隔,表示两个日期的间隔,以【年+月+日】衡量。
@Test
public void test10() {// Period: 表示两个日期的间隔, 以 年+月+日 衡量LocalDate date1 = LocalDate.now();LocalDate date2 = LocalDate.of(2038, 3, 18);// Period.between(date1, date2): 获取两个日期的间隔Period period1 = Period.between(date1, date2);System.out.println(period1); //P16Y8M27D// getYears()/getMonths()/getDays(): 分别获得 年份/月份/月内天数 部分System.out.println(period1.getYears()); //16System.out.println(period1.getMonths()); //8System.out.println(period1.getDays()); //27// withYears()/withMonths()/withDays(): 分别修改 年份/月份/月内天数, 返回一个新Period对象Period period2 = period1.withYears(2);System.out.println(period1.getYears()); //16System.out.println(period2.getYears()); //2
}
与旧API的类型转换
主要就是调用旧API的一些方法来进行类型转换。
类型转换 | 方法 |
---|---|
Instant → Date | Date.from(instant) |
Date → Instant | date.toInstant() |
Instant → java.sql.Timestamp | Timestamp.from(instant) |
java.sql.Timestamp → Instant | timestamp.toInstant() |
ZonedDateTime → GregorianCalendar | GregorianCalendar.from(zonedDateTime) |
GregorianCalendar → ZonedDateTime | calendar.toZonedDateTime() |
LocalTime → java.sql.Time | time.valueOf(lcoalTime) |
java.sql.Time → LocalTime | time.toLocalTime() |
LocalDateTime → java.sql.Timestamp | Timestamp.valueOf(localDateTime) |
java.sql.Timestamp → LocalDateTime | timestamp.toLocalDateTime() |
ZoneId → TimeZone | Timezone.getTimeZone(zoneId) |
TimeZone → ZoneId | timeZone.toZoneId() |
DateTimeFormatter → SimpleDateFormat | formatter.toFormat() |
SimpleDateFormat → DateTimeFormatter | 无 |
其他历法
Java 中使用的历法是 ISO 8601 日历系统,它是世界民用历法,也就是我们所说的公历。平年有 365 天,闰年是 366 天。闰年的定义是:非世纪年,能被 4 整除;世纪年能被 400 整除。为了计算的一致性,公元 1 年的前一年被当做公元 0 年,以此类推。
此外 Java 8 还提供了 4 套其他历法(很奇怪为什么没有汉族人使用的农历),每套历法都对应一个日期类,分别是:
ThaiBuddhistDate
:泰国佛教历MinguoDate
:中华民国历JapaneseDate
:日本历HijrahDate
:伊斯兰历
每个日期类都继承ChronoLocalDate
类,所以可以在不知道具体历法的情况下也可以操作。不过这些历法一般不常用,除非是有某些特殊需求情况下才会使用。
这些不同的历法也可以从公历转换而来:
LocalDate date = LocalDate.now();
JapaneseDate jpDate = JapaneseDate.from(date);
由于它们都继承ChronoLocalDate
类,所以在不知道具体历法情况下,可以通过ChronoLocalDate
类操作日期:
Chronology jpChronology = Chronology.ofLocale(Locale.JAPANESE);
ChronoLocalDate jpChronoLocalDate = jpChronology.dateNow();
我们在开发过程中应该尽量避免使用ChronoLocalDate
,尽量可以用与历法无关的方式操作时间,但是不同的历法计算日期的方式不一样,比如开发者会在程序中做一些假设,假设一年中有 12 个月,如果是中国农历中包含了闰月,一年有可能是 13 个月,但开发者认为是 12 个月,多出来的一个月被当作是明年的。再比如假设开发者认为年份是累加的,过了一年就在原来的年份上加一,但日本天皇在换代之后需要重新纪年,所以过了一年的年份后可能会从 1 开始计算。
在实际开发过程中建议使用LocalDate
,包括存储、操作、业务规则的解读;除非需要将程序的输入或者输出本地化,这时才使用ChronoLocalDate
类。
日期时间练习题
常见需求
获取当前时间戳
@Test
public void test12() {// 法一long timeMillis1 = System.currentTimeMillis();// 法二long timeMillis2 = new Date().getTime();// 法三~~long timeMillis3 = Calendar.getInstance().getTimeInMillis();~~// 法四long timeMillis4 = Instant.now().toEpochMilli();
}
获取当前时间
@Test
public void test13() {// 不带时区信息:// 法一, 新APILocalDateTime localDateTime = LocalDateTime.now();// 法二, 旧APIDate date = new Date();// 带时区信息:// 法一, 新APIZonedDateTime zonedDateTime = ZonedDateTime.now();// 法二, 旧APICalendar calendar = Calendar.getInstance();
}
获取两个日期相差的毫秒数
@Test
public void test14() {LocalDateTime datetime1 = LocalDateTime.now();LocalDateTime datetime2 = LocalDateTime.of(2048, 1, 1, 0, 0, 0);// 法一, 基于DurationDuration duration = Duration.between(datetime1, datetime2);long millis1 = duration.toMillis();// 法二, 基于时间戳long millis2 = datetime2.toInstant(ZoneOffset.ofHours(8)).toEpochMilli()- datetime1.toInstant(ZoneOffset.ofHours(8)).toEpochMilli();System.out.println(millis1);System.out.println(millis2);
}
Java日期时间API相关推荐
- java8 日期api_我们多么想要新的Java日期/时间API?
java8 日期api 当前的Java.net 民意测验问题是:" 对于用Java 8实现的JSR-310(新的日期和时间API)有多重要? "在我撰写本文时,将近150位受访者投 ...
- 我们多么想要新的Java日期/时间API?
当前的Java.net 民意测验问题是:" 对于用Java 8实现的JSR-310(新的日期和时间API)有多重要? "在我撰写本文时,将近150位受访者投了赞成票,绝大多数人回答 ...
- 一文告诉你Java日期时间API到底有多烂
前言 你好,我是A哥(YourBatman). 好看的代码,千篇一律!难看的代码,卧槽卧槽~其实没有什么代码是"史上最烂"的,要有也只有"史上更烂". 日期是商 ...
- Java —— 日期时间 API
一.java.util.Date 在 JDK 1.1 之前, Date 有两个附加功能. 它允许将日期解释为年,月,日,小时,分钟和第二个值. 它还允许格式化和解析日期字符串. 不幸的是,这些功能的 ...
- java date只保留年月日_Java日期时间API系列14-----Jdk8中日期API类,日期计算1,获取年月日时分秒等...
通过Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析 ,可以看出java8设计非常好,实现接口Temporal, Tempora ...
- 6 日期字符串转日期_Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类...
因为Jdk7及以前的日期时间类的不方便使用问题和线程安全问题等问题,2005年,Stephen Colebourne创建了Joda-Time库,作为替代的日期和时间API.Stephen向JCP提交了 ...
- java date加一天_Java日期时间API系列15-----Jdk8中API类,java日期计算2,年月日时分秒的加减等...
通过Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析 ,可以看出java8设计非常好,实现接口Temporal, Tempora ...
- java 包结构 枚举类_Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类...
因为Jdk7及以前的日期时间类的不方便使用问题和线程安全问题等问题,2005年,Stephen Colebourne创建了Joda-Time库,作为替代的日期和时间API.Stephen向JCP提交了 ...
- java 纳秒 格式化_Java日期时间API系列35-----Jdk8中java.time包中的新的日期时间API类应用,微秒和纳秒等更精确的时间格式化和解析。...
通过Java日期时间API系列1-----Jdk7及以前的日期时间类中得知,Java8以前除了java.sql.Timestamp扩充纳秒,其他类最大只精确到毫秒:Java8 time包所有相关类都支 ...
最新文章
- ABAP性能实例七例
- DuiLib的Combo控件点击无响应的问题
- matlab 雷达图函数,R语言之可视化(20)ggradar雷达图
- Druid 配置_DruidDataSource参考配置
- 拒绝了对对象 'sp_sdidebug'(数据库 'master',所有者 'dbo')的 EXECUTE 权限
- enumset.allof_Java EnumSet allOf()方法与示例
- div固定大小文字溢出自动缩小_Figma 教程 | 文字工具
- mui 头部tab代码2
- 白话解释 Javascript 原型继承(prototype inheritance)
- 在 Linux 下使用 RAID(九):如何使用 ‘Mdadm’ 工具管理软件 RAID
- 01:golang开发环境
- Java Queue 使用总结
- 系统分析师-论文题目
- XSS跨站脚本攻击详解(包括攻击方式和防御方式)
- HR人事管理系统软件有哪些?如何选择HR人事管理软件?
- C语言求三角形斜边长
- windows放到Linux替换,windows过渡到linux之软件的替换
- 《惢客创业日记》2021.01.22(周五)英雄心,狗熊命?
- oracle学习入门系列之五内存结构、数据库结构、进程
- sphinx php搜索引擎,sphinx 全文搜索引擎
热门文章
- 一个显示器分屏显示两个画面_测了两个爆款游戏显示器,结果我发现他们都有坑...
- java 二维码生成和加密base64压码
- Java程序员的薪资取决于年限还是技术?
- 写给数据分析入门者:一种通用的数据分析思路
- 推荐几个海外优秀的新闻网站[中文]
- 操作系统经典问题之吸烟者问题
- 中山大学计算机在职研究生分数线,中山大学在职研究生复试分数线详情
- R语言和医学统计学(6):重复测量方差分析
- python百度云盘搜索引擎_2016百度云网盘搜索引擎源码,附带Python爬虫+PHP网站+Xunsearch搜索引擎...
- 华为云workerman超时ERR_CONNECTION_TIMED_OUT