Java8 日期 API 业务使用
最近在做账单结算业务,需要根据客户选择的结算方式来推算下一次结算日期以及该次结算日期段。
推算日期这样的业务直男君以前就写过,只不过使用的是熟悉的 java.util.date 和 java.util.Calendar。
现在公司使用的 JDK8,所以本次就决定新的日期 API 啦,顺便结合业务实现对比回顾下。
- Java8 前喜闻乐见的日期操作
- 为什么推荐用新的日期 API
- 新 API 的典型使用
- 两种业务场景实现
以前喜闻乐见的日期操作
熟悉的日期操作三基友:java.util 包下的 Date 和 Calendar 加上 java.text.SimpleDateFormat。
1)得到当前日期对象
//获取日期对象(包含时间)
Date date = new Date();
Date date1 = new Date(System.currentTimeMillis());
2)日历操作
//获取日历对象
Calendar cald = Calendar.getInstance();
cald.setTime(date); //目标日期对象对应的日历
cald.add(Calendar.MONTH, 1); //日历选取(下个月本号)
Date date2 = cald.getTime(); //目标日历对应的日期对象
3)日期格式化
//日期格式化
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowDateStr = df.format(now); //日期 >> 格式串
System.out.println(nowDateStr); //2019-05-28 20:11:11
String dateStr = "2019-05-28 20:33:33";
Date aimDate = df.parse(dateStr); //格式串 >> 日期
System.out.println(aimDate);
System.out.println(aimDate.after(now)); //日期比较
System.out.println(aimDate.compareTo(now));
为什么推荐 Java8 日期 API (为啥用的爽)
1)更清晰合理的语义和架构
不再使用 Date(util 和 sql 包都有) 表示日期、时间、日期时间,而是 LocalDate、LocalTime、LocalDateTime。
以前日期格式化的类是在 java.text 包下,现在全部在 java.time 下,且语义分工明确。
- java.time 最常用的基础类 LocalDate、LocalTime、LocalDateTime、Instant、Period、Duration 等。
- java.time.format 日期格式化的类在这里,当然基础类已经提供了相关方法。
- java.time.zone 时区支持
- java.time.xxx 等
2)线程安全的设计
SimpleDataFormat 类一直为人诟病的线程安全问题:
//SimpleDataFormat 源码部分↓
private StringBuffer format(Date date, StringBuffer toAppendTo,
FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
......
PS:直男君倒觉得没什么,避免单例或者线程共享的场景就行了。
新的 API 类都是不可变类,避免了线程安全隐患。
public final class LocalDateTime
public final class Instant
......
PS:什么叫不可变类?
String 类就是不可变类,所以有这样的说法:每次使用 + 连接字符串都会生成新的 String 对象,对于重复拼接的场景应该使用 StringBuilder/StringBuffer。
参考 String 类的设计,可总结不可变类的关键特点:类名 final,保证类不会被拓展;所有成员变量 final 且不提供任何可以修改实例状态的方法。
3)更方便的日期操作
基础类比如 LocalDateTime 就已经合理的提供了足够多的场景方法,日期调整、格式化、比较等等。
4)时区、日历系统支持(体会还不深)
5)其他
新 API 典型使用
1)获取日期对象
提供了各种静态方法构造日期时间对象,以 LocalDate 为例:
LocalDate date = LocalDate.now();
System.out.println(date);
date = LocalDate.of(2019, 5, 30);//LocalDate.of(2019, Month.MAY, 30)
System.out.println(date);
date = LocalDate.ofYearDay(2019, 300);
System.out.println(date);
//其他
2)日期时间比较
LocalDateTime now = LocalDateTime.now();
logger.info("now: {}", now);
LocalDate date = LocalDate.of(2017, 7, 27);
LocalTime time = LocalTime.of(17, 11);
LocalDateTime aim = LocalDateTime.of(date, time);
logger.info("aim: {}", aim);
System.out.println(now.compareTo(aim));
System.out.println(now.isBefore(aim));
System.out.println(now.isEqual(aim));
System.out.println(now.isAfter(aim));
3)格式化
//DateTimeFormatter 提供了很多内置格式,但好像都不是我们想要的
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime now = LocalDateTime.now();
String str = now.format(formatter);
System.out.println(str);
//我们想要的格式
formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(now.format(formatter));
//日期字符串转日期对象
String dateStr = "2019-05-30";
LocalDate aim = LocalDate.parse(dateStr);
System.out.println(aim);
//日期时间串一起转对象
String dateTimeStr = "2019-05-30 10:30:00";
LocalDateTime aim2 = LocalDateTime.parse(dateTimeStr, formatter);
System.out.println(aim2);
4)日历操作(日期调整)
这个是业务实现上最有用的。
不再像以前使用 Calendar,而是基础类搭配 TemporalAdjusters 很好用!
//今天
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//分别获取今天日历值 也可以使用 ChronoField 的常量指定获取
logger.info("今天是,本月{}号, 周{}, 今年的第{}天, 今年的第{}月",
now.getDayOfMonth(), now.getDayOfWeek().getValue(), now.getDayOfYear(), now.getMonthValue()); //明年今天
LocalDateTime aim = now.plusYears(1);
System.out.println(aim);
//下月今天
aim = now.plusMonths(1);
System.out.println(aim);
//下周今天
aim = now.plusWeeks(1);
System.out.println(aim); //本月一号
aim = now.with(TemporalAdjusters.firstDayOfMonth());
System.out.println(aim);
//本月最后一天
aim = now.with(TemporalAdjusters.lastDayOfMonth());
System.out.println(aim);
//...
5)时长对象使用
- java.time.Period 日期时长
- java.time.Duration 时间时长
//现在
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//3个月时长
Period months3 = Period.ofMonths(3);
//3个月后
LocalDateTime aim = now.plus(months3);
System.out.println(aim);
//2个小时时长
Duration hours2 = Duration.ofHours(2);
//2个小时后
aim = now.plus(hours2);
System.out.println(aim);
6)兼容旧 API
当系统升级 JDK8 ,很容易将遗留的 Date 过渡为新 API 类使用,使用 java.time.Instant。
//Date >> LocalDate(Time)
Date date = new Date();
LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
System.out.println(localDateTime);
//Calendar >> LocalDate
Calendar calen = Calendar.getInstance();
localDateTime = LocalDateTime.ofInstant(calen.toInstant(), ZoneId.systemDefault());
System.out.println(localDateTime);
两种业务场景实现
1)定投业务
这个支付宝和各大银行都有的业务,即得到客户允许,按照客户选择的方式(按月或按周)投入本金至余额宝或其他理财产品,一般投入动作工作日进行,节假日顺延。
/**
* 模拟客户输入
*/
//假设客户选择 按月,每月5号定投
Integer type = 1, day = 5;
//前置校验 日期号检查 略
ActionType aimType = ActionType.ordinalContain(type);
if(aimType==null) {
throw new RuntimeException("定投类型输入不合法!");
}
/**
* 业务处理
*/
LocalDate now = LocalDate.now(); //当天日期
LocalDate aimDate = LocalDate.now(); //结果日期
switch (aimType) {
case T_WEEK: //按周,假设每周5
aimDate = now.with(ChronoField.DAY_OF_WEEK, day); //本周5
if(!now.isBefore(aimDate)) { //如果当天>=本周5,取下周5
aimDate = aimDate.plusWeeks(1);
}
break;
case T_MONTH: //按月,假设每月5号
aimDate = now.withDayOfMonth(day);//本月5号
if(!now.isBefore(aimDate)) { //如果当天>=本月5号,取下月5号
aimDate = aimDate.plusMonths(1);
}
break;
default:
break;
}
/**
* 输出结果
*/
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
String aimDateStr = aimDate.format(formatter);
//节假日顺延需要有工作日历蓝本,此处略
logger.info("下次投入日期:{}", aimDateStr);
2)账单业务
这个也很常见,比如每月几号结算上个月/季度的账单(分红,缴费啥的)。关键一点和定投场景不同的是,总是下个月开始动作,而定投会根据当前日期比对。
/**
* 模拟客户输入
*/
//假设客户选择 按月,每月5号结算上个月的账单
Integer type = 1, day = 5;
//前置校验 日期号检查 略
ActionType aimType = ActionType.ordinalContain(type);
if(aimType==null) {
throw new RuntimeException("结算类型输入不合法!");
}
/**
* 业务处理
*/
LocalDate now = LocalDate.now(); //当天日期 6.5
//结算日期 下个月5号(7.5) or 三个月后的的5号(9.5)
LocalDate aimDate = now.withDayOfMonth(day).plusMonths(aimType.monthPeriod);
//账单起始日 6.1
//账单结束日 6.30 or 8.31
LocalDate startDate = now.with(TemporalAdjusters.firstDayOfMonth());
LocalDate endDate = aimDate.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
/**
* 输出结果
*/
DateTimeFormatter formatter = DateTimeFormatter.BASIC_ISO_DATE;
logger.info("下次结算日期:{},账单周期段:[{}, {}]", aimDate.format(formatter),
startDate.format(formatter), endDate.format(formatter));
Java8 日期 API 业务使用的更多相关文章
- java8 异步api、循环、日期
java8 异步api.循环.日期 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/10801470.html 异步api 对于多任务耗时的业务场景,一般我们会用 ...
- JAVA8学习——新的时间日期API&Java8总结
JAVA8-时间日期API java8之前用过的时间日期类. Date Calendar SimpleDateFormat 有很多致命的问题. 1.没有时区概念 2.计算麻烦,实现困难 3.类是可变的 ...
- (转载)Java8新的日期API LocalDate, LocalTime
前言 由于Java Date的各种问题,Java8推出了新的日期API,很受一拨人的追捧. 为什么我们需要新的Java日期/时间API? 在开始研究Java 8日期/时间API之前,让我们先来看一下为 ...
- java8新特性——时间日期API
传统的时间 API 存在线程安全的问题,在多线程开发中必须要上锁,所以 java8 现在为我们提供了一套全新的时间日期 API ,今天进来学习一下java8 的时间日期 API. 一.使用 Local ...
- [转] Java8 日期/时间(Date Time)API指南
[From] http://www.importnew.com/14140.html Java 8日期/时间( Date/Time)API是开发人员最受追捧的变化之一,Java从一开始就没有对日期时间 ...
- Java8新特性(三)——Optional类、接口方法与新时间日期API
一.Optional容器类 这是一个可以为null的容器对象.如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象. 查看结构图可以看到有如下常用方法: of(T)—— ...
- 为什么不建议使用Date,而是使用Java8新的时间和日期API?
Java 8:新的时间和日期API 在Java 8之前,所有关于时间和日期的API都存在各种使用方面的缺陷,因此建议使用新的时间和日期API,分别从旧的时间和日期的API的缺点以及解决方法.Java ...
- [转]JavaSE 8—新的时间和日期API
为什么我们需要一个新的时间日期API Java开发中一直存在一个问题,JDK提供的时间日期API一直对开发者没有提供良好的支持. 比如,已有的的类(如java.util.Date和SimpleDate ...
- Java8 日期/时间(Date Time)使用简介
特别说明: LocalDateTime 为日期时间的计算提供了很大的方便, 在构造对象/运算/toString等方便都非常便利. 3个常用的类: java.time.LocalDateTime; ja ...
随机推荐
- jQuery入门二(DOM对象与jQuery对象互相转换)
- DOM对象与jQuery对象互相转换 第一篇说过,DOM对象不能调用jQuery对象的属性和方法,同样jQuery对象也不能调用DOM对象的属性和方法.但是在实际开发中,可能两者间需要互相调用对方 ...
- 如何在jsp中显示数据库的内容
用Eclipse tomcat新建一个JSP页面(一)介绍了如何创建一个web程序和第一个jsp页面,以及Eclipse需要的一些必要配置.今天,我们重点说一下如何从数据库中查询数据,并且在JSP页面 ...
- rabbitMQ_workQueue(二)
生产者发送多个消息到队列,由多个消费者消费. 如果一个消费者需要处理一个耗时的任务,那么队列中其他的任务将被迫等待这个消费者处理完成,所以为了避免这样的情况,可以建立对个消费者进行工作. 本例中使 ...
- android 界面提示框架WisdomProgressHUD,为金典而生
一:简述 今天给android开发者们,推荐一个金典的界面提示框架WisdomProgressHUD,使用简洁方便. WisdomProgressHUD 是一个半透明的 HUD 指示器. Wisdom ...
- JS实现在线ps功能
功能介绍 本系统是基于fabric.js实现的canvas版图片,文本编辑器,支持对图片的放大,缩小,旋转,镜面翻转,拖动,显示/隐藏图层,删除图层,替换图层等操作,对文本支持修改文本内容,颜色,字体 ...
- Java 性能优化(一)
Java 性能调优(一) 1.衡量程序性能的标准 (1) 程序响应速度: (2) 内存占有情况: 2.程序调优措施 (1) 设计调优 设计调优处于所有调优手段 的上层,需要在软件开发之前进行.在软件开 ...
- Apache之——多虚拟主机多站点配置的两种实现方案
Apache中配置多主机多站点,可以通过两种方式实现: 将同一个域名的不同端口映射到不同的虚拟主机,不同端口映射到不同的站点: 将同一个端口映射成不同的域名,不同的域名映射到不同的站点. 我们只需要修 ...
- 1、Java小白之路前言
大二一年准备好好学习Java,养成一个良好的习惯写博客,但是由于各种各样的原因,并没有坚持下来.而正好又赶上大三结束,去实习,发现自己的基础还是有些薄弱,所以决定,重新走上这条Java小白之路. 时隔 ...
- Java ActionListenner类的一些理解
Java的ActionListenner事实上我去年年这个时候大概就已经接触到了,也学会了比较简单的使用.但却始终不能理解ActionListenner的一系列的运行是怎么维持这么一个联系的? 我产生 ...
- Java虚拟机学习笔记(二)--- 判断对象是否存活
Java堆中存放着所有的对象实例,垃圾收集器在堆进行回收之前,需要判断对象是“存活”还是“死亡”(即不可能再被任何途径引用的对象). 最常见的一种判断对象是否存活算法是引用计数算法, 给对象加一个引用 ...