• JAVA中Calendar与Date类型


    1.参考文档:

         Java之Date类和Calendar类的区别

         Java日期处理的十个坑

          SimpleDateFormat线程不安全及解决办法

          Java时间格式化原来这么多玩法

          JAVA LocalDateTime,Date,String,Long 日期时间用DateTimeFormatter相互转换以及Calendar的简单使用  

         【Java】日期时间类

    2.背景:

             《java核心技术1》 中,类库的设计者希望能够将时间点和日期分开:一个是用来表示时间点的Date类,一个是用来表示大家熟悉的日历表示法的GregorianCalendar类,事实上,GregorianCalendar类拓展了一个更加通用的Calendar类,这个类描述了日历的一般属性。

              在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不便于实现国际化,所以从JDK1.1版本开始,推荐使用Calendar类进行时间和

    日期处理 。

    主要区别:java.util.Date是个日期数据java.util.Calendar 用于日期相关的计算

    3.常用时间函数与注意事项

    3.1 Date与Calendar之间的区别与转换   

    查看代码
    //1. 两个对象之间的不同
            Date date=new Date(); // Sun Apr 17 22:05:46 CST 2022
            Calendar ca=Calendar.getInstance(); //java.util.GregorianCalendar[time=1650204346836,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,
            // lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=3,WEEK_OF_YEAR=17,WEEK_OF_MONTH=4,DAY_OF_MONTH=17,DAY_OF_YEAR=107,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=5,SECOND=46,MILLISECOND=836,ZONE_OFFSET=28800000,DST_OFFSET=0]
            System.out.println(date);
            System.out.println(ca);
           //2.两者之间的相互转换
            Date caDate=ca.getTime();
            Calendar cb=Calendar.getInstance();
            cb.setTime(date);
            System.out.println(caDate); //Sun Apr 17 22:05:46 CST 2022
            System.out.println(cb); // cb 与ca一致

    3.2、Date常用函数

                     Date类常用的构造器:到目前为止,还有两个构造器是推荐使用,其他的构造器已经过时就不在此说明:  

        •  new Date()     分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒Sun Apr 17 22:12:08 CST 2022); 
        •  new Date(long value)     构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。 

                     Date中常用方法

        • after(Date when)    测试此日期是否在指定日期之后,返回值:Boolean
        • before(Date when)  测试此日期是否在指定日期之前,返回值:Boolean
        • CompareTo(Date anotherDate)   比较两个日期的顺序,返回值:int
        • Equals(Object obj): 比较两个日期的相等性,返回值:Boolean
        • Date和String之间互转;   需要借助日期转换类SimpleDateFormat(线程不安全的类)

    3.3、Calendar常用函数已经注意事项(在jdk api 1.8中可以查看)

    时间概念:Java 8开始,明确了日期时间概念,例如:瞬时(instant)、 长短(duration)、日期、时间、时区和周期。

        • Instant:瞬时实例。
        • LocalDate:本地日期,不包含具体时间 例如:2014-01-14 可以用来记录生日、纪念日、加盟日等。
        • LocalTime:本地时间,不包含日期。
        • LocalDateTime:组合了日期和时间,但不包含时差和时区信息。
        • ZonedDateTime:最完整的日期时间,包含时区和相对UTC或格林威治的时

                            常用方法汇总: (Date中的方法全部包含此处不在累述): protected Calendar() 由于修饰符是protected,所以无法直接创建对象

        • static Calendar getlnstance() 使用默认时区和区域获取日历
        • void setTime(Date date) 用给定的Date设置此日历的时间。Date-Calendar
        • void set(int year,int month,int date,int hourofday,int minute,int second) 设置日历的年,月,日,时,分,秒。
        • int get(int field) 返回给定日历字段的值,字段比如年,月,日等。
        • Date getTime() 返回一个Date表示此日历的时间。Calendar-Date
        • void add(int field,int amount) 按照日历规则,给指定字段添加或减少时间量
        • long getTimeInMillies() 毫秒为单位返回该日历的时间值
        • int getActualMaximum(int field) 、getActualMinimum 返回日历的最大值与最小值
        •  void   clear()    将此 Calendar 的所日历字段值和时间值(从历元至现在的毫秒偏移量)设置成未定义。
        •  int    getFirstDayOfWeek()  获取一星期的第一天;例如,在美国,这一天是 SUNDAY,而在法国,这一天是 MONDAY。
     其中Calendar 中 field为Calendar类中设置的各个常量
    常量 描述
    Calendar.YEAR 年份
    Calendar.MONTH 月份
    Calendar.DATE 日期
    Calendar.DAY_OF_MONTH 日期,和上面的字段意义完全相同
    Calendar.HOUR 12小时制的小时
    Calendar.HOUR_OF_DAY 24小时制的小时
    Calendar.MINUTE 分钟
    Calendar.SECOND
    Calendar.DAY_OF_WEEK 星期几

                     注意事项

                          a.Calendar获取的月份比实际数字少1即(0-11)

                          b.西方星期的开始为周日,中国为周一

                          c. 日期是有大小关系的,时间靠后,时间越大

    3.4、时间转换格式

                         a.yyyy与YYYY之间的区分:

            2019年12月31号,就转了一下格式,就变成了2020年12月31号了?因为YYYY是基于周来计算年的,它指向当天所在周属于的年份,一周从周日开始算起,周六结束,只要本周跨年,那么 这一周就算下一年的了。正确姿势是使用yyyy格式。

                          b.hh与HH之间的区分:

                                  hh是12制的日期格式,当时间为12点,会处理为0点。正确姿势是使用HH,它才是24小时制。

                           c.dd和DD之间的区分:

                                   DD表示的是一年中的第几天,而dd表示的是一月中的第几天,所以应该用的是dd。

                           d.SimleDateFormat的format初始化问题:

          • format格式化日期是,要输入的是一个Date类型的日期,而不是一个整型或者字符串。
          • SimpleDateFormat 可以解析长于/等于它定义的时间精度,但是不能解析小于它定义的时间精度。
          • SimpleDateFormat并发情况下,存在安全性问题

            SimpleDateFormat继承了 DateFormat,DateFormat类中维护了一个全局的Calendar变量,sdf.parse(dateStr)和sdf.format(date),都是由Calendar引用来储存的。如果SimpleDateFormat是static全局共享的,Calendar引用也会被共享。又因为Calendar内部并没有线程安全机制,所以全局共享的SimpleDateFormat不是线性安全的。

    解决SimpleDateFormat线性不安全问题的方式:

    1、将SimpleDateFormat定义成局部变量。

    缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。

    2、方法加同步锁synchronized,在同一时刻,只有一个线程可以执行类中的某个方法。

    缺点:性能较差,每次都要等待锁释放后其他线程才能进入。

    3、使用第三方库joda-time,由第三方考虑线程不安全的问题。(可以使用)DateUtils (采用SimpleDateFormat定义成局部变量)

    4、使用ThreadLocal:每个线程拥有自己的SimpleDateFormat对象。(推荐使用)

                                            JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple                                       beautiful strong immutable thread-safe。

                           e.日期本地化问题

                                  DateTimeFormatter 这个类默认进行本地化设置,如果默认是中文,解析英文字符串就会报异常。可以传入一个本地化参数(Locale.US)解决这个问题.

    String dateStr = "Wed Mar 18 10:00:00 2020";

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy",Locale.US);

    LocalDateTime dateTime = LocalDateTime.parse(dateStr, formatter);

    System.out.println(dateTime) 

           3.5、时间函数的常用案例

                       1.java Date日期去掉时分秒: 参考(java Date日期去掉时分秒

    查看代码
    @Test
        public void  DateToDateForYYYYMMDD(){
            //第一种方式: 格式转换SimpleDateFormat
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                Date dt= sdf.parse(sdf.format(new Date()));
                System.out.println(dt);
            } catch (ParseException e) {
                throw new RuntimeException("时间转换异常", e);
            }
            //第二种方式 直接清除的方式
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.set(Calendar.HOUR_OF_DAY, 0);
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
            Date dt2=calendar.getTime();
            //第三种方式
            Long createTime = new Date().getTime();
            Date dt3 =new Date(createTime - ((createTime + 28800000) % (86400000)));
    
            System.out.println(dt2);
            System.out.println(dt3);
        }

                          执行结果分析:

                                   三种方式执行结果是一致的;其中第三种方式中(把毫秒转换为日期:1 天 = 24 × 60 × 60 = 86400 秒  = 86400 x 1000 = 86400000毫秒,28800000是补上时区的8小时。

                       2.两个Date值比较年月的大小        

    查看代码
    /*
     * 
     * format(yyyy, yyyyMM,yyyyMMdd)
     * */
        public static  int companyDateYYYYMM(Date t1first,Date t2second, String format){//传入日期
            Calendar  cal=Calendar.getInstance();
            cal.setTime(t1first);
    
            Calendar  cal2=Calendar.getInstance();
            cal2.setTime(t2second);
            int t1YM=0;
            int t2YM=0;
            if(format.equals("yyyy")){
                t1YM=cal.get(Calendar.YEAR);
                t2YM=cal2.get(Calendar.YEAR);
            }
            if(format.equals("yyyyMM")){
                t1YM=cal.get(Calendar.YEAR)+cal.get(Calendar.MONTH);
                t2YM=cal2.get(Calendar.YEAR)+cal2.get(Calendar.MONTH);
            }
            if(format.equals("yyyyMMdd")){
                t1YM=cal.get(Calendar.YEAR)+cal.get(Calendar.MONTH)+cal.get(Calendar.DATE);
                t2YM=cal2.get(Calendar.YEAR)+cal2.get(Calendar.MONTH)+cal2.get(Calendar.DATE);
            }
            return t1YM>t2YM?1:(t1YM==t2YM?0:-1);
        }

                       3.两个Date相差几个月,几天,几年,几个小时,几分钟,几秒问题    (参考:Java 计算两个日期相差年数字、月数、天数及时分秒)     

    查看代码
    package sdas;
     
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Calendar;
    import java.util.Date;
     
    public class MyCalendar {
        private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        private static Calendar startDate = Calendar.getInstance();
        private static Calendar endDate = Calendar.getInstance();
        private static DateFormat df = DateFormat.getDateInstance();
        private static Date earlydate = new Date();
        private static Date latedate = new Date();
     
        /**
         * 计算两个时间相差多少个年
         *
         * @param early
         * @param late
         * @return
         * @throws ParseException
         */
        public static int yearsBetween(String start, String end) throws ParseException {
            startDate.setTime(sdf.parse(start));
            endDate.setTime(sdf.parse(end));
            return (endDate.get(Calendar.YEAR) - startDate.get(Calendar.YEAR));
        }
     
        /**
         * 计算两个时间相差多少个月
         *
         * @param date1
         *            <String>
         * @param date2
         *            <String>
         * @return int
         * @throws ParseException
         */
        public static int monthsBetween(String start, String end) throws ParseException {
            startDate.setTime(sdf.parse(start));
            endDate.setTime(sdf.parse(end));
            int result = yearsBetween(start, end) * 12 + endDate.get(Calendar.MONTH) - startDate.get(Calendar.MONTH);
            return result == 0 ? 1 : Math.abs(result);
     
        }
     
        /**
         * 计算两个时间相差多少个天
         *
         * @param early
         * @param late
         * @return
         * @throws ParseException
         */
        public static int daysBetween(String start, String end) throws ParseException {
            // 得到两个日期相差多少天
            return hoursBetween(start, end) / 24;
        }
     
        /**
         * 计算两个时间相差多少小时
         *
         * @param early
         * @param late
         * @return
         * @throws ParseException
         */
        public static int hoursBetween(String start, String end) throws ParseException {
            // 得到两个日期相差多少小时
            return minutesBetween(start, end) / 60;
        }
     
        /**
         * 计算两个时间相差多少分
         *
         * @param early
         * @param late
         * @return
         * @throws ParseException
         */
        public static int minutesBetween(String start, String end) throws ParseException {
            // 得到两个日期相差多少分
            return secondesBetween(start, end) / 60;
        }
     
        /**
         * 计算两个时间相差多少秒
         *
         * @param early
         * @param late
         * @return
         * @throws ParseException
         */
        public static int secondesBetween(String start, String end) throws ParseException {
            earlydate = df.parse(start);
            latedate = df.parse(end);
            startDate.setTime(earlydate);
            endDate.setTime(latedate);
            // 设置时间为0时
            startDate.set(Calendar.HOUR_OF_DAY, 0);
            startDate.set(Calendar.MINUTE, 0);
            startDate.set(Calendar.SECOND, 0);
            endDate.set(Calendar.HOUR_OF_DAY, 0);
            endDate.set(Calendar.MINUTE, 0);
            endDate.set(Calendar.SECOND, 0);
            // 得到两个日期相差多少秒
            return ((int) (endDate.getTime().getTime() / 1000) - (int) (startDate.getTime().getTime() / 1000));
        }
        /**
         * @param args
         * @throws ParseException
         */
        public static void main(String[] args) throws ParseException {
            MyCalendar d = new MyCalendar();
            System.out.println(yearsBetween("2016-8-15", "2017-3-1"));
            System.out.println(monthsBetween("2016-8-15", "2017-3-1"));
            System.out.println(daysBetween("2016-8-15", "2017-3-1"));
     
        }
    }

                     执行结果分析: 根据时间每个单位之间的换算相互复用,但方法存在线程安全的问题SimpleDateFormat 为全局公用。

                       4.时间格式的使用 DateTimeFormatter,SimpleDateFormat(SimpleDateFormat线程不安全及解决办法

                            Java时间格式化原来这么多玩法   

                            JAVA LocalDateTime,Date,String,Long 日期时间用DateTimeFormatter相互转换以及Calendar的简单使用  

    有三个静态方法及其重载来格式化本地化时间,具体已经整理成了思维导图:

     

    ISO/RFC规范格式DateTimeFormatter还内置了ISORFC的时间格式,基于内置的DateTimeFormatter静态实例

    查看代码
    
    //SimpleDateFormat 的使用
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            try {
                Date dt= sdf.parse(sdf.format(new Date()));
                System.out.println(dt);
            } catch (ParseException e) {
                throw new RuntimeException("时间转换异常", e);
            }
    
    
    //1.通用创建方式
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    //对于含有英文字符的需要添加时区
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.US);
    
    //2.使用
             //LocalDate 练习
            String dateStr= "2016年10月25日";
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
            //LocalDate练习
            LocalDate date= LocalDate.parse(dateStr, formatter);
            System.out.println(date); //2016-10-25
            String format1 = date.format(formatter);
            System.out.println(format1);//2016年10月25日
    
            //LocalDateTime练习
            String dateTimeStr= "2016-10-25 12:00:00";
            DateTimeFormatter formatter02 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            LocalDateTime localDateTime=LocalDateTime.parse(dateTimeStr,formatter02);
            System.out.println(localDateTime);// 2016-10-25T12:00
            String format = localDateTime.format(formatter02);
            System.out.println(format); // 2016-10-25 12:00:00
            
    //定义的特殊格式
        LocalDateTime ldt = LocalDateTime.now();
            System.out.println(DateTimeFormatter.ISO_DATE.format(ldt)); //2022-04-18
            System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(ldt)); //2022-04-18T23:37:37.329

                       5.Calendar使用验证,月,日相加减溢出等问题

    查看代码
    @Test
        public void  DateTest() throws ParseException {
            //1.月份加一个月,导致日期超过本月
            String dt1="2021-03-31";
            String dt2="2021-02-29";
            String dt3="2021-02-28";
            String dt4="2021-02-03";
            String dt5="2021-00-01";
            String dt6="2021-00-31";
    
            dateADDMonth(dt1);
            dateADDMonth(dt2);
            dateADDMonth(dt3);
            dateADDMonth(dt4);
            dateADDMonth(dt5);
            dateADDMonth(dt6);
        }
        public void  dateADDMonth(String dt) throws ParseException {
            SimpleDateFormat dtf=new SimpleDateFormat ("yyyy-MM-dd");
            Calendar ca=Calendar.getInstance();
            Date  lt1=dtf.parse(dt);
            ca.setTime(lt1);  //ca  的时间是从0开始的,Date与ca 之间的月份会自动转换
            ca.add(Calendar.MONTH,1);
            System.out.println(dt+"一个月之后的:"+dtf.format(ca.getTime()));
        }

    执行结果与分析

            //        2021-03-31一个月之后的:2021-04-30
            //        2021-02-29一个月之后的:2021-04-01
            //        2021-02-28一个月之后的:2021-03-28
            //        2021-02-03一个月之后的:2021-03-03
            //        2021-00-01一个月之后的:2021-01-01
            //        2021-00-31一个月之后的:2021-01-31                  

    结论:a.相加一个月之后如果日期大于下个月日期,直接用下个月的最后一天

              b.传入的字符串转换成Date时,月份或者日期不在当前范围内,会自动往后添加溢出部分的时间。

                       3.6 计算两个时间的差值

      java.util.Date:表示日期和时间的类
        类 Date 表示特定的瞬间,精确到毫秒。
        毫秒:千分之一秒 1000毫秒=1秒
        特定的瞬间:一个时间点,一刹那时间
        2088-08-08 09:55:33:333 瞬间
        2088-08-08 09:55:33:334 瞬间
        2088-08-08 09:55:33:334 瞬间
        ...
        毫秒值的作用:可以对时间和日期进行计算
        2020-10-07 到 2022-01-20 中间一共有多少天
        可以日期转换为毫秒进行计算,计算完毕,在把毫秒转换为日期

        把日期转换为毫秒:
            当前的日期:2088-01-01
            时间原点(0毫秒):1970 年 1 月 1 日 00:00:00(英国格林威治)
            就是计算当前日期到时间原点之间一共经历了多少毫秒 (3742767540068L)
        注意:
            中国属于东八区,会把时间增加8个小时
            1970 年 1 月 1 日 08:00:00

        把毫秒转换为日期:
            1 天 = 24 × 60 × 60 = 86400 秒  = 86400 x 1000 = 86400000毫秒

         

  • 相关阅读:
    海涛老师的面试题作业12打印从1到最大的n位数
    海涛老师的面试题作业28字符串的排列组合问题。
    二叉堆 算法基础篇(一)
    海涛老师的面试题作业5从尾到头打印链表
    关于C++访问控制的理解
    关于STL优先级队列的一些应用
    海涛老师的面试题作业27二叉搜索树与双向链表
    编程之美快速找出满足条件的两个数课后题
    Effective C++ C++虚函数默认参数机制
    变位词程序的设计与实现编程珠玑第二章读后感
  • 原文地址:https://www.cnblogs.com/q994321263/p/16153470.html
Copyright © 2020-2023  润新知