银行等单位工作日计算的实现
原先想在JavaEye上发表这篇日志,可是论坛规则小测验让我倍感麻烦,所以暂且让它出现在QZone。
下面就进入正题,这里主要介绍工作日实现的思路及实现。
年末有段时间需要维护新疆FOA,其中有个需求就是在数据库中查询某个时间段工作日>3天的记录。由于同时存在农历和阳历,造成有些工作日可以休息或者有些周末要正常上班的问题,所以计算工作日还需要考虑到这个问题。
先介绍下工作日计算的思路:
1、确定计算公式
工作日 = 正常工作日 - 工作休息日 + 休息工作日
说明:正常工作日:按照阳历算出除去周六周日的天数。
工作休息日:在正常工作日下的休息天数。如国庆、春节7天,工作日可以休息。
休息工作日:在周末需要正常上班的天数。如国庆、春节后需要在周六或者周日正常上班。
2、根据公式选择实现方式
正常工作日实现:遍历某个时间段内每一天,过滤掉周六周日,最终得到正常工作日天数。
工作休息日实现:将工作休息日的日期写入字典,提供对比,最终计算出工作休息日的天数。
休息工作日实现:类似工作休息日实现方式。
说明:由于实现方式简单,以文字方式可以描述清楚问题,这里就不考虑画流程图。
下面简要介绍Java语言的实现:
1、类图
(QZone不支持日志图片上传,转到JavaEye下再上传图片)
2、实现源代码(代码注释有不周到处请谅解)
/* ==================================================================
* Created [2010-1-22 下午05:00:29] by Neal Miao
* ==================================================================
* XXX
* ==================================================================
* XXX License v1.0
* Copyright (c) Gsoft S&T Co.ltd HangZhou, 2009-2010
* ==================================================================
* 杭州中科天翔科技有限公司拥有该文件的使用、复制、修改和分发的许可权
* 如果你想得到更多信息,请访问 <http://www.g-soft.com.cn>
*
* Gsoft S&T Co.ltd HangZhou owns permission to use, copy, modify and
* distribute this documentation.
* For more information on gs, please
* see <http://www.g-soft.com.cn>.
* ==================================================================
*/
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
/**
* <p>
* WeekdayCounter.java
* </p>
* <p>
* 可用于工作日计算。<br>
* 由于中国的部分节假日为农历节日,如春节、清明节等等。
* 从而使得工作日和周末的混乱,计算工作日要重新考虑。
* </p>
*
* @author $Author: nealmiao $
* @version $Revision: 1.1 $
*/
public class WeekdayCountHelper {
/** 控制是否显示console */
private static boolean needConsoleInfo = false;
/**
* needConsoleInfo
*
* @return the needConsoleInfo
* @since Ver 1.0
*/
public static boolean isNeedConsoleInfo() {
return needConsoleInfo;
}
/**
* needConsoleInfo
*
* @param needConsoleInfo the needConsoleInfo to set
* @since Ver 1.0
*/
public static void setNeedConsoleInfo(boolean needConsoleInfo) {
WeekdayCountHelper.needConsoleInfo = needConsoleInfo;
}
/**
* 判断工作日是否大于3,如果是则返回true
*
* @param revDate
* @param retnDate
* @return 返回判断工作日是否大于3结果。
* @since Ver 1.0
*/
public static boolean isValid(Date revDate, Date retnDate) {
if (getWeekDays(revDate, retnDate, null, null) > 3) {
return true;
} else {
return false;
}
}
/**
* 判断工作日是否大于3
*
* @param revDate
* @param retnDate
* @param specialHolidayList
* @param specialNonHolidayList
* @return 返回判断工作日是否大于3结果。
* @since Ver 1.0
*/
public static boolean isValid(Date revDate, Date retnDate,
List<Date> specialHolidayList, List<Date> specialNonHolidayList) {
if (getWeekDays(revDate, retnDate, specialHolidayList,
specialNonHolidayList) > 3) {
return true;
} else {
return false;
}
}
/**
* 判断工作日是否大于3
*
* @param startDate
* @param endDate
* @param specialHolidayList
* @param specialNonHolidayList
* @throws ParseException
* @return boolean
* @since Ver 1.0
*/
public static boolean isValid(String startDate, String endDate,
List<Date> specialHolidayList, List<Date> specialNonHolidayList)
throws ParseException {
if (isNeedConsoleInfo()) {
System.out.println("/nFrom " + startDate + " to " + endDate);
}
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
return isValid(format.parse(startDate), format.parse(endDate),
specialHolidayList, specialNonHolidayList);
}
/**
* 根据日期判断是否为周末,只考虑周六和周日。
*
* @param date
* @return 返回是否为周末。
* @since Ver 1.0
*/
public static boolean isWeekend(Date date) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int week = calendar.get(Calendar.DAY_OF_WEEK);
if (Calendar.SUNDAY == week || Calendar.SATURDAY == week) {
return true;
} else {
return false;
}
}
/**
* 周末要上班的天数
*
* @param startDate
* @param endDate
* @param specialHolidayList
* @return int
* @exception
* @since Ver 1.0
*/
public static int getSpecialNonHolidays(Date startDate, Date endDate,
List<Date> specialNonHolidayList) {
Calendar start = Calendar.getInstance();
start.setTime(startDate);
Calendar end = Calendar.getInstance();
end.setTime(endDate);
Calendar compareDate = Calendar.getInstance();
int days = 0;
while (start.compareTo(end) <= 0) {
for (Date date : specialNonHolidayList) {
compareDate.setTime(date);
int day = compareDate.get(Calendar.DAY_OF_WEEK);
if (day == Calendar.SUNDAY || day == Calendar.SATURDAY) {
if (start.compareTo(compareDate) == 0) {
days++;
continue;
}
} else {
// do nothing, 过滤掉输入的周末日期
}
}
start.set(Calendar.DATE, start.get(Calendar.DATE) + 1);
}
if (isNeedConsoleInfo()) {
System.out.print("周末上班的工作日:" + days + "/t");
}
return days;
}
/**
* 特殊工作日,主要由农历节日引起。
*
* @param startDate
* @param endDate
* @param specialHolidayList
* @return int
* @exception
* @since Ver 1.0
*/
public static int getSpecialHolidays(Date startDate, Date endDate,
List<Date> specialHolidayList) {
Calendar start = Calendar.getInstance();
start.setTime(startDate);
Calendar end = Calendar.getInstance();
end.setTime(endDate);
Calendar compareDate = Calendar.getInstance();
int days = 0;
while (start.compareTo(end) <= 0) {
for (Date date : specialHolidayList) {
compareDate.setTime(date);
int day = compareDate.get(Calendar.DAY_OF_WEEK);
if (day == Calendar.SUNDAY || day == Calendar.SATURDAY) {
// do nothing, 过滤掉输入的非周末日期
} else {
if (start.compareTo(compareDate) == 0) {
days++;
continue;
}
}
}
start.set(Calendar.DATE, start.get(Calendar.DATE) + 1);
}
if (isNeedConsoleInfo()) {
System.out.print("正常工作日的休息日:" + days + "/t");
}
return days;
}
/**
* 循环遍历日期,获取工作日天数。
*
* @param startDate 起始日期
* @param endDate 结束日期
* @return 返回工作日天数。
* @since Ver 1.0
*/
public static int getNormalWeekdays(Date startDate, Date endDate) {
Calendar start = Calendar.getInstance();
start.setTime(startDate);
Calendar end = Calendar.getInstance();
end.setTime(endDate);
int days = 0;
while (start.compareTo(end) <= 0) {
int day = start.get(Calendar.DAY_OF_WEEK);
start.set(Calendar.DATE, start.get(Calendar.DATE) + 1);
if (day == Calendar.SUNDAY || day == Calendar.SATURDAY) {
continue;
} else {
days++;
}
}
if (isNeedConsoleInfo()) {
System.out.print("正常工作日:" + days + "/t");
}
return days;
}
/**
* 计算获得实际工作天数。<br>
* 工作天数 = 计算公式正常的工作天数-特殊的(农历)节假日+特殊的工作日
*
* @param startDate
* @param endDate
* @param specialHolidayList
* @param specialNonHolidayList
* @return 返回实际工作日天数。
*/
public static int getWeekDays(Date startDate, Date endDate,
List<Date> specialHolidayList, List<Date> specialNonHolidayList) {
int days = getNormalWeekdays(startDate, endDate)
- getSpecialHolidays(startDate, endDate, specialHolidayList)
+ getSpecialNonHolidays(startDate, endDate, specialNonHolidayList);
if (isNeedConsoleInfo()) {
System.out.print("/n实际工作日:" + days + "/t");
}
return days;
}
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
List<Date> holidayList = new LinkedList<Date>();
holidayList.add(format.parse("2010-2-10"));
holidayList.add(format.parse("2010-2-9"));
holidayList.add(format.parse("1990-2-9"));
List<Date> nonHolidayList = new LinkedList<Date>();
nonHolidayList.add(format.parse("2010-2-13"));
setNeedConsoleInfo(true);
isValid("2010-2-8", "2010-2-13", holidayList, nonHolidayList);
isValid("1992-2-8", "2010-2-13", holidayList, nonHolidayList);
isValid("1990-2-8", "2010-2-13", holidayList, nonHolidayList);
isValid("1990-2-8", "1990-2-13", holidayList, nonHolidayList);
}
}
测试结果:
From 2010-2-8 to 2010-2-13
正常工作日:5 工作休息日:2 休息工作日:1
实际工作日:4
From 1992-2-8 to 2010-2-13
正常工作日:4700 工作休息日:2 休息工作日:1
实际工作日:4699
From 1990-2-8 to 2010-2-13
正常工作日:5222 工作休息日:1 休息工作日:0
实际工作日:5221
From 1990-2-8 to 1990-2-13
正常工作日:4 工作休息日:1 休息工作日:0
实际工作日:3
对比1992-2-8 to 2010-2-13和1990-2-8 to 2010-2-13的结果,发现后者的结果有问题。后者的正确结果应该是5220。时间跨度达到某一个值程序会出现位置错误。一般情况下,工作日的时间跨度不会大于4000多天,所以这个程序还是具有一定实用性和通用性。