#过年不停更#SpringMVC关于接收时间相关的问题 原创

新一代程序猿
发布于 2022-1-20 09:22
浏览
0收藏

春节不停更,此文正在参加「星光计划-春节更帖活动

java有那些时间操作类:

JDK8之前,处理日期时间的类是Date、Calendar
JDK8发布的时候,推出了LocalDate、LocalTime、LocalDateTime这个三个时间处理类

Date

在JDK 1.1之前, Date有两个附加功能。 它允许将日期解释为年,月,日,小时,分钟和第二个值。 它还允许格式化和解析日期字符串。 不幸的是,这些功能的API不适合国际化。 从JDK 1.1开始, Calendar类应该用于在日期和时间字段之间进行转换,并且DateFormat类应用于格式化和解析日期字符串。 在相应的方法Date被弃用。

尽管Date类旨在反映协调的世界时间(UTC),但根据Java虚拟机的主机环境的不同,可能不会这样做。 几乎所有的现代操作系统都假设在所有情况下1天= 24×60×60 = 86400秒。 然而,在UTC的时候,大概每一两年会有一秒钟的时间,叫做“闰秒”。 闰秒总是作为一天的最后一秒,总是在12月31日或6月30日。例如,1995年的最后一分钟是61秒,由于增加了闰秒。 大多数计算机时钟不够准确,不能反映出闰秒的区别。

一些计算机标准是根据格林尼治标准时间(GMT)定义的,相当于世界时间(UT)。 GMT是标准的“民用”名称; UT是同一标准的“科学”名称。 UTC和UT之间的区别是UTC是基于原子钟,UT是基于天文观测,对于所有的实际目的来说,这是一个看不见的细毛。 因为地球的旋转不均匀(减速并以复杂的方式加速),UT并不总是均匀地流动。 根据需要,将时差引入到UTC中,以使UT在UT1的0.9秒内保持UTC,这是UT的版本,并应用了某些修正。 还有其他的时间和日期系统; 例如,基于卫星的全球定位系统(GPS)使用的时间尺度与UTC同步,但不对闰秒进行调整。 进一步信息的一个有趣的来源是美国海军天文台,特别是时间局在:

    //创建一个代表系统当前日期的Date对象
        Date d1 = new Date();

       log.info("当前时间:"+d1);
       log.info("时间毫秒数:"+System.currentTimeMillis());

        //y 代表年
        //M 代表月
        //d 代表日
        //H 代表24进制的小时
        //h 代表12进制的小时
        //m 代表分钟
        //s 代表秒
        //S 代表毫秒
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
        String format = sdf.format(d1);
        log.info("格式化后:"+format);

        //使用带参数的构造方法Date(int year, int month, int day) ,
        // 可以构造指定日期的Date类对象,Date类中年份的参数应该是实际需要代表的年份减去1900,
        // 实际需要代表的月份减去1以后的值。
        Date d2 =  new Date(2020-1900, 6-1, 12);//过时了不推荐使用
        log.info("指定时间:"+d2);

        //获得年份 (注意年份要加上1900,这样才是日期对象d2所代表的年份)

        int year = d2.getYear() + 1900;//过时了不推荐使用
        log.info("年份:"+year);

        //获得月份  (注意月份要加1,这样才是日期对象d2所代表的月份)

        int month = d2.getMonth() + 1;//过时了不推荐使用
        log.info("月份:"+month);

        //获得日期

        int date = d2.getDate();//过时了不推荐使用
        log.info("日期:"+month);


        //获得小时

        int hour = d2.getHours();//不设置默认为0 。过时了不推荐使用
        log.info("小时:"+hour);

        //获得分钟

        int minute = d2.getMinutes();//过时了不推荐使用
        log.info("分钟:"+minute);

        //获得秒

        int second = d2.getSeconds();//过时了不推荐使用
        log.info("秒:"+second);

        //获得星期 (注意:0代表星期日、1代表星期1、2代表星期2,其他的一次类推了)

        int day = d2.getDay();//过时了不推荐使用
        log.info("星期:"+day);

