Расчет времени до 1-го или 15-го числа месяца в питоне - PullRequest
3 голосов
/ 30 апреля 2010

Я пытаюсь написать небольшую бюджетную программу на python. Это моя первая программа, которую я пишу для изучения Python. Первым шагом является подсчет количества дней до 1-го или 15-го (выплаты) в зависимости от текущей даты. Может кто-нибудь мне немного помочь?

Ответы [ 5 ]

6 голосов
/ 30 апреля 2010

Интересный вопрос, и вот полное решение. Я начну с определения своей функции, я поместил это в файл с именем 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, я собираюсь изо всех сил и привести вам пример использования юнит-тестов.

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_payday.py и запустить его с python test_payday.py. Это должно немедленно завершиться ошибкой с различными сообщениями об ошибках, потому что мы еще не написали правильный код.

После некоторого изучения того, как работает datetime.date, я решил: mydatetime.day - это день месяца, mydatetime + datetime.timedelta(days=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

Запустите юнит-тесты, и все должно быть золотым. Обратите внимание, что в моих тестах я сделал так, что если я проверяю, какой «следующий» день выплаты у зарплаты, он возвращает свой собственный день. Изменение этого слова на возвращение «следующего» дня зарплаты оставлено читателю в качестве упражнения.

2 голосов
/ 30 апреля 2010

Классы в модуле datetime помогут.

Вам просто нужно проверить, наступило ли это после 15 числа месяца. Если это так, найдите 1 числа следующего месяца. Если это не так, найдите 15 число текущего месяца.

2 голосов
/ 30 апреля 2010

Я не хочу полностью портить ваш опыт обучения, просто набирая ответ, но библиотека Python делает это довольно легко. Взгляните на модуль datetime , особенно классы date и timedelta.

0 голосов
/ 30 апреля 2010

Это будет работать с любыми разумными paydays ... отсортированными, а all(1 <= payday <= 28 for payday in paydays) # в противном случае рабочие бастуют каждый февраль.

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)
0 голосов
/ 30 апреля 2010

Безобразно без строк документации, но немного легче, чем перебор с помощью timedelta в цикле while в конце месяца ...

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...