Java8系列之日期专题

一、Java 8系列之Date API

Java8在包java.time下面包括了一款新的date和time的API。新的Date API和Joda-Time库是相兼容的,但是它们不是一样的。java.time包中的是类是不可变且线程安全的。新的时间及日期API位于java.time中,下面是一些关键类

  • Instant——它代表的是时间戳
  • LocalDate——不包含具体时间的日期,比如2014-01-14。它可以用来存储生日,周年纪念日,入职日期等。
  • LocalTime——它代表的是不含日期的时间
  • LocalDateTime——它包含了日期及时间,不过还是没有偏移信息或者说时区。
  • ZonedDateTime——这是一个包含时区的完整的日期时间,偏移量是以UTC/格林威治时间为基准的。
  • Duration:表示一个时间段。
  • Period:用来表示以年月日来衡量一个时间段。

1、java8是如何处理时间及日期的

LocalDate类中提供了一些很方便的方法可以用来提取年月日以及其他的日期属性,特别方便,只需要使用对应的getter方法就可以了,非常直观

LocalDate localDate = LocalDate.now();
System.out.println("日期 :"+localDate.toString());
System.out.println(String.format("今天是%s年%s月%s日.",
      localDate.getYear(),localDate.getMonth().getValue(),localDate.getDayOfMonth()));
System.out.println(String.format("今天星期是:%s,一周的第 %s 天,一月中的第 %s 天,一年中的第 %s 天",
        localDate.getDayOfWeek(),localDate.getDayOfWeek().getValue(),
        localDate.getDayOfMonth(),localDate.getDayOfYear()));
//获取指定年月日时分秒
LocalDateTime of = LocalDateTime.of(2021, 6, 28, 12, 12, 12);

1.1 java8中的日期与java.util.Date转换

Date转换成LocalDateTime

//Date 转 LocalDateTime
Date startDate=new Date();
LocalDateTime dateToLocalDateTime = startDate.toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDateTime();
System.out.println(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(dateToLocalDateTime));

LocalDateTime 转换成Date

//LocalDateTime 转 Date
Date date1 = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());

时间戳

//java8获取时间戳
long aLong = Instant.now().toEpochMilli();
//以前获取时间戳
long time = new Date().getTime();
long timeMillis = System.currentTimeMillis();

1.2 java8日期与字符串转换

在java8之前,时间日期的格式化非常麻烦,经常使用SimpleDateFormat来进行格式化,但是SimpleDateFormat并不是线程安全的。在java8中,引入了一个全新的线程安全的日期与时间格式器DateTimeFormatter。并且预定义好了格式。

//结果: 20210703
DateTimeFormatter formatter1 = DateTimeFormatter.BASIC_ISO_DATE;
String format1 = formatter1.format(LocalDateTime.now());

//时间转换为字符串
//结果: 2021-07-03 16:50:36
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
String format2 = formatter2.format(LocalDateTime.now());
//字符串格式化为时间
//结果: 2021-07-05T12:12:12
String str1 = "2021-07-05 12:12:12";
LocalDateTime dateTime5 = LocalDateTime.from(formatter2.parse(str1));

//结果: 2021-06-07T12:12:12.666
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.CHINA);
LocalDateTime localDateTime3 = LocalDateTime.parse("2021-06-07T12:12:12.666Z", formatter3);

//自定义格式化工具解析日期
DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
				.parseCaseInsensitive()
                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                .appendLiteral(" ")
                .append(DateTimeFormatter.ISO_LOCAL_TIME)
                .toFormatter(Locale.CHINA);
//转换字符串为LocalDateTime
//结果: 2021-07-03T12:13:14.001
System.out.println(LocalDateTime.parse("2021-07-03 12:13:14.001", dateTimeFormatter));
 //结果: 2022-02-12 09:49:37.589
System.out.println(dateTimeFormatter.format(LocalDateTime.now(ZoneId.systemDefault())));
 //结果: 2022-02-12 09:49:37.589
 System.out.println(dateTimeFormatter.format(LocalDateTime.now(ZoneId.of("Asia/Shanghai"))));

1.3 LocalDateTime日期进行加减

//对LocalDateTime进行时间加减
LocalDateTime now1 = LocalDateTime.now();
//加5天
LocalDateTime dateTime = now1.plus(5, ChronoUnit.DAYS);
System.out.println(dateTime.toString());
//加一个月
LocalDateTime dateTime1 = now1.plusMonths(1);
//加3个小时
LocalDateTime dateTime2 = now1.plusHours(3);
 //减1年1月1天
LocalDateTime dateTime3 = now1.plusYears(-1).plusMonths(-1).plusDays(-1);
//减1小时3分30秒
LocalDateTime dateTime4 = now1.plusHours(-1).plusMinutes(-5).plusSeconds(-30);

1.4 比较两个日期

//java8日期进行比较
LocalDateTime localDateTime1 = LocalDateTime.of(2021, 12, 12, 12, 12, 12);
LocalDateTime localDateTime2 = LocalDateTime.of(2021, 12, 12, 12, 12, 10);
//false
boolean before = localDateTime1.isBefore(localDateTime2);
//true
boolean after = localDateTime1.isAfter(localDateTime2);
//false
boolean equals = localDateTime1.equals(localDateTime2);

1.5 获取两个时间的差值

在Java8中,我们可以使用以下类来计算日期时间差异:

  1. Period : 主要是Period类方法getYears(),getMonths()和getDays()来计算.
//自定义格式化工具解析日期
DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
 				.parseCaseInsensitive()
                .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral(" ")
                .append(DateTimeFormatter.ISO_LOCAL_TIME)
                .toFormatter(Locale.CHINA);

//方法一:使用Period,只能获取到年、月、日的差异
//结果: 差异是: 0年,-10月,-29日
LocalDateTime dt1 = LocalDateTime.parse("2021-07-03 12:13:14.001", dateTimeFormatter);
LocalDateTime dt2 = LocalDateTime.parse("2020-08-05 13:13:19.222", dateTimeFormatter);
Period period = Period.between(dt1.toLocalDate(), dt2.toLocalDate());
log.info(String.format("差异是: %s年,%s月,%s日", 
			period.getYears(), period.getMonths(), period.getDays()));
  1. Duration : 提供了使用基于时间的值(如秒,纳秒)测量时间量的方法。
//方法二:Duration类, 提供了使用基于时间的值(如秒,纳秒)测量时间量的方法。
//结果: 相差秒为: -86395, 相差微秒为: -86394779
LocalDateTime dt21 = LocalDateTime.parse("2021-07-03 12:13:14.001", dateTimeFormatter);
LocalDateTime dt22 = LocalDateTime.parse("2021-07-02 12:13:19.222", dateTimeFormatter);
log.info("相差秒为: {}, 相差微秒为: {}", 
			Duration.between(dt21, dt22).getSeconds(), Duration.between(dt21, dt22).toMillis());
  1. ChronoUnit : 可用于在单个时间单位内测量一段时间,例如天数或秒。
//方法三:ChronoUnit类, ChronoUnit类可用于在单个时间单位内测量一段时间
LocalDateTime dt31 = LocalDateTime.parse("2020-06-05 13:13:19.222", dateTimeFormatter);
LocalDateTime dt32 = LocalDateTime.parse("2021-07-05 14:13:14.001", dateTimeFormatter);
log.info("相差{}年, 相差{}月, 相差{}天, 相差{}时, 相差{}分, 相差{}秒",
                ChronoUnit.YEARS.between(dt31, dt32), ChronoUnit.MONTHS.between(dt31, dt32), 
                ChronoUnit.DAYS.between(dt31, dt32),ChronoUnit.HOURS.between(dt31, dt32), 
                ChronoUnit.MINUTES.between(dt31, dt32), ChronoUnit.SECONDS.between(dt31, dt32));

1.6 时区

关注时区的日期时间 API: java.time.ZonedDateTime 和 java.time.ZoneId。前者用于处理需要时区的日期时间,后者用于处理时区。ZonedDateTime 和 LocalDateTime 类似,几乎有着相同的 API。从某些方面说,ZonedLocalTime 如果不传递时区信息,那么它会默认使用操作系统的时区,这样,结果其实和 LocalDateTime 是类似的。

java7及之前时区

//java7及之前的时区
	Calendar calendar = Calendar.getInstance();
	calendar.set(2021,6,12,10,11,30);
	Date dTime = calendar.getTime();

//1.1 将当指定时间格式化为GMT时间
	SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
	df.setTimeZone(TimeZone.getTimeZone("GMT"));
	String data1 = df.format(dTime);
	//结果: 2021-07-12T02:11:30.736Z
	System.out.println(data1);
	
//1.2 将指定GMT时间格式化为当前时区时间
	Date parse = df.parse("2021-07-06T14:46:01.964Z");
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	dateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Shanghai")));
	System.out.println(dateFormat.format(parse));

java8时区

LocalDateTime time1 = LocalDateTime.now();
        ZonedDateTime zonedDateTime2 = time1.atZone(ZoneId.of("Asia/Shanghai"));
        //2021-07-06T22:57:07.054+08:00[Asia/Shanghai]
        System.out.println(zonedDateTime2);
        
        ZonedDateTime zonedDateTime1 = time1.atZone(ZoneId.of("America/New_York"));
        //2021-07-06T22:57:07.054-04:00[America/New_York]
        System.out.println(zonedDateTime1);
       
        //获取当前亚洲上海时间
        System.out.println(LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
        //获取当前美图纽约时间
        System.out.println(LocalDateTime.now(ZoneId.of("America/New_York")));

2、Spring Boot和Feign中Java 8时间日期API的序列化问题

LocalDate、LocalTime、LocalDateTime是Java 8开始提供的时间日期API,主要用来优化Java 8以前对于时间日期的处理操作。然而,我们在使用Spring Boot或使用Spring Cloud Feign的时候,往往会发现使用请求参数或返回结果中有LocalDate、LocalTime、LocalDateTime的时候会发生各种问题。

下面将提出解决方案:
为了解决上面的问题非常简单,因为jackson也为此提供了一整套的序列化方案,我们只需要在pom.xml中引入jackson-datatype-jsr310依赖,具体如下:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

注意:在设置了spring boot的parent的情况下不需要指定具体的版本,也不建议指定某个具体版本。

方法一:
在该模块中封装对Java 8的时间日期API序列化的实现,其具体实现在这个类中:com.fasterxml.jackson.datatype.jsr310.JavaTimeModule。在配置了依赖之后,我们只需要在上面的应用主类中增加这个序列化模块,并禁用对日期以时间戳方式输出的特性:

private static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
   @Override
   public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers)
                throws IOException {
            gen.writeString(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        }
    }

private static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
    }

// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();

static {
	JavaTimeModule javaTimeModule = new JavaTimeModule();
	javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
	javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
	MAPPER.registerModule(javaTimeModule);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值