在python中计算到每月1日或15日的时间

在python中计算到每月1日或15日的时间,python,datetime,Python,Datetime,我正在尝试用python编写一个小的预算程序。这是我为学习python而编写的第一个程序。第一步是根据今天的日期计算距离第1天或第15天(发薪日)的天数。有人能帮我一点忙吗?我不想因为只键入答案而完全破坏您的学习体验,但是Python库使这非常容易。查看该模块,尤其是日期和时间增量类。日期时间模块中的类将有所帮助 你只需要检查它是否在本月15日之后。如果是,找到下个月的1号。如果不是,请找出当月的15号。有趣的问题,这里有一个完整的解决方案。我将从我的函数定义开始,我已将其放入名为payday.

我正在尝试用python编写一个小的预算程序。这是我为学习python而编写的第一个程序。第一步是根据今天的日期计算距离第1天或第15天(发薪日)的天数。有人能帮我一点忙吗?

我不想因为只键入答案而完全破坏您的学习体验,但是Python库使这非常容易。查看该模块,尤其是
日期
时间增量
类。

日期时间模块中的类将有所帮助


你只需要检查它是否在本月15日之后。如果是,找到下个月的1号。如果不是,请找出当月的15号。

有趣的问题,这里有一个完整的解决方案。我将从我的函数定义开始,我已将其放入名为
payday.py的文件中:

def nexypayday(fromdate=None):
    """
    @param fromdate: An instance of datetime.date that is the day to go from. If
                     not specified, todays date is used.
    @return: The first payday on or after the date specified.
    """
接下来我们需要一些测试。这是为了清楚地定义我们方法的行为。因为您是python新手,所以我将全力以赴为您提供一个使用unittests的示例

from unittest import TestCase, main
import payday
import datetime

class TestPayday(TestCase):
    def test_first_jan(self):
        self.assertEqual(payday.nextpayday(datetime.date(2010, 1, 1)),
                         datetime.date(2010, 1, 1))

    def test_second_jan(self):
        self.assertEqual(payday.nextpayday(datetime.date(2010, 1, 2)),
                         datetime.date(2010, 1, 15))

    def test_fifteenth_jan(self):
        self.assertEqual(payday.nextpayday(datetime.date(2010, 1, 15)),
                         datetime.date(2010, 1, 15))

    def test_thirty_one_jan(self):
        self.assertEqual(payday.nextpayday(datetime.date(2010, 1, 31)),
                         datetime.date(2010, 2, 1))

    def test_today(self):
        self.assertTrue(payday.nextpayday() >= datetime.date.today())

if __name__ == '__main__':
    main()
这是一个可运行的python模块。您可以将其命名为
test\u payday.py
,并使用
python test\u payday.py
运行它。这应该会立即失败,并显示各种错误消息,因为我们还没有编写正确的代码

在摆弄了datetime.date的工作原理之后,我发现:
mydatetime.day
是一个月中的一天,
mydatetime+datetime.timedelta(天=1)
将在一年中的某一天创建一个新的
datetime
。因此,我可以在
payday.py
中汇总这些内容

import datetime

def nextpayday(fromdate=None):
    """
    @param fromdate: An instance of datetime.date that is the day to go from. If
                     not specified, todays date is used.
    @return: The first payday on or after the date specified.
    """
    if fromdate is None:
        fromdate = datetime.date.today()

    # while the day of the month isn't 1 or 15, increase the day by 1
    while fromdate.day not in (1, 15):
        fromdate = fromdate + datetime.timedelta(days=1)

    return fromdate

运行单元测试,它应该是金色的。请注意,在我的测试中,如果我从发薪日中检查“下一个”发薪日是什么,它将返回自己的发薪日。将此更改为返回“下一个”发薪日留给读者作为练习

丑陋,没有docstring,但比月末在while循环中使用timedelta强制执行要轻一些

import datetime
from itertools import cycle

PAYDAYS = (1, 8, 15, 22, 20, 25, 30)

def calculate_days_difference(date_, day=None, next_month=False):
    day = day or date_.day
    year = date_.year
    month = date_.month
    if next_month:
        if month == 12:
            year = year + 1
            month = 1
        else:
            month = month + 1    
    return (datetime.date(year, month, day) - date_).days

def calculate_days_to_payday(date_, paydays=PAYDAYS):
    day = date_.day
    if day in paydays:
        return 0
    if day > max(paydays):
        return calculate_days_difference(date_, paydays[0], True)
    for payday in cycle(paydays):
        if (day > payday):
            continue
        return calculate_days_difference(date_, payday)
用法:

test_data = (
    ((2010,04,28), 2),
    ((2010,04,01), 0),
    ((2010,04,30), 0),
)

for _input, _output in test_data:
    d = datetime.date(*input)
    for i in xrange(1000000):
        assert calculate_days_to_payday(d) == _output

这将适用于任何合理的
发薪日
。。。已排序,并且
all(1)另一种方法是查找两者并返回较短的值one@Carson迈尔斯,显式的更好。你需要找到一个短的和积极的,否则。具体来说,找到本月15日的tdelta,如果是积极的,很好,全部完成;否则,在下个月的第一天返回tdelta。是的,你是对的,我一直在想“查找下一个1和下一个15,并返回更接近的1”的es,但我想你不能只找到下一个15个,你必须在几个月内进行检查。正如下面所说,datetime是继续进行的方式,但这必须是一个更复杂的模块,让你切齿。它并不复杂,因为它写得不好,只是格里高利历和球形地球有很多属性+1用于完整的解释和单元测试。然而,实际的解决方案可能更优雅-这是一种蛮力方法。此外,OP想要从今天算起的天数,而不是实际的付款日期(当然,添加起来很简单)。而且它无法运行…1s/nexy/next/如果答案错误。请编辑它。有测试,请确保它们通过。:)
from datetime import date

def calculate_days_to_payday(start_date=None, paydays=(1, 15)):
    if start_date is None:
        start_date = date.today()
    day = start_date.day
    for payday in paydays:
        if payday >= day:
            return payday - day
    # next payday is in next month
    month = start_date.month + 1
    year = start_date.year
    if month == 13:
        month = 1
        year += 1
    return (date(year, month, paydays[0]) - start_date).days

if __name__ == "__main__":
    tests = (
        (1, 12, 0),
        (2, 12, 13),
        (14, 12, 1),
        (15, 12, 0),
        (16, 12, 16),
        (31, 12, 1),
        )
    for d, m, expected in tests:
        actual = calculate_days_to_payday(date(2009, m, d))
        print "%5s" * 5 % (d, m, expected, actual, actual == expected)