python round up
Python-Ceil a datetime para el próximo cuarto de hora (4)
¡Éste tiene en cuenta los microsegundos!
import math
def ceil_dt(dt):
# how many secs have passed this hour
nsecs = dt.minute*60 + dt.second + dt.microsecond*1e-6
# number of seconds to next quarter hour mark
# Non-analytic (brute force is fun) way:
# delta = next(x for x in xrange(0,3601,900) if x>=nsecs) - nsecs
# analytic way:
delta = math.ceil(nsecs / 900) * 900 - nsecs
#time + number of seconds to quarter hour mark.
return dt + datetime.timedelta(seconds=delta)
t1 = datetime.datetime(2017, 3, 6, 7, 0)
assert ceil_dt(t1) == t1
t2 = datetime.datetime(2017, 3, 6, 7, 1)
assert ceil_dt(t2) == datetime.datetime(2017, 3, 6, 7, 15)
t3 = datetime.datetime(2017, 3, 6, 7, 15)
assert ceil_dt(t3) == t3
t4 = datetime.datetime(2017, 3, 6, 7, 16)
assert ceil_dt(t4) == datetime.datetime(2017, 3, 6, 7, 30)
t5 = datetime.datetime(2017, 3, 6, 7, 30)
assert ceil_dt(t5) == t5
t6 = datetime.datetime(2017, 3, 6, 7, 31)
assert ceil_dt(t6) == datetime.datetime(2017, 3, 6, 7, 45)
t7 = datetime.datetime(2017, 3, 6, 7, 45)
assert ceil_dt(t7) == t7
t8 = datetime.datetime(2017, 3, 6, 7, 46)
assert ceil_dt(t8) == datetime.datetime(2017, 3, 6, 8, 0)
Explicación del delta
:
- 900 segundos son 15 minutos (un cuarto de hora sin segundos de salto, lo cual no creo que datetime maneje ... )
-
nsecs / 900
es el número de trozos de cuarto de hora que han transcurrido. Tomando elceil
de este número se redondea la cantidad de trozos de cuarto de hora. - Multiplique el número de segmentos de cuarto de hora por 900 para calcular cuántos segundos han transcurrido desde el comienzo de la hora después del "redondeo".
Imaginemos esta fecha y hora
>>> import datetime
>>> dt = datetime.datetime(2012, 10, 25, 17, 32, 16)
Me gustaría ceñirlo al siguiente cuarto de hora para obtener
datetime.datetime(2012, 10, 25, 17, 45)
Me imagino algo como
>>> quarter = datetime.timedelta(minutes=15)
>>> import math
>>> ceiled_dt = math.ceil(dt / quarter) * quarter
Pero claro que esto no funciona.
Solo necesita calcular los minutos correctos y agregarlos en el objeto datetime después de configurar los minutos, segundos a cero
import datetime
def quarter_datetime(dt):
minute = (dt.minute//15+1)*15
return dt.replace(minute=0, second=0)+datetime.timedelta(minutes=minute)
for minute in [12, 22, 35, 52]:
print quarter_datetime(datetime.datetime(2012, 10, 25, 17, minute, 16))
Funciona para todos los casos:
2012-10-25 17:15:00
2012-10-25 17:30:00
2012-10-25 17:45:00
2012-10-25 18:00:00
@Mark Dickinson sugirió la mejor fórmula hasta ahora:
def ceil_dt(dt, delta):
return dt + (datetime.min - dt) % delta
En Python 3, para un delta de tiempo arbitrario (no solo 15 minutos):
#!/usr/bin/env python3
import math
from datetime import datetime, timedelta
def ceil_dt(dt, delta):
return datetime.min + math.ceil((dt - datetime.min) / delta) * delta
print(ceil_dt(datetime(2012, 10, 25, 17, 32, 16), timedelta(minutes=15)))
# -> 2012-10-25 17:45:00
Para evitar flotadores intermedios, divmod()
podría ser usado:
def ceil_dt(dt, delta):
q, r = divmod(dt - datetime.min, delta)
return (datetime.min + (q + 1)*delta) if r else dt
Ejemplo:
>>> ceil_dt(datetime(2012, 10, 25, 17, 32, 16), timedelta(minutes=15))
datetime.datetime(2012, 10, 25, 17, 45)
>>> ceil_dt(datetime.min, datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0)
>>> ceil_dt(datetime.min, 2*datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0)
>>> ceil_dt(datetime.max, datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
>>> ceil_dt(datetime.max, 2*datetime.resolution)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ceil_dt
OverflowError: date value out of range
>>> ceil_dt(datetime.min+datetime.resolution, datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0, 0, 1)
>>> ceil_dt(datetime.min+datetime.resolution, 2*datetime.resolution)
datetime.datetime(1, 1, 1, 0, 0, 0, 2)
>>> ceil_dt(datetime.max-datetime.resolution, datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998)
>>> ceil_dt(datetime.max-datetime.resolution, 2*datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998)
>>> ceil_dt(datetime.max-2*datetime.resolution, datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999997)
>>> ceil_dt(datetime.max-2*datetime.resolution, 2*datetime.resolution)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998)
>>> ceil_dt(datetime.max-timedelta(1), datetime.resolution)
datetime.datetime(9999, 12, 30, 23, 59, 59, 999999)
>>> ceil_dt(datetime.max-timedelta(1), 2*datetime.resolution)
datetime.datetime(9999, 12, 31, 0, 0)
>>> ceil_dt(datetime.min, datetime.max-datetime.min)
datetime.datetime(1, 1, 1, 0, 0)
>>> ceil_dt(datetime.max, datetime.max-datetime.min)
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)
def ceil(dt):
if dt.minute % 15 or dt.second:
return dt + datetime.timedelta(minutes = 15 - dt.minute % 15,
seconds = -(dt.second % 60))
else:
return dt
Esto te da:
>>> ceil(datetime.datetime(2012,10,25, 17,45))
datetime.datetime(2012, 10, 25, 17, 45)
>>> ceil(datetime.datetime(2012,10,25, 17,45,1))
datetime.datetime(2012, 10, 25, 18, 0)
>>> ceil(datetime.datetime(2012,12,31,23,59,0))
datetime.datetime(2013,1,1,0,0)