当前位置: 首页 > 科技观察

Java8新特性探索(七):深入解析日期和时间——JSR310

时间:2023-03-12 01:47:50 科技观察

博客一个月没更新了,这次给大家讲讲java8时间日期API。众所周知,日期是业务逻辑计算的关键部分,任何企业应用都需要处理时间问题。应用程序需要知道当前时间点和下一个时间点,有时还必须计算这两个时间点之间的路径。但是java以前的约会做法太恶心了。我们先投诉。爪哇。那是日期API。Slot1.最开始Date需要携带日期信息,日期之间的转换,显示不同的日期格式。职责复杂(我不懂单一职责,你妈妈知道吗?~哈哈)后来从JDK1.1开始,这三个职责分开了:使用Calendar类在日期和时间字段之间进行转换;使用DateFormat类来格式化和分析日期字符串;而Date只是用来携带日期和时间信息。原始Date中的相应方法已过时。不过不管是Date还是Calendar,用起来都太不方便了。这是API设计不好的地方。Slot2作弊年月Datedate=newDate(2012,1,1);System.out.println(日期);输出ThuFeb0100:00:00CST3912观察输出结果,年份为2012+1900,月份和月份参数没有给1吗?如何输出二月(Feb)?应该有人告诉过你,如果你想设置日期,你应该使用java.util.Calendar,像这样...Calendarcalendar=Calendar.getInstance();日历.set(2013,8,2);这又错了,日历的月份也是从0开始,应该用数字7表示八月,或者干脆用枚举calendar.set(2013,Calendar.AUGUST,2);注意上面的代码,日历年的值不需要减到1900(当然month的定义还是和Date一样),这种不一致真是气死人了!可能有人知道Calendar相关的API是IBM捐赠的,所以导致不一致。插槽3java.util.Date和java.util.Calendar中的所有属性都是可变的。以下代码计算两个日期之间的天数....publicstaticvoidmain(String[]args){Calendarbirth=Calendar.getInstance();birth.set(1975,Calendar.MAY,26);Calendarnow=Calendar.getInstance();System.out.println(daysBetween(birth,now));System.out.println(daysBetween(birth,now));//显示0?}publicstaticlongdaysBetween(Calendarbegin,Calendarend){longdaysBetween=0;while(begin.before(end)){begin.add(Calendar.DAY_OF_MONTH,1);几天之间++;}returndaysBetween;如果是这样,它将第二次得到0,因为Calendar的状态是可变的。考虑到重复计算的场合,***copyanewCalendarpublicstaticlongdaysBetween(Calendarbegin,Calendarend){Calendarcalendar=(Calendar)begin.clone();//复制longdaysBetween=0;while(calendar.before(end)){calendar.add(Calendar.DAY_OF_MONTH,1);几天之间++;}returndaysBetween;}JSR310及以上版本导致了一些第三方java日期库的诞生,比如广泛使用的JODA-TIME、Date4j等,虽然第三方库足够强大好用,但仍然存在兼容性问题.例如,标准的JSF日期转换器与joda-timeAPI不兼容。需要自己写converter,所以标准的API还是有必要的,于是就有了JSR310。JSR310实际上有两个日期概念。第一个是Instant,它大致对应于java.util.Date类,因为它表示一个确定的时间点,与标准Java纪元(1970年1月1日)的偏移量;.util.Date类的不同之处在于它精确到纳秒级别。第二种对应的是人类自己的概念,比如LocalDate、LocalTime。它们代表了时区的一般概念,要么是日期(不包括时间),要么是时间(不包括日期),类似于java.sql的表示。此外,还有一个MonthDay,它存储某人的生日(没有年份)。每个类内部都存储了正确的数据,而不是像java.util.Date那样用午夜12点来区分日期,用1970-01-01来表示时间。目前Java8已经实现了JSR310的全部内容。添加了java.time包定义的类来表示日期时间概念的规则,包括瞬间、持续时间、日期、时间、时区和周期。这些基于ISO日历系统,该系统再次遵循公历规则。最重要的一点是该值是不可变的并且是线程安全的。通过下图,我们快速了解一下java.time包下的一些主要类的取值格式,方便理解。方法概述本包的API提供了大量的相关方法,这些方法一般都有一个一致的方法前缀:of:staticfactorymethod。parse:静态工厂方法,专注于解析。get:得到某物的价值。is:检查某事是否为真。with:不可变的setter等价物。plus:向对象添加一些量。减:从一个对象中减去一些量。to:转换为另一种类型。at:将这个对象与另一个对象结合起来,eg:date.atTime(time)。与老API的对应简单使用java.time的API参考http://jinnianshilongnian.iteye.com/blog/1994164是我一起搓的,可读性很差,对应的代码都注释了,我就是不看很多解释。publicclassTimeIntroduction{publicstaticvoidtestClock()throwsInterruptedException{//时钟为我们提供了获取特定时区的瞬时时间、日期和时间的途径。Clockc1=Clock.systemUTC();//系统默认UTC时钟(当前瞬时时间System.currentTimeMillis())System.out.println(c1.millis());//每次调用都会返回当前瞬时时间(UTC)Clockc2=Clock.systemDefaultZone();//系统默认时区时钟(当前瞬时时间)Clockc31=Clock.system(ZoneId.of("Europe/Paris"));//巴黎时区System.out.println(c31.millis());//每次调用都会返回当前的瞬时时间(UTC)Clockc32=Clock.system(ZoneId.of("Asia/Shanghai"));//上海时区System.out.println(c32.millis());//每次调用都会返回当前瞬时时间(UTC)Clockc4=Clock.fixed(Instant.now(),ZoneId.of("Asia/Shanghai"));//固定上海时区时钟系统.out.println(c4.millis());线程.睡眠(1000);System.out.println(c4.millis());//此时时钟不变Clockc5=Clock.offset(c1,Duration.ofSeconds(2));//相对于系统默认的两秒时钟时钟System.out.println(c1.millis());System.out.println(c5.millis());}publicstaticvoidtestInstant(){//瞬时时间相当于前面的System.currentTimeMillis()Instantinstant1=Instant.now();System.out.println(instant1.getEpochSecond());//精确到秒得到相对于1970-01-0100:00:00UTC的一次System.out.println(instant1.toEpochMilli());//精确到毫秒Clockclock1=Clock.systemUTC();//获取系统UTC默认时钟Instantinstant2=Instant.now(clock1);//获取时钟的瞬时时间System.out.println(instant2.toEpochMilli());Clockclock2=Clock.fixed(instant1,ZoneId.systemDefault());//固定瞬时时间clockInstantinstant3=Instant.now(clock2);//获取时钟的瞬时时间System.out.println(instant3.toEpochMilli());//equalsinstant1}publicstaticvoidtestLocalDateTime(){//创建Clock.systemDefaultZone()-->相对于ZoneId.systemDefault()默认时区LocalDateTimenow=LocalDateTime.now();系统输出.println(现在);//自定义时区LocalDateTimenow2=LocalDateTime.now(ZoneId.of("Europe/Paris"));System.out.println(now2);//日期将显示在相应的时区//从定义时钟Clockclock=Clock.system(ZoneId.of("Asia/Dhaka"));LocalDateTimenow3=LocalDateTime.now(时钟);System.out.println(now3);//日期会显示在对应的时区//不需要写什么相对时间,比如java.util.Date年份是相对于1900的,从0开始//2013-12-3123:59LocalDateTimed1=LocalDateTime.of(2013,12,31,23,59);//年月日时分秒纳秒LocalDateTimed2=LocalDateTime.of(2013,12,31,23,59,59,11);//使用即时时间+时区Instantinstant=Instant.now();LocalDateTimed3=LocalDateTime.ofInstant(Instant.now(),ZoneId.systemDefault());System.out.println(d3);//解析字符串-->LocalDateTimeLocalDateTimed4=LocalDateTime.parse("2013-12-31T23:59");System.out.println(d4);LocalDateTimed5=LocalDateTime.parse("2013-12-31T23:59:59.999");//999毫秒相当于999000000纳秒System.out.println(d5);//使用DateTimeFormatterAPI解析格式化DateTimeFormatterformatter=DateTimeFormatter.ofPattern("yyyy/MM/ddHH:mm:ss");LocalDateTimed6=LocalDateTime.parse("2013/12/3123:59:59",格式化程序);System.out.println(格式化程序.format(d6));//获取时间System.out.println(d6.getYear());系统输出。println(d6.getMonth());System.out.println(d6.getDayOfYear());System.out.println(d6.getDayOfMonth());System.out.println(d6.getDayOfWeek());系统。out.println(d6.getHour());System.out.println(d6.getMinute());System.out.println(d6.getSecond());System.out.println(d6.getNano());//时间增加或减少LocalDateTimed7=d6.minusDays(1);LocalDateTimed8=d7.plus(1,IsoFields.QUARTER_YEARS);//LocalDate表示年月日不带时分秒//LocalTime即时分秒不带年月日//API与LocalDateTime类似,不再演示}publicstaticvoidtestZonedDateTime(){//即是,带时区的日期时间存储纳秒、时区和时差(以避免与本地日期时间产生歧义)//API类似于LocalDateTime,但时差更大(如2013-12-20T10:35:50.711+08:00[Asia/Shanghai])ZonedDateTimenow=ZonedDateTime.now();System.out.println(现在);ZonedDateTimenow2=ZonedDateTime.now(ZoneId.of("欧洲/巴黎"));System.out.println(now2);//其他用法类似,不再介绍ZonedDateTimez1=ZonedDateTime.parse("2013-12-31T23:59:59Z[Europe/Paris]");System.out.println(z1);}publicstaticvoidtestDuration(){//Durationd1=Duration.between(Instant.ofEpochMilli(System.currentTimeMillis()-12323123),Instant.now());//获取对应的时间差System.out.println(d1.toDays());System.out.println(d1.toHours());System.out.println(d1.toMinutes());System.out.println(d1.toMillis());System.out.println(d1.toNanos());//1天时差类似,如ofHours()Durationd2=Duration.ofDays(1);System.out.println(d2.toDays());}publicstaticvoidtestChronology(){//提供java.util.Calendar的替代品,提供对年历系统Chronologyc=HijrahChronology.INSTANCE的支持;ChronoLocalDateTimed=c。localDateTime(LocalDateTime.now());西stem.out.println(d);}/***新旧日期转换*/publicstaticvoidtestNewOldDateConversion(){Instantinstant=newDate().toInstant();Datedate=Date.from(即时);System.out.println(即时);System.out.println(日期);}publicstaticvoidmain(String[]args)throwsInterruptedException{testClock();测试即时();testLocalDateTime();testZonedDateTime();测试持续时间();与Joda-Time的区别事实上,JSR310的规范负责人StephenColebourne也是Joda-Time的创造者。JSR310是在Joda-Time的基础上构建的,指的是绝大部分的API,但并不代表JSR310=JODA-Time,以下比较明显的区别最明显的变化是包名(来自org.joda.timeandjava.time)JSR310不接受NULL值,Joda-Time将NULL值视为0JSR310计算机相关时间(Instant)和人类相关时间(DateTime)之间的差异变得更加明显。JSR310抛出的所有异常都是DateTimeException的子类虽然DateTimeException是一个RuntimeException总结与旧的日期APIJava.timeJava.util.Calendar和DateFluentAPI相比不流畅APIinstanceimmutableinstancevariablethreadsafenonthreadsafedateandtimeprocessingAPI,invariouslanguages,Itmaybejust一个不起眼的API。如果你没有更复杂的时间处理需求,可以直接使用日期时间处理接口获取系统时间,简单显示即可。但是,如果你认真对待日期和时间,其复杂性可能远超你的想象,天文、地理、历史、政治、文化等因素都会影响你对时间的处理。所以在处理时间上,最好选择JSR310(如果用java8可以做到310),或者Joda-Time。不仅java面临着时间处理的尴尬,其他语言也遇到了类似的问题,比如Arrow:Python中比较好的日期时间处理库Moment.js:JavaScript中的日期库Noda-Time:来自.NET阵营Joda-Time的复制原文链接:http://my.oschina.net/benhaile/blog/193956