一:序
本篇文章将介绍JDK 1.8前的日期处理,其实已经不推荐使用了,但以前的项目也有少部分使用,所以在这里引出一下;在JDK1.8之前,Java中的日期和时间处理是由java.util.Date
和java.util.Calendar
类负责的。然而这些类存在一些问题,使得它们不太适合进行日期和时间操作。下面是一些原因:
java
代码解读
复制代码
'Ⅰ:可变性:' Date是可变的,这意味着可以直接修改日期对象的值。这样的设计使得日期对象在多线程环境下容易出现并发问题。 'Ⅱ:线程不安全:' Date和Calendar类不是线程安全的。若多个线程同时访问同个日期对象,可能会导致数据不一致或出现错误的结果。 'Ⅲ:API设计不佳:' Date的API设计不够友好,很多方法的命名和用法不够直观,容易引起误解。 例如 getYear() 方法返回的是从 1900 年开始计算的年份,这样的设计容易引起开发者的困惑。 'Ⅳ:日期与时间耦合:' 在旧版API中,日期和时间是紧密耦合的,因此无法单独处理日期或时间。 这给程序员带来了不必要的麻烦,特别是在只需要处理日期或时间的场景下。
为了解决这些问题,Java 8 引入了新的日期和时间 API(即java.time
包),提供了一组新的类来处理日期和时间。其中最重要的类就是java.time.LocalDate
,它是一个不可变的日期类,非常适合处理不涉及时间和时区的日期操作。
新的日期和时间API的设计更加清晰、简洁,而且遵循了面向对象的原则,提供了丰富的方法来处理日期和时间。它还引入了新的概念,如LocalDate
、LocalTime
、LocalDateTime
、ZonedDateTime
等,使得日期和时间的操作更加灵活和易于理解。
总之,JDK 1.8 之前的日期类存在一些问题,包括可变性、线程不安全性、API 设计不佳以及日期与时间耦合等。通过使用 JDK 1.8 提供的新日期和时间API,我们可以更好地处理日期和时间,并避免潜在的问题。
注:本篇文章下方全是围绕老版本的𝐽𝑎𝑣𝑎1.1日期时间说明注:本篇文章下方全是围绕老版本的Java1.1日期时间说明 (已经不推荐使用)(已经不推荐使用)
二:Date时间类
Date时间类在JDK1.0时就存在了,但是它存在许多问题,如:潜在的线程安全问题、获取年月日等困难、缺乏时区、日期运算困难等;但在这里我还是简单说明讲解,在日常开发中尽量避免使用Date时间类。
整理了一份Java面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处】即可免费获取
java
代码解读
复制代码
构造方法: public Date(): 分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。 public Date(long date): 分配Date对象并初始化此对象, 以表示自从标准基准时间(称为"历元(epoch)",即1970年1月1日00:00:00 GMT)以来的指定毫秒数 public Date(String s) 按照传入的字符串日期来设置时间,格式:yyyy/MM/dd HH:mm:ss; '注:由于我们处于东八区,所以我们的基准时间为1970年01月01日08时00分00秒。' 若使用无参构造器则获取当前系统的时间; System.out.println(new Date()); // 打印:Tue Jul 25 23:48:12 CST 2023 若使用有参构造函数,则根据传入的毫秒值推算从基准时间后增加指定的秒数时间 System.out.println(new Date(1000L));// 打印:Thu Jan 01 08:00:01 CST 1970 1000毫秒 = 1秒 若设置字符串日期则: new Date("2023/12/27 12:25:61") // 对应日期为:2023年12月27日 12时26分1秒(格式化后的) new Date("2023/13/27 12:25:59") // 对应日期为:2024年1月27日 12时25分59秒(格式化后的) '常用方法:' getTime():获取当前Date日期Long的毫秒时间(这个方法还是有人用的) getYear():获取年份;注:需要+1900 getMonth():获取月份;注:0~11,0代表1月,11代表12月 getDate():获取日期 getHours():获取小时;0~23小时 getMinutes():获取分钟:0~59分钟 getSeconds():获取秒数:0~59秒数 getDay():获取星期:0=周日、1=周一、2=周二、3=周三、4=周四、5=周五、6=周六 before(Date when):测试此日期是否在指定日期之前 after(Date when):测试此日期是否在指定日期之后 compareTo(Date anotherDate):日期比较 equals(Object obj):判断两个日期是否相等 toGMTString():返回0时区时间 '测试日期获取:' Date date = new Date(); String dateStr = 1900 + date.getYear() + "年" + (date.getMonth() + 1) + "月" + date.getDate() + "日 " + date.getHours() + "时" + date.getMinutes() + "分" + date.getSeconds() + "秒"; dateStr打印结果:2024年6月23日 19时52分7秒 '测试日期比较:' Date A = new Date("2023/07/20 12:30:00"); Date B = new Date("2023/07/21 12:30:00"); Date C = new Date("2023/07/21 12:30:00"); A.before(B) 日期A在日期B前(true) A.after(B) 日期A在日期B前(false) C.compareTo(B):返回0日期相等; A.compareTo(B):A日期在传入的B日期前则返回-1;反之返回1 B.equals(C):日期相等返回true '测试返回格林尼治标准时间:' Date A = new Date("2023/07/20 8:30:00") A.toGMTString():返回 20 Jul 2023 00:30:00 GMT;简单说就是日期减八小时,因为我们在东八区
java.util.Date类还有好多构造方法,但是不推荐使用,而且使用起来还贼难受,构建年份时还得+1900,所以我就不在给出示例,而上面调用的方法也有好多是被标为“已弃用”;所以总结就是Date类已经不推荐使用了,最多也就是用Date类里的getTime()方法获取Long的毫秒日期格式。
三:Calendar日期类
因为Date时间类的缺陷,所以在JDK1.1版本立马发布一个Calendar的日期类,它用来解决之前的Date日期类设计不足,其优化包括如 支持时区、日历运算、时间设置 等方法,虽然说Calendar日期类提供了许多日期和时间操作的方法,但它在设计上还是存在一些问题。因此在Java 8引入了全新的日期和时间工具类(LocalDate、LocalTime、LocalDateTime),所以在JDK 8时尽量不要使用Date和Calendar。
(一):构造日期对象和常量
Calendar类的构造函数是受保护的,我们必须使用静态方法getInstance()来获取Calendar实例;这个方法是一个工厂方法,它根据默认的时区和区域设置返回一个Calendar对象。这样做的好处是可以根据环境的不同自动适应时区和地区设置。
java
代码解读
复制代码
常用构造日期对象方法: Calendar getInstance(): 使用默认时区和区域设置获取日历。 Calendar getInstance(TimeZone zone) 使用指定的时区和默认区域设置获取日历。 Calendar getInstance(Locale aLocale) 使用默认时区和指定的区域设置获取日历。 基本示例: 示例1: Calendar.getInstance() 示例2: TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); // TimeZone timeZone = TimeZone.getTimeZone("GMT+08:00"); Calendar calendar = Calendar.getInstance(timeZone); 示例3: // Locale locale = Locale.getDefault(); Locale locale = new Locale("fr", "FR"); // 区域法国 Calendar calendar = Calendar.getInstance(locale); '时区TimeZone说明:' 只有设置对象时间才准确:calendar.set()方法 其实构建TimeZone存在常用的两种方式,一种是偏移量的方式,一种为时区ID方式 偏移量: 如偏移+8小时则“GMT+08:00“,代表比格林尼治时间晚8小时 如偏移-8小时则“GMT-08:00“,代表比格林尼治时间早8小时 如偏移-8.30小时则“GMT-08:30“,代表比格林尼治时间早8小时30分钟 注:一共24区 -11(西十一区)~ 格林尼治(0时区)~ +12(东十二区) 时区ID如下(推荐使用时区ID可以有准确的时区表示): 中国:Asia/Shanghai (上海); 美国:America/New_York (纽约); 美国:America/Los_Angeles (洛杉矶); 德国:Europe/Berlin (柏林); 加拿大:America/Toronto (多伦多); 日本:Asia/Tokyo (东京); 墨西哥:America/Mexico_City (墨西哥城);法国:Europe/Paris (巴黎); 英国:Europe/London (伦敦); 韩国:Asia/Seoul (首尔); 印度:Asia/Kolkata (加尔各答); 巴西:America/Sao_Paulo (圣保罗); 澳大利亚:Australia/Sydney (悉尼); 西班牙:Europe/Madrid (马德里); 俄罗斯:Europe/Moscow (莫斯科); 意大利:Europe/Rome (罗马); '区域Locale说明:' 设置了区域后,打印出的日期和时间在格式上可能会有所不同,但其实际的数值仍然代表相同的日期和时间。 常用区域: ("en","US")-美国 ("en","GB")英国 ("en","CA")加拿大 ("en","AU")澳大利亚 ("fr","BE")-比利时 ("fr","CH")瑞士 ("de","DE")德国 ("de","AT")奥地利 ("es","ES")-西班牙 ("es","MX")墨西哥 ("es","AR")阿根廷 ("zh","CN")中国 ("ja","JP")-日本 ("ko","KR")韩国 ("pt","PT")葡萄牙 ("pt","BR")巴西 ("it","IT")-意大利 ("fr","FR")法国 ("de","CH")瑞士 ("zh","TW")台湾 ("ru","RU")俄罗斯
其实Calendar内部其实存在许多常量,在方法使用上会使用上常量
js
代码解读
复制代码
'Calendar类中用以下这些常量表示不同的意义:' YEAR:表示年份 MONTH:表示月份(从0月开始) DATE / DAY_OF_MONTH:表���日期 HOUR:时(12小时制,12点为0点;配合AM_PM使用) HOUR_OF_DAY:时(24小时制) MINUTE:分 SECOND:秒 MILLISECOND:毫秒 DAY_OF_YEAR:本年度过去了多少天(包含当天) DAY_OF_WEEK:表示本周周几(周日(1)、周一(2)、周二(3)...、周六(7)) WEEK_OF_MONTH:本月已经过了几周(不包含本周)(注:跨月周也得计算) WEEK_OF_YEAR:本年已经经过了几周(不包含本周)(一年最多52周,跨年周可能会有53周) AM_PM:表示是上午还是下午(1点~11点为0上午、12点~23点为1下午) Calendar类日期枚举: JANUARY(一月)、FEBRUARY(二月)、MARCH(三月)、APRIL(四月)、MAY(五月)、JUNE(六月)、JULY(七月)、 AUGUST(八月)、SEPTEMBER(九月)、OCTOBER(十月)、NOVEMBER(十一月)、DECEMBER(十二月)、UNDECIMBER(农历十三)
(二):常用方法
java
代码解读
复制代码
构建对象: Calendar ca = Calendar.getInstance(); 'Ⅰ:获取日期' int get(int field):返回给定日历字段的值 String calStr = ca.get(Calendar.YEAR) + "-" + (ca.get(Calendar.MONTH) + 1) + "-" + ca.get(Calendar.DATE) + " " + ca.get(Calendar.HOUR_OF_DAY) + ":" + ca.get(Calendar.MINUTE) + ":" + ca.get(Calendar.SECOND); 如上calStr打印:2024-6-23 20:16:17 'Ⅱ:设置日期' 设置指定值,通过Calendar常量选择设置: void set(int field, int value) 设置年月日、年月日时分、年月日时分秒: void set(int year, int month, int date) void set(int year, int month, int date, int hourOfDay, int minute) void set(int year, int month, int date, int hourOfDay, int minute, int second) 如下示例: ca.set(Calendar.HOUR_OF_DAY,12); -- 设置当前时间为12时(其它保持不变) ca.set(2023, Calendar.JANUARY, 31, 12, 22, 25); -- 设置日期 "2023-01-31 12:22:25" 根据提供的Date日期设置Calendar日期时间 : void setTime(Date date) 根据提供的日期秒数设置Calendar日期时间: void setTimeInMillis(long millis) 'Ⅲ:日期计算' 根据日历的规则,将指定的时间添加或减去给定的日历字段: void add(int field, int amount) 如下示例(假设当前日期为”2023-07-31 12:20:30“) ca.add(Calendar.HOUR_OF_DAY,2); -- 时间增加2小时,此时为15:20分 ca.add(Calendar.DATE,2); -- 日期增加2天,此时为2023-08-02(日期进位) 注:负数则代表减去指定日期 在给定时间字段上添加或减少单个时间单位,而不会日期进位: abstract void roll(int field, boolean up): 如下示例(假设当前日期为”2023-07-31 12:20:30“): ca.roll(Calendar.DATE,true); -- 日期增加一天,因为超出日期,则不会进位,则会为2023-07-01 ca.roll(Calendar.HOUR_OF_DAY,true); -- 时间增加一个小时,但若为23时加一小时则变为0点 说明:true为增加一个单位,false则减少一个单位 在给定时间字段上添加或减少指定时间单位,而不会日期进位: void roll(int field, int amount): 如下示例(假设当前日期为”2023-07-31 12:20:30“): ca.roll(Calendar.DATE,2); -- 日期增加一天,因为超出日期,则不会进位,则会为2023-07-02 ca.roll(Calendar.HOUR_OF_DAY,3); -- 时间增加一个小时, 注:负数则代表减去指定日期 'Ⅳ:日期比较' boolean before(Object when) 如:a.before(b) a日期是否在指定的b日期之前,true之前、false之后 boolean after(Object when) 如:a.after(b) a日期是否在指定的b日期之后,true之后、false之前 int compareTo(Calendar anotherCalendar) 如:返回0日期相等;A.compareTo(B):A日期在传入的B日期前则返回-1;反之返回1 boolean equals(Object obj) 如:日期相等返回true 'Ⅴ:获取其它信息' Date getTime():把Calendar的时间值转换为Date日期返回 long getTimeInMillis():以毫秒为单位返回此日历的时间值。 'Ⅵ:设置其它信息' void setTimeZone(TimeZone value):设置给定的时区值(设置时区) 'Ⅶ:其它方法' 获取给定的时间值中,返回日历字段中可能具有的最大值,如获取当前最大多少天: int getActualMaximum(int field) 获取给定的时间值中,返回日历字段中可能具有的最小值,如获取当前最小多少天: int getActualMinimum(int field) 如:calendar.getActualMaximum(Calendar.DAY_OF_MONTH); -- 返回30,因为2024-04月一共有30天;
四:DateFormat格式化
DateFormat是一个抽象类,用于格式化和解析日期和时间,并提供了国际化的支持。而DateFormat抽象类有个SimpleDateFormat的实现类,它提供了一种简单的方式来指定日期和时间的格式。
注:DateFormat和SimpleDateFormat是线程不安全的,若在多线程下需要同步处理,访问同一个实例则会不准确。
(一):DateFormat说明
java
代码解读
复制代码
'DateFormat日期样式常量(以中国区域样式来说):' SHORT 短风格:如:”2023/7/31 下午11:21“ MEDIUM 中等风格:如:”2023年7月31日 下午11:22:29“ LONG 长风格:如:“2023年7月31日 CST 下午11:22:40” FULL 完整风格:如:”2023年7月31日星期一 中国标准时间 下午11:22:17“ '获取DateFormat对象的方法:' -- 根据指定的样式常量创建一个格式化日期的实例。 static DateFormat getDateInstance() static DateFormat getDateInstance(int style) static DateFormat getDateInstance(int style, Locale aLocale) -- 根据指定的样式常量创建一个格式化时间的实例。 static DateFormat getTimeInstance() static DateFormat getTimeInstance(int style) static DateFormat getTimeInstance(int style, Locale aLocale) -- 根据指定的样式常量创建一个格式化日期和时间实例。 static DateFormat getDateTimeInstance() static DateFormat getDateTimeInstance(int dateStyle, int timeStyle) static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale) -- 默认时间和日期格式化实例 static DateFormat getInstance();其内部执行getDateTimeInstance(SHORT, SHORT) 参数说明: style、dateStyle、timeStyle 未设置则默认为 DateFormat.MEDIUM(中等风格) aLocale 未设置则获取的是当前环境的默认区域 Locale对象常用区域: FRANCE(法国)、GERMANY(德国)、ITALY(意大利)、 JAPAN(日本)、KOREA(韩国)、UK(英国)、US(美国)、 CHINA(中国)、CANADA(加拿大) 示例说明: // 获取不同格式化风格和中国环境的日期 DateFormat df1 = DateFormat.getDateInstance(DateFormat.LONG, Locale.CHINA); // 获取不同格式化风格和中国环境的时间 DateFormat df2 = DateFormat.getTimeInstance(DateFormat.LONG, Locale.CHINA); // 将不同格式化风格的日期格式化为日期字符串 String date1 = df1.format(new Date()); // 将不同格式化风格的时间格式化为时间字符串 String time1 = df2.format(new Date()); // 输出日期(具体打印“LONG:2023年7月31日 CST 下午11:57:42”) System.out.println("LONG:" + date1 + " " + time1);
(二):SimpleDateFormat说明
使用DateFormat类格式化日期/时间并不能满足要求,就可使用DateFormat类的子类SimpleDateFormat。它允许进行格式化(日期→文本)、解析(文本→日期) 和规范化。这样就可以满足自定义任何日期/时间格式的模式。
java
代码解读
复制代码
'SimpleDateFormat类主要有如下三种构造方法:' SimpleDateFormat():使用默认模式和日期格式符号为默认的FORMAT区域设置。如我们一般都是“CHINA”区域 SimpleDateFormat(String pattern):用指定的格式和默认的FORMAT区域设置构造对象。 SimpleDateFormat(String pattern, Locale locale):用指定的格式和指定的区域设置构造对象。 示例: new SimpleDateFormat().format(new Date()) 打印:”2023/8/1 上午12:21“ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) 打印:”2023-08-01 00:25:15“ '注:再了解具体方法前需要先了解自定义pattern语法:' 假设日期为:2023-08-01 01:35:36:568 此时为周二 y(年份):yy表示两位年份,yyyy表示4位年份;如yy=23、yyyy=2023 M(月份):MM表示月份,若MMM则会根据语言环境显示不同语言的月份; 如MM=8;MMM在US区域下=Aug,在CHINA区域下=八月 d(月份中的天数):dd表示天数;如1天 D(年份中的天数):表示当天是当年的第几天(包含当天);如213天 H(一天中的小时数0~23):一般用HH表示24小时制 h(一天中的小时数0~11):一般用hh表示12小时制,am/pm方式 K(一天中的小时数1~24):一般用KK表示24小时制 k(一天中的小时数1~12):一般用kk表示12小时制,am/pm方式 a(AM/PM标记):用a标记是上午还是下午(0~11上午,12~23下午); 如a=上午;a在US区域下=AM,在CHINA区域下=上午 m(分钟数):一般使用mm表示分钟数;如35分 s(秒数):一般使用ss表示秒数;如36秒 S(毫秒数):一般使用SSS表示毫秒数;如568毫秒 z(CST中央标准时间):一般搭配时区使用 Z(获取RFC 822时区):Z获取时区;如当前时区为中国则Z=+0800 X(获取ISO 8601时区):X获取时区;如当前时区为中国则X=+08 w(本年已经经过了几周(不包含本周)):如31(最小从1开始) W(本月已经过了几周(不包含本周)):如1(最小从1开始) E(月份中的星期几):按照区域信息显示不同格式日期; 如E=星期二;E在US区域下=Tue,在CHINA区域下=星期二 u(周的天数):返回1=Monday, ..., 7 = Sunday,如上日期返回 2 常用方法(核心就2个):文本->时间->文本 String format(Date date) 时间转指定格式的文本 Date parse(String source) 指定的文本转时间信息 示例: // 日期对象转文本对象 SimpleDateFormat sdfA = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStr = sdfA.format(new Date()); System.out.println("当前日期转文本为:" + dateStr); -- 打印:当前日期转文本为:2023-08-01 21:02:15 // 文本对象转时间对象 SimpleDateFormat sdfB = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateStrA = "2023-12-22 23:59:59"; Date date = sdfA.parse(dateStrA); System.out.println("当前文本转日期对象:" + date); -- 打印:当前文本转日期对象:Fri Dec 22 23:59:59 CST 2023