I am trying to get detailed calendar information on all my birthdays to 2024(i.e. week #, day of week etc...). I noticed Pandas as date_range function/method, but am trying to do it using time/datetime because I couldn't get "freq=" to work. This is what I have so far, and I think I can get what I need from myBirthdays list, but am wondering if there is/was an easier way? Seems like a lot of extra work.
TIA.
#import pandas as pd
from datetime import date
import time
def BdayList(birthdate, enddate):
print(birthdate, type(birthdate), endDate, type(endDate))
#print(birthdate.weekday(), endDate.isocalendar())
myMonth = date.strftime(birthdate, "%m")
myDay = date.strftime(birthdate, "%d")
myBirthDays = []
daysDelta = (enddate - birthdate)
daysDeltaInt = daysDelta.days / 365
for year in range(int(date.strftime(birthdate, "%Y")), int(date.strftime(enddate, "%Y"))): #13148
year = str(year)
myBirthday = time.strptime(year+" "+myMonth+" "+myDay, "%Y %m %d")
print(myBirthday)
myBirthDays.append(myBirthday)
#dateRange = pd.date_range(start, periods = NumPeriods, freq="A")
return myBirthDays#DaysDelta, type(DaysDelta)
myBday = date(1988, 12, 22)
endDate = date(2024, 12, 22)
BdayList(myBday, endDate)
time.struct_time(tm_year=1988, tm_mon=12, tm_mday=22, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=357, tm_isdst=-1)
Because it is possible to just replace the year in original birth_date, there is no need to switch between dates and strings. (Note that I have also PEP8'd the code and used slightly different variable names + added type hints)
from datetime import date
from typing import List
from pprint import pprint
def get_birthdays(birth_date: date, end_date: date) -> List[date]:
birthday_list = list()
while birth_date <= end_date:
birthday_list.append(birth_date)
birth_date = birth_date.replace(year=birth_date.year + 1)
return birthday_list
if __name__ == "__main__":
birthdays = get_birthdays(
birth_date=date(1988, month=12, day=22),
end_date=date(2024, month=12, day=22)
)
pprint([(x.strftime("%Y-%m-%d %A, week: %U")) for x in birthdays])
The output should be:
['1988-12-22 Thursday, week: 51',
'1989-12-22 Friday, week: 51',
'1990-12-22 Saturday, week: 50',
'1991-12-22 Sunday, week: 51',
'1992-12-22 Tuesday, week: 51',
'1993-12-22 Wednesday, week: 51',
'1994-12-22 Thursday, week: 51',
'1995-12-22 Friday, week: 51']
To format output, please check datetime documentation. Hopefully this helps!
Related
This code return ValueError: day is out of range for month :
from pandas.tseries.holiday import Holiday, AbstractHolidayCalendar
from pandas.tseries.offsets import MonthEnd
import datetime as dt
class MyHolidays(AbstractHolidayCalendar):
rules = [Holiday('Sapeur',month=2,day=29)]
cal=MyHolidays()
if __name__ == '__main__':
start = dt.date(2020, 2, 1)
myholidays =cal.holidays(start, start + MonthEnd())
How do you make this code work in non-leap years?
I thought class Holiday handled that, didn't you?
Thx a lot for advance.
ps: 2020 is a leap year but not 2019 or 2021.
ps2: I seek a better solution than :
rules = [Holiday('Sapeur',year=2020, month=2,day=29),
Holiday('NoSapeur',year=2021, month=2,day=28),
Holiday('NoSapeur',year=2022, month=2,day=28),
Holiday('NoSapeur',year=2023, month=2,day=28),
Holiday('Sapeur',year=2024, month=2,day=29),
Holiday('NoSapeur',year=2025, month=2,day=28),
Holiday('NoSapeur',year=2026, month=2,day=28),
Holiday('NoSapeur',year=2027, month=2,day=28),
Holiday('Sapeur',year=2028, month=2,day=29),
...]
Because I'm on vacation every February and I make my rules with:
cejour=pd.Timestamp.today()
RÈGLES=[]
mois=2
for jour in pd.date_range(début_mois:=cejour.replace(month=mois, day=1), début_mois+MonthEnd(), normalize=True):
RÈGLES+=[Holiday('Vacances Février',month=mois, day=jour.day)]
class MyHolidays(AbstractHolidayCalendar):
rules = RÈGLES
Define your own observance
def leap_year(dt):
if dt.is_leap_year:
return dt + MonthEnd()
class MyHolidays(AbstractHolidayCalendar):
rules = [Holiday('Sapeur', month=2, day=28, observance=leap_year)]
cal = MyHolidays()
start = dt.date(2020, 2, 1)
myholidays =cal.holidays(start, start + MonthEnd())
print(myholidays) # DatetimeIndex(['2020-02-29'], dtype='datetime64[ns]', freq=None)
If you try this with a non leap year then it will assign nothing
start = dt.date(2019, 2, 1)
myholidays =cal.holidays(start, start + MonthEnd())
print(myholidays) # DatetimeIndex([], dtype='datetime64[ns]', freq=None)
I use this code to format my time but the time that comes out is 5 hours wrong. I should be 06 something in calcutta now and it formats the time now as 01... something. What is wrong with the code?
def datetimeformat_viewad(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
tzinfo = timezone(timezoneinfo)
month = MONTHS[to_format.month - 1]
input = pytz.timezone(timezoneinfo).localize(
datetime(int(to_format.year), int(to_format.month), int(to_format.day), int(to_format.hour), int(to_format.minute)))
date_str = '{0} {1}'.format(input.day, _(month))
time_str = format_time(input, 'H:mm', tzinfo=tzinfo, locale=locale)
return "{0} {1}".format(date_str, time_str)
Update
This code worked which was according to the answer below.
def datetimeformat_viewad(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(int(to_format.year), int(to_format.month), int(to_format.day), int(to_format.hour), int(to_format.minute))
utc_date = utc.localize(to_format)
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
month = MONTHS[int(tzone_date.month) - 1]
time_str = format_time(tzone_date, 'H:mm')
date_str = '{0} {1}'.format(tzone_date.day, _(month))
return "{0} {1}".format(date_str, time_str)
It sounds like to_format is a naive datetime in UTC time.
You want to convert it to Calcutta time.
To do this, you localize to_format to UTC time1, and then use astimezone to convert that timezone-aware time to Calcutta time:
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(2015,7,17,1,0)
print(to_format)
# 2015-07-17 01:00:00
utc_date = utc.localize(to_format)
print(utc_date)
# 2015-07-17 01:00:00+00:00
timezoneinfo = 'Asia/Calcutta'
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
print(tzone_date)
# 2015-07-17 06:30:00+05:30
1The tzone.localize method does not convert between timezones. It
interprets the given localtime as one given in tzone. So if to_format is
meant to be interpreted as a UTC time, then use utc.localize to convert the
naive datetime to a timezone-aware UTC time.
I am trying to pass a string into as function and need to convert it into a time tuple:
def sim(startdate, enddate):
# need to convert the date from string to integer time tuple:
dt_start = dt.date(startdate)
print 'Start Date: ', dt_start
dt_end = dt.date(enddate)
print 'End Date: ', dt_end
# in String format
sim('Jan 1, 2011', 'Dec 31, 2011')
# in interger in string format
sim('2011,1,1', '2011,12,31')
Another way would be to use strptime(). Have a time format defined for both of your formats and use them accordingly. This is what I mean:
import datetime as dt
def sim(startdate, enddate):
time_format_one = "%b %d, %Y"
time_format_two = "%Y,%m,%d"
try:
dt_start = dt.datetime.strptime(startdate, time_format_one)
dt_end = dt.datetime.strptime(enddate, time_format_one)
except ValueError:
dt_start = dt.datetime.strptime(startdate, time_format_two)
dt_end = dt.datetime.strptime(enddate, time_format_two)
print 'Start Date: ', dt_start.date()
print 'End Date: ', dt_end.date()
# in String format
sim('Jan 1, 2011', 'Dec 31, 2011')
# in interger in string format
sim('2011,1,1', '2011,12,31')
prints:
Start Date: 2011-01-01
End Date: 2011-12-31
Start Date: 2011-01-01
End Date: 2011-12-31
You could use timetuple() on dt_start and dt_end if you need time tuple.
I assume you want to convert date ('Jan 1, 2011', 'Dec 31, 2011') and ('2011,1,1', '2011,12,31') into timetuple
from datetime import datetime
date_str = "Jan 1, 2011"
fmt = "%b %d, %Y"
# Construct a datetime object
date_obj = datetime.strptime(date_str, fmt)
# Convert it to any string format you want
new_fmt = "%Y, %m, %d"
print date_obj.strftime(new_fmt)
# Prints'2011, 01, 01'
# If you want python timetuple then
t_tuple = date_obj.timetuple()
What you're probably trying to do is the following:
import datetime as dt
year,month,day = map(int, '2011,1,1'.split(','))
dt_start = dt.date(year,month,day)
print dt_start # prints 2011-01-01
The error is because of the use of a string '2011,1,1' instead of integers: 2011,1,1 as an input to: datetime.date()
I'm currently writing some reporting code that allows users to optionally specify a date range. The way it works (simplified), is:
A user (optionally) specifies a year.
A user (optionally) specifies a month.
A user (optionally) specifies a day.
Here's a code snippet, along with comments describing what I'd like to do:
from datetime import datetime, timedelta
# ...
now = datetime.now()
start_time = now.replace(hour=0, minute=0, second=0, microsecond=0)
stop_time = now
# If the user enters no year, month, or day--then we'll simply run a
# report that only spans the current day (from the start of today to now).
if options['year']:
start_time = start_time.replace(year=options['year'], month=0, day=0)
stop_time = stop_time.replace(year=options['year'])
# If the user specifies a year value, we should set stop_time to the last
# day / minute / hour / second / microsecond of the year, that way we'll
# only generate reports from the start of the specified year, to the end
# of the specified year.
if options['month']:
start_time = start_time.replace(month=options['month'], day=0)
stop_time = stop_time.replace(month=options['month'])
# If the user specifies a month value, then set stop_time to the last
# day / minute / hour / second / microsecond of the specified month, that
# way we'll only generate reports for the specified month.
if options['day']:
start_time = start_time.replace(day=options['day'])
stop_time = stop_time.replace(day=options['day'])
# If the user specifies a day value, then set stop_time to the last moment of
# the current day, so that reports ONLY run on the current day.
I'm trying to find the most elegant way to write the code above--I've been trying to find a way to do it with timedelta, but can't seem to figure it out. Any advice would be appreciated.
To set the stop_time, advance start_time one year, month or day as appropriate, then subtract one timedelta(microseconds=1)
if options['year']:
start_time = start_time.replace(year=options['year'], month=1, day=1)
stop_time = stop_time.replace(year=options['year']+1)-timedelta(microseconds=1)
elif options['month']:
start_time = start_time.replace(month=options['month'], day=1)
months=options['month']%12+1
stop_time = stop_time.replace(month=months,day=1)-timedelta(microseconds=1)
else:
start_time = start_time.replace(day=options['day'])
stop_time = stop_time.replace(day=options['day'])+timedelta(days=1,microseconds=-1)
Using dict.get can simplify your code. It is a bit cleaner than using datetime.replace and timedelta objects.
Here's something to get you started:
from datetime import datetime
options = dict(month=5, day=20)
now = datetime.now()
start_time = datetime(year=options.get('year', now.year),
month=options.get('month', 1),
day=options.get('day', 1)
hour=0,
minute=0,
second=0)
stop_time = datetime(year=options.get('year', now.year),
month=options.get('month', now.month),
day=options.get('day', now.day),
hour=now.hour,
minute=now.minute,
second=now.second)
today = datetime.date.today()
begintime = today.strftime("%Y-%m-%d 00:00:00")
endtime = today.strftime("%Y-%m-%d 23:59:59")
from datetime import datetime, date, timedelta
def get_current_timestamp():
return int(datetime.now().timestamp())
def get_end_today_timestamp():
# get 23:59:59
result = datetime.combine(date.today() + timedelta(days=1), datetime.min.time())
return int(result.timestamp()) - 1
def get_datetime_from_timestamp(timestamp):
return datetime.fromtimestamp(timestamp)
end_today = get_datetime_from_timestamp(get_end_today_timestamp())
date = datetime.strftime('<input date str>')
date.replace(hour=0, minute=0, second=0, microsecond=0) # now we get begin of the day
date += timedelta(days=1, microseconds=-1) # now end of the day
After looking at some of the answers here, and not really finding anything extremely elegant, I did some poking around the standard library and found my current solution (which I like quite well): dateutil.
Here's how I implemented it:
from datetime import date
from dateutil.relativedelta import relativedelta
now = date.today()
stop_time = now + relativedelta(days=1)
start_time = date(
# NOTE: I'm not doing dict.get() since in my implementation, these dict
# keys are guaranteed to exist.
year = options['year'] or now.year,
month = options['month'] or now.month,
day = options['day'] or now.day
)
if options['year']:
start_time = date(year=options['year'] or now.year, month=1, day=1)
stop_time = start_time + relativedelta(years=1)
if options['month']:
start_time = date(
year = options['year'] or now.year,
month = options['month'] or now.month,
day = 1
)
stop_time = start_time + relativedelta(months=1)
if options['day']:
start_time = date(
year = options['year'] or now.year,
month = options['month'] or now.month,
day = options['day'] or now.day,
)
stop_time = start_time + relativedelta(days=1)
# ... do stuff with start_time and stop_time here ...
What I like about this implementation, is that python's dateutil.relativedata.relativedata works really well on edge cases. It gets the days/months/years correct. If I have month=12, and do relativedata(months=1), it'll increment the year and set the month to 1 (works nicely).
Also: in the above implementation, if the user specifies none of the optional dates (year, month, or day)--we'll fallback to a nice default (start_time = this morning, stop_time = tonight), that way we'll default to doing stuff for the current day only.
Thanks to everyone for their answers--they were helpful in my research.
How to increment the day of a datetime?
for i in range(1, 35)
date = datetime.datetime(2003, 8, i)
print(date)
But I need pass through months and years correctly? Any ideas?
date = datetime.datetime(2003,8,1,12,4,5)
for i in range(5):
date += datetime.timedelta(days=1)
print(date)
Incrementing dates can be accomplished using timedelta objects:
import datetime
datetime.datetime.now() + datetime.timedelta(days=1)
Look up timedelta objects in the Python docs: http://docs.python.org/library/datetime.html
All of the current answers are wrong in some cases as they do not consider that timezones change their offset relative to UTC. So in some cases adding 24h is different from adding a calendar day.
Proposed solution
The following solution works for Samoa and keeps the local time constant.
def add_day(today):
"""
Add a day to the current day.
This takes care of historic offset changes and DST.
Parameters
----------
today : timezone-aware datetime object
Returns
-------
tomorrow : timezone-aware datetime object
"""
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
tomorrow_utc_tz = tomorrow_utc_tz.replace(hour=today.hour,
minute=today.minute,
second=today.second)
return tomorrow_utc_tz
Tested Code
# core modules
import datetime
# 3rd party modules
import pytz
# add_day methods
def add_day(today):
"""
Add a day to the current day.
This takes care of historic offset changes and DST.
Parameters
----------
today : timezone-aware datetime object
Returns
-------
tomorrow : timezone-aware datetime object
"""
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
tomorrow_utc_tz = tomorrow_utc_tz.replace(hour=today.hour,
minute=today.minute,
second=today.second)
return tomorrow_utc_tz
def add_day_datetime_timedelta_conversion(today):
# Correct for Samoa, but dst shift
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
return tomorrow_utc_tz
def add_day_dateutil_relativedelta(today):
# WRONG!
from dateutil.relativedelta import relativedelta
return today + relativedelta(days=1)
def add_day_datetime_timedelta(today):
# WRONG!
return today + datetime.timedelta(days=1)
# Test cases
def test_samoa(add_day):
"""
Test if add_day properly increases the calendar day for Samoa.
Due to economic considerations, Samoa went from 2011-12-30 10:00-11:00
to 2011-12-30 10:00+13:00. Hence the country skipped 2011-12-30 in its
local time.
See https://stackoverflow.com/q/52084423/562769
A common wrong result here is 2011-12-30T23:59:00-10:00. This date never
happened in Samoa.
"""
tz = pytz.timezone('Pacific/Apia')
today_utc = datetime.datetime(2011, 12, 30, 9, 59,
tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz) # 2011-12-29T23:59:00-10:00
tomorrow = add_day(today_tz)
return tomorrow.isoformat() == '2011-12-31T23:59:00+14:00'
def test_dst(add_day):
"""Test if add_day properly increases the calendar day if DST happens."""
tz = pytz.timezone('Europe/Berlin')
today_utc = datetime.datetime(2018, 3, 25, 0, 59,
tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz) # 2018-03-25T01:59:00+01:00
tomorrow = add_day(today_tz)
return tomorrow.isoformat() == '2018-03-26T01:59:00+02:00'
to_test = [(add_day_dateutil_relativedelta, 'relativedelta'),
(add_day_datetime_timedelta, 'timedelta'),
(add_day_datetime_timedelta_conversion, 'timedelta+conversion'),
(add_day, 'timedelta+conversion+dst')]
print('{:<25}: {:>5} {:>5}'.format('Method', 'Samoa', 'DST'))
for method, name in to_test:
print('{:<25}: {:>5} {:>5}'
.format(name,
test_samoa(method),
test_dst(method)))
Test results
Method : Samoa DST
relativedelta : 0 0
timedelta : 0 0
timedelta+conversion : 1 0
timedelta+conversion+dst : 1 1
Here is another method to add days on date using dateutil's relativedelta.
from datetime import datetime
from dateutil.relativedelta import relativedelta
print 'Today: ',datetime.now().strftime('%d/%m/%Y %H:%M:%S')
date_after_month = datetime.now()+ relativedelta(day=1)
print 'After a Days:', date_after_month.strftime('%d/%m/%Y %H:%M:%S')
Output:
Today: 25/06/2015 20:41:44
After a Days: 01/06/2015 20:41:44
Most Simplest solution
from datetime import timedelta, datetime
date = datetime(2003,8,1,12,4,5)
for i in range(5):
date += timedelta(days=1)
print(date)
This was a straightforward solution for me:
from datetime import timedelta, datetime
today = datetime.today().strftime("%Y-%m-%d")
tomorrow = datetime.today() + timedelta(1)
You can also import timedelta so the code is cleaner.
from datetime import datetime, timedelta
date = datetime.now() + timedelta(seconds=[delta_value])
Then convert to date to string
date = date.strftime('%Y-%m-%d %H:%M:%S')
Python one liner is
date = (datetime.now() + timedelta(seconds=[delta_value])).strftime('%Y-%m-%d %H:%M:%S')
A short solution without libraries at all. :)
d = "8/16/18"
day_value = d[(d.find('/')+1):d.find('/18')]
tomorrow = f"{d[0:d.find('/')]}/{int(day_value)+1}{d[d.find('/18'):len(d)]}".format()
print(tomorrow)
# 8/17/18
Make sure that "string d" is actually in the form of %m/%d/%Y so that you won't have problems transitioning from one month to the next.