编程珠玑之第三章习题3-4

问题

4.编写处理如下日期的函数:给定两个日期,计算两者之间的天数;给定一个日期,返回值为周几;给定月和年,使用字符数组生成该月的日历。

思路

分为三个小问
第一问
计算日期差,我们用图来解释具体计算逻辑

在这里插入图片描述

如图所示,d1, d2之差为绿色画线部分。我们从年份的角度入手,上图可以划分为三块:d1所在年份,中间年份,d2所在年份

具体计算逻辑如下
d1年剩下天数 + 中间年份包含天数 + d2所在年份经历的天数

计算天数的时候,需要将闰年因素考虑进去,也就是对最终计算结果增加修正逻辑

第二问
返回周几?
这一问就简单很多了,我们只需要计算当前日期经历的天数,然后对7整除,即可得到本周是周几

第三问
返回数组,生成日历
生成日历的关键点,就是弄清楚本月第一天是周几。弄清楚这一点后,创建数组时,添加额外的元素,直到月初第一天排序到正确的下标,表示正确的星期几即可

代码

简单说一下代码思路,为了更方便编写,做了一些抽象

Timer
month_day: int
is_leap_year(year) : Boolean
Date
year: int
month: int
day: int
day_convert() : int
year_diff() : int
get_week_day() : int
get_day_count_of_month() : int
Calendar
day_bigger(Date, Date) : Boolean
day_diff(Date, Date) : int
get_week(Date) : int
print_month(year, month) : int

Timer抽象出Calendar,Date共有的一些方法和变量

Date用于表示日期,其中包含一些处理日期的方法

Calendar用于处理日期与日期之间关系,并对日期做出更为复杂的处理。Calendar包含的三个方法,直接对应题目要求的三个问题

'''
时间基类
'''


class Timer:
    month_day = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

    def __init__(self):
        pass

    @staticmethod
    def is_leap_year(year):
        if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
            return True
        else:
            return False


'''
日期类, 继承时间类
'''


class Date(Timer):
    def __init__(self, year: int, month: int, day: int):
        super().__init__()  # 调用父类的构造函数
        self.year = year
        self.month = month
        self.day = day

    '''
    转化为天数
    '''

    def day_convert(self) -> int:
        if self.month == 1:
            return self.day
        if self.month == 2:
            return 31 + self.day
        day = sum(Timer.month_day[0:self.month - 1]) + self.day
        # 如果是闰年, + 1
        return day + (1 if Timer.is_leap_year(self.year) else 0)

    '''
    年做差
    '''

    def year_diff(self, d2: 'Date') -> int:
        return self.year - d2.year

    '''
    获取当前日期所在week的day
    '''

    def get_week_day(self) -> int:
        days = self.day_convert()
        week_day = days % 7
        return 7 if week_day == 0 else week_day

    '''
    获取当前月份的天数 
    '''
    def get_day_count_of_month(self) -> int:
        day_count = Timer.month_day[self.month - 1]
        return day_count + (1 if Timer.is_leap_year(self.month) else 0)


'''
日历类, 继承时间类
'''


class Calendar(Timer):
    def __init__(self):
        super().__init__()  # 调用父类的构造函数

    @staticmethod
    def day_bigger(d1: Date, d2: Date) -> int:
        day1, day2 = d1.day_convert() + d1.year * 365, d2.day_convert() + d2.year * 365
        if day1 > day2:
            return 1
        elif day1 < day2:
            return -1
        else:
            return 0

    '''
    获取日期之差
    '''

    def day_diff(self, date1: Date, date2: Date) -> int:
        # 日月转换
        _d1, _d2 = date1, date2
        bigger_flag = Calendar.day_bigger(_d1, _d2)
        if bigger_flag == -1:
            _d1, _d2 = _d2, _d1
        day1, day2 = _d1.day_convert(), _d2.day_convert()
        # 年做差
        year_diff = _d1.year_diff(_d2)
        days = 0
        if _d1.year == _d2.year:
            days = day1 - day2
        else:
        	# 计算核心逻辑: _d1年剩下天数 + 中间年份包含天数 + _d2所在年份经历的天数
            days = (Date(_d2.year, 12, 31).day_convert() - day2) + (year_diff - 1) * 365 + day1
        # 修正
        for year in range(_d1.year + 1, _d2.year):
            if self.is_leap_year(year):
                days += 1
        return -days if bigger_flag == -1 else days
        
    '''
    获取当前日期所在week 
    '''
    @staticmethod
    def get_week(d: Date) -> int:
        days = d.day_convert()
        return days // 7 + (0 if days % 7 == 0 else 1)

    @staticmethod
    def print_month(year, month):
        d = Date(year, month, 1)
        week_day = d.get_week_day()
        week_list = ['' for _ in range(week_day - 1)]
        for day in range(1, d.get_day_count_of_month() + 1):
            week_list.append(day)
        # 处理成二维数组
        row_count = len(week_list)//7 + (1 if len(week_list) % 7 != 0 else 0)
        res = [week_list[i * 7:(i * 7) + 7] for i in range(row_count)]
        for row in res:
            print(row)

测试

测试代码使用python自带的时间模块检验编写代码的正确性。如果使用我编写的时间模块计算得到的答案和python自带的时间模块得到的答案相同,那么说明我编写的代码没有问题

def test_day_diff():
    from datetime import datetime
    # 定义两个日期字符串
    date_str1 = "2020-02-10"
    date_str2 = "2024-01-30"
    # 将字符串转换为datetime对象
    date_format = "%Y-%m-%d"
    date1 = datetime.strptime(date_str1, date_format)
    date2 = datetime.strptime(date_str2, date_format)
    # 计算两个日期之间的差值
    delta = date1 - date2
    # 输出日期差值
    print("两个日期相差:", delta.days, "天")

    d1 = Date(2020, 2, 10)
    d2 = Date(2024, 1, 30)
    c = Calendar()
    print(c.day_diff(d1, d2))


def test_get_week():
    from datetime import datetime
    # 定义日期字符串
    date_str = "2024-09-09"
    # 设置日期格式
    date_format = "%Y-%m-%d"
    # 将字符串转换为datetime对象
    date_obj = datetime.strptime(date_str, date_format)
    # 使用isocalendar()方法获取ISO周数
    year, week_num, _ = date_obj.isocalendar()
    # 输出结果
    print(f"{date_str}{year} 年的第 {week_num} 周。")
    d1 = Date(2024, 9, 9)
    c = Calendar()
    print(c.get_week(d1))

def test_print_month():
    Calendar.print_month(2024, 9)


test_day_diff()
test_get_week()
test_print_month()

在这里插入图片描述
正确!

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值