Java工具-获取某月份天数、某月最后一天、某月工作日天数(支持自定义节假日)

因为之前在项目中有一个工作日志的功能,所以在网上找了一些相关的工具类,都是零零散散,我在这总结一下。废话不多说,上代码!

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;/*** @ClassName WorkDayUtil   计算日期、工作日相关的工具类* @Auther JunKai* @Date 2019/12/24*/
public class WorkDayUtil {/** 预设工作日数据的开始年份 */private static final int START_YEAR = 2017;/** 预设工作日数据的结束年份 */private static final int END_YEAR = 5020;/** 工作日map,true为补休,false为放假 */public static final Map<Integer, Boolean> WORKDAY_MAP = new HashMap<>();private static final SegmentTree SEGMENT_TREE;private WorkDayUtil() {}/** 获取一个月天数 */public static int getDaysOfMonth(Date date) {Calendar calendar = Calendar.getInstance();calendar.setTime(date);return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);}/** 获取一个月的最后一天 */public static String getLastDay(Date date) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");Calendar calendar = Calendar.getInstance();calendar.setTime(date);int lastDays = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);Date time = calendar.getTime();time.setDate(lastDays);return df.format(time);}/** 获取一个月的第一天 */public static String getFirstDay(Date date) {SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");Calendar calendar = Calendar.getInstance();calendar.setTime(date);//calendar.add(Calendar.MONTH, -1);calendar.set(Calendar.DAY_OF_MONTH, 1);String time = df.format(calendar.getTime());return time;}/*** 计算两个日期之间有多少个工作日<br/>* @param startDate* @param endDate* @return*/public static int howManyWorkday(Date startDate, Date endDate) {if (startDate.after(endDate)) {return howManyWorkday(endDate, startDate);}Calendar startCalendar = Calendar.getInstance();startCalendar.setTime(startDate);int startDays = getDaysAfterStartYear(startCalendar) - 1;   // 第一天从0开始Calendar endCalendar = Calendar.getInstance();endCalendar.setTime(endDate);int endDays = getDaysAfterStartYear(endCalendar) - 1;   // 第一天从0开始if (startDays == endDays) { // 如果开始日期和结束日期在同一天的话return isWorkDay(startDate) ? 1 : 0;    // 当天为工作日则返回1天,否则0天}if (!START_DATE_HANDLING_STRATEGY.ifCountAsOneDay(startDate)) { // 根据处理策略,如果开始日期不算一天的话++startDays;    // 起始日期向后移一天}if (!END_DATE_HANDLING_STRATEGY.ifCountAsOneDay(endDate)) { // 根据处理策略,如果结束日期不算一天的话--endDays;  // 结束日期向前移一天}return SEGMENT_TREE.query(startDays, endDays);}static {initWorkday(); // 初始化工作日map// 计算从START_YEAR到END_YEAR一共有多少天int totalDays = 0;for (int year = START_YEAR; year <= END_YEAR; ++year) {totalDays += getDaysOfYear(year);}int[] workdayArray = new int[totalDays];    // 将工作日的数据存入到数组Calendar calendar = new GregorianCalendar(START_YEAR, 0, 1);for (int i = 0; i < totalDays; ++i) {// 将日期转为yyyyMMdd格式的intint datestamp = calendar.get(Calendar.YEAR) * 10000 + (calendar.get(Calendar.MONTH) + 1) * 100 + calendar.get(Calendar.DAY_OF_MONTH);Boolean isWorkDay = WORKDAY_MAP.get(datestamp);if (isWorkDay != null) { // 如果在工作日map里有记录,则按此判断工作日workdayArray[i] = isWorkDay ? 1 : 0;} else { // 如果在工作日map里没记录,则按是否为周末判断工作日int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);workdayArray[i] = (dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY) ? 1 : 0;}calendar.add(Calendar.DAY_OF_YEAR, 1);}SEGMENT_TREE = new SegmentTree(workdayArray);   // 生成线段树}/*** 判断某一日期是否为工作日* @param date* @return*/public static boolean isWorkDay(int date) {if(WORKDAY_MAP.get(date)== null) {Calendar calendar = Calendar.getInstance();SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");try {calendar.setTime(sdf.parse(String.valueOf(date)));} catch (ParseException e) {e.printStackTrace();}//如果当天不是补休也不是放假,但是为周末,则为非工作日if(calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY || calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY){return false;}} else {//判断如何当天是法定假日,则为非工作日if(WORKDAY_MAP.get(date) == false) {return false;}//判断如何当天是补休,则为工作日if(WORKDAY_MAP.get(date)== true) {return true;}}return true;}/*** 是否为工作日* @param date* @return*/public static boolean isWorkDay(Date date) {Calendar calendar = Calendar.getInstance();calendar.setTime(date);int days = getDaysAfterStartYear(calendar) - 1;return SEGMENT_TREE.query(days, days) == 1;}/***  计算从开始年份到这个日期有多少天* @param calendar* @return*/private static int getDaysAfterStartYear(Calendar calendar) {int year = calendar.get(Calendar.YEAR);if (year < START_YEAR || year > END_YEAR) {throw new IllegalArgumentException(String.format("系统目前仅支持计算%d年至%d年之间的工作日,无法计算%d年!", START_YEAR, END_YEAR, year));}int days = 0;for (int i=START_YEAR; i<year; ++i) {days += getDaysOfYear(i);}days += calendar.get(Calendar.DAY_OF_YEAR);return days;}/*** 计算该年有几天,闰年返回366,平年返回365* @param year* @return*/private static int getDaysOfYear(int year) {return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ? 366 : 365;}/*** 初始化工作日Map<br/>* 日期格式必须为yyyyMMdd,true为补休,false为放假,如果本来就是周末的节假日则不需再设置*  手动进行维护*/private static void initWorkday() {// ---------------2020------------------WORKDAY_MAP.put(20200101, false);WORKDAY_MAP.put(20200124, false);WORKDAY_MAP.put(20200125, false);WORKDAY_MAP.put(20200126, false);WORKDAY_MAP.put(20200127, false);WORKDAY_MAP.put(20200128, false);WORKDAY_MAP.put(20200129, false);WORKDAY_MAP.put(20200130, false);WORKDAY_MAP.put(20200201, true);// ---------------2019------------------WORKDAY_MAP.put(20190101, false);WORKDAY_MAP.put(20190202, true);WORKDAY_MAP.put(20190203, true);WORKDAY_MAP.put(20190204, false);WORKDAY_MAP.put(20190205, false);WORKDAY_MAP.put(20190206, false);WORKDAY_MAP.put(20190207, false);WORKDAY_MAP.put(20190208, false);WORKDAY_MAP.put(20190209, false);WORKDAY_MAP.put(20190210, false);WORKDAY_MAP.put(20190405, false);WORKDAY_MAP.put(20190501, false);WORKDAY_MAP.put(20190502, false);WORKDAY_MAP.put(20190503, false);WORKDAY_MAP.put(20190505, true);WORKDAY_MAP.put(20190607, false);WORKDAY_MAP.put(20190913, false);WORKDAY_MAP.put(20190929, true);WORKDAY_MAP.put(20191001, false);WORKDAY_MAP.put(20191002, false);WORKDAY_MAP.put(20191003, false);WORKDAY_MAP.put(20191004, false);WORKDAY_MAP.put(20191007, false);WORKDAY_MAP.put(20191012, true);// ------------------2018----------------WORKDAY_MAP.put(20180101, false);WORKDAY_MAP.put(20180211, true);WORKDAY_MAP.put(20180215, false);WORKDAY_MAP.put(20180216, false);WORKDAY_MAP.put(20180219, false);WORKDAY_MAP.put(20180220, false);WORKDAY_MAP.put(20180221, false);WORKDAY_MAP.put(20180224, true);WORKDAY_MAP.put(20180405, false);WORKDAY_MAP.put(20180406, false);WORKDAY_MAP.put(20180408, true);WORKDAY_MAP.put(20180428, true);WORKDAY_MAP.put(20180430, false);WORKDAY_MAP.put(20180501, false);WORKDAY_MAP.put(20180618, false);WORKDAY_MAP.put(20180924, false);WORKDAY_MAP.put(20180929, true);WORKDAY_MAP.put(20180930, true);WORKDAY_MAP.put(20181001, false);WORKDAY_MAP.put(20181002, false);WORKDAY_MAP.put(20181003, false);WORKDAY_MAP.put(20181004, false);WORKDAY_MAP.put(20181005, false);}/*** 边界日期处理策略<br/>* 在计算两个日期之间有多少个工作日时,有的特殊需求是如果开始/结束的日期在某个时间之前/后(如中午十二点前),则不把当天算作一天<br/>* 因此特将此逻辑分离出来,各自按照不同需求实现该接口即可* @Auther JunKai* @Date 2019/12/24*/private interface BoundaryDateHandlingStrategy {/** 是否把这个日期算作一天 */boolean ifCountAsOneDay(Date date);}/*** zkw线段树* @author Corvey*/private static class SegmentTree {private int[] data; // 线段树数据private int numOfLeaf; // 叶子结点个数public SegmentTree(int[] srcData) {for (numOfLeaf = 1; numOfLeaf < srcData.length; numOfLeaf <<= 1);data = new int[numOfLeaf << 1];for (int i = 0; i < srcData.length; ++i) {data[i + numOfLeaf] = srcData[i];}for (int i = numOfLeaf - 1; i > 0; --i) {data[i] = data[i << 1] + data[i << 1 | 1];}}/** [left, right]区间求和,left从0开始 */public int query(int left, int right) {if (left > right) {return query(right, left);}left = left + numOfLeaf - 1;right = right + numOfLeaf + 1;int sum = 0;for (; (left ^ right ^ 1) != 0; left >>= 1, right >>= 1) {if ((~left & 1) == 1)   sum += data[left ^ 1];if ((right & 1) == 1)   sum += data[right ^ 1];}return sum;}}/** 起始日期处理策略 */private static final BoundaryDateHandlingStrategy START_DATE_HANDLING_STRATEGY = date -> {Calendar calendar = Calendar.getInstance();calendar.setTime(date);return calendar.get(Calendar.HOUR_OF_DAY) < 12; // 如果开始时间在中午12点前,则当天也算作一天,否则不算};/** 结束日期处理策略 */private static final BoundaryDateHandlingStrategy END_DATE_HANDLING_STRATEGY = date -> {return true;    // 结束时间无论几点,都算作1天};}

好了,这工具类就完事了!

每天进步一点点

Java工具-获取某月份天数、某月最后一天、某月工作日天数(支持自定义节假日)相关推荐

  1. java 和 mysql 获取周 星期 的第一天 最后一天 或者 月的 日期(字符串转日期,日期转字符串,日期加减)...

    获取周的第一天,最后一天 System.out.println(getStartEndDate("2016-05-01", 1)); 获取星期的第一天和最后一天 System.ou ...

  2. java里获取当前月份_Java如何获取当前月份的名称?

    要从系统获取当前月份的名称,我们可以使用java.util.Calendarclass.Calendar.get(Calendar.MONTH)从0开始的第一个月和11作为上月的整数返回月份的值.这意 ...

  3. java代码获取当前月第一天和最后一天的毫秒值

    Calendar calendar = Calendar.getInstance();//获取当前日期calendar.add(Calendar.MONTH, 0);//n代表和本月偏移 0本月.1后 ...

  4. 【Android 安全】DEX 加密 ( Java 工具开发 | apk 文件签名 )

    文章目录 一.生成 jks 文件 二.签名命令 三.执行结果 四.处理 Unsupported major.minor version 52.0 错误 参考博客 : [Android 安全]DEX 加 ...

  5. js获取一个月份最大天数和获取月的最后一天

    代码如下: <html xmlns=http://www.w3.org/1999/xhtml >     <head>     <title>标题页</tit ...

  6. DateUtils 工具类:获取指定月份第一天时间,最后一天时间

    获取指定月份第一天时间 getMinDateMonth ,最后一天时间 getMaxDateMonth ,将时间区间按照月份分组,拆分多个片段 getDateSplitMonth 指定月份的下个月 g ...

  7. Java获取指定月份的最后一天

    https://blog.csdn.net/itmyhome1990/article/details/85619804 Java获取指定月份的最后一天 麦田 2019-01-02 17:43:38   ...

  8. 练习-Java多路分支之月份天数计算

    任务描述 本关任务:根据给定的年份和月份,获取该月份的天数. 提示:在求二月份的天数时,需要判断年份是否是闰年.闰年的条件是,年份是否能够被 400 整除,或者年份能够被 4 整除但是不能被 100 ...

  9. Java获取指定月份第一周第一天

    Java获取指定月份第一周第一天 思路: 1.获取当前月份第一天 2.获取第一天对应周几 3.根据周几减去对应天数得到指定日期 @Testpublic void test2(){Date lastWe ...

最新文章

  1. 时间源服务器|授时仪|GPS时钟同步系统
  2. WCF服务开发与调用的完整示例
  3. C和指针之strcat函数 strchr函数 strcmp函数 strcpy函数 strnchr函数 strstr函数实现
  4. python变量和常量_Python数学模块常量和示例
  5. “有意思的前端函数面试题”第一题答案原理解析
  6. 如何使用cURL一次测量请求和响应时间?
  7. POJ 2393 Yogurt factory
  8. 天勤数据结构代码——排序
  9. 中国居民身份证、通行证(含香港、澳门、台湾)资料整理,含编码规则
  10. bgp (二)改变下一跳本地,
  11. Kafka学习之Replication tools之Reassign Partitions Tool
  12. 轻论坛StartBBS、YouBBS、Xiuno对比
  13. 如何快速的把m4a转换成mp3格式
  14. iTween基础之Move(移动)
  15. 盘一盘 Python 系列 - SciPy
  16. VMware CentOS6.5 安装VMware Tools
  17. 喜闻乐见之Activity生命周期
  18. Visual Studio滚动条设置
  19. 条码WMS系统与ERP接口实现方法
  20. FFT从入门到使用(ACM/OI)

热门文章

  1. 网速、宽带、带宽、流量三者之间的关系是什么?
  2. 《京韵大鼓——活捉三郎》(李树盛)(唱词文本)
  3. 三角形与圆的相交问题
  4. 19岁表弟,暑假接了一个五百的外包。。。
  5. 检測wifi是否须要portal验证 公共场所wifi验证
  6. win7 java 卸载_win7系统彻底卸载java jdk的操作方法
  7. thinkphp5.1中间件是什么,有什么用
  8. 百马百担问题:100匹马驮100担货物,其中大马驮3担货,中马驮2担,两匹小马驮1担。问共有大、中、小马各有多少匹?编程实现求解的算法。
  9. 一场pandas与SQL的巅峰大战(七)
  10. Oracle实现去重的两种方式总结