Java日期时间API系列38-----一种高效的工作日计算计算方法
如果没有节日放假调休的话,工作日很好计算,周一到周五就是工作日,但因为有节日放假调休,使得这个计算需要外部放假安排数据来支持。计算原理: 先按照放假安排数据计算,再按照周一周五计算。
下面以LocalDateTime 为例。
1.第一版,没有使用缓存
- /**
- * 判断是否中国工作日,包含法定节假日调整日期,节假日数据holidayData,如果节假日数据不支持年份,将使用周一到周五为工作日来判断。
- * @param localDateTime LocalDateTime
- * @param holidayData 放假信息0表示放假,1表示工作日,如:2021-01-01:0,2021-02-07:1
- * @return boolean
- */
- public static boolean isChineseWorkDay(LocalDateTime localDateTime, String holidayData){
- Objects.requireNonNull(holidayData, "holidayData");
- Map<String, Integer> dateTypeMap = StringUtil.convertHolidayDataToMap(holidayData);
- Integer dateType = dateTypeMap.get(DateTimeFormatterUtil.formatToDateStr(localDateTime));
- if(dateType != null){
- return dateType == 1 ? true : false;
- }
- return isWorkDay(localDateTime);
- }
- // StringUtil.convertHolidayDataToMap
- /**
- * 转换节日数据为map
- * @param holidayData 节日map
- * @return 返回节日map
- */
- public static Map<String, Integer> convertHolidayDataToMap(String holidayData){
- Map<String, Integer> dateTypeMap = new HashMap<>();
- if(isEmpty(holidayData)){
- return dateTypeMap;
- }
- String[] dateTypeArr = holidayData.replace(" ", "").split(",");
- for(String dateType : dateTypeArr){
- String[] arr = dateType.split(":");
- dateTypeMap.put(arr[0], Integer.valueOf(arr[1]));
- }
- return dateTypeMap;
- }
- /**
- * 判断是否工作日 (周一到周五)
- * @param localDateTime LocalDateTime
- * @return boolean
- */
- public static boolean isWorkDay(LocalDateTime localDateTime){
- int dayOfWeek = getDayOfWeek(localDateTime);
- if(dayOfWeek == 6 || dayOfWeek == 7){
- return false;
- }else{
- return true;
- }
- }
这个方法,先将放假安排数据解析成Map,然后对比,最后使用周一到周五判断。
2.第二版,使用缓存优化
第一版中,每次调用都先将放假安排数据解析成Map,但其实是不需要的,因为放假安排数据每年只发布一次(特殊情况除外),一年都不需要变化,这些数据第一次调用时放进缓存,后面直接使用,有变化时再更新缓存。
缓存使用本地缓存和Redis缓存都可以,本地缓存速度更快一些,下面使用本地缓存。
- public static boolean isChineseWorkDay2(LocalDateTime localDateTime, String holidayData){
- Objects.requireNonNull(holidayData, "holidayData");
- Map<String, Integer> dateTypeMap = StringUtil.convertHolidayDataToMapUseCache(holidayData);
- Integer dateType = dateTypeMap.get(DateTimeFormatterUtil.formatToDateStr(localDateTime));
- if(dateType != null){
- return dateType == 1 ? true : false;
- }
- return isWorkDay(localDateTime);
- }
- //StringUtil.convertHolidayDataToMapUseCache
- /**
- * 转换节日数据为map,使用缓存提高性能
- * @param holidayData 节日map
- * @return 返回节日map
- */
- @SuppressWarnings("unchecked")
- public static Map<String, Integer> convertHolidayDataToMapUseCache(String holidayData){
- Map<String, Integer> dateTypeMap = new HashMap<>();
- //参数为空,直接返回
- if(isEmpty(holidayData)){
- return dateTypeMap;
- }
- //查询缓存
- dateTypeMap = (Map<String, Integer>)CommonCache.get(holidayData);
- //缓存存在,返回缓存
- if(CollectionUtil.isNotEmpty(dateTypeMap)){
- return dateTypeMap;
- }
- //缓存不存在,先设置缓存然后返回
- Supplier<Object> supplier = new Supplier<Object>() {
- @Override
- public Object get() {
- Map<String, Integer> dateTypeMap = new HashMap<>();
- String[] dateTypeArr = holidayData.replace(" ", "").split(",");
- for(String dateType : dateTypeArr){
- String[] arr = dateType.split(":");
- dateTypeMap.put(arr[0], Integer.valueOf(arr[1]));
- }
- return dateTypeMap;
- }
- };
- return (Map<String, Integer>)CommonCache.get(holidayData, supplier);
- }
- /**
- * 判断是否工作日 (周一到周五)
- * @param localDateTime LocalDateTime
- * @return boolean
- */
- public static boolean isWorkDay(LocalDateTime localDateTime){
- int dayOfWeek = getDayOfWeek(localDateTime);
- if(dayOfWeek == 6 || dayOfWeek == 7){
- return false;
- }else{
- return true;
- }
- }
缓存使用了WeakHashMap实现缓存自动清理,使用ReentrantReadWriteLock实现读写线程安全。详细代码见com.xkzhangsan.time.utils.CommonCache,核心代码片段如下:
- /**
- * 从缓存池中查找值
- *
- * @param key
- * 键
- * @return 值
- */
- public V get(K key) {
- lock.readLock().lock();
- try {
- return cache.get(key);
- } finally {
- lock.readLock().unlock();
- }
- }
- /**
- * 从缓存池中查找值,没有时尝试生成
- *
- * @param key
- * 键
- * @param supplier 提供者
- * @return 值
- */
- public V get(K key, Supplier<V> supplier) {
- V value = get(key);
- if (value == null && supplier != null) {
- lock.writeLock().lock();
- try {
- value = cache.get(key);
- // 双重检查,防止在竞争锁的过程中已经有其它线程写入
- if (null == value) {
- try {
- value = supplier.get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- cache.put(key, value);
- }
- } finally {
- lock.writeLock().unlock();
- }
- }
- return value;
- }
3. 二种实现性能对比
这里以2021年放假信息为例,分别调用100万次。忽略第一次创建缓存的时间,从第二次开始,测试数据如下:
- 2021-01-01:0,2021-02-07:1,2021-02-11:0,2021-02-12:0,2021-02-15:0,2021-02-16:0,2021-02-17:0,2021-02-20:1,2021-04-05:0,2021-04-25:1,2021-05-03:0,2021-05-04:0,2021-05-05:0,2021-05-08:1,2021-06-14:0,2021-09-18:1,2021-09-20:0,2021-09-21:0,2021-09-26:1,2021-10-01:0,2021-10-04:0,2021-10-05:0,2021-10-06:0,2021-10-07:0,2021-10-09:1
- @Test
- public void isChineseWorkDay1(){
- //2021年放假信息
- String holidayData = "2021-01-01:0,2021-02-07:1,2021-02-11:0,2021-02-12:0,2021-02-15:0,2021-02-16:0,2021-02-17:0,2021-02-20:1,2021-04-05:0,2021-04-25:1,2021-05-03:0,2021-05-04:0,2021-05-05:0,2021-05-08:1,2021-06-14:0,2021-09-18:1,2021-09-20:0,2021-09-21:0,2021-09-26:1,2021-10-01:0,2021-10-04:0,2021-10-05:0,2021-10-06:0,2021-10-07:0,2021-10-09:1";
- //指定日期是否是工作日
- long s = 0;
- for (int i = 0; i < 1000001; i++) {
- if(i==1){
- s = System.currentTimeMillis();
- }
- DateTimeCalculatorUtil.isChineseWorkDay(LocalDateTime.now(), holidayData);
- }
- System.out.println("isChineseWorkDay1 cost1:"+(System.currentTimeMillis()-s));
- }
- @Test
- public void isChineseWorkDay2(){
- //2021年放假信息
- String holidayData = "2021-01-01:0,2021-02-07:1,2021-02-11:0,2021-02-12:0,2021-02-15:0,2021-02-16:0,2021-02-17:0,2021-02-20:1,2021-04-05:0,2021-04-25:1,2021-05-03:0,2021-05-04:0,2021-05-05:0,2021-05-08:1,2021-06-14:0,2021-09-18:1,2021-09-20:0,2021-09-21:0,2021-09-26:1,2021-10-01:0,2021-10-04:0,2021-10-05:0,2021-10-06:0,2021-10-07:0,2021-10-09:1";
- //指定日期是否是工作日
- long s = 0;
- for (int i = 0; i < 1000001; i++) {
- if(i==1){
- s = System.currentTimeMillis();
- }
- DateTimeCalculatorUtil.isChineseWorkDay2(LocalDateTime.now(), holidayData);
- }
- System.out.println("isChineseWorkDay2 cost2:"+(System.currentTimeMillis()-s));
- }
结果(单位:ms):
- isChineseWorkDay1 cost1:5589
- isChineseWorkDay2 cost2:366
可以看到,使用缓存后性能对比 5589/366=15.27 , 速度提高15倍多,代码性能的小优化,大量调用后会被累加放大,优化非常值得!
Java日期时间API系列38-----一种高效的工作日计算计算方法的更多相关文章
- Java日期时间API系列19-----Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格式化和时区转换等。
通过Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类中时间范围示意图:可以很清晰的看出ZonedDateTime相当于LocalDateTime+ZoneI ...
- Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
目录 0.前言 1.TemporalAccessor源码 2.Temporal源码 3.TemporalAdjuster源码 4.ChronoLocalDate源码 5.LocalDate源码 6.总 ...
- Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate
通过Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的优点,java8具有很多优点,现在网上查到的农历转换工具类都是基于jdk7及以前的类写的,下面使用ja ...
- Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全
通过Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter, 可以看出java8的DateTimeFormatter完美解决 ...
- Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类
因为Jdk7及以前的日期时间类的不方便使用问题和线程安全问题等问题,2005年,Stephen Colebourne创建了Joda-Time库,作为替代的日期和时间API.Stephen向JCP提交了 ...
- Java日期时间API系列13-----Jdk8中java.time包中的新的日期时间API类,时间类转换,Date转LocalDateTime,LocalDateTime转Date等
从前面的系列博客中可以看出Jdk8中java.time包中的新的日期时间API类设计的很好,但Date由于使用仍非常广泛,这就涉及到Date转LocalDateTime,LocalDateTime转D ...
- Java日期时间API系列3-----Jdk7及以前的日期时间类的不方便使用问题
使用Java日期时间类,每个人都很熟悉每个项目中必不可少的工具类就是dateutil,包含各种日期计算,格式化等处理,而且常常会遇到找不到可用的处理方法,需要自己新增方法,处理过程很复杂. 1.Dat ...
- Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的特点
1.不变性 新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处. 比如:LocalDateTime 2.关注点分离 新的API将人可读的日期时间和机器时间(unix timestamp ...
- Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter
1.DateTimeFormatter final修饰,线程安全,用于打印和解析日期-时间对象的格式化程序. 创建DateTimeFormatter: DateTimeFormatter dateTi ...
- Java日期时间API系列39-----中文语句中的时间语义识别(time NLP 输入一句话,能识别出话里的时间)原理分析
NLP (Natural Language Processing) 是人工智能(AI)的一个子领域.自然语言是人类智慧的结晶,自然语言处理是人工智能中最为困难的问题之一(来自百度百科). 其中中文更是 ...
随机推荐
- 计算机网络第一章bb测试
错题8,31 课程 211计算机网络 测试 网络概论与体系结构 状态 已完成 尝试分数 得 340 分,满分 360 分 已用时间 14 分钟 说明 第一章 网络概论测试 显示的结果 所有答案, 已提 ...
- Docker笔记(一) 基础知识
官方文档地址:https://www.docker.com/get-started 中文参考手册:https://docker_practice.gitee.io/zh-cn 笔记原作者:陈艳男 B站 ...
- IDEA的Debug技巧
01_Debug简介和意义 什么是程序DeBug? Debug,是程序开发人员必会的一项调试程序的技能. 企业中程序开发和程序调试的比例为1:1.5,可以说如果你不会调试程序,你就没有办法从事编程工作 ...
- 1040 Longest Symmetric String
Given a string, you are supposed to output the length of the longest symmetric sub-string. For examp ...
- phpstorm 方法名类名 作者日期 注释
phpstorm 设置方法名 函数名注释 新建页面作者日期信息注释 官方提供的文档地址: http://www.jetbrains.com/phpstorm/help/creating-php-do ...
- 【Scrapy(三)】Scrapy 中的 logging 模块
logging模块的使用: 1.在scrapy中使用 2.在普通项目中使用
- nginx添加module之threads
一.安装nginx yum安装nginx 折叠源码 1 2 3 4 5 6 7 8 9 10 11 12 # 添加nginx源 rpm -ivh http://nginx.org/packages/c ...
- poj2418map或者字典树
题意: 给你一些串,然后求出每个串出现的概率. 思路: 简单题目,做法也很多,我用字典树做了下,然后又用map做了下,其实这个题目我感觉直接排序一遍之后线性输出应该是最简单最快的( ...
- POJ3114强连通+spfa
题意: 给你n个点,m条有向边,q询问,每次询问给两个数a,b输出a->b的最短路,但是题目有个限制,就是在一个环上的任意两点距离为0. 思路: 简单题目,直接强连通压缩 ...
- Nacos 1.3.2 启动报错[db-load-error]load jdbc.properties error
原因: 1.3.2版本Nacos默认启动模式为集群,在startup.cmd文件中第27行可以看到. 解决办法: 一.选择以默认的集群方式启动,就需要配置集群所需环境: 1.创建持久化数据库,推荐使用 ...