2010-08-12 33 views
61

Tengo un objeto datetime producido usando strptime().Cómo redondear el minuto de un objeto datetime python

>>> tm 
datetime.datetime(2010, 6, 10, 3, 56, 23) 

Lo que tengo que hacer es redondear el minuto al décimo minuto más cercano. Lo que he estado haciendo hasta este momento fue tomar el valor de los minutos y usar round() en él.

min = round(tm.minute, -1) 

Sin embargo, al igual que con el ejemplo anterior, se da un tiempo no válido cuando el valor de minutos es mayor que 56. es decir .: 3:60

¿Qué es una mejor manera de hacer esto? ¿Datetime apoya esto?

Respuesta

3

si no desea utilizar la condición, puede utilizar modulo operador:

minutes = int(round(tm.minute, -1)) % 60 

ACTUALIZACIÓN

querías que algo como esto?

def timeround10(dt): 
    a, b = divmod(round(dt.minute, -1), 60) 
    return '%i:%02i' % ((dt.hour + a) % 24, b) 

timeround10(datetime.datetime(2010, 1, 1, 0, 56, 0)) # 0:56 
# -> 1:00 

timeround10(datetime.datetime(2010, 1, 1, 23, 56, 0)) # 23:56 
# -> 0:00 

.. si desea que el resultado sea una cadena. para obtener el resultado datetime, es mejor usar timedelta - ver otras respuestas;)

+0

Ah, pero entonces el problema aquí es que la hora debe aumentar también –

+1

@Lucas Manco - Mi solución también funciona bien y creo que tiene más sentido. – Omnifarious

93

Esto obtendrá el 'piso' de un objeto datetime almacenado en tm redondeado a la marca de 10 minutos antes de tm.

tm = tm - datetime.timedelta(minutes=tm.minute % 10, 
          seconds=tm.second, 
          microseconds=tm.microsecond) 

Si desea redondeo clásico de la marca de 10 minuto más cercano, hacer esto:

discard = datetime.timedelta(minutes=tm.minute % 10, 
          seconds=tm.second, 
          microseconds=tm.microsecond) 
tm -= discard 
if discard >= datetime.timedelta(minutes=5): 
    tm += datetime.timedelta(minutes=10) 

o esto:

tm += datetime.timedelta(minutes=5) 
tm -= datetime.timedelta(minutes=tm.minute % 10, 
         seconds=tm.second, 
         microseconds=tm.microsecond) 
68

Función general para redondear una fecha y hora en las vueltas de tiempo en segundos:

def roundTime(dt=None, roundTo=60): 
    """Round a datetime object to any time laps in seconds 
    dt : datetime.datetime object, default now. 
    roundTo : Closest number of seconds to round to, default 1 minute. 
    Author: Thierry Husson 2012 - Use it as you want but don't blame me. 
    """ 
    if dt == None : dt = datetime.datetime.now() 
    seconds = (dt.replace(tzinfo=None) - dt.min).seconds 
    rounding = (seconds+roundTo/2) // roundTo * roundTo 
    return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond) 

Las muestras con 1 hora de redondeo & 30 minutos de redondeo:

print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60) 
2013-01-01 00:00:00 

print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=30*60) 
2012-12-31 23:30:00 
+5

Desafortunadamente, esto no funciona con tz-aware datetime. Uno debería usar 'dt.replace (hour = 0, minute = 0, second = 0)' en vez de 'dt.min'. – skoval00

+1

@ skoval00 + druska Editado siguiendo sus consejos para admitir tz-aware datetime. ¡Gracias! –

+0

Gracias @ skoval00 - me tomó un tiempo averiguar por qué la función no funcionaba con mis datos – mmeclimate

0
def get_rounded_datetime(self, dt, freq, nearest_type='inf'): 

    if freq.lower() == '1h': 
     round_to = 3600 
    elif freq.lower() == '3h': 
     round_to = 3 * 3600 
    elif freq.lower() == '6h': 
     round_to = 6 * 3600 
    else: 
     raise NotImplementedError("Freq %s is not handled yet" % freq) 

    # // is a floor division, not a comment on following line: 
    seconds_from_midnight = dt.hour * 3600 + dt.minute * 60 + dt.second 
    if nearest_type == 'inf': 
     rounded_sec = int(seconds_from_midnight/round_to) * round_to 
    elif nearest_type == 'sup': 
     rounded_sec = (int(seconds_from_midnight/round_to) + 1) * round_to 
    else: 
     raise IllegalArgumentException("nearest_type should be 'inf' or 'sup'") 

    dt_midnight = datetime.datetime(dt.year, dt.month, dt.day) 

    return dt_midnight + datetime.timedelta(0, rounded_sec) 
9

De la mejor respuesta que modificado para una versión adaptada usando sólo los objetos de fecha y hora, esto evita tener que hacer la conversión a segundo y hace que el código de llamada más legible:

def roundTime(dt=None, dateDelta=datetime.timedelta(minutes=1)): 
    """Round a datetime object to a multiple of a timedelta 
    dt : datetime.datetime object, default now. 
    dateDelta : timedelta object, we round to a multiple of this, default 1 minute. 
    Author: Thierry Husson 2012 - Use it as you want but don't blame me. 
      Stijn Nevens 2014 - Changed to use only datetime objects as variables 
    """ 
    roundTo = dateDelta.total_seconds() 

    if dt == None : dt = datetime.datetime.now() 
    seconds = (dt - dt.min).seconds 
    # // is a floor division, not a comment on following line: 
    rounding = (seconds+roundTo/2) // roundTo * roundTo 
    return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond) 

muestras con 1 hora de redondeo & 15 minutos de redondeo:

print roundTime(datetime.datetime(2012,12,31,23,44,59),datetime.timedelta(hour=1)) 
2013-01-01 00:00:00 

print roundTime(datetime.datetime(2012,12,31,23,44,49),datetime.timedelta(minutes=15)) 
2012-12-31 23:30:00 
+0

También no es bueno: 'print roundTime (datetime.datetime (2012,12,20,23,44,49), datetime.timedelta (days = 15))' '2012-12-20 00: 00: 00' while 'print roundTime (datetime.datetime (2012,12,21,23,44,49), datetime.timedelta (días = 15))' '2012-12-21 00: 00: 00' – CPBL

+1

Seguimiento de arriba: simplemente señalando que no funciona para deltas de tiempo arbitrarios, por ejemplo aquellos más de 1 día. Esta pregunta se trata de minutos de redondeo, por lo que es una restricción adecuada, pero podría ser más clara en la forma en que se escribe el código. – CPBL

0

Basado en Stijn Nevens y modificado para el uso de Django para redondear la hora actual a los 15 minutos más cercanos.

from datetime import date, timedelta, datetime, time 

    def roundTime(dt=None, dateDelta=timedelta(minutes=1)): 

     roundTo = dateDelta.total_seconds() 

     if dt == None : dt = datetime.now() 
     seconds = (dt - dt.min).seconds 
     # // is a floor division, not a comment on following line: 
     rounding = (seconds+roundTo/2) // roundTo * roundTo 
     return dt + timedelta(0,rounding-seconds,-dt.microsecond) 

    dt = roundTime(datetime.now(),timedelta(minutes=15)).strftime('%H:%M:%S') 

dt = 11:45:00 

si necesita fecha y tiempo completo sólo quitar el .strftime('%H:%M:%S')

5

Solía ​​código Stijn Nevens con gran efecto (gracias Stijn) y tenía un pequeño complemento para compartir. Redondeando, bajando y redondeando al más cercano.

def round_time(dt=None, date_delta=timedelta(minutes=1), to='average'): 
    """ 
    Round a datetime object to a multiple of a timedelta 
    dt : datetime.datetime object, default now. 
    dateDelta : timedelta object, we round to a multiple of this, default 1 minute. 
    from: http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python 
    """ 
    round_to = date_delta.total_seconds() 

    if dt is None: 
     dt = datetime.now() 
    seconds = (dt - dt.min).seconds 

    if to == 'up': 
     # // is a floor division, not a comment on following line (like in javascript): 
     rounding = (seconds + round_to) // round_to * round_to 
    elif to == 'down': 
     rounding = seconds // round_to * round_to 
    else: 
     rounding = (seconds + round_to/2) // round_to * round_to 

return dt + timedelta(0, rounding - seconds, -dt.microsecond) 
+0

Esto me ayudó. Quiero agregar que si se usa en PySpark, para analizar el tiempo de fecha como una cadena en lugar de un objeto de fecha y hora. – Max

+1

El redondeo 'hacia arriba' quizás no está haciendo lo que la mayoría de la gente espera. Debería redondear hasta la próxima fecha_delta incluso si dt no necesitara redondeo: p. 15: 30: 00,000 con round_to = 60 se convertiría en 15: 31: 00.000 – spinxz

0

No es el mejor para la velocidad cuando se detecta la excepción, sin embargo, esto funcionaría.

def _minute10(dt=datetime.utcnow()): 
    try: 
     return dt.replace(minute=round(dt.minute, -1)) 
    except ValueError: 
     return dt.replace(minute=0) + timedelta(hours=1) 

sincronizaciones

%timeit _minute10(datetime(2016, 12, 31, 23, 55)) 
100000 loops, best of 3: 5.12 µs per loop 

%timeit _minute10(datetime(2016, 12, 31, 23, 31)) 
100000 loops, best of 3: 2.21 µs per loop 
Cuestiones relacionadas