本文源码:GitHub·点这里 || GitEE·点这里
一、时间和日期
在系统开发中,日期与时间作为重要的业务因素,起到十分关键的作用,例如同一个时间节点下的数据生成,基于时间范围的各种数据统计和分析,集群节点统一时间避免超时等。
在时间和日期中有几个关键概念:
- 日期:通常年月日的组合表示当前日期。
- 时间:通常时分秒的组合表示当前时间。
- 时区:世界各国家与地区经度不同,划分24个标准时区,相邻时区的时间相差一个小时。
- 时间戳:从UTC时间的
1970-1-1 00:00:00
起到现在的总秒数。
日期和时间的用法在系统中通常是获取时间和一些常见的计算与格式转换处理,在一些垮时区的业务中就会变的复杂很多,例如在电商业务中的全球贸易或者海淘等。
二、JDK原生API
1、Date基础
基础用法
java.sql.Date继承java.util.Date,相关方法也大部分直接调用父类方法。
public class DateTime01 {
public static void main(String[] args) {
long nowTime = System.currentTimeMillis() ;
java.util.Date data01 = new java.util.Date(nowTime);
java.sql.Date date02 = new java.sql.Date(nowTime);
System.out.println(data01);
System.out.println(date02.getTime());
}
}
打印:
Fri Jan 29 15:11:25 CST 2021
1611904285848
计算规则
public class DateTime02 {
public static void main(String[] args) {
Date nowDate = new Date();
System.out.println("年:"+nowDate.getYear());
System.out.println("月:"+nowDate.getMonth());
System.out.println("日:"+nowDate.getDay());
}
}
年份:当前时间减去1900;
public int getYear() {
return normalize().getYear() - 1900;
}
月份:0-11表示1-12月份;
public int getMonth() {
return normalize().getMonth() - 1;
}
天份:正常表示;
public int getDay() {
return normalize().getDayOfWeek() - BaseCalendar.SUNDAY;
}
格式转换
非线程安全的日期转换API,该用法在规范的开发中是不允许使用的。
public class DateTime02 {
public static void main(String[] args) throws Exception {
// 默认转换
DateFormat dateFormat01 = new SimpleDateFormat() ;
String nowDate01 = dateFormat01.format(new Date()) ;
System.out.println("nowDate01="+nowDate01);
// 指定格式转换
String format = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat dateFormat02 = new SimpleDateFormat(format);
String nowDate02 = dateFormat02.format(new Date()) ;
System.out.println("nowDate02="+nowDate02);
// 解析时间
String parse = "yyyy-MM-dd HH:mm";
SimpleDateFormat dateFormat03 = new SimpleDateFormat(parse);
Date parseDate = dateFormat03.parse("2021-01-18 16:59:59") ;
System.out.println("parseDate="+parseDate);
}
}
作为JDK初始版本就使用的日期和时间,Date类一直在项目中使用,但是相关API的方法都已经基本废弃,通常使用一些二次封装的时间组件。该API的设计堪称Java中的最烂。
2、Calendar升级
Calendar作为一个抽象类,定义日期时间相关转换与计算的方法,这个类目测
public class DateTime04 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR,2021);
calendar.set(Calendar.MONTH,1);
calendar.set(Calendar.DAY_OF_MONTH,12);
calendar.set(Calendar.HOUR_OF_DAY,23);
calendar.set(Calendar.MINUTE,59);
calendar.set(Calendar.SECOND,59);
calendar.set(Calendar.MILLISECOND,0);
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") ;
Date defDate = calendar.getTime();
System.out.println(defDate+"||"+dateFormat.format(defDate));
}
}
输出:Fri Feb 12 23:59:59 CST 2021||2021-02-12 23:59:59
直观感觉,Date中相关方法迁移Calendar实现,简化Date的功能侧重对日期与时间的实体封装,Calendar复杂相关计算策略,DateFormat依旧用来做格式处理。但是Calendar依旧很少被使用,上述基础API就已经是很好的说明了。
3、JDK1.8升级API
Java8之后的版本中,核心API类包括LocalDate-日期、LocalTime-时间、LocalDateTime-日期加时间。
LocalDate:日期描述是final修饰的不可变类,默认格式yyyy-MM-dd。
LocalTime:时间描述是final修饰的不可变类,默认格式hh:mm:ss.zzz。
LocalDateTime:日期与时间描述final修饰的不可变类。
public class DateTime05 { public static void main(String[] args) { // 日期:年-月-日 System.out.println(LocalDate.now()); // 时间:时-分-秒-毫秒 System.out.println(LocalTime.now()); // 日期时间:年-月-日 时-分-秒-毫秒 System.out.println(LocalDateTime.now()); // 日期节点获取 LocalDate localDate = LocalDate.now(); System.out.println("[" + localDate.getYear() + "年];[" + localDate.getMonthValue() + "月];[" + localDate.getDayOfMonth()+"日]"); // 计算方法 System.out.println("1年后:" + localDate.plusYears(1)); System.out.println("2月前:" + localDate.minusMonths(2)); System.out.println("3周后:" + localDate.plusWeeks(3)); System.out.println("3天前:" + localDate.minusDays(3)); // 时间比较 LocalTime localTime1 = LocalTime.of(12, 45, 45); ; LocalTime localTime2 = LocalTime.of(16, 30, 30); ; System.out.println(localTime1.isAfter(localTime2)); System.out.println(localTime2.isAfter(localTime1)); System.out.println(localTime2.equals(localTime1)); // 日期和时间格式 LocalDateTime localDateTime = LocalDateTime.now() ; LocalDate myLocalDate = localDateTime.toLocalDate(); LocalTime myLocalTime = localDateTime.toLocalTime(); System.out.println("日期:" + myLocalDate); System.out.println("时间:" + myLocalTime); } }
如果作为JodaTime组件的深度用户,对这个几个API使用基本无压力。
4、时间戳
时间戳也是业务中常用的方式,基于Long类型表示时间,在很多时候远比常规日期与时间的格式更好用。
public class DateTime06 {
public static void main(String[] args) {
// 精确到毫秒级别
System.out.println(System.currentTimeMillis());
System.out.println(new Date().getTime());
System.out.println(Calendar.getInstance().getTime().getTime());
System.out.println(LocalDateTime.now().toInstant(
ZoneOffset.of("+8")).toEpochMilli());
}
}
这里需要注意的是在实际业务中由于获取时间戳的方式是多样的,所以建议统一工具方法,和规定精确度,避免部分精确到秒部分精确到毫秒的问题,这样可以规避在使用时相互转换的情况。
三、JodaTime组件
在Java8之前JodaTime组件是大部分系统中的常见选择,有很多方便好用的日期与时间的处理方法封装。
基础依赖:
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
在joda-time提供的组件之上做一个简单的工具类封装,保证业务处理风格统一。
public class JodaTimeUtil {
// 时间格式
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private JodaTimeUtil (){}
// 获取当前时间
public static DateTime getCurrentTime (){
return new DateTime() ;
}
// 获取指定时间
public static DateTime getDateTime (Object obj){
return new DateTime(obj) ;
}
// 把时间以指定格式转换为字符串
public static String getNowDate (Date date, String format){
return new DateTime(date).toString(format) ;
}
// 获取星期时间
public static String getWeek (Object date){
DateTime time = getDateTime (date) ;
String week = null ;
switch (time.getDayOfWeek()) {
case DateTimeConstants.SUNDAY:
week = "星期日";
break;
case DateTimeConstants.MONDAY:
week = "星期一";
break;
case DateTimeConstants.TUESDAY:
week = "星期二";
break;
case DateTimeConstants.WEDNESDAY:
week = "星期三";
break;
case DateTimeConstants.THURSDAY:
week = "星期四";
break;
case DateTimeConstants.FRIDAY:
week = "星期五";
break;
case DateTimeConstants.SATURDAY:
week = "星期六";
break;
default:
break;
}
return week ;
}
}
四、源代码地址
GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent