场景编程集锦-月光族的期待

时间:2023-01-08 12:01:17

1. 场景描述

“月光族”是指没到下一个发薪日,工资就被全部花光的一群人。这个称谓是一个没有贬义的中性词。“月光族”通常是有知识有文化、有固定的工作收入,秉持着乐观豁达的生活态度,力争做到量入为出,挣多少用多少。他/她们喜欢消费,但不疯狂。偶尔也有因为房贷、车贷或者房租等压力,造成收支不平衡,使用信用卡透支的状况,显然“月光族”是银行信用卡的理想客群。“月光族”时常盘算着还有几天发工资,何时还上信用卡透支款项,总是期待着发薪日的早点到来。 下面我们就来编写一段Python代码,帮助“月光族”计算距离下一个发薪日还有多少天,信用卡透支消费的还款金额的计算问题。


2. 编程思路

编写这个程序还是有挑战性的,它可能比你想象的要复杂得多。因为在程序设计中你必须对“闰年和闰月,月大和月小”的特殊情况进行处理,这种特殊情形导致每个月包含的天数不同。如果你要自己实现这些功能,还是有一定的编码工作量。许多程序设计语言在日期处理上有点复杂,幸运的是Python已经为我们考虑到了特殊日期的处理!我们可以使用Python的标准库模块calendar和datetime来处理以上“闰年闰月和月大月小”的问题。 编程的基本思路是这样的: 如果今天日期在发薪日之前,计算今天到发薪日之间的天数; 如果今天日期等于发薪日,今天就是发薪日; 如果今天日期在发薪日之后,计算今天到下一个月的发薪日之间的天数。由于涉及跨越两个月,需要处理“闰年闰月和月大月小”的问题。 编程逻辑简单清晰,下面进入代码实现部分。


3. 代码实现

"""
  next_payday.py : 月光族的期待
"""
from calendar import monthrange   # ①
from datetime import datetime, timedelta

def calc_days(payday=15):
    """
    功能:计算距离发薪日的天数
    """
    today = datetime.now()
    if today.day <= payday:
        return payday-today.day
    else:
        last_day = monthrange(today.year, today.month)[1]   # ②
        paydt = datetime(today.year, today.month, last_day) + timedelta(days=payday)   # ③
        return (paydt.date()-today.date()).days

def print_days(payday=15):
    """
    打印距离发薪日的天数
    """
    print('距离发薪日天数'.center(15, '-'))
    today = datetime.now()
    print('当前日期:' + today.strftime('%Y-%m-%d'))   # ④
    print('发 薪 日:' + '每月'+str(payday) + '号')
    days = calc_days(payday)
    if days:
        print(f'离发薪日:{days}天')
    else:
        print('今天发工资啦!')

def main():
    print_days(payday=1)   # ⑤

if __name__ == '__main__':
    main()

主要的代码说明如下: 语句①导入calendar和datetime模块中的相关函数。 语句②获取指定日期所在月份的最大天数。在这里我们使用了Python的calendar模块中的monthrange()函数,调用它可以获取指定月份的天数。需要特别强调的是monthrange()函数本身实现了对闰年润月和月大月小的自动处理。 语句③计算下一个发薪日的日期,将自动处理闰年润月和月大月小的特殊情况。 语句④首先使用datetime.strftime()将日期转换为“年-月-日”格式字符串,然后再打印输出。 语句⑤在主程序main()中验证程序功能。假如发薪日是每月1号,计算今天(2022-12-3)距离发薪日的天数。


4. 执行效果

结果如下所示: (cases) D:\cases\月光族的期待>python next_payday.py ----距离发薪日天数---- 当前日期:2022-12-03 发 薪 日:每月1号 离发薪日:29天

(cases) D:\cases\月光族的期待> 需要注意的是,如果你执行这段程序时所看到的显示结果可能与以上结果不同,这是因为你执行程序时的系统日期不同而已,带来处理结果的差异,结果均是正确的。


5. 扩展场景

接下来,我们来解决第二个问题,计算信用卡透支还款额的计算问题。

5.1 弄清几个术语

信用卡业务有几个术语需要我们必须理解,方能正确地编写程序代码。

1. 还款期数

信用卡是按月还款,每月是1期,一年共有12期。

2. 账单日

账单日是发卡行每月定期对客户的信用卡账户当前发生的各项交易费用进行汇总结算,计算出客户当前的总欠款金额和最小还款额后为客户发送对账单的日期。例如:浦发银行账单日可以选择每月的1号、3号、8号、14、15、16、17、19、20、22、23、25和26号等。

3. 还款日

还款日是发卡行要求持卡人偿还应付款项的最后期限。实际上,信用卡还款日是免息还款的最后一天,在这之后还款就是逾期了,需要缴纳利息。例如:浦发银行的信用卡还款日就是账单日后的第20天。

