4

so I am a beginner with python and have been working with the datetime, time, and timedelta libraries a little bit. I am trying to create a piece of code that gives me the date approximately two months ago(exact_two_months_date) from today (whatever today happens to be). The catch is, I want to find that date approx. two months ago AND begin the actual start_date on the Monday of that week. So in theory, the actual start date will not be exactly two months ago. It will be the week beginning on Monday two months ago from today.

Example pseudocode:
    today = '20150425' ## '%Y%m%d' ... Saturday
    exact_two_months_date = '20150225' ## EXACTLY two months ago ... Wednesday
    start_date = '20150223' ## this is the Monday of that week two months ago

So how do I find the 'start_date' above? If the day exactly two months ago begins on a Saturday or Sunday, then I would just want to go to the next Monday. Hope this is clear and makes sense... Once I find the start date, I would like to increment day by day(only week days) up to 'today'.
Appreciate any feedback, thanks.

d_rooney
  • 89
  • 2
  • 15

4 Answers4

11

Calculating with dates using python-dateutil
If a dependency on a third-party package is an option, then python-dateutil provides a convenient method to calculate with dates.

Browse the docs for ☞ relativedelta to see the wealth of supported parameters. The more calculations a package needs to do with dates, the more a helper module like dateutil justifies its dependency. For more inspiration on what it has to offer see the ☞ examples page.

Quick run-through:

>>> import datetime
>>> from dateutil.relativedelta import relativedelta

>>> today = datetime.date.today()
>>> two_m_ago = today - relativedelta(months=2)
>>> # print two_m_ago ~> datetime.date(2015, 2, 25)

>>> monday = two_m_ago - datetime.timedelta(days=two_m_ago.weekday())
>>> # print monday ~> datetime.date(2015, 2, 23)

Getting the Monday with weekday()
Once we have the date from two months ago in the variable two_m_ago, we subtract the index of the weekday() from it. This index is 0 for Monday and goes all the way to 6 for Sunday. If two_m_ago already is a Monday, then subtracting by 0 will not cause any changes.

sthzg
  • 5,514
  • 2
  • 29
  • 50
1

Honestly, I find working with datetimes to be the hardest thing I ever have to regularly do and I make a lot of mistakes, so I'm going to work through this one and show some of the failures I regularly have with it. Here goes.

Two constraints: 1) Date two months ago, 2) Monday of that week

Date Two Months Ago

Okay, so Python's datetime library has a useful method called replace, which seems like it might help here:

>>> import datetime
>>> now = datetime.date.today()
>>> today
datetime.date(2015, 4, 25)
>>> today.month
4
>>> two_months_ago = today.replace(month=today.month-2)
>>> two_months_ago
datetime.date(2015, 2, 25)
>>> two_months_ago.month
2

But wait: what about negative numbers? That won't work:

>>> older = datetime.date(2015, 01, 01)
>>> older.replace(month=older.month-2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: month must be in 1..12

So there are two solutions:

1) I can build a 1-12 range that cycles forwards or back, or

2) To find two months previous, I can merely replace the day part of my date with the 1st day of the month I'm in and then go back 1 day to the previous month and then replace that day in the previous month with the day I want.

(If you think about it, you'll find that either of these may present bugs if I land on day 31 in a month with fewer days than that, for instance. This is part of what makes datetimes difficult.)

def previous_month(date):
    current_day = date.day
    first_day = date.replace(day=1)
    last_day_prev_month = first_day - datetime.timedelta(days=1)
    prev_month_day = last_day_prev_month.replace(day=current_day)
    return prev_month_day

>>> today = datetime.date.today()
>>> older = previous_month(today)
>>> older
datetime.date(2015, 3, 25)

Well, let's say we're getting close, though, and we need to include some error-checking to make sure the day we want is a valid date inside the month we land in. Ultimately, the problem is that "two months ago" means a lot more than we think it means when we say it out loud.

Next, we'll take a crack at problem number two: How to get to the Monday of that week?

Well, datetime objects have a weekday method, so this part shouldn't be too hard and here's a nice SO answer on how to do that.

Simple version is: use the difference in weekday integers to figure out how many days to go back and do that using datetime.timedelta(days=days_difference).

Takeaway: Working with datetimes can be tough.

Community
  • 1
  • 1
erewok
  • 7,555
  • 3
  • 33
  • 45
  • Thanks for the explanation. I will carry this information with me as I play with dates more. – d_rooney Apr 25 '15 at 18:05
  • Sure. I think this is one case where knowing the common pitfalls is about as good as knowing "the answer." – erewok Apr 25 '15 at 20:02
  • I agree with you the datetime package in the python one is the most ugly core library. The simple things from real life not simple at all with that library. Perhaps the little refactoring helps resolve the most issues but it's still stable like a stone. – Vanya Usalko May 26 '23 at 12:59
1

Does something like this work for you?

import datetime

today = datetime.date.today()
delta = datetime.timedelta(days=60) # ~ 2 months

thatDay = today - delta
# subtract weekdays to get monday
thatMonday = thatDay - datetime.timedelta(days=thatDay.weekday()) 
ashwinjv
  • 2,787
  • 1
  • 23
  • 32
  • @Vincenzzzochi Yes you are right, but the user does not mind approximates. I can change it to 60 days to be slightly better. – ashwinjv Apr 25 '15 at 17:30
  • and if 60 days ago was a sunday or saturday, you'd still be giving the wrong answer. – the_constant Apr 25 '15 at 17:43
  • An average month is 30.4375 days if that helps (rarely does, except when calculating intervals containing large number of months, e.g. 22 months ago). – thebjorn Apr 25 '15 at 18:22
0

Date manipulation in Python is horribly convoluted. You will save a lot of time by using the arrow package which greatly simplifies these operations.

First install it

pip install arrow

Now your question:

import arrow

# get local current time
now = arrow.now('local')

# move 2 months back
old = now.replace(months=-2)

# what day of the week was that?
dow = old.isoweekday()

# reset old to Monday, for instance at 9:32 in the morning (this is just an example, just to show case)
old = old.replace(days=-dow, hour=9, minute=32, second=0)

print('now is {now}, we went back to {old}'.format(now=now.isoformat(), old=old.isoformat()))

The output:

now is 2015-04-25T20:37:38.174000+02:00, we went back to 2015-02-22T09:32:00.174000+01:00

Note that the various formats, timezones etc. are now transparent and you just need to rely on one package.

WoJ
  • 27,165
  • 48
  • 180
  • 345