Depending on whether the item is over a week old or not, the date comes in the following formats:
Less than a week old comprised of day of the week and time:
date1 = "Monday 21:14"
More than a week old, is just the date:
date2 = "5 Apr '21"
I read them in as a string and need to parse them both to a consistent timestamp.
One way I tried is:
from dateutil.parser import parse
parse(date1)
# output: datetime.datetime(2021, 4, 23, 22, 18)
parse(date2)
# output: datetime.datetime(2021, 4, 5, 0, 0)
Using the dateutil package I can get it to easily parse date2, but it gives me the forward looking friday for date1 not the previous friday.
How would you suggest I parse the two dates, without knowing which will be received? and can I instruct the parser to take the last previous weekday (i.e. previous friday)
Many thanks
Setting a default for the parser might work. Since you want to take the last previous weekday in case the day of the month is not defined, you can use today's date one week ago.
Ex:
from datetime import datetime, timedelta
from dateutil import parser
date1 = "Monday 21:14"
date2 = "5 Apr '21"
# reference = date of today one week ago (as datetime object):
ref_date = datetime(*datetime.now().timetuple()[:3]) - timedelta(7)
for d in (date1, date2):
print(parser.parse(d, default=ref_date))
# 2021-04-12 21:14:00
# 2021-04-05 00:00:00
Note that today is Monday 2021-4-19 but this code gives you the previous Monday, 2021-4-12.
I've seen about 30 similar posts to this, but nothing really doing exactly what I'm looking for and some which just don't work..
I'm trying to return a list of N business dates, to then iterate through a dictionary and pull data out according to the corresponding dates.
Assuming the current date is:
refreshed = str(data['Meta Data']['3. Last Refreshed'])
For completion, the value of above right now is:2020-1-30
I want to be able to calculate n days prior to this date..
I don't really want to import a bunch of funky modules, and have tried a function using a loop and datetime.date.isoweekday() - but I always come across an issue when passing refreshed in.
One of the main issues I'm seeing with some of the examples elsewhere is where the examples are calculating the dates from datetime.date.today() - seemingly it's fine to pass that to isoweekday() but I can't pass refreshed to isoweekday() to calculate it's 0-6 reference. I've tried using strfrtime() to reformat the date into a suitable format for isoweekday but to no avail.
Subtracting days from a date
You can subtract 30 days from a datetime.datetime object by subtracting a datetime.timedelta object:
>>> import datetime
>>> datetime.datetime.today()
datetime.datetime(2020, 10, 31, 10, 20, 0, 704133)
>>> datetime.datetime.today() - datetime.timedelta(30)
datetime.datetime(2020, 10, 1, 10, 19, 49, 680385)
>>> datetime.datetime.strptime('2020-01-30', '%Y-%m-%d') - datetime.timedelta(30)
datetime.datetime(2019, 12, 31, 0, 0)
Skipping week-ends by subtracting 7 days instead of 5
We are starting from date d and you want to subtract N=30 non-week-end days. A general way could be:
Figure out which day of the week is d;
Figure out how many week-ends there are between d and d-N;
Remove the appropriate number of days.
However, you want to subtract 30 days, and 30 is a multiple of 5. This makes things particularly easy: when you subtract 5 days from a date, you are guaranteed to encounter exactly one week-end in those five days. So you can immediately remove 7 days instead of 5.
Removing 30 days is the same as removing 6 times 5 days. So you can remove 6 times 7 days instead, which is achieved by subtracting datetime.timedelta(42) from your date.
Note: this accounts for week-ends, but not for special holidays.
Skipping week-ends iteratively
You can test for days of the week using .weekday(). This is already answered on this other question: Loop through dates except for week-ends
You can add N days using a timedelta:
data['Meta Data']['3. Last Refreshed'] = pd.to_datetime(data['Meta Data']['3. Last Refreshed']) + pd.to_timedelta(4, unit="D")
Replace 4 with your n days.
Note this is not quite the same as this question. That question assumes the time you want is "now", which is not the same as for an arbitrary point in time.
I have a UTC, aware, datetime object, call it point_in_time (e.g. datetime(2017, 3, 12, 16, tzinfo=tz.tzutc())).
I have a timezone, call it location (e.g. 'US/Pacific'), because I care about where it is, but its hours offset from UTC may change throughout the year with daylight savings and whatnot.
I want to
1) get the date of point_in_time if I'm standing in location,
2) get midnight of that date if I'm standing in location.
===
I tried to simply use .astimezone(timezone('US/Pacific')) and then .replace(hours=0, ...) to move to midnight, but as you might notice about my example point_in_time, the midnight for that date is on the other side of a daylight savings switch!
The result was that I got a time representing UTC datetime(2017, 3, 12, 7), instead of a time representing UTC datetime(2017, 3, 12, 8), which is the true midnight.
EDIT:
I'm actually thinking the difference between mine and the linked question is that I'm looking for the most recent midnight in the past. That question's answer seems to be able to give a midnight that could be in the past or future, perhaps?
Your example highlights the perils of doing datetime arithmetic in a local time zone.
You can probably achieve this using pytz's normalize() function, but here's the method that occurs to me:
point_in_time = datetime(2017, 3, 12, 16, tzinfo=pytz.utc)
pacific = pytz.timezone("US/Pacific")
pacific_time = point_in_time.astimezone(pacific)
pacific_midnight_naive = pacific_time.replace(hour=0, tzinfo=None)
pacific_midnight_aware = pacific.localize(pacific_midnight_naive)
pacific_midnight_aware.astimezone(pytz.utc) # datetime(2017, 3, 12, 8)
In other words, you first convert to Pacific time to figure out the right date; then you convert again from midnight on that date to get the correct local time.
Named timezones such as "US/Pacific" are by definition daylight-savings aware. If you wish to use a fixed non-daylight-savings-aware offset from GMT you can use the timezones "Etc/GMT+*", where * is the desired offset. For example for US Pacific Standard Time you would use "Etc/GMT+8":
import pandas as pd
point_in_time = pd.to_datetime('2017-03-12 16:00:00').tz_localize('UTC')
# not what you want
local_time = point_in_time.tz_convert("US/Pacific")
(local_time - pd.Timedelta(hours=local_time.hour)).tz_convert('UTC')
# Timestamp('2017-03-12 07:00:00+0000', tz='UTC')
# what you want
local_time = point_in_time.tz_convert("Etc/GMT+8")
(local_time - pd.Timedelta(hours=local_time.hour)).tz_convert('UTC')
# Timestamp('2017-03-12 08:00:00+0000', tz='UTC')
See the docs at http://pvlib-python.readthedocs.io/en/latest/timetimezones.html for more info.
EDIT Now that I think about it, Midnight PST will always be 8am UTC, so you could simplify this as
if point_in_time.hour >=8:
local_midnight = point_in_time - point_in_time.hour + 8
else:
local_midnight = point_in_time - point_in_time.hour - 16
I am using the datetime Python module in django. I am looking to calculate the date if the expiry date is less than or equal to 6 months from the current date.
The reason I want to generate a date 6 months from the current date is to set an alert that will highlight the field/column in which that event occurs. I dont know if my question is clear. I have been reading about timedelta function but cant really get my head round it. I am trying to write an if statement to statisfy this condition. Anyone able to help me please? Am a newbie to django and python.
There are two approaches, one only slightly inaccurate, one inaccurate in a different way:
Add a datetime.timedelta() of 365.25 / 2 days (average year length divided by two):
import datetime
sixmonths = datetime.datetime.now() + datetime.timedelta(days=365.25/2)
This method will give you a datetime stamp 6 months into the future, where we define 6 monhs as exactly half a year (on average).
Use the external dateutil library, it has a excellent relativedelta class that will add 6 months based on calendar calculations to your current date:
import datetime
from dateutil.relativedelat import relativedelta
sixmonths = datetime.datetime.now() + relativedelta(months=6)
This method will give you a datetime stamp 6 months into the future, where the month component of the date has been forwarded by 6, and it'll take into account month boundaries, making sure not to cross them. August 30th plus 6 months becomes February 28th or 29th (leap years permitting), for example.
A demonstration could be helpful. In my timezone, at the time of posting, this translates to:
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2013, 2, 18, 12, 16, 0, 547567)
>>> now + datetime.timedelta(days=365.25/2)
datetime.datetime(2013, 8, 20, 3, 16, 0, 547567)
>>> now + relativedelta(months=6)
datetime.datetime(2013, 8, 18, 12, 16, 0, 547567)
So there is a 1 day and 15 hour difference between the two methods.
The same methods work fine with datetime.date objects too:
>>> today = datetime.date.today()
>>> today
datetime.date(2013, 2, 18)
>>> today + datetime.timedelta(days=365.25/2)
datetime.date(2013, 8, 19)
>>> today + relativedelta(months=6)
datetime.date(2013, 8, 18)
The half-year timedelta becomes a teensy less accurate when applied to dates only (the 5/8th day component of the delta is ignored now).
If by "6 months" you mean 180 days, you can use:
import datetime
d = datetime.date.today()
d + datetime.timedelta(6 * 30)
Alternatively if you, mean actual 6 months by calendar you'll have to lookup the calendar module and do some lookups on each month. For example:
import datetime
import calendar
def add_6_months(a_date):
month = a_date.month - 1 + 6
year = a_date.year + month / 12
month = month % 12 + 1
day = min(a_date.day,calendar.monthrange(year, month)[1])
return datetime.date(year, month, day)
I would give Delorean a look a serious look. It is built on top of dateutil and pytz to do what you asked would simply be the following.
>>> d = Delorean()
>>> d
Delorean(datetime=2013-02-21 06:00:21.195025+00:00, timezone=UTC)
>>> d.next_month(6)
Delorean(datetime=2013-08-21 06:00:21.195025+00:00, timezone=UTC)
It takes into account all the dateutil calculations as well as provide and interface for timezone shifts. to get the needed datetime simple .datetime on the Delorean object.
this might work for you in case you want to get an specfic date back:
from calendar import datetime
datetime.date(2019,1,1).strftime("%a %d")
#The arguments here are: year, month, day and the method is there to return a formated kind of date - in this case - day of the week and day of the month.
The outcome would be:
Tue 01
Hope it helps!
How to find out what week number is current year on June 16th (wk24) with Python?
datetime.date has a isocalendar() method, which returns a tuple containing the calendar week:
>>> import datetime
>>> datetime.date(2010, 6, 16).isocalendar()[1]
24
datetime.date.isocalendar() is an instance-method returning a tuple containing year, weeknumber and weekday in respective order for the given date instance.
In Python 3.9+ isocalendar() returns a namedtuple with the fields year, week and weekday which means you can access the week explicitly using a named attribute:
>>> import datetime
>>> datetime.date(2010, 6, 16).isocalendar().week
24
You can get the week number directly from datetime as string.
>>> import datetime
>>> datetime.date(2010, 6, 16).strftime("%V")
'24'
Also you can get different "types" of the week number of the year changing the strftime parameter for:
%U - Week number of the year (Sunday as the first day of the week) as a zero padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0. Examples: 00, 01, …, 53
%W - Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0. Examples: 00, 01, …, 53
[...]
(Added in Python 3.6, backported to some distribution's Python 2.7's) Several additional directives not required by the C89 standard are included for convenience. These parameters all correspond to ISO 8601 date values. These may not be available on all platforms when used with the strftime() method.
[...]
%V - ISO 8601 week as a decimal number with Monday as the first day of the week. Week 01 is the week containing Jan 4. Examples: 01, 02, …, 53
from: datetime — Basic date and time types — Python 3.7.3 documentation
I've found out about it from here. It worked for me in Python 2.7.6
I believe date.isocalendar() is going to be the answer. This article explains the math behind ISO 8601 Calendar. Check out the date.isocalendar() portion of the datetime page of the Python documentation.
>>> dt = datetime.date(2010, 6, 16)
>>> wk = dt.isocalendar()[1]
24
.isocalendar() return a 3-tuple with (year, wk num, wk day). dt.isocalendar()[0] returns the year,dt.isocalendar()[1] returns the week number, dt.isocalendar()[2] returns the week day. Simple as can be.
There are many systems for week numbering. The following are the most common systems simply put with code examples:
ISO: First week starts with Monday and must contain the January 4th (or first Thursday of the year). The ISO calendar is already implemented in Python:
>>> from datetime import date
>>> date(2014, 12, 29).isocalendar()[:2]
(2015, 1)
North American: First week starts with Sunday and must contain the January 1st. The following code is my modified version of Python's ISO calendar implementation for the North American system:
from datetime import date
def week_from_date(date_object):
date_ordinal = date_object.toordinal()
year = date_object.year
week = ((date_ordinal - _week1_start_ordinal(year)) // 7) + 1
if week >= 52:
if date_ordinal >= _week1_start_ordinal(year + 1):
year += 1
week = 1
return year, week
def _week1_start_ordinal(year):
jan1 = date(year, 1, 1)
jan1_ordinal = jan1.toordinal()
jan1_weekday = jan1.weekday()
week1_start_ordinal = jan1_ordinal - ((jan1_weekday + 1) % 7)
return week1_start_ordinal
>>> from datetime import date
>>> week_from_date(date(2014, 12, 29))
(2015, 1)
MMWR (CDC): First week starts with Sunday and must contain the January 4th (or first Wednesday of the year). I created the epiweeks package specifically for this numbering system (also has support for the ISO system). Here is an example:
>>> from datetime import date
>>> from epiweeks import Week
>>> Week.fromdate(date(2014, 12, 29))
(2014, 53)
Here's another option:
import time
from time import gmtime, strftime
d = time.strptime("16 Jun 2010", "%d %b %Y")
print(strftime(d, '%U'))
which prints 24.
See: http://docs.python.org/library/datetime.html#strftime-and-strptime-behavior
The ISO week suggested by others is a good one, but it might not fit your needs. It assumes each week begins with a Monday, which leads to some interesting anomalies at the beginning and end of the year.
If you'd rather use a definition that says week 1 is always January 1 through January 7, regardless of the day of the week, use a derivation like this:
>>> testdate=datetime.datetime(2010,6,16)
>>> print(((testdate - datetime.datetime(testdate.year,1,1)).days // 7) + 1)
24
Generally to get the current week number (starts from Sunday):
from datetime import *
today = datetime.today()
print today.strftime("%U")
For the integer value of the instantaneous week of the year try:
import datetime
datetime.datetime.utcnow().isocalendar()[1]
If you are only using the isocalendar week number across the board the following should be sufficient:
import datetime
week = date(year=2014, month=1, day=1).isocalendar()[1]
This retrieves the second member of the tuple returned by isocalendar for our week number.
However, if you are going to be using date functions that deal in the Gregorian calendar, isocalendar alone will not work! Take the following example:
import datetime
date = datetime.datetime.strptime("2014-1-1", "%Y-%W-%w")
week = date.isocalendar()[1]
The string here says to return the Monday of the first week in 2014 as our date. When we use isocalendar to retrieve the week number here, we would expect to get the same week number back, but we don't. Instead we get a week number of 2. Why?
Week 1 in the Gregorian calendar is the first week containing a Monday. Week 1 in the isocalendar is the first week containing a Thursday. The partial week at the beginning of 2014 contains a Thursday, so this is week 1 by the isocalendar, and making date week 2.
If we want to get the Gregorian week, we will need to convert from the isocalendar to the Gregorian. Here is a simple function that does the trick.
import datetime
def gregorian_week(date):
# The isocalendar week for this date
iso_week = date.isocalendar()[1]
# The baseline Gregorian date for the beginning of our date's year
base_greg = datetime.datetime.strptime('%d-1-1' % date.year, "%Y-%W-%w")
# If the isocalendar week for this date is not 1, we need to
# decrement the iso_week by 1 to get the Gregorian week number
return iso_week if base_greg.isocalendar()[1] == 1 else iso_week - 1
I found these to be the quickest way to get the week number; all of the variants.
from datetime import datetime
dt = datetime(2021, 1, 3) # Date is January 3rd 2021 (Sunday), year starts with Friday
dt.strftime("%W") # '00'; Monday is considered first day of week, Sunday is the last day of the week which started in the previous year
dt.strftime("%U") # '01'; Sunday is considered first day of week
dt.strftime("%V") # '53'; ISO week number; result is '53' since there is no Thursday in this year's part of the week
Further clarification for %V can be found in the Python doc:
The ISO year consists of 52 or 53 full weeks, and where a week starts on a Monday and ends on a Sunday. The first week of an ISO year is the first (Gregorian) calendar week of a year containing a Thursday. This is called week number 1, and the ISO year of that Thursday is the same as its Gregorian year.
https://docs.python.org/3/library/datetime.html#datetime.date.isocalendar
NOTE: Bear in mind the return value is a string, so pass the result to a int constructor if you need a number.
I summarize the discussion to two steps:
Convert the raw format to a datetime object.
Use the function of a datetime object or a date object to calculate the week number.
Warm up
from datetime import datetime, date, time
d = date(2005, 7, 14)
t = time(12, 30)
dt = datetime.combine(d, t)
print(dt)
1st step
To manually generate a datetime object, we can use datetime.datetime(2017,5,3) or datetime.datetime.now().
But in reality, we usually need to parse an existing string. we can use strptime function, such as datetime.strptime('2017-5-3','%Y-%m-%d') in which you have to specific the format. Detail of different format code can be found in the official documentation.
Alternatively, a more convenient way is to use dateparse module. Examples are dateparser.parse('16 Jun 2010'), dateparser.parse('12/2/12') or dateparser.parse('2017-5-3')
The above two approaches will return a datetime object.
2nd step
Use the obtained datetime object to call strptime(format). For example,
python
dt = datetime.strptime('2017-01-1','%Y-%m-%d') # return a datetime object. This day is Sunday
print(dt.strftime("%W")) # '00' Monday as the 1st day of the week. All days in a new year preceding the 1st Monday are considered to be in week 0.
print(dt.strftime("%U")) # '01' Sunday as the 1st day of the week. All days in a new year preceding the 1st Sunday are considered to be in week 0.
print(dt.strftime("%V")) # '52' Monday as the 1st day of the week. Week 01 is the week containing Jan 4.
It's very tricky to decide which format to use. A better way is to get a date object to call isocalendar(). For example,
python
dt = datetime.strptime('2017-01-1','%Y-%m-%d') # return a datetime object
d = dt.date() # convert to a date object. equivalent to d = date(2017,1,1), but date.strptime() don't have the parse function
year, week, weekday = d.isocalendar()
print(year, week, weekday) # (2016,52,7) in the ISO standard
In reality, you will be more likely to use date.isocalendar() to prepare a weekly report, especially in the Christmas-New Year shopping season.
You can try %W directive as below:
d = datetime.datetime.strptime('2016-06-16','%Y-%m-%d')
print(datetime.datetime.strftime(d,'%W'))
'%W': Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0. (00, 01, ..., 53)
For pandas users, if you want to get a column of week number:
df['weekofyear'] = df['Date'].dt.week
isocalendar() returns incorrect year and weeknumber values for some dates:
Python 2.7.3 (default, Feb 27 2014, 19:58:35)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime as dt
>>> myDateTime = dt.datetime.strptime("20141229T000000.000Z",'%Y%m%dT%H%M%S.%fZ')
>>> yr,weekNumber,weekDay = myDateTime.isocalendar()
>>> print "Year is " + str(yr) + ", weekNumber is " + str(weekNumber)
Year is 2015, weekNumber is 1
Compare with Mark Ransom's approach:
>>> yr = myDateTime.year
>>> weekNumber = ((myDateTime - dt.datetime(yr,1,1)).days/7) + 1
>>> print "Year is " + str(yr) + ", weekNumber is " + str(weekNumber)
Year is 2014, weekNumber is 52
Let's say you need to have a week combined with the year of the current day as a string.
import datetime
year,week = datetime.date.today().isocalendar()[:2]
week_of_the_year = f"{year}-{week}"
print(week_of_the_year)
You might get something like 2021-28
If you want to change the first day of the week you can make use of the calendar module.
import calendar
import datetime
calendar.setfirstweekday(calendar.WEDNESDAY)
isodate = datetime.datetime.strptime(sweek,"%Y-%m-%d").isocalendar()
week_of_year = isodate[1]
For example, calculate the sprint number for a week starting on WEDNESDAY:
def calculate_sprint(sweek):
calendar.setfirstweekday(calendar.WEDNESDAY)
isodate=datetime.datetime.strptime(sweek,"%Y-%m-%d").isocalendar()
return "{year}-{month}".format(year=isodate[0], month=isodate[1])
calculate_sprint('2021-01-01')
>>>'2020-53'
We have a similar issue and we came up with this logic
I have tested for 1year test cases & all passed
import datetime
def week_of_month(dt):
first_day = dt.replace(day=1)
dom = dt.day
if first_day.weekday() == 6:
adjusted_dom = dom
else:
adjusted_dom = dom + first_day.weekday()
if adjusted_dom % 7 == 0 and first_day.weekday() != 6:
value = adjusted_dom / 7.0 + 1
elif first_day.weekday() == 6 and adjusted_dom % 7 == 0 and adjusted_dom == 7:
value = 1
else:
value = int(ceil(adjusted_dom / 7.0))
return int(value)
year = 2020
month = 01
date = 01
date_value = datetime.datetime(year, month, date).date()
no = week_of_month(date_value)
userInput = input ("Please enter project deadline date (dd/mm/yyyy/): ")
import datetime
currentDate = datetime.datetime.today()
testVar = datetime.datetime.strptime(userInput ,"%d/%b/%Y").date()
remainDays = testVar - currentDate.date()
remainWeeks = (remainDays.days / 7.0) + 1
print ("Please pay attention for deadline of project X in days and weeks are : " ,(remainDays) , "and" ,(remainWeeks) , "Weeks ,\nSo hurryup.............!!!")
A lot of answers have been given, but id like to add to them.
If you need the week to display as a year/week style (ex. 1953 - week 53 of 2019, 2001 - week 1 of 2020 etc.), you can do this:
import datetime
year = datetime.datetime.now()
week_num = datetime.date(year.year, year.month, year.day).strftime("%V")
long_week_num = str(year.year)[0:2] + str(week_num)
It will take the current year and week, and long_week_num in the day of writing this will be:
>>> 2006