# workday **Repository Path**: chmodke/workday ## Basic Information - **Project Name**: workday - **Description**: Python工作日计算器实现与测试 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-04 - **Last Updated**: 2026-01-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ```promopt 你是一名精通Python的工程师,现在需要完成计算工作日相关的需求,请编写代码实现。 1、编写代码判断一年中某天是否工作日; 2、工作日指除节假日和周末; 3、节假日将以json文件形式提供,格式如下`[{"year": 2026,"dates": [{"date": "2026-01-01","name": "元旦","name_cn": "元旦","name_en": "New Year's Day","type": "public_holiday"}]}]`; 4、节假日json文件中type为`public_holiday`表示假期,type为`transfer_workday`表示换休的工作日; 5、如果是周末并且不是换休的工作日则是假期,如果是工作日并且不是假期则是工作日 6、还需要实现计算某个时间段内有那些工作日和假期; 7、基于以上基础,实现计算某个时间段内有多少个工作日 8、每个功能以方法提供 9、请给出完整的代码及测试用用例 10、使用python3.7版本 ``` [DeepSeek](https://chat.deepseek.com/share/x91txvk1rb8s3yxxvf) 以下是一个完整的Python解决方案,包含所有要求的函数和测试用例: ```python import json import datetime from datetime import date, timedelta from typing import List, Dict, Tuple from pathlib import Path import unittest class WorkdayCalculator: """工作日计算器""" def __init__(self, holiday_file_path: str = None): """ 初始化工作日计算器 Args: holiday_file_path: 节假日JSON文件路径 """ self.holiday_data = {} self.public_holidays = set() # 存储所有节假日日期字符串 self.transfer_workdays = set() # 存储所有调休工作日日期字符串 if holiday_file_path: self.load_holiday_data(holiday_file_path) def load_holiday_data(self, file_path: str): """加载节假日数据文件""" try: with open(file_path, 'r', encoding='utf-8') as f: holiday_json = json.load(f) self._parse_holiday_data(holiday_json) except FileNotFoundError: print(f"警告: 节假日文件 {file_path} 未找到,将使用空数据") except json.JSONDecodeError: print(f"错误: 节假日文件 {file_path} 格式错误") raise def _parse_holiday_data(self, holiday_json: List[Dict]): """解析节假日JSON数据""" self.holiday_data = holiday_json for year_data in holiday_json: year = year_data.get("year") dates = year_data.get("dates", []) for date_info in dates: date_str = date_info.get("date") holiday_type = date_info.get("type") if holiday_type == "public_holiday": self.public_holidays.add(date_str) elif holiday_type == "transfer_workday": self.transfer_workdays.add(date_str) def is_workday(self, target_date: date) -> bool: """ 判断指定日期是否为工作日 Args: target_date: 要判断的日期 Returns: bool: True表示工作日,False表示非工作日 """ date_str = target_date.isoformat() # 判断是否为周末 is_weekend = target_date.weekday() >= 5 # 5=周六, 6=周日 # 判断是否为节假日 is_holiday = date_str in self.public_holidays # 判断是否为调休工作日 is_transfer_workday = date_str in self.transfer_workdays # 判断逻辑: # 1. 如果是调休工作日,则是工作日 if is_transfer_workday: return True # 2. 如果是节假日,则不是工作日 if is_holiday: return False # 3. 如果是周末,则不是工作日 if is_weekend: return False # 4. 其他情况(周一到周五且不是节假日)是工作日 return True def get_workdays_and_holidays(self, start_date: date, end_date: date) -> Tuple[List[date], List[date]]: """ 获取指定时间段内的工作日和假期列表 Args: start_date: 开始日期(包含) end_date: 结束日期(包含) Returns: Tuple[List[date], List[date]]: (工作日列表, 假期列表) """ if start_date > end_date: raise ValueError("开始日期不能晚于结束日期") workdays = [] holidays = [] current_date = start_date while current_date <= end_date: if self.is_workday(current_date): workdays.append(current_date) else: holidays.append(current_date) current_date += timedelta(days=1) return workdays, holidays def count_workdays(self, start_date: date, end_date: date) -> int: """ 计算指定时间段内的工作日数量 Args: start_date: 开始日期(包含) end_date: 结束日期(包含) Returns: int: 工作日数量 """ workdays, _ = self.get_workdays_and_holidays(start_date, end_date) return len(workdays) def get_workdays_between(self, start_date: date, end_date: date) -> List[date]: """ 获取指定时间段内的工作日列表 Args: start_date: 开始日期(包含) end_date: 结束日期(包含) Returns: List[date]: 工作日列表 """ workdays, _ = self.get_workdays_and_holidays(start_date, end_date) return workdays def get_holidays_between(self, start_date: date, end_date: date) -> List[date]: """ 获取指定时间段内的假期列表 Args: start_date: 开始日期(包含) end_date: 结束日期(包含) Returns: List[date]: 假期列表 """ _, holidays = self.get_workdays_and_holidays(start_date, end_date) return holidays def is_weekend(self, target_date: date) -> bool: """判断是否为周末""" return target_date.weekday() >= 5 def is_public_holiday(self, target_date: date) -> bool: """判断是否为节假日""" return target_date.isoformat() in self.public_holidays def is_transfer_workday(self, target_date: date) -> bool: """判断是否为调休工作日""" return target_date.isoformat() in self.transfer_workdays def create_sample_holiday_file(file_path: str): """创建示例节假日文件""" sample_data = [ { "year": 2026, "dates": [ {"date": "2026-01-01", "name": "元旦", "name_cn": "元旦", "name_en": "New Year's Day", "type": "public_holiday"}, {"date": "2026-01-02", "name": "元旦调休", "name_cn": "元旦调休", "name_en": "New Year's Day Holiday", "type": "public_holiday"}, {"date": "2026-01-03", "name": "元旦调休", "name_cn": "元旦调休", "name_en": "New Year's Day Holiday", "type": "public_holiday"}, {"date": "2026-01-04", "name": "元旦调班", "name_cn": "元旦调班", "name_en": "New Year's Day Transfer", "type": "transfer_workday"}, {"date": "2026-02-17", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-18", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-19", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-20", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-21", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-22", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-23", "name": "春节", "name_cn": "春节", "name_en": "Spring Festival", "type": "public_holiday"}, {"date": "2026-02-14", "name": "春节调班", "name_cn": "春节调班", "name_en": "Spring Festival Transfer", "type": "transfer_workday"}, {"date": "2026-02-15", "name": "春节调班", "name_cn": "春节调班", "name_en": "Spring Festival Transfer", "type": "transfer_workday"}, {"date": "2026-04-04", "name": "清明节", "name_cn": "清明节", "name_en": "Qingming Festival", "type": "public_holiday"}, {"date": "2026-04-05", "name": "清明节", "name_cn": "清明节", "name_en": "Qingming Festival", "type": "public_holiday"}, {"date": "2026-04-06", "name": "清明节", "name_cn": "清明节", "name_en": "Qingming Festival", "type": "public_holiday"}, {"date": "2026-05-01", "name": "劳动节", "name_cn": "劳动节", "name_en": "Labor Day", "type": "public_holiday"}, {"date": "2026-05-02", "name": "劳动节", "name_cn": "劳动节", "name_en": "Labor Day", "type": "public_holiday"}, {"date": "2026-05-03", "name": "劳动节", "name_cn": "劳动节", "name_en": "Labor Day", "type": "public_holiday"}, {"date": "2026-05-04", "name": "劳动节", "name_cn": "劳动节", "name_en": "Labor Day", "type": "public_holiday"}, {"date": "2026-05-05", "name": "劳动节", "name_cn": "劳动节", "name_en": "Labor Day", "type": "public_holiday"}, {"date": "2026-04-27", "name": "劳动节调班", "name_cn": "劳动节调班", "name_en": "Labor Day Transfer", "type": "transfer_workday"}, {"date": "2026-04-28", "name": "劳动节调班", "name_cn": "劳动节调班", "name_en": "Labor Day Transfer", "type": "transfer_workday"} ] }, { "year": 2027, "dates": [ {"date": "2027-01-01", "name": "元旦", "name_cn": "元旦", "name_en": "New Year's Day", "type": "public_holiday"}, {"date": "2027-01-02", "name": "元旦调休", "name_cn": "元旦调休", "name_en": "New Year's Day Holiday", "type": "public_holiday"}, {"date": "2027-01-03", "name": "元旦调休", "name_cn": "元旦调休", "name_en": "New Year's Day Holiday", "type": "public_holiday"} ] } ] with open(file_path, 'w', encoding='utf-8') as f: json.dump(sample_data, f, ensure_ascii=False, indent=2) print(f"示例节假日文件已创建: {file_path}") class TestWorkdayCalculator(unittest.TestCase): """测试工作日计算器""" def setUp(self): """测试前准备""" # 创建临时节假日文件 self.holiday_file = "test_holidays.json" create_sample_holiday_file(self.holiday_file) # 初始化计算器 self.calculator = WorkdayCalculator(self.holiday_file) def tearDown(self): """测试后清理""" # 删除临时文件 if Path(self.holiday_file).exists(): Path(self.holiday_file).unlink() def test_is_workday(self): """测试单日判断""" # 测试普通工作日(周一到周五,非节假日) self.assertTrue(self.calculator.is_workday(date(2026, 1, 5))) # 周一 self.assertTrue(self.calculator.is_workday(date(2026, 1, 6))) # 周二 # 测试节假日 self.assertFalse(self.calculator.is_workday(date(2026, 1, 1))) # 元旦 self.assertFalse(self.calculator.is_workday(date(2026, 1, 2))) # 元旦调休 # 测试周末 self.assertFalse(self.calculator.is_workday(date(2026, 1, 3))) # 周六 self.assertFalse(self.calculator.is_workday(date(2026, 1, 4))) # 周日 # 测试调休工作日(周末但调休上班) self.assertTrue(self.calculator.is_workday(date(2026, 1, 4))) # 周日,但元旦调班 # 测试未在数据中的年份 self.assertTrue(self.calculator.is_workday(date(2028, 1, 3))) # 普通工作日 self.assertFalse(self.calculator.is_workday(date(2028, 1, 1))) # 元旦,但无数据,按周末判断 def test_get_workdays_and_holidays(self): """测试获取工作日和假期列表""" start_date = date(2026, 1, 1) end_date = date(2026, 1, 7) workdays, holidays = self.calculator.get_workdays_and_holidays(start_date, end_date) # 验证工作日 expected_workdays = [ date(2026, 1, 4), # 周日,但调休上班 date(2026, 1, 5), # 周一 date(2026, 1, 6), # 周二 date(2026, 1, 7), # 周三 ] self.assertEqual(workdays, expected_workdays) # 验证假期 expected_holidays = [ date(2026, 1, 1), # 元旦 date(2026, 1, 2), # 元旦调休 date(2026, 1, 3), # 周六 ] self.assertEqual(holidays, expected_holidays) def test_count_workdays(self): """测试工作日计数""" # 测试一周 start_date = date(2026, 1, 1) end_date = date(2026, 1, 7) self.assertEqual(self.calculator.count_workdays(start_date, end_date), 4) # 测试春节假期 start_date = date(2026, 2, 16) # 周一 end_date = date(2026, 2, 27) # 周五 # 2月16-22日春节假期,23-24日周末,25-26日调休上班,27日周五上班 workdays_count = self.calculator.count_workdays(start_date, end_date) self.assertEqual(workdays_count, 3) # 25, 26, 27日 # 测试单日 self.assertEqual(self.calculator.count_workdays(date(2026, 1, 1), date(2026, 1, 1)), 0) self.assertEqual(self.calculator.count_workdays(date(2026, 1, 5), date(2026, 1, 5)), 1) def test_edge_cases(self): """测试边界情况""" # 开始日期晚于结束日期 with self.assertRaises(ValueError): self.calculator.get_workdays_and_holidays( date(2026, 1, 10), date(2026, 1, 1) ) # 相同日期 workdays, holidays = self.calculator.get_workdays_and_holidays( date(2026, 1, 1), date(2026, 1, 1) ) self.assertEqual(len(workdays), 0) self.assertEqual(len(holidays), 1) # 测试长时间段 start_date = date(2026, 1, 1) end_date = date(2026, 12, 31) workdays_count = self.calculator.count_workdays(start_date, end_date) self.assertGreater(workdays_count, 0) self.assertLess(workdays_count, 366) # 2026年有365天 def test_helper_methods(self): """测试辅助方法""" # 测试周末判断 self.assertTrue(self.calculator.is_weekend(date(2026, 1, 3))) # 周六 self.assertTrue(self.calculator.is_weekend(date(2026, 1, 4))) # 周日 self.assertFalse(self.calculator.is_weekend(date(2026, 1, 5))) # 周一 # 测试节假日判断 self.assertTrue(self.calculator.is_public_holiday(date(2026, 1, 1))) # 元旦 self.assertFalse(self.calculator.is_public_holiday(date(2026, 1, 5))) # 普通工作日 # 测试调休工作日判断 self.assertTrue(self.calculator.is_transfer_workday(date(2026, 1, 4))) # 元旦调班 self.assertFalse(self.calculator.is_transfer_workday(date(2026, 1, 5))) # 普通工作日 def main(): """主函数:演示使用方法""" print("=" * 50) print("工作日计算器演示") print("=" * 50) # 创建示例节假日文件 holiday_file = "holidays_2026.json" create_sample_holiday_file(holiday_file) # 初始化计算器 calculator = WorkdayCalculator(holiday_file) # 示例1: 判断单日是否为工作日 test_dates = [ date(2026, 1, 1), # 元旦 date(2026, 1, 4), # 周日,但调休上班 date(2026, 1, 5), # 周一,工作日 date(2026, 2, 17), # 春节 date(2026, 5, 1), # 劳动节 ] print("\n1. 单日判断示例:") for test_date in test_dates: is_workday = calculator.is_workday(test_date) status = "工作日" if is_workday else "假期" print(f" {test_date}: {status}") # 示例2: 计算时间段内的工作日 start_date = date(2026, 1, 1) end_date = date(2026, 1, 10) workdays_count = calculator.count_workdays(start_date, end_date) workdays_list = calculator.get_workdays_between(start_date, end_date) print(f"\n2. 时间段工作日计算示例:") print(f" 时间段: {start_date} 到 {end_date}") print(f" 工作日数量: {workdays_count}") print(f" 工作日列表: {[d.strftime('%Y-%m-%d') for d in workdays_list]}") # 示例3: 获取工作日和假期列表 print(f"\n3. 工作日和假期列表示例:") workdays, holidays = calculator.get_workdays_and_holidays(start_date, end_date) print(f" 工作日: {[d.strftime('%m-%d') for d in workdays]}") print(f" 假期: {[d.strftime('%m-%d') for d in holidays]}") # 示例4: 计算一个月的工作日 start_date = date(2026, 5, 1) end_date = date(2026, 5, 31) workdays_count = calculator.count_workdays(start_date, end_date) print(f"\n4. 月工作日计算示例:") print(f" 2026年5月工作日数量: {workdays_count}") # 运行单元测试 print("\n" + "=" * 50) print("运行单元测试...") print("=" * 50) # 创建测试套件并运行 suite = unittest.TestLoader().loadTestsFromTestCase(TestWorkdayCalculator) runner = unittest.TextTestRunner(verbosity=2) result = runner.run(suite) # 清理临时文件 if Path(holiday_file).exists(): Path(holiday_file).unlink() return result if __name__ == "__main__": main() ``` 这个解决方案提供了以下功能: ## 主要特性: 1. **`WorkdayCalculator` 类**:核心计算类,包含所有工作日计算功能 2. **支持的功能**: - `is_workday(date)`: 判断单日是否为工作日 - `get_workdays_and_holidays(start, end)`: 获取时间段内的工作日和假期列表 - `count_workdays(start, end)`: 计算时间段内的工作日数量 - `get_workdays_between(start, end)`: 获取工作日列表 - `get_holidays_between(start, end)`: 获取假期列表 - 辅助方法:判断周末、节假日、调休工作日 3. **节假日数据处理**: - 支持从JSON文件加载节假日数据 - 自动解析节假日和调休工作日 - 支持多年份数据 4. **完整的测试用例**: - 使用Python标准库`unittest`编写 - 覆盖所有主要功能和边界情况 - 包含演示主函数 ## 使用说明: 1. 确保使用Python 3.7或更高版本 2. 节假日JSON文件格式需符合题目要求 3. 可以直接运行脚本查看演示和测试结果 ## 测试用例覆盖: 1. 单日判断(工作日、节假日、周末、调休日) 2. 时间段工作日计算 3. 工作日和假期列表获取 4. 边界情况处理(开始日期晚于结束日期等) 5. 辅助方法测试 这个解决方案具有很好的可扩展性和健壮性,可以处理复杂的节假日安排,包括调休上班的情况。