python周次工具类

5 阅读3分钟
import datetime
from typing import List, Dict


class WeekUtils:
    """周次工具类,提供周次计算相关方法"""

    @staticmethod
    def get_weeks_of_year(year: int) -> List[str]:
        """
        获取指定年份的所有周次列表,格式为 [YYYY-WW]
        规则:以周日作为周的最后一天,跨年周归属以周日所在年份为准
        
        :param year: 目标年份(如 2024)
        :return: 周次列表(如 ["2024-01", "2024-02", ...])
        """
        weeks = []
        # 找到目标年份的第一个周六
        date = datetime.date(year, 1, 1)
        while date.weekday() != 5:  # 5表示周六(0=周一,5=周六,6=周日)
            date += datetime.timedelta(days=1)
        
        week_num = 1
        # 遍历直到周六超出目标年份
        while date.year == year:
            week_str = f"{year}-{week_num:02d}"
            weeks.append(week_str)
            date += datetime.timedelta(weeks=1)  # 下一周的周六
            week_num += 1
        
        return weeks

    @staticmethod
    def get_week_by_date(date: datetime.datetime) -> str:
        """
        按日期获取周次--周日为每周第一天--判断周六所在年份/月份为周次归属年份/月份
        如:2025/12/31为2026-01
        
        :param date: 日期对象
        :return: 周次字符串(如 "2025-01")
        """
        year = date.year
        # 找到目标年份的第一个周六
        week_date = datetime.datetime(year, 1, 1)
        while week_date.weekday() != 5:  # 5表示周六
            week_date += datetime.timedelta(days=1)
        
        week_num = 1
        # 遍历直到周六超出目标年份
        while week_date.year == year:
            if date <= week_date:
                return f"{year}-{week_num:02d}"
            week_date += datetime.timedelta(weeks=1)  # 下一周的周六
            week_num += 1
        
        # 如果超出原年份,返回下一年的第一周
        return f"{year + 1}-01"

    @staticmethod
    def get_loose_iso_week_number(date: datetime.date) -> str:
        """
        获取符合ISO框架但放宽第一周限制的周次(周一开始,第一周只需包含1天)
        
        :param date: 需要计算的日期
        :return: 周次字符串(如 "WK01")
        """
        # 计算宽松ISO周次(周一开始,包含1月1日的周为第1周)
        year_start = datetime.date(date.year, 1, 1)
        # 计算1月1日是星期几(0=周一,6=周日)
        start_weekday = year_start.weekday()
        # 计算日期所在周与第一周的偏移
        days_diff = (date - year_start).days
        week_num = (days_diff + start_weekday) // 7 + 1
        return f"WK{week_num:02d}"

    @staticmethod
    def get_loose_iso_week_based_year(date: datetime.date) -> str:
        """
        获取包含年份的完整周次标识(如2024-W53)
        
        :param date: 需要计算的日期
        :return: 符合宽松ISO格式的年份周次
        """
        year = date.year
        # 检查是否属于上一年的最后一周
        jan_first = datetime.date(year, 1, 1)
        if date < jan_first and date.weekday() < 4:  # 1月1日前且是周四之前
            year -= 1
        
        # 计算该年的周次
        year_start = datetime.date(year, 1, 1)
        start_weekday = year_start.weekday()
        days_diff = (date - year_start).days
        week_num = (days_diff + start_weekday) // 7 + 1
        return f"{year:04d}-W{week_num:02d}"

    @staticmethod
    def get_week_range(week_based_year: int, week_number: int) -> Dict[str, datetime.date]:
        """
        获取指定周次的日期范围(周一开始,共7天)
        
        :param week_based_year: 基于周的年份
        :param week_number: 周次(从1开始)
        :return: 包含start和end的字典,跨度为7天
        """
        # 获取该年的第一个周一
        first_day = datetime.date(week_based_year, 1, 1)
        while first_day.weekday() != 0:  # 0表示周一
            first_day -= datetime.timedelta(days=1)
        
        # 计算目标周的起始和结束日期
        start = first_day + datetime.timedelta(weeks=week_number - 1)
        end = start + datetime.timedelta(days=6)
        
        return {
            "start": start,
            "end": end
        }