3

I'm trying to find a custom date format like YYYYW eg. 201620 after subtracting a number of week periods.

So far I've been able to convert a date string like YYYYW into date using the following code;

from datetime import datetime

myDate = "201642"
year = myDate[:4]
week = myDate[-2:]
day = '1'
myDate = year + " " + week + " " + day
print(myDate)
date = datetime.strptime(myDate, "%Y %W %w")
print(date)

But I'm unable to find a way where I can subtract for eg. 43 weeks from 201642 and get a result like 201552.

EDIT 1: Using suggestions from the comments, here is the solution.

from datetime import datetime from datetime import timedelta

myDate = "201642"
year = myDate[:4]
week = myDate[-2:]
day = '1'
myDate = year + " " + week + " " + day
print(myDate)
date = datetime.strptime(myDate, "%Y %W %w")
print(date)
new_date = date - timedelta(weeks=42)
str_new_date = datetime.strftime(new_date, '%Y%W')
print(str_new_date)
user6083088
  • 1,047
  • 1
  • 9
  • 27
  • hint: there are 52 weeks in a year. Just use math. – Harvey Apr 13 '18 at 03:38
  • 1
    I did think about that, but unsure will it hold true for leap years as its has 52.2 weeks – user6083088 Apr 13 '18 at 03:40
  • Does your code have to handle fractional weeks? Your input of integer year and week seems to imply no. – Harvey Apr 13 '18 at 03:41
  • 1
    Good point, it's not fractional as of yet – user6083088 Apr 13 '18 at 03:44
  • You have a `datetime` object. Import the `timedelta` type from the same module. Then you can just do `date - timedelta(weeks=43)`, and then convert back to your string format with `strftime` and you're done. (Although you might want to use `date` objects rather than `datetime`.) – abarnert Apr 13 '18 at 04:00
  • Meanwhile, why is this tagged both python-3.x and python-2.7? Do you need code that runs in both versions for some reason? – abarnert Apr 13 '18 at 04:00
  • @abarnert Thanks, I did and worked - if you post as and answer I can accept. and to answer your question I do have two systems running on 2 diff python version. But I'm not sure what you mean by using date objects – user6083088 Apr 13 '18 at 04:15
  • abarnert, Harvey - please help - who's answer must I accept. – user6083088 Apr 13 '18 at 04:22
  • Hildy's answer, but all three have merit. – Harvey Apr 13 '18 at 04:52

3 Answers3

2

Other than doing the math yourself, here's an attempt using datetime. Doing everything using ISO week numbers (using methods from this answer):

>>> import datetime
>>> date = '201642'
>>> weeks = 43
>>> year = date[:4]
>>> week = date[4:]

>>> start = iso_to_gregorian(int(year), int(week), 1)
>>> start
datetime.date(2016, 10, 17)
>>> start.isocalendar()
(2016, 42, 1)
>>> offset_weeks = datetime.timedelta(weeks=43)
>>> end = start - offset_weeks
>>> end
datetime.date(2015, 12, 21)
>>> end.isocalendar()
(2015, 52, 1)
>>> '{}{}'.format(*end.isocalendar())
'201552'

A modified version of @Hildy's answer below:

In [29]: start = datetime.datetime.strptime(date + '0', '%Y%W%w')

In [30]: start
Out[30]: datetime.datetime(2016, 10, 23, 0, 0)

In [31]: start.strftime('%Y%W')
Out[31]: '201642'

In [32]: end = start - datetime.timedelta(weeks=43)

In [33]: end
Out[33]: datetime.datetime(2015, 12, 27, 0, 0)

In [34]: end.strftime('%Y%W')
Out[34]: '201551'
Harvey
  • 5,703
  • 1
  • 32
  • 41
  • Who's answer should I select, both of you have given the same suggestion. Pardon me I'm just getting accustomed to SO etiquettes. – user6083088 Apr 13 '18 at 04:19
  • Go with @Hildy's answer. It's shorter. Maybe combine the two in your actual code. – Harvey Apr 13 '18 at 04:45
2

You haven't actually parsed the date properly in your sample code, but since you say your actual code does, let's ignore that.

So, you have a datetime object. You want to subtract 43 weeks from that. All you have to do is… exactly that, using a timedelta:

>>> dt = datetime.datetime(2016, 10, 21)
>>> dt - datetime.timedelta(weeks=43)
datetime.datetime(2015, 12, 25, 0, 0)

The result is the same time, same day of the week, 43 weeks earlier.


But it's worth noting that, for local times, that "same time" could be off by an hour because of crossing a daylight saving border. And being off by an hour can be especially confusing when you're using midnight. So it's usually better to use date objects instead of datetime objects for this kind of work:

>>> dt = datetime.datetime(2016, 10, 21)
>>> dt.date()
datetime.date(2016, 10, 21)
>>> dt.date() - datetime.timedelta(weeks=43)
datetime.date(2015, 12, 25)

Either way, if you want to turn that back into a YYYYWW string, both datetime and date have strftime methods for that:

>>> d2 = dt - datetime.timedelta(weeks=43)
>>> d2.strftime('%Y%W')
'201551'
abarnert
  • 354,177
  • 51
  • 601
  • 671
2

This can be done with relative delta, from the dateutil library; it should be noted that said library is part of the standard lib in Python 2x, but not 3x...you'll have to install it.

The next thing you need to know is that, per the Python docs, "When used with the strptime() method, %U and %W are only used in calculations when the day of the week and the year are specified."

Try this out:

from dateutil.relativedelta import relativedelta
from datetime import datetime

#I've added the day number here, as described in the docs
myDate = "2016042"
# with '%w' added, strptime() will work
date = datetime.strptime(myDate, "%Y%w%W")
# now use relativedelta() to do the date math
date = date - relativedelta(weeks=2)
# finally, use strftime() to return the date back to the format you like
myDate = date.strftime("%Y%W")

print(string_date)

I've used this in ETL operations to calculate things like fiscal periods and it works great. As noted by others here, timedelta() can be used in a similar fashion.

Hildy
  • 510
  • 5
  • 15