Получите (год, месяц) за последние X месяцев - PullRequest
6 голосов
/ 05 июля 2011

Я получил очень простую вещь в Python: Мне нужен список кортежей (year,month) за последние x месяцев, начиная (и включая) с сегодняшнего дня. Таким образом, для x = 10 и сегодня (июль 2011 г.) команда должна вывести:

[(2011, 7), (2011, 6), (2011, 5), (2011, 4), (2011, 3), 
(2011, 2), (2011, 1), (2010, 12), (2010, 11), (2010, 10)]

Следует использовать только реализацию Python по умолчанию для даты и времени. Я придумал следующее решение:

import datetime
[(d.year, d.month) for d in [datetime.date.today()-datetime.timedelta(weeks=4*i) for i in range(0,10)]]

Это решение выводит правильное решение для моих тестовых случаев, но мне не нравится это решение: оно предполагает, что у месяца есть четыре недели, и это просто не соответствует действительности. Я мог бы заменить weeks=4 на days=30, что сделало бы лучшее решение, но это все еще не правильно.

Другое решение, которое пришло мне в голову, состоит в том, чтобы использовать простую математику и вычесть 1 из счетчика месяцев, а если счетчик месяцев равен 0, вычтите 1 из счетчика года. Проблема с этим решением: он требует больше кода и не очень удобочитаем.

Так как это можно сделать правильно?

Ответы [ 7 ]

18 голосов
/ 05 июля 2011

Я не вижу, чтобы это было документировано нигде, но time.mktime будет "переворачиваться" в правильный год, когда он выходит за пределы диапазона, включая отрицательные значения месяца:

x = 10
now = time.localtime()
print [time.localtime(time.mktime((now.tm_year, now.tm_mon - n, 1, 0, 0, 0, 0, 0, 0)))[:2] for n in range(x)]
5 голосов
/ 05 июля 2011

Самым полезным будет использовать функции целочисленное деление (//) и модуль (%), представляющие месяц числом месяцев с года 0:

months = year * 12 + month - 1 # Months since year 0 minus 1
tuples = [((months - i) // 12, (months - i) % 12 + 1) for i in range(10)]

- 1 в выражении months требуется, чтобы получить правильный ответ, когда мы добавляем 1 к результату функции модуля позже, чтобы получить 1-индексирование (т.е. месяцы идут от 1 до 12, аот 0 до 11).

Или вы можете создать генератор:

def year_month_tuples(year, month):
    months = year * 12 + month - 1 # -1 to reflect 1-indexing
    while True:
        yield (months // 12, months % 12 + 1) # +1 to reflect 1-indexing
        months -= 1 # next time we want the previous month

, который можно использовать как:

>>> tuples = year_month_tuples(2011, 7)
>>> [tuples.next() for i in range(10)]
4 голосов
/ 01 мая 2014

Использование relativedelta ...

import datetime
from dateutil.relativedelta import relativedelta

def get_last_months(start_date, months):
    for i in range(months):
        yield (start_date.year,start_date.month)
        start_date += relativedelta(months = -1)

>>> X = 10       
>>> [i for i in get_last_months(datetime.datetime.today(), X)]
>>> [(2013, 2), (2013, 1), (2012, 12), (2012, 11), (2012, 10), (2012, 9), (2012, 8), (2012, 7), (2012, 6), (2012, 5)]
2 голосов
/ 05 июля 2011

Обновление : добавление версии timedelta в любом случае, так как она выглядит красивее:)

def get_years_months(start_date, months):
    for i in range(months):
        yield (start_date.year, start_date.month)
        start_date -= datetime.timedelta(days=calendar.monthrange(start_date.year, start_date.month)[1])

Вам не нужно работать с timedelta, так как вам нужен только годи месяц, который является фиксированным.

def get_years_months(my_date, num_months):
    cur_month = my_date.month
    cur_year = my_date.year

    result = []
    for i in range(num_months):
        if cur_month == 0:
            cur_month = 12
            cur_year -= 1
        result.append((cur_year, cur_month))
        cur_month -= 1

    return result

if __name__ == "__main__":
    import datetime
    result = get_years_months(datetime.date.today(), 10)
    print result
2 голосов
/ 05 июля 2011

Если вы создаете функцию для вычисления даты, она становится почти такой же приятной, как и ваша первоначальная реализация:

def next_month(this_year, this_month):
  if this_month == 0: 
    return (this_year - 1, 12)
  else:
    return (this_year, this_month - 1)

this_month = datetime.date.today().month()
this_year = datetime.date.today().year()
for m in range(0, 10):
  yield (this_year, this_month)
  this_year, this_month = next_month(this_year, this_month)
0 голосов
/ 06 декабря 2018

Или вы можете определить функцию для получения последнего месяца, а затем распечатать месяцы ( это немного элементарно)

def last_month(year_month):#format YYYY-MM
    aux = year_month.split('-')
    m = int(aux[1])
    y = int(aux[0])

    if m-1 == 0:
        return str(y-1)+"-12"
    else:
        return str(y)+"-"+str(m-1)

def print_last_month(ran, year_month= str(datetime.datetime.today().year)+'-'+str(datetime.datetime.today().month)):
    i = 1 
    if ran != 10:
        print( last_month(year_month) )
        print_last_month(i+1, year_month= last_month(year_month))
0 голосов
/ 21 октября 2014

Если вы хотите сделать это без библиотек даты и времени, вы можете конвертировать в месяцы с года 0, а затем конвертировать обратно

start_year = 2014
start_month = 5
end_year = 2013
end_month = 7

print list = [(a/12,a % 12+1) for a in range(12*start_year+start_month-1,12*end_year+end_month-2,-1)]

[(2014, 5),(2014, 4), (2014, 3), (2014, 2), (2014, 1), (2013, 12), (2013, 11), (2013, 10), (2013, 9), (2013, 8), (2013, 7)]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...