Java 时间日期
Java 8 推出了全新的日期时间API。
Java处理日期、日历和时间的不足之处:将 java.util.Date 设定为可变类型,以及 SimpleDateFormat 的非线程安全使其应用非常受限。然后就在 java8 上面增加新的特性。
在旧版的 Java 中,日期时间 API 存在诸多问题,例如:
- 非线程安全:
java.util.Date是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。 - 设计很差:Java的日期/时间类的定义并不一致,在
java.util和java.sql的包中都有日期类,此外用于格式化和解析的类被定义在java.text包中。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。 - 时区处理麻烦:日期类并不提供国际化,没有时区支持,因此 Java 引入了
java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
因为上面这些原因,诞生了第三方库Joda-Time,可以替代 Java 的时间管理 API 。
Java 8 中新的时间和日期管理 API 深受Joda-Time影响,并吸收了很多Joda-Time的精华,新的java.time包包含了所有关于日期、时间、时区、Instant(跟日期类似但是精确到纳秒)、Duration(持续时间)和时钟操作的类。
新设计的 API 认真考虑了这些类的不变性,如果某个实例需要修改,则返回一个新的对象。
全新API的众多好处之一就是,明确了日期时间概念,例如:瞬时(instant)、 长短(duration)、日期、时间、时区和周期。
同时继承了Joda 库按人类语言和计算机各自解析的时间处理方式。不同于老版本,新API基于ISO标准日历系统,java.time包下的所有类都是不可变类型而且线程安全。
Instant:瞬时实例。LocalDate:本地日期,不包含具体时间 例如:2014-01-14 可以用来记录生日、纪念日、加盟日等。LocalTime:本地时间,不包含日期。LocalDateTime:组合了日期和时间,但不包含时差和时区信息。ZonedDateTime:最完整的日期时间,包含时区和相对UTC或格林威治的时差。
新API还引入了 ZoneOffSet 和 ZoneId 类,使得解决时区问题更为简便。解析、格式化时间的 DateTimeFormatter类也全部重新设计。
1、Clock类——获取当前的纳秒时间和日期
Clock类使用时区来返回当前的纳秒时间和日期。Clock可以替代System.currentTimeMillis()和TimeZone.getDefault(),实例如下:
final Clock clock = Clock.systemUTC();System.out.println( clock.instant() );System.out.println( clock.millis() );
输出结果是
2021-02-24T12:24:54.678Z1614169494678
2、LocalDate、LocalTime、LocalDateTime类
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。
注:ISO-8601日历系统是国际标准化组织指定的现代公民的日期和时间的表示法。
核心类
LocalDate:不包含具体时间的日期。LocalTime:不含日期的时间。LocalDateTime:包含了日期及时间。
LocalDate常用API——只获取年月日
getYear()int 获取当前日期的年份getMonth()Month 获取当前日期的月份对象getMonthValue()int 获取当前日期是第几月getDayOfWeek()DayOfWeek 表示该对象表示的日期是星期几getDayOfMonth()int 表示该对象表示的日期是这个月第几天getDayOfYear()int 表示该对象表示的日期是今年第几天withYear(int year)LocalDate 修改当前对象的年份withMonth(int month)LocalDate 修改当前对象的月份withDayOfMonth(int dayOfMonth)LocalDate 修改当前对象在当月的日期plusYears(long yearsToAdd)LocalDate 当前对象增加指定的年份数plusMonths(long monthsToAdd)LocalDate 当前对象增加指定的月份数plusWeeks(long weeksToAdd)LocalDate 当前对象增加指定的周数plusDays(long daysToAdd)LocalDate 当前对象增加指定的天数minusYears(long yearsToSubtract)LocalDate 当前对象减去指定的年数minusMonths(long monthsToSubtract)LocalDate 当前对象减去注定的月数minusWeeks(long weeksToSubtract)LocalDate 当前对象减去指定的周数minusDays(long daysToSubtract)LocalDate 当前对象减去指定的天数compareTo(ChronoLocalDate other)int 比较当前对象和other对象在时间上的大小,返回值如果为正,则当前对象时间较晚,isBefore(ChronoLocalDate other)boolean 比较当前对象日期是否在other对象日期之前isAfter(ChronoLocalDate other)boolean 比较当前对象日期是否在other对象日期之后isEqual(ChronoLocalDate other)boolean 比较两个日期对象是否相等创建
LocalDate//获取当前年月日LocalDate localDate = LocalDate.now();//构造指定的年月日LocalDate localDate1 = LocalDate.of(2019, 9, 10);
获取年、月、日、星期几
int year = localDate.getYear();int year1 = localDate.get(ChronoField.YEAR);Month month = localDate.getMonth();int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);int day = localDate.getDayOfMonth();int day1 = localDate.get(ChronoField.DAY_OF_MONTH);DayOfWeek dayOfWeek = localDate.getDayOfWeek();int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
LocalTime——只获取几点几分几秒创建
LocalTimeLocalTime localTime = LocalTime.of(13, 51, 10);LocalTime localTime1 = LocalTime.now();
获取时分秒
//获取小时int hour = localTime.getHour();int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);//获取分int minute = localTime.getMinute();int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);//获取秒int second = localTime.getSecond();int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
LocalDateTime——获取年月日时分秒,等于LocalDate+LocalTime创建
LocalDateTimeLocalDateTime localDateTime = LocalDateTime.now();LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);LocalDateTime localDateTime3 = localDate.atTime(localTime);LocalDateTime localDateTime4 = localTime.atDate(localDate);
获取
LocalDateLocalDate localDate2 = localDateTime.toLocalDate();
获取
LocalTimeLocalTime localTime2 = localDateTime.toLocalTime();
3、
Instant类——时间戳、获取秒数创建
Instant对象Instant instant = Instant.now();
获取秒数
long currentSecond = instant.getEpochSecond();
获取毫秒数
long currentMilli = instant.toEpochMilli();
// 2.Instant:时间戳(以Unix元年:1970年1月1日 00:00:00 到某个时间之间的毫秒值@Testpublic void instantTest() {// 默认获取UTC时区Instant instant = Instant.now();System.out.println(instant);// 在原来的时间上加8个小时OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));System.out.println(offsetDateTime);System.out.println(instant.toEpochMilli());// 在Unix元年时间戳加60秒Instant ofEpochSecond = Instant.ofEpochSecond(60);System.out.println(ofEpochSecond);}
4、时间计算
Duration:计算两个时间的时间差/*** Period:计算两个日期之间的差*/@Testpublic void calTimeTest() throws InterruptedException {System.out.println("------------计算两个时间之间的间隔-----------");Instant now = Instant.now();Thread.sleep(1000);Instant end = Instant.now();Duration duration = Duration.between(now, end);System.out.println(duration.toMillis());System.out.println("--------------------------");LocalTime localTime = LocalTime.now();Thread.sleep(1000);LocalTime l = LocalTime.now();Duration between = Duration.between(localTime, l);System.out.println(between.toMillis());}
Period:计算两个日期之间的差/*** Duration:计算两个时间的时间差*/@Testpublic void calTimeTest() throws InterruptedException {System.out.println("------------计算两个日期之间的间隔-----------");LocalDate localDate = LocalDate.of(2025, 6, 12);LocalDate date = LocalDate.now();Period period = Period.between(date, localDate);System.out.println(period.getYears());System.out.println(period.getMonths());System.out.println(period.getDays());}
5、日期的操纵-时间校正器
TemporalAdjuster:时间校正器有时可能需要获取将日期调整至“下个周日”等操作。
LocalDate nextSunday = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));// 修改年为2019localDateTime = localDateTime.withYear(2020);// 修改为2022localDateTime = localDateTime.with(ChronoField.YEAR, 2022);
TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster的实现。// TemporalAdjuster : 时间校正器@Testpublic void temporalAdjusterTest() {LocalDateTime now = LocalDateTime.now();System.out.println(now);LocalDateTime localDateTime = now.withDayOfMonth(10);System.out.println(localDateTime);LocalDateTime friday = now.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));System.out.println(friday);// 自定义:下一个工作日LocalDateTime nextWorkDay = now.with((d) -> {LocalDateTime dateTime = (LocalDateTime) d;DayOfWeek dayOfWeek = dateTime.getDayOfWeek();if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {return dateTime.plusDays(3);} else if (dateTime.equals(DayOfWeek.FRIDAY)) {return dateTime.plusDays(2);} else {return dateTime.plusDays(1);}});System.out.println(nextWorkDay);}
通过
firstDayOfYear()返回了当前日期的第一天日期```java import java.time.LocalDate; import static java.time.temporal.TemporalAdjusters.firstDayOfYear;
public class TestTemporalAdjusters {
public static void main(String[] args) {LocalDate.now().with(firstDayOfYear());}
}
<a name="NOLtb"></a>## 6、`DateTimeFormatter`——格式化时间和日期<a name="37cef521"></a>### 解析时间```javaLocalDate localDate1 = LocalDate.parse("20190910", DateTimeFormatter.BASIC_ISO_DATE);LocalDate localDate2 = LocalDate.parse("2019-09-10", DateTimeFormatter.ISO_LOCAL_DATE);
和SimpleDateFormat相比,DateTimeFormatter是线程安全的。
// DateTimeFormatter:格式化时间日期@Testpublic void dateTimeFormatterTest() {// 使用官方提供的格式DateTimeFormatter ISO_DATE = DateTimeFormatter.ISO_DATE;LocalDateTime now = LocalDateTime.now();String formatDate = now.format(ISO_DATE);System.out.println(formatDate);// 自定义格式DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");String selfDate = dateTimeFormatter.format(now);System.out.println(selfDate);// 字符串日期转回日期类型LocalDateTime parseDate = now.parse(selfDate, dateTimeFormatter);System.out.println(parseDate);}
将Date类型的日期转换为LocalDateTime
/*** 将Date类型的日期转换为LocalDateTime** @param date Date类型的对象* @return LocalDateTime* @author @author Fcant 下午 18:27 2020/6/30/0030*/public static LocalDateTime dateToLocalDateTime(Date date) {return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());}
将LocalDate日期进行格式化
/*** 将LocalDate日期进行格式化** @param date 格式化的Date对象* @param format 格式化的样式:"yyyy-MM-dd HH:mm:ss"* @return String* @author @author Fcant 下午 18:27 2020/6/30/0030*/public static String formatDateTime(Date date, String format) {LocalDateTime localDateTime = dateToLocalDateTime(date);return formatLocalDateTime(localDateTime, format);}/*** 格式化LocalDateTime** @param localDateTime 要格式化的localDateTime* @param format 格式化的样式:"yyyy-MM-dd HH:mm:ss"* @return String* @author @author Fcant 下午 18:27 2020/6/30/0030*/public static String formatLocalDateTime(LocalDateTime localDateTime, String format) {return DateTimeFormatter.ofPattern(format).format(localDateTime);}
格式化LocalDateTime
/*** 格式化LocalDateTime** @param localDateTime 要格式化的localDateTime* @param format 格式化的样式:"yyyy-MM-dd HH:mm:ss"* @return String* @author @author Fcant 下午 18:27 2020/6/30/0030*/public static String formatLocalDateTime(LocalDateTime localDateTime, String format) {return DateTimeFormatter.ofPattern(format).format(localDateTime);}
String类型的日期转换为LocalDateTime
/*** String类型的日期转换为LocalDateTime** @param date String类型的日期参数* @param fmt String类型的日期的格式* @return LocalDateTime* @author Fcant 下午 17:22 2020/7/9/0009*/public static LocalDateTime stringToLocal(String date, String fmt) {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(fmt);return LocalDateTime.parse(date, dateTimeFormatter);}
将LocalDateTime转换为Date类型的日期
/*** 将LocalDateTime转换为Date类型的日期** @param localDateTime 要转换的LocalDateTime参数* @return Date* @author Fcant 下午 17:21 2020/7/9/0009*/public static Date localToDate(LocalDateTime localDateTime) {ZoneId zoneId = ZoneId.systemDefault();ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);return Date.from(zonedDateTime.toInstant());}
7、时区的操作
Java8加入了对时区的支持,带时区的时间分别为:ZoneDate、ZoneTime、ZoneDateTime,其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如Asis/Shanghai等ZoneId:该类中包含了所有的时区信息。getAvailableZoneIDs():可以获取所有时区信息;of(id):用指定的时区信息获取ZoneId对象。
// ZoneDate、ZoneTime、ZoneDateTime@Testpublic void zoneDateTimeTest() {Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();availableZoneIds.forEach(System.out::println);// 根据指定时区获取当前时间LocalDateTime now = LocalDateTime.now(ZoneId.of("Africa/Nairobi"));System.out.println(now);// 获取时间并为之指定时区LocalDateTime localDateTime = LocalDateTime.now();ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("US/Pacific"));System.out.println(zonedDateTime);}
// 获取当前时间日期final ZonedDateTime zonedDatetime = ZonedDateTime.now();//获取指定时钟的日期时间final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );//获取纽约时区的当前时间日期final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of("America/New_York") );System.out.println( zonedDatetime );System.out.println( zonedDatetimeFromClock );System.out.println( zonedDatetimeFromZone );
2021-02-24T20:42:27.238+08:00[Asia/Shanghai]2021-02-24T12:42:27.238Z2021-02-24T07:42:27.241-05:00[America/New_York]
8、SpringBoot中应用LocalDateTime
将LocalDateTime字段以时间戳的方式返回给前端
添加日期转化类
public class LocalDateTimeConverter extends JsonSerializer<LocalDateTime> {@Overridepublic void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {gen.writeNumber(value.toInstant(ZoneOffset.of("+8")).toEpochMilli());}}
并在LocalDateTime字段上添加
@JsonSerialize(using = LocalDateTimeConverter.class)
注解,如下:
@JsonSerialize(using = LocalDateTimeConverter.class)protected LocalDateTime gmtModified;
将LocalDateTime字段以指定格式化日期的方式返回给前端
在LocalDateTime字段上添加
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
注解即可,如下:
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")protected LocalDateTime gmtModified;
对前端传入的日期进行格式化
在LocalDateTime字段上添加
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
注解即可,如下:
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")protected LocalDateTime gmtModified;
