If there are no holidays, working days are easy to calculate. Monday to Friday is working days, but because there are holidays, this calculation needs external holiday schedule data to support. Calculation principle: first according to the holiday schedule data calculation, and then according to Monday and Friday calculation. The following uses LocalDateTime as an example.

1. The first version does not use cache /** * to judge whether it is a Chinese working day, including the adjusted date of legal holidays and holidayData. If the holidayData does not support the year, it will use Monday to Friday as the working day to judge. * @param localDateTime localDateTime * @param holidayData holidayData 0 indicates holiday, 1 indicates working day, for example: The 2021-01-01:1 0202-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

Public static map <String, public static map <String, 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; }}Copy the code

In this method, vacation schedule data is parsed into a Map, then compared, and finally judged using Monday to Friday.

2. The second version, use the cache optimization First edition, each call to the holiday arrangement data parsed into the Map, but it is not needed, because the holiday arrangement data published only once a year (except for special cases), don’t need to change in one year, the data call for the first time in the cache, the direct use of, have a change to update the cache.

Both Redis cache and local cache are available. Local cache is faster.

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); }Copy the code

//StringUtil.convertHolidayDataToMapUseCache

/** * Convert holiday data to map, Use caching to improve performance * @ param holidayData festival map * @ return to return to the 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); / / cache exists, return to cache the if (CollectionUtil. IsNotEmpty (dateTypeMap)) {return dateTypeMap; } // Cache does not exist, <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; }}Copy the code

WeakHashMap is used to realize automatic cache cleaning, and ReentrantReadWriteLock is used to realize read-write thread safety. Detailed code see com.xkzhangsan.time.utils.Com monCache, core code snippet below:

Public V get(K key) {lock.readLock().lock(); try { return cache.get(key); } finally { lock.readLock().unlock(); }} /** * find the value from the cache pool, * * @param supplier provider * @return value */ 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); // Double check if (null == value) {try {value = supplier. Get (); } catch (Exception e) { throw new RuntimeException(e); } cache.put(key, value); } } finally { lock.writeLock().unlock(); } } return value; }Copy the code
  1. Two kinds of performance comparison

Take the holiday message of 2021 as an example and call it 1 million times each. Ignoring the time of the first cache creation, starting from the second time, the test data is as follows: The 2021-01-01:1 0202-02-07:1, 2021-02-11:1 0202-02-12:0, 2021-02-15:0202-02-16:0 1, 2021-02-17:0202-02 – part 1, 2021-04-05:0, 20 The 21-04-25:1 1202-05-03:0, 2021-05-04:0202-05-05:0 1, 2021-05-08:1202-06-14:0 1, 2021-09-18:1202-09-20:0 1, 2021-09-21:0202-1 09 – moreover, 2021-10-01:1-10-0202 04:0, 2021-10-05:1-10-0202 06:0, 2021-10-07:1-10-0202 09:1

@test public void isChineseWorkDay1(){//2021 holidayData = "The 2021-01-01:1 0202-02-07:1, 2021-02-11:1 0202-02-12:0, 2021-02-15:1 0202-02-16:0, 2021-02-17:0202-02 - part 1, 2021-04-05:0, 20 The 21-04-25:1 1202-05-03:0, 2021-05-04:0202-05-05:0 1, 2021-05-08:1202-06-14:0 1, 2021-09-18:1202-09-20:0 1, 2021-09-21:0202-1 09 - moreover, 2021-10-01:1-10-0202 04:0, 2021-10-05:1-10-0202 06:0, 2021-10-07:1-10-0202 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(){// holidayData = "The 2021-01-01:1 0202-02-07:1, 2021-02-11:1 0202-02-12:0, 2021-02-15:1 0202-02-16:0, 2021-02-17:0202-02 - part 1, 2021-04-05:0, 20 The 21-04-25:1 1202-05-03:0, 2021-05-04:0202-05-05:0 1, 2021-05-08:1202-06-14:0 1, 2021-09-18:1202-09-20:0 1, 2021-09-21:0202-1 09 - moreover, 2021-10-01:1-10-0202 04:0, 2021-10-05:1-10-0202 06:0, 2021-10-07:1-10-0202 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)); }Copy the code

Results (unit: ms) :

isChineseWorkDay1 cost1:5589 isChineseWorkDay2 cost2:366

As can be seen, after the use of cache performance comparison 5589/366=15.27, speed increased by more than 15 times, small optimization of code performance, a large number of calls will be cumulative amplification, optimization is very worth it!

Source code address: github.com/xkzhangsan/…