java老日期处理API的诟病
Date
java的时间处理类Date一直被人们所诟病,这个类无法表示日期,只能以毫秒的精度表示时间,如果想获得年、月、日等信息需要借助Calender类;另外如果通过Date的构造函数来创建Date非常别扭,比如要创建2018年10月18日这个日期:
Date date = new Date(118,9,18);
System.out.println(date);
输出为:
Thu Oct 18 00:00:00 CST 2018
第一个参数118,表示年份,从1900算起(是不是很别扭);第二参数9,表示月份,其实是表示10月,因为月份是从0算起(是不是更别扭);第三个参数18,表示日,还算正常。
Calendar
可见 用Date带参数的构造函数创建日期,后面引入了Calendar.set(year + 1900, month, date)进行替代,具体可见Date的api:
具体用法:
Calendar cl = Calendar.getInstance();
cl.set(2018, 9, 18);
System.out.println(cl.getTime());
输出:
Thu Oct 18 08:45:05 CST 2018
可见Calendar对年份的处理进行了改进,但对月份还是处理还是从0开始表示第一个月,即这里的9表示的是10月(依然很别扭)。
作为Date类的补充,Calendar中提供了很多有用的方法,比如获取月份的最大天数,获取年份、月份、日期等api。
另外需要注意的是Date和Calendar都是可变类(相对的 String、Integer等是不可变类)
DateFormat
另外你会惊奇的发现,无论是Date还是Calendar类中都没有对日期进行格式化处理的方法,又不得不引入的DateFormat这个API(实现类SimpleDateFormat)。但由于SimpleDateFormat中使用了Calender,Calender前面提到过是可变类设计,很容易引起线程安全问题。比如下面的代码:
public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
这行代码在多线程高并发的情况下会有线程安全问题,其实这是一种错误的做法,正确的做法是每次使用时都new一个SimpleDateFormat;或者放到ThreaLocal中。
上述是java的Date、Calendar、DateFormat这些API中部分为人们所诟病的问题,对于有经验的老鸟来说,都可以一一化解,但有时也会一不小心就会掉到坑里。这也是java8引入新的日期出来api的直接原因。
java新日期和时间API
LocalDate
LocalDate是不可变类设计,所谓不可变类简单的理解就是初始化后就不能被修改,不可变类有很多优良的特性,最通常见到的也就是可以作为HashMap的key(还可以是线程安全的);同样的LocalTime、LocalDateTime都是不可变类的设计,建议在使用jdk1.8开发的项目中使用这些新的日期、时间API。下面首先来看LocalDate的基本用法:
LocalDate ld = LocalDate.of(2018,10,18) ;
int year = ld.getYear();
Month month = ld.getMonth();
int day = ld.getDayOfMonth();
System.out.println(year+"年"+month.getValue()+"月"+day+"日");
//获取当前的星期数
DayOfWeek dow = ld.getDayOfWeek();
int len=ld.lengthOfMonth();
//获取当天在当年中的天数
int lenyear = ld.lengthOfYear();
boolean leap = ld.isLeapYear();
//从系统时钟中获取当前的日期
LocalDate today = LocalDate.now();
// 根据字符串取:
// 严格按照ISO yyyy-MM-dd验证,02写成2都不行,当然也有一个重载方法允许自己定义格式
LocalDate endOfFeb = LocalDate.parse("2014-02-28");
// 无效日期无法通过:DateTimeParseException: Invalid date
LocalDate.parse("2014-02-29");
// 取本月最后一天,再也不用计算是28,29,30还是31:
LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
//还有很多其他有用的方法可以直接查看 LocalDate的api即可。
//ChronoField参数方式,ChronoField是枚举类型,枚举值都可以作为参数
//可以根据实际需要进行选择
int now_year = today.get(ChronoField.YEAR);
int now_month = today.get(ChronoField.MONTH_OF_YEAR) ;
int now_day = today.get(ChronoField.DAY_OF_MONTH);
System.out.println(now_year+"年"+now_month+"月"+now_day+"日");
可以看到LocalDate的使用非常简单,而且对应的构造函数的参数更符合人们的习惯,创建LocalDate一般有三种方式:of方法、parse方法、now方法。根据不同的情况自行选择即可。
LocalTime
LocalTime跟LocalDate的功能基本相同,特性完全相同(如 不可变性),只是前者用于表示时间,后者由于表示日期而已。
//参数顺序分别为 时、分、秒
LocalTime t1 = LocalTime.of(9,34,20);
int h = t1.getHour();
int m = t1.getMinute();
int s = t1.getSecond();
LocalTime t2 = LocalTime.now();
//顺序也是时分秒,以冒号间隔,格式必须为xx:xx:xx,不足10必须补0
LocalTime t3 = LocalTime.parse("09:34:20");
System.out.println(t3);
创建LocalTime也是三种方式:of方法、parse方法、now方法。
LocalDateTime
LocalDate和LocalTime分别表示日期、时间,LocalDateTime则是二者的合体,创建方式也与二者基本相同。
//of方法有两种参数格式
LocalDateTime dt1 = LocalDateTime.of(2018,10,22,9,10,20);
LocalDateTime dt2 = LocalDateTime.of(today,t2);
LocalDateTime dt3 = LocalDateTime.now();
System.out.println(dt3);
//atTime(知道日期,设置时间)和atDate方法(知道时间,设置日期)
LocalDateTime dt4 = today.atTime(t2);
LocalDateTime dt5 = t2.atDate(today);
//从LocalDateTime中提取Date和time
LocalDate date_x = dt5.toLocalDate();
LocalTime day_x = dt5.toLocalTime();
Instant
上述年月日时分秒时方便人类识别时间的方式,对于机器而已就不太合适需要一个转换,这就是Instant的作用:它是以Unix元年(UTC时间1970年1月1日)为基准,当前时间的秒数。
//Unix元年时间 + 2秒
Instant ins1 = Instant.ofEpochSecond(2);
System.out.println(ins1.getEpochSecond());
//Unix元年时间+3秒-12纳秒
Instant ins2 = Instant.ofEpochSecond(3,-12);
System.out.println(ins2);
//当前时间
Instant ins3 = Instant.now();
System.out.println(ins3.getEpochSecond());
//与System.currentTimeMillis()对比
System.out.println(System.currentTimeMillis());
Duration和Period
Duration用于计算两个时间之间的间隔,不能用于日期:
LocalTime localTime1 = LocalTime.now();
Thread.sleep(1);
LocalTime localTime2 = LocalTime.now();
//参数可以是LocalTime 也可以是Instant
Duration du1 = Duration.between(localTime1,localTime2);
System.out.println(du1.getNano());
要计算日期之间的间隔,要用Period:
//参数为两个LocalDate
Period tenDays = Period.between(LocalDate.of(2014, 3, 8),
LocalDate.of(2014, 3, 18));
System.out.println(tenDays.getDays());
Duration类和Period类共享了很多相似的方法:
平时大家都是使用的mysql数据库,mysql数据库中的日期时间字段与新api的对应转换关系如下:
SQL -> Java --------------------------
date -> LocalDate
time -> LocalTime
timestamp -> LocalDateTime
操纵日期
修改日期(本质上不是修改,而是创建一个新的LocalDate),首先来看LocalDate的withAttribute用法:
LocalDate ld1 = LocalDate.of(2018,10,23);
LocalDate ld2 = ld1.withYear(2017);
LocalDate ld3 = ld2.withDayOfMonth(25);
LocalDate ld4 = ld3.with(ChronoField.MONTH_OF_YEAR,8);
System.out.println(ld4);
日期的加减(plus和minus)
plus和minus方法可以对日期进行增减,注意这里也不是修改,而是重新创建一个新的LocalDate:
LocalDate date1 = today.plusWeeks(1);//加一周
LocalDate date2 = date1.plusYears(1);//加一年
LocalDate date3 = date2.minusMonths(1);//减一月
//自定义加减
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);
System.out.println(date4);
TemporalAdjuster的使用
使用日期的带TemporalAdjusters工厂方法参数的重载with方法可以很方便的创建需要的日期:
//静态导入方法
import static java.time.temporal.TemporalAdjusters.*;
//下一个周日
LocalDate ld5 = ld4.with(nextOrSame(DayOfWeek.SUNDAY));
System.out.println(ld5);
LocalDate ld6 = ld5.with(lastDayOfMonth());
System.out.println(ld6);
//本月第4周的周日
LocalDate ld7 = ld6.with(dayOfWeekInMonth(4,DayOfWeek.SUNDAY));
System.out.println(ld7);
TemporalAdjusters中提供的其他工厂方法列表(这些方法都返回一个具体的TemporalAdjuster对象):
当然如果上述方法都不满足你的要求的化,还可以自己实现TemporalAdjuster接口,然后作为参数传递给LocalDate的with方法。
格式化处理日期和时间
文章开头提到过,以前对Date的格式化只能使用DateFormat,但DateFormat是线程不安全的,所以很容易踩坑。
在新的api中,可以使用LocalDate和LocalTime的format方法,结合DateTimeFormatter进行格式化处理:
//使用已定义的DateTimeFormatter格式
//LocalDate转string
String s1 = today.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(s1);
String s2 = today.format(DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(s2);
//String转LocalDate
LocalDate s3 = LocalDate.parse("20181023",DateTimeFormatter.BASIC_ISO_DATE);
LocalDate s4 = LocalDate.parse("2018-08-13",DateTimeFormatter.ISO_LOCAL_DATE);
System.out.println(s3);
System.out.println(s4);
//自己指定格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String s5 = today.format(formatter);
System.out.println(s5);
//国际化处理
DateTimeFormatter italianFormatter =
DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN);
LocalDate dd = LocalDate.of(2014, 3, 18);
String formattedDate = dd.format(italianFormatter);
System.out.println(formattedDate);
//使用DateTimeFormatterBuilder自定义DateTimeFormatter
DateTimeFormatter italianFormatter2 = new DateTimeFormatterBuilder() .appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
时区和历法
新的java.time.ZoneId类是老版java.util.TimeZone的替代品,同样ZoneId是不可变的。
ZoneId romeZone = ZoneId.of("Europe/Rome");
//TimeZone转换为ZoneId
ZoneId zoneId = TimeZone.getDefault().toZoneId();
有了ZoneId之后,就可以用于创建ZonedDateTime:
LocalDate ldate = LocalDate.of(2014,Month.APRIL,18);
ZonedDateTime zdate = ldate.atStartOfDay(romeZone);
System.out.println(zdate);
LocalDateTime ldatetime = LocalDateTime.of(2014,10,24,13,13,13);
ZonedDateTime zdate2 = ldatetime.atZone(romeZone);
System.out.println(zdate2);
Instant nowInstant = Instant.now();
ZonedDateTime zdate3 = nowInstant.atZone(romeZone);
System.out.println(zdate3);
日本日历系统
LocalDate dated = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(dated);
System.out.println(japaneseDate);
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.dateNow();
System.out.println(now);
伊斯兰教日历
HijrahDate ramadanDate = HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)
.with(ChronoField.MONTH_OF_YEAR, 9);
System.out.println("Ramadan starts on " +
IsoChronology.INSTANCE.date(ramadanDate) +
" and ends on " +
IsoChronology.INSTANCE.date(
ramadanDate.with(
TemporalAdjusters.lastDayOfMonth())));
相关推荐
JDK1.8_Windows_64是一个Java开发工具包(Java Development Kit)的安装程序,适用于64位Windows...4. 新的日期和时间API:JDK1.8引入了全新的日期和时间API,解决了老版本中的一些问题,并提供了更好的性能和易用性。
Java Development Kit (JDK) 1.8是一个Java平台的开发环境。它提供了Java编译器、Java运行...此外,JDK 1.8还包含了JavaFX 8,一个新的Java图形用户界面工具包,以及新的工具和API,用于Java并发编程和日期/时间处理。
4、Date/Time API:Java 8中引入了新的Date/Time API,它提供了一组强大的日期和时间处理工具,包括时区和夏令时的支持。 5、Nashorn引擎:Java 8中引入了一个新的JavaScript引擎,称为Nashorn。它比原来的...
JDK(Java Development Kit)1.8是Java平台的一个版本,也被称为Java 8。下面是关于JDK 1.8的一些介绍: ...5. 新的Date和Time API:JDK 1.8中引入了新的Date和Time API,提供了更好的日期和时间处理方式,解决
java8 源码 Java8 Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。...,它支持函数式编程,新的 ...引擎,新的日期 API,新的Stream ...表达式:Lambda允许把函数...API:加强对日期与时间的处理。 Optional 类:Opti
于是就诞生了Joda-Time这个专门处理日期时间的库。(JDK1.8的API已经重写了日期库,引入的java.time包,其实也是借鉴了Joda-Time) --------------------- 作者:--nodeps 来源:CSDN 原文:...
本工程包含了SpringAOP,死锁,JUC同步锁,读-写同步锁,线程本地使用,JUC线程池和Spring提供的线程池,jdk 1.8中的日期时间API,数据结构中图的实现及操作和广度优先遍历/深度优先遍历(其他待完善),生成XML文件...
Java虚拟机:JDK1.8 Java开发工具:IntelliJ IDEA 2021.2 Web服务器:Tomcat10.0 数据库系统:Mysql8.0 数据库管理工具:Navicat 所用技术: 后端方面:JavaSe核心语法、MVC(controlle层、service、dao层)...
在这样的国际和国内经济背景下,我国经济发展面临着严峻考验,特别是以出口导向型劳动密集型产业面临的形势更为严峻:一方面劳动力、原材料、污水处理等成本上升,另一方面出口订单减少。在这种情况下,沿海某纺织...
6.2.4 日期和时间控件 6.2.5 MapView控件 6.3 适配器 6.3.1 SimpleCursorAdapter 6.3.2 了解ArrayAdapter 6.4 结合使用适配器和AdapterView 6.4.1 基本的列表控件:ListView 6.4.2 GridView...
6.2.4 日期和时间控件 6.2.5 MapView控件 6.3 适配器 6.3.1 SimpleCursorAdapter 6.3.2 了解ArrayAdapter 6.4 结合使用适配器和AdapterView 6.4.1 基本的列表控件:ListView 6.4.2 GridView...
Language: Java 8(jdk版本建议1.8.201以上) Python2.7(支持Python3需要修改替换datax/bin下面的三个python文件,替换文件在doc/datax-web/datax-python3下) Environment: MacOS, Windows,Linux Database: Mysql5.7...
11.1.2 Java时间和日期类型的Hibernate映射类型 11.1.3 Java大对象类型的Hibernate映射类型 11.1.4 JDK自带的个别Java类的Hibernate映射类型 11.1.5 使用Hibernate内置映射类型 11.2 客户化映射类型 ...
11.1.2 Java时间和日期类型的Hibernate映射类型 11.1.3 Java大对象类型的Hibernate映射类型 11.1.4 JDK自带的个别Java类的Hibernate映射类型 11.1.5 使用Hibernate内置映射类型 11.2 客户化映射类型 ...
11.1.2 Java时间和日期类型的Hibernate映射类型 11.1.3 Java大对象类型的Hibernate映射类型 11.1.4 JDK自带的个别Java类的Hibernate映射类型 11.1.5 使用Hibernate内置映射类型 11.2 客户化映射类型 ...
11.1.2 Java时间和日期类型的Hibernate映射类型 11.1.3 Java大对象类型的Hibernate映射类型 11.1.4 JDK自带的个别Java类的Hibernate映射类型 11.1.5 使用Hibernate内置映射类型 11.2 客户化映射类型 ...