java.util.Date,java.sql.Date区别
1、java.sql.Date是针对SQL语句使用的,它只包含日期而没有时间部分,一般在读写数据库的时候用,PreparedStament的setDate()的参数和ResultSet的getDate()方法的都是java.sql.Date
2、java.util.Date是在除了SQL语句的情况下面使用,一般是日常日期字段
3、java.util.Date 是 java.sql.Date 的父类,即:继承关系:java.lang.Object --》 java.util.Date --》 java.sql.Date
4.java.sql.Date 使用率非常低

Calendar

Calendar类是一个抽象类,可以为在某一特定时刻和一组之间的转换的方法calendar fields如YEAR , MONTH , DAY_OF_MONTH , HOUR ,等等,以及用于操纵该日历字段,如获取的日期下个星期。 时间上的瞬间可以用毫秒值表示,该值是从1970年1月1日00:00 00:00.000 GMT(Gregorian)的Epoch的偏移量

@Slf4j
public class Calendardome {
    public static void main(String[] args) {

        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss HHH" );

        // 使用当前时间创建
        Calendar calendar1 = Calendar.getInstance();
        log.info("当前时间:"+calendar1);
        log.info("格式化后:"+sdf.format(calendar1.getTime()));
        // 指定时间创建,参数依次为:年,月,日,时,分,秒,月份需要减一
        Calendar calendar2 = new GregorianCalendar(2016, 8, 1, 15, 41, 22);
        log.info("指定时间:"+calendar2);

        log.info("格式化后:"+sdf.format(calendar2.getTime()));
         //获得年
        int year = calendar2.get(Calendar.YEAR);
        log.info("年:"+year);
        // 取月份要加1
        int month = calendar2.get(Calendar.MONTH) + 1;
        log.info("月份:"+month);
        //获得天
        int day = calendar2.get(Calendar.DAY_OF_MONTH);
        log.info("天:"+day);
        //获得时
        int hover = calendar2.get(Calendar.HOUR_OF_DAY);
        log.info("时:"+hover);
        //获得分钟
        int minute = calendar2.get(Calendar.MINUTE);
        log.info("分钟:"+minute);
        //获得秒
        int seconds = calendar2.get(Calendar.SECOND);
        log.info("秒:"+seconds);
       // 1-7分别代表 -- 星期日,星期一,星期二,星期三,星期四,星期五,星期六
        //获得星期
        int week = calendar2.get(Calendar.DAY_OF_WEEK);
        log.info("星期:"+week);

        String timeDeffer = getTimeDeffer(sdf.format(calendar1.getTime()), sdf.format(calendar2.getTime()));
         log.info(timeDeffer);

    }
    /**
     2      * 获取时间差
     3      * @param time1 time2 需要计算的时间
     4      * @return
     5      */
      public static String getTimeDeffer(String time1,String time2)
    {
               String result = "";
                 if(StringUtils.isNotBlank(time1)&& StringUtils.isNotBlank(time2)){
                      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                      try {
                               long dt1 = sdf.parse( time1 ).getTime();
                               long dt2 = sdf.parse( time2 ).getTime();
                                long dc = Math.abs(dt2-dt1);
                                long seconds = dc/1000;
                                long date = seconds/(24*60*60);     //相差的天数
                          long hour = (seconds-date*24*60*60)/(60*60);//相差的小时数
                                long minut = (seconds-date*24*60*60-hour*60*60)/(60);//相差的分钟数
                          long second = (seconds-date*24*60*60-hour*60*60-minut*60);//相差的秒数
                          return (date==0?"":(date+"天"))+(hour==0?"":(hour+"小时"))+(minut==0?"":(minut+"分"))+(second==0?"":(second+"秒"));
                      } catch ( ParseException e) {
                          e.printStackTrace();
                             }
                     }
                 return result;
             }
}

LocalDate、LocalTime、LocalDateTime

JDK8为什么要推出LocalDate、LocalTime、LocalDateTime这个三个时间处理类?
以此来弥补之前的日期时间类的不足,简化日期时间的操作。在Java8之前,处理日期时间的类是Date、Calendar,这两个在使用起来总是让人感觉不是很舒服,在设计上面有一些缺陷,并且还不是线程安全的。

想要详细的了解这几个类,我们得要了解一点关于时间、时区划分的一些知识。1884 年, 国际经度会议将地球表面按经线等分为24 区,称为时区。即以本初子午线为基准, 东西经度各7.5 度的范围作为零时区, 然后每隔15度为一时区,每个时区相差一小时。北京时区为东八区,要比零时区早8个小时。如果现在零时区的时间是10点的话,那北京时间就是18点。

我们平时在程序里面所见到的UTC时间,就是零时区的时间,它的全称是Coordinated Universal Time ,即世界协调时间。另一个常见的缩写是GMT,即格林威治标准时间,格林威治位于 零时区,因此,我们平时说的UTC时间和GMT时间在数值上面都是一样的。

可能会有一些疑问,同样是在地球上啊,不同的地区时间怎么还不一样了?难道我现在从北京乘个飞机飞到格林威治,还可以来个时光倒流?如果这样,岂不很神奇?哈哈,当然事情的真相并不是这样的。时间的流逝、细胞的衰老对于整个地球来说都是一样的,因此,不管你在地球的哪里,这一秒过去了,就是过去了,它最为公平,不会多你一分,也不会少你一秒。那怎么记录这流逝的分分秒秒呢?有一个名词,叫做时间戳,它是指格林威治(地球零时区)时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数,这个时间戳,在地球的各个地方都是一致的。在1970年以前,有其他的计时方式,由于没有统一,还造成了一些软件的运行在时间上的错乱,险些酿成了的行业灾难(具体的事件可以网上搜索)。如果单纯指望这个时间戳作为人们的计时标准,那也是不现实的,因为这对人类的生产生活来说没有任何的意义。举个例子:每天早上八点上课(上班)符合我们的习惯,如果是***秒到了上课时间就有违人的正常脑回路了。这时候,根据地球上的不同经纬度、地球自转的特点,就划分了时区,时间戳根据不同的时区转化成当地的时间,以此作为作息标准,从而方便人们的生产生活。北京时间的8点我们可以见到太阳的升起,伦敦时间的八点他们也能见到太阳的升起。

就这样,可以知道,时间戳对地球上的任何一个地方都是一样的,如果我们想要把时间戳转化成当地的时间,就需要根据所在地区的时区进行转化。不同时区之间进行时间转化也是一样的道理,我们需要根据时区的差异来转化当地的时间

@Slf4j
public class LocalTimedome {
    public static void main(String[] args) {
    //获取当前时区的日期
        LocalDate localDate = LocalDate.now();
     log.info("localDate: " + localDate);
        //时间
        LocalTime localTime = LocalTime.now();
       log.info("localTime: " + localTime);
        //根据上面两个对象,获取日期时间
        LocalDateTime localDateTime = LocalDateTime.of(localDate,localTime);
       log.info("localDateTime: " + localDateTime);
        //使用静态方法生成此对象
        LocalDateTime localDateTime2 = LocalDateTime.now();
       log.info("localDateTime2: " + localDateTime2);
        //格式化时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
       log.info("格式化之后的时间: " + localDateTime2.format(formatter));

        //转化为时间戳(秒)
        long epochSecond = localDateTime2.toEpochSecond(ZoneOffset.of("+8"));
        //转化为毫秒
        long epochMilli = localDateTime2.atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();
       log.info("时间戳为:(秒) " + epochSecond + "; (毫秒): " + epochMilli);

        //时间戳(毫秒)转化成LocalDateTime
        Instant instant = Instant.ofEpochMilli(epochMilli);
        LocalDateTime localDateTime3 = LocalDateTime.ofInstant(instant, ZoneOffset.systemDefault());
       log.info("时间戳(毫秒)转化成LocalDateTime: " + localDateTime3.format(formatter));
        //时间戳(秒)转化成LocalDateTime
        Instant instant2 = Instant.ofEpochSecond(epochSecond);
        LocalDateTime localDateTime4 = LocalDateTime.ofInstant(instant2, ZoneOffset.systemDefault());
       log.info("时间戳(秒)转化成LocalDateTime: " + localDateTime4.format(formatter));

    }
}

新推出来的这三个类,与MySQL中的日期时间类型正好对应,如果我们使用MySQL数据库的话,在插入相应字段的时候,都不需要再进行任何的转化了。对应关系如下:

  • LocalTime  对应  time
  • LocalDate  对应  date
  • LocalDateTime  对应  datetime(timestamp)

时区(Zone)

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS ", Locale.US);

        Calendar cd = Calendar.getInstance();
        // 设置时区为GMT
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        String str = sdf.format(cd.getTime());
        log.info("格林尼治标准时间:"+str);
        Calendar cd2 = Calendar.getInstance();
        // 设置时区为GMT
        sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        String str2 = sdf.format(cd2.getTime());
        log.info("北京时间:"+str2);



        // 输出所有时区
        //Set<String> set = ZoneId.getAvailableZoneIds();
        //set.forEach(System.out::println);
        LocalDateTime dateTime = LocalDateTime.now();
        dateTime.atZone(ZoneId.of("Asia/Shanghai"));
        log.info("中国上海时间:"+dateTime);
        log.info("中国上海时间:"+LocalDateTime.now(ZoneId.of("Asia/Shanghai")));
        log.info("美国纽约时间:"+LocalDateTime.now(ZoneId.of("America/New_York")));
        log.info("英国伦敦时间:"+LocalDateTime.now(ZoneId.of("Europe/London")));
        // 带时区的时间或日期
        log.info("中国上海时间(带时区的时间或日期):"+ZonedDateTime.now(ZoneId.of("Asia/Shanghai")));

        log.info("中国上海时间(带时区的时间或日期):"+ZonedDateTime.now());
        // LocalDateTime转为ZonedDateTime(带时区的时间或日期)
        LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of("Asia/Shanghai"));
        log.info("中国上海时间(localDateTime):"+localDateTime);
        log.info("中国上海时间(zonedDateTime):"+zonedDateTime);
        // ZonedDateTime(带时区的时间或日期)转 LocalDateTime
        LocalDateTime localDateTime1 = zonedDateTime.toLocalDateTime();
        log.info("中国上海时间(带时区的时间或日期):"+localDateTime1);


Spring关于接收时间相关的问题

前面带大家认识了java关于时间的操作类,在springMvc中你们有没有遇到这样的问题?

  • LocalDateTime 类型转换问题
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2020-5-19": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2020-5-19' could not be parsed at index 5; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2020-5-19": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2020-5-19' could not be parsed at index 5
 at [Source: (PushbackInputStream); line: 5, column: 17] (through reference chain: com.wpp.springboottime.bean.User["currenttime"])]
  • Date类型转换问题
Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.util.Date` from String "1980-09-08 11:30": not a valid representation (error: Failed to parse Date value '1980-09-08 11:30': Cannot parse date "1980-09-08 11:30": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null)); nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "1980-09-08 11:30": not a valid representation (error: Failed to parse Date value '1980-09-08 11:30': Cannot parse date "1980-09-08 11:30": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSSX', parsing fails (leniency? null))
 at [Source: (PushbackInputStream); line: 4, column: 14] (through reference chain: com.wpp.springboottime.bean.User["birthday"])]
  • 前端时间没有格式化
    #过年不停更#SpringMVC关于接收时间相关的问题-鸿蒙开发者社区

全局处理

处理Date类型

spring:
  jackson:
    #参数意义:
    #JsonInclude.Include.ALWAYS              默认
    #JsonInclude.Include.NON_DEFAULT     属性为默认值不序列化
    #JsonInclude.Include.NON_EMPTY         属性为 空(””) 或者为 NULL 都不序列化
    #JsonInclude.Include.NON_NULL           属性为NULL   不序列化
    default-property-inclusion: ALWAYS
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss

处理LocalDateTime 类型

@Configuration
public class DateformatConfig {

    /**
     * Date格式化字符串
     */
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    /**
     * DateTime格式化字符串
     */
    private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * Time格式化字符串
     */
    private static final String TIME_FORMAT = "HH:mm:ss";

    /**
     * 自定义Bean
     *
     * @return
     */
    @Bean
    @Primary
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)))
                .serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
                .serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT)))
                .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATETIME_FORMAT)))
                .deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DATE_FORMAT)))
                .deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT)));
    }

}

局部通用方法

@DateTimeFormat,@DateTimeFormat针对LocalDateTime ,Date 类型转换问题通用

  @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")

@JsonFormat只适合 Content-Type 为application/json的请求
@DateTimeFormat只适合表单请求

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
收藏
回复
举报
回复
    相关推荐