4. 利息

目前各信用卡发卡行的逾期利率均是每天万分之五。凡是超过免息期的欠款均要按照逾期利率和实际逾期天数计付利息。 至此有了以上知识,我们可以开始着手编写信用卡透支还款程序了。

5.2 编写程序

"""
  card_payback.py : 信用卡还款
"""
from datetime import datetime, timedelta

BILL_DAY = 8    # 账单日
INTEREST_RATE = 0.0005    # 逾期利率
BILLS = 'bills.txt'   # 账单文件

def get_interest(payback_dt):
    """
    功能:计算信用卡欠款利息
    参数:payback_dt 计划还款日期
    """
    today = datetime.now()
    bill_dt = datetime(today.year, today.month, BILL_DAY)   # ① 
    last_dt = bill_dt + timedelta(days=20)   # ② 
    payback_dt = datetime.strptime(payback_dt, '%Y-%m-%d')  # ③
    amount = get_amount()
    if payback_dt.date() < last_dt.date():   # ④
        interest = 0
    else:
        days = (payback_dt-last_dt).days
        interest = INTEREST_RATE * amount * days   # ⑤
    return round(interest, 2)

def print_result(payback_dt):
    """
    功能:打印还款金额(本金和利息)
    参数:payback_dt 计划还款日期
    """
    today = datetime.now()
    amount = get_amount()
    interest = get_interest(payback_dt)
    print('信用卡还款'.center(20, '-'))
    print('当前日期:', today.strftime('%Y-%m-%d'))
    print('计划还款:', payback_dt)
    print('欠款金额:', amount)
    print('还款利息:', interest)
    print('合计金额:', amount+interest)

def get_amount():
    """
    功能:获取信用卡当期欠款总额
    """
    today = datetime.now()
    bill_dt = datetime(today.year, today.month, BILL_DAY)    
    last_dt = datetime(today.year, today.month, 1) - timedelta(days=1)   # ⑥
    last_bill_dt = datetime(last_dt.year, last_dt.month, BILL_DAY)  # ⑦

    with open(BILLS) as f:
        lines = f.readlines()
        amount = 0
        for line in lines:
            dt, money = line.split()
            dt = datetime.strptime(dt, '%Y-%m-%d')
            if last_bill_dt.date() < dt.date() <= bill_dt.date():   # ⑧
                amount += float(money)
    return amount

def main():
    payback_dt = '2022-12-30'
    print_result(payback_dt)

if __name__ == '__main__':
    main()

程序中重要语句解释如下: 语句①计算当前日期所在月份的账单日期(当期账单日或本期账单日); 语句②计算当期还款日期,也就是免息期最后1天; 语句③将字符串格式的计划还款日期转换为datetime类型的日期; 语句④判断免息条件是否成立; 语句⑤按照逾期实际天数和逾期利率计算欠款的逾期利息; 语句⑥上月最后一天的日期,这里隐含着对闰年闰月和月大月小的自动处理; 语句⑦获取上一期的账单日(上期账单日) 语句⑧判断是否本期消费账单

5.3 执行程序

下面程序演示场景是:计划还款日期2022-12-30,账单存放在bills.txt文件中,其内容和格式如下: 2022-11-15 7800.25 2022-11-19 2500 2022-11-8 1000 2022-11-3 2000 2022-10-23 534.59 2022-10-5 1000 这是一个文本文件格式。文件每一行代表一笔信用卡消费,包含日期和金额,之间使用空格分隔。 D:\cases\月光族的期待>python card_payback.py -------信用卡还款-------- 当前日期: 2022-12-03 计划还款: 2022-12-30 欠款金额: 10300.25 还款利息: 10.3 合计金额: 10310.55

D:\cases\月光族的期待> 经执行程序结果表明,程序正确地计算了信用卡还款的欠款金额和透支利息,达到了设计的目标。但是有一点需要在此强调,当你执行这段程序时,可能会有不同的显示结果,其原因是账单文件bills.txt中没有当期信用卡消费明细数据。解决办法是在bills.txt账单文件中加入当期信用卡消费数据,再次执行程序就可以显示相关信用卡还款信息了。

5.4 功能完善

我们拟增强两个方面的功能: 1)增加打印信用卡当期的每一笔消费明细,格式如下: -------信用卡还款-------- 当前日期: 2022-12-03 计划还款: 2022-12-30 消费明细: 2022-11-15 7800.25 2022-11-19 2500


欠款金额: 10300.25 还款利息: 10.3 合计金额: 10310.55 2)从程序命令行中获取参数,提取计划还款日期。命令行执行方式如下: python card_payback.py 2022-12-30 有兴趣的读者可以自行完成以上程序的功能优化。