Count occurrences of midnight between datetimes - python

Is there a native facility or third party library for python that can count the number of occurrences of a time (in particular the number of midnights) between two datetimes?
It is not sufficient to count the number of days because the datetimes might not have the same time component, so you end up with the kind of intervals shown in this example:
from datetime import datetime as dt
dt1 = dt(2014,05,21,23)
dt2 = dt(2014,05,22,01)
i1 = dt2-dt1
print(i1) # 1 midnight but 2 hours
dt3 = dt(2014,05,21,01)
dt4 = dt(2014,05,22,23)
i2 = dt4-dt3 # 1 midnight but 1 day + 22 hours
print(i2)

Set the time component of both datetimes to the same time, then do the difference.
def midnights(dt1, dt2):
dt1 = dt1.replace(hour=0, minute=0, second=0, microsecond=0)
dt2 = dt2.replace(hour=0, minute=0, second=0, microsecond=0)
return (dt2 - dt1).days

Use .date() for the difference:
from datetime import datetime as dt
def count_midnights(dt1, dt2):
return (dt2.date() - dt1.date()).days
This way you get:
>>> dt1 = dt(2014,05,21,23)
>>> dt2 = dt(2014,05,22,01)
>>> print count_midnights(dt1, dt2)
1
>>> dt3 = dt(2014,05,21,01)
>>> dt4 = dt(2014,05,22,23)
>>> print count_midnights(dt3, dt4)
1

Just a bit crazy alternative, mostly FYI, - you can use a nice Delorean third-party:
from datetime import datetime as dt
import delorean
from delorean import stops
dt1 = dt(2014,05,21,23)
dt2 = dt(2014,05,22,01)
print sum(1 for stop in stops(start=dt1, stop=dt2, freq=delorean.HOURLY)
if stop.datetime == stop.midnight())

Related

Python How to Check if time is midnight and not display time if true

I'm modifying our pacific time zone filter to include a time option. I don't want the time component to be shown if midnight. The only import thus far we are using is dateutil.parser. Any pointers on best solution would be appreciated! Thanks.
def to_pacific_date_str(timestamp, format='%Y-%m-%d', time=False):
pacific_timestamp = timestamp
if time:
format='%Y-%m-%d %H:%M' # 2016-10-03 00:00
if timestamp.tzname() is None:
# setting timezone lost when pulled from DB
utc_timestamp = timestamp.replace(tzinfo=pytz.utc)
# always converting to pacific timezone
pacific_timestamp = utc_timestamp.astimezone(pytz.timezone('US/Pacific'))
return pacific_timestamp.strftime(format)
I believe the best thing to do would be to just take the time() from the datetime before passing it, then compare that to datetime.time(0, 0).
import pytz
import datetime
def to_pacific_date_str(timestamp, date_fmt='%Y-%m-%d', time=False):
pacific_timestamp = timestamp
if timestamp.tzinfo is None:
# setting timezone lost when pulled from DB
utc_timestamp = timestamp.replace(tzinfo=pytz.utc)
# always converting to pacific timezone
pacific_timestamp = utc_timestamp.astimezone(pytz.timezone('US/Pacific'))
if time and pacific_timestamp.time() != datetime.time(0, 0):
date_fmt = '%Y-%m-%d %H:%M' # 2016-10-03 00:00
return pacific_timestamp.strftime(date_fmt)
Note that I've changed format to date_fmt, because format() is already a builtin. Also, from a design standpoint, it's probably not a great idea to have time override the specified format string, so maybe change the "add time" portion to be date_fmt = date_fmt + ' %H:%M'.
Demonstration:
>>> PST = pytz.timezone('US/Pacific')
>>> to_pacific_date_str(PST.localize(datetime.datetime(2015, 4, 1, 0, 0)), time=True)
'2015-04-01'
>>> PST = pytz.timezone('US/Pacific')
>>> to_pacific_date_str(PST.localize(datetime.datetime(2015, 4, 1, 2, 0)), time=True)
'2015-04-01 02:00'
Try this for UTC:
def checkIfMidnight():
return (time.time() % 86400) == 0
To check if the time is midnight:
from datetime import datetime
def checkIfMidnight():
now = datetime.now()
seconds_since_midnight = (now - now.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
return seconds_since_midnight == 0
Alternatively you can use the .hour, .minute and .second attributes of the datetime object. Like this:
from datetime import datetime as dt
from pytz import timezone
now = dt.now(timezone('US/Pacific'))
midnight = now.hour == 0 and now.minute == 0 and now.second == 0 and now.microsecond == 0
midnight is a boolean indicating if it is midnight in the US/Pacific timezone.
I am not sure if this is the solution you are/were looking for but personally I use simple comparison:
import datetime
...
time == datetime.time(hour=0, minute=0, second=0, microsecond=0)
where time is TimeObject (datetime.time)

Python: how to get number of days from fixed past date to now

I'm trying to use Python to get the number of days from an arbitrary fixed past date (e.g., 2016/03/15) to now. I see that I can use datetime to specify now and the past date, and get a timedelta containing the number of days between:
>>> noo = datetime.now()
>>> then = noo.replace(month=3, day=15, hour=0, minute=0, second=0, microsecond=0)
>>> diffy = noo - then
>>> diffy
datetime.timedelta(43, 65233, 748370)
But, being still pretty new to Python, I don't know how to extract the number of days (43) from the timedelta. Can somebody help me out with this?
Just look at the .days property:
diffy.days
e.g.:
>>> from datetime import datetime
>>> noo = datetime.now()
>>> then = noo.replace(month=3, day=15, hour=0, minute=0, second=0, microsecond=0)
>>> diffy = noo - then
>>> diffy
datetime.timedelta(43, 55471, 209431)
>>> diffy.days
43
One thing to watch out for is the normalization of negative timedeltas... The documentation even says that these can be surprising...

How do I find the nth day of the next month in Python?

I am trying to get the date delta by subtracting today's date from the nth day of the next month.
delta = nth_of_next_month - todays_date
print delta.days
How do you get the date object for the 1st (or 2nd, 3rd.. nth) day of the next month. I tried taking the month number from the date object and increasing it by 1. Which is obviously a dumb idea because 12 + 1 = 13. I also tried adding one month to today and tried to get to the first of the month. I am sure that there is a much more efficient way of doing this.
The dateutil library is useful for this:
from dateutil.relativedelta import relativedelta
from datetime import datetime
# Where day is the day you want in the following month
dt = datetime.now() + relativedelta(months=1, day=20)
This should be straightforward unless I'm missing something in your question:
import datetime
now = datetime.datetime.now()
nth_day = 5
next_month = now.month + 1 if now.month < 12 else 1 # February
year = now.year if now.month < 12 else now.year+1
nth_of_next_month = datetime.datetime(year, next_month, nth_day)
print(nth_of_next_month)
Result:
2014-02-05 00:00:00
Using dateutil as suggested in another answer is a much better idea than this, though.
Another alternative is to use delorean library:
Delorean is a library that provides easy and convenient datetime
conversions in Python.
>>> from delorean import Delorean
>>> d = Delorean()
>>> d.next_month()
Delorean(datetime=2014-02-15 18:51:14.325350+00:00, timezone=UTC)
>>> d.next_month().next_day(2)
Delorean(datetime=2014-02-17 18:51:14.325350+00:00, timezone=UTC)
My approach to calculating the next month without external libraries:
def nth_day_of_next_month(dt, n):
return dt.replace(
year=dt.year + (dt.month // 12), # +1 for december, +0 otherwise
month=(dt.month % 12) + 1, # december becomes january
day=n)
This works for both datetime.datetime() and datetime.date() objects.
Demo:
>>> import datetime
>>> def nth_day_of_next_month(dt, n):
... return dt.replace(year=dt.year + (dt.month // 12), month=(dt.month % 12) + 1, day=n)
...
>>> nth_day_of_next_month(datetime.datetime.now(), 4)
datetime.datetime(2014, 2, 4, 19, 20, 51, 177860)
>>> nth_day_of_next_month(datetime.date.today(), 18)
datetime.date(2014, 2, 18)
Without using any external library, this can be achived as follows
from datetime import datetime, timedelta
def nth_day_of_next_month(n):
today = datetime.now()
next_month_dt = today + timedelta(days=32-today.day)
return next_month_dt.replace(day=n)

Convert timedelta to total seconds

I have a time difference
import time
import datetime
time1 = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
...
time2 = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
diff = time2 - time1
Now, how do I find the total number of seconds that passed? diff.seconds doesn't count days. I could do:
diff.seconds + diff.days * 24 * 3600
Is there a built-in method for this?
Use timedelta.total_seconds().
>>> import datetime
>>> datetime.timedelta(seconds=24*60*60).total_seconds()
86400.0
You have a problem one way or the other with your datetime.datetime.fromtimestamp(time.mktime(time.gmtime())) expression.
(1) If all you need is the difference between two instants in seconds, the very simple time.time() does the job.
(2) If you are using those timestamps for other purposes, you need to consider what you are doing, because the result has a big smell all over it:
gmtime() returns a time tuple in UTC but mktime() expects a time tuple in local time.
I'm in Melbourne, Australia where the standard TZ is UTC+10, but daylight saving is still in force until tomorrow morning so it's UTC+11. When I executed the following, it was 2011-04-02T20:31 local time here ... UTC was 2011-04-02T09:31
>>> import time, datetime
>>> t1 = time.gmtime()
>>> t2 = time.mktime(t1)
>>> t3 = datetime.datetime.fromtimestamp(t2)
>>> print t0
1301735358.78
>>> print t1
time.struct_time(tm_year=2011, tm_mon=4, tm_mday=2, tm_hour=9, tm_min=31, tm_sec=3, tm_wday=5, tm_yday=92, tm_isdst=0) ### this is UTC
>>> print t2
1301700663.0
>>> print t3
2011-04-02 10:31:03 ### this is UTC+1
>>> tt = time.time(); print tt
1301736663.88
>>> print datetime.datetime.now()
2011-04-02 20:31:03.882000 ### UTC+11, my local time
>>> print datetime.datetime(1970,1,1) + datetime.timedelta(seconds=tt)
2011-04-02 09:31:03.880000 ### UTC
>>> print time.localtime()
time.struct_time(tm_year=2011, tm_mon=4, tm_mday=2, tm_hour=20, tm_min=31, tm_sec=3, tm_wday=5, tm_yday=92, tm_isdst=1) ### UTC+11, my local time
You'll notice that t3, the result of your expression is UTC+1, which appears to be UTC + (my local DST difference) ... not very meaningful. You should consider using datetime.datetime.utcnow() which won't jump by an hour when DST goes on/off and may give you more precision than time.time()
More compact way to get the difference between two datetime objects and then convert the difference into seconds is shown below (Python 3x):
from datetime import datetime
time1 = datetime.strftime('18 01 2021', '%d %m %Y')
time2 = datetime.strftime('19 01 2021', '%d %m %Y')
difference = time2 - time1
difference_in_seconds = difference.total_seconds()
You can use mx.DateTime module
import mx.DateTime as mt
t1 = mt.now()
t2 = mt.now()
print int((t2-t1).seconds)

How to iterate over a timespan after days, hours, weeks and months?

How do I iterate over a timespan after days, hours, weeks or months?
Something like:
for date in foo(from_date, to_date, delta=HOURS):
print date
Where foo is a function, returning an iterator. I've been looking at the calendar module, but that only works for one specific year or month, not between dates.
Use dateutil and its rrule implementation, like so:
from dateutil import rrule
from datetime import datetime, timedelta
now = datetime.now()
hundredDaysLater = now + timedelta(days=100)
for dt in rrule.rrule(rrule.MONTHLY, dtstart=now, until=hundredDaysLater):
print dt
Output is
2008-09-30 23:29:54
2008-10-30 23:29:54
2008-11-30 23:29:54
2008-12-30 23:29:54
Replace MONTHLY with any of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, or SECONDLY. Replace dtstart and until with whatever datetime object you want.
This recipe has the advantage for working in all cases, including MONTHLY. Only caveat I could find is that if you pass a day number that doesn't exist for all months, it skips those months.
I don't think there is a method in Python library, but you can easily create one yourself using datetime module:
from datetime import date, datetime, timedelta
def datespan(startDate, endDate, delta=timedelta(days=1)):
currentDate = startDate
while currentDate < endDate:
yield currentDate
currentDate += delta
Then you could use it like this:
>>> for day in datespan(date(2007, 3, 30), date(2007, 4, 3),
>>> delta=timedelta(days=1)):
>>> print day
2007-03-30
2007-03-31
2007-04-01
2007-04-02
Or, if you wish to make your delta smaller:
>>> for timestamp in datespan(datetime(2007, 3, 30, 15, 30),
>>> datetime(2007, 3, 30, 18, 35),
>>> delta=timedelta(hours=1)):
>>> print timestamp
2007-03-30 15:30:00
2007-03-30 16:30:00
2007-03-30 17:30:00
2007-03-30 18:30:00
I achieved this using pandas and datetime libraries as follows. It was much more convenient for me.
import pandas as pd
from datetime import datetime
DATE_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
start_datetime = datetime.strptime('2018-05-18 00:00:00', DATE_TIME_FORMAT)
end_datetime = datetime.strptime('2018-05-23 13:00:00', DATE_TIME_FORMAT)
timedelta_index = pd.date_range(start=start_datetime, end=end_datetime, freq='H').to_series()
for index, value in timedelta_index.iteritems():
dt = index.to_pydatetime()
print(dt)
For iterating over months you need a different recipe, since timedeltas can't express "one month".
from datetime import date
def jump_by_month(start_date, end_date, month_step=1):
current_date = start_date
while current_date < end_date:
yield current_date
carry, new_month = divmod(current_date.month - 1 + month_step, 12)
new_month += 1
current_date = current_date.replace(year=current_date.year + carry,
month=new_month)
(NB: you have to subtract 1 from the month for the modulus operation then add it back to new_month, since months in datetime.dates start at 1.)
Month iteration approach:
def months_between(date_start, date_end):
months = []
# Make sure start_date is smaller than end_date
if date_start > date_end:
tmp = date_start
date_start = date_end
date_end = tmp
tmp_date = date_start
while tmp_date.month <= date_end.month or tmp_date.year < date_end.year:
months.append(tmp_date) # Here you could do for example: months.append(datetime.datetime.strftime(tmp_date, "%b '%y"))
if tmp_date.month == 12: # New year
tmp_date = datetime.date(tmp_date.year + 1, 1, 1)
else:
tmp_date = datetime.date(tmp_date.year, tmp_date.month + 1, 1)
return months
More code but it will do fine dealing with long periods of time checking that the given dates are in order...
Also can use the module arrow
https://arrow.readthedocs.io/en/latest/guide.html#ranges-spans
>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 17, 15)
>>> for r in arrow.Arrow.range('hour', start, end):
... print(repr(r))
...
<Arrow [2013-05-05T12:30:00+00:00]>
<Arrow [2013-05-05T13:30:00+00:00]>
<Arrow [2013-05-05T14:30:00+00:00]>
<Arrow [2013-05-05T15:30:00+00:00]>
<Arrow [2013-05-05T16:30:00+00:00]>
This library provides a handy calendar tool: mxDateTime, that should be enough :)
You should modify this line to make this work correctly:
current_date = current_date.replace(year=current_date.year + carry,month=new_month,day=1)
;)

Categories