tiempo sumar semana restar obtener horas formato fechas fecha entre diferencia con clase python datetime iteration

sumar - Iterando a través de un rango de fechas en Python



restar tiempo python (19)

¿Por qué hay dos iteraciones anidadas? Para mí produce la misma lista de datos con una sola iteración:

for single_date in (start_date + timedelta(n) for n in range(day_count)): print ...

Y ninguna lista se almacena, solo se itera un generador. También el "si" en el generador parece ser innecesario.

Después de todo, una secuencia lineal solo debe requerir un iterador, no dos.

Actualización después de la discusión con John Machin:

Quizás la solución más elegante es usar una función de generador para ocultar / abstraer completamente la iteración en el rango de fechas

from datetime import timedelta, date def daterange(start_date, end_date): for n in range(int ((end_date - start_date).days)): yield start_date + timedelta(n) start_date = date(2013, 1, 1) end_date = date(2015, 6, 2) for single_date in daterange(start_date, end_date): print single_date.strftime("%Y-%m-%d")

NB: Para mantener la coherencia con la función de range() incorporada, esta iteración se detiene antes de llegar a la fecha end_date . Entonces, para la iteración inclusiva, use el día siguiente, como lo haría con range() .

Tengo el siguiente código para hacer esto, pero ¿cómo puedo hacerlo mejor? En este momento creo que es mejor que los bucles anidados, pero comienza a tener Perl-one-linerish cuando tienes un generador en una lista de comprensión.

day_count = (end_date - start_date).days + 1 for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]: print strftime("%Y-%m-%d", single_date.timetuple())

Notas

  • En realidad no estoy usando esto para imprimir. Eso es sólo para fines de demostración.
  • Las variables start_date y end_date son objetos datetime.date porque no necesito las marcas de tiempo. (Se van a utilizar para generar un informe).

Salida de muestra

Para una fecha de inicio de 2009-05-30 y una fecha de finalización de 2009-06-09 :

2009-05-30 2009-05-31 2009-06-01 2009-06-02 2009-06-03 2009-06-04 2009-06-05 2009-06-06 2009-06-07 2009-06-08 2009-06-09


¿Por qué no intentarlo?

import datetime as dt start_date = dt.datetime(2012, 12,1) end_date = dt.datetime(2012, 12,5) total_days = (end_date - start_date).days + 1 #inclusive 5 days for day_number in range(total_days): current_date = (start_date + dt.timedelta(days = day_number)).date() print current_date


¿Qué pasa con lo siguiente para hacer un rango incrementado por días:

for d in map( lambda x: startDate+datetime.timedelta(days=x), xrange( (stopDate-startDate).days ) ): # Do stuff here

  • startDate y stopDate son objetos datetime.date

Para una versión genérica:

for d in map( lambda x: startTime+x*stepTime, xrange( (stopTime-startTime).total_seconds() / stepTime.total_seconds() ) ): # Do stuff here

  • startTime y stopTime son objetos datetime.date o datetime.datetime (ambos deben ser del mismo tipo)
  • stepTime es un objeto timedelta

Tenga en cuenta que .total_seconds () solo se admite después de python 2.7 Si está atascado con una versión anterior, puede escribir su propia función:

def total_seconds( td ): return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6


Aquí está el código para una función de rango de fechas general, similar a la respuesta de Ber, pero más flexible:

def count_timedelta(delta, step, seconds_in_interval): """Helper function for iterate. Finds the number of intervals in the timedelta.""" return int(delta.total_seconds() / (seconds_in_interval * step)) def range_dt(start, end, step=1, interval=''day''): """Iterate over datetimes or dates, similar to builtin range.""" intervals = functools.partial(count_timedelta, (end - start), step) if interval == ''week'': for i in range(intervals(3600 * 24 * 7)): yield start + datetime.timedelta(weeks=i) * step elif interval == ''day'': for i in range(intervals(3600 * 24)): yield start + datetime.timedelta(days=i) * step elif interval == ''hour'': for i in range(intervals(3600)): yield start + datetime.timedelta(hours=i) * step elif interval == ''minute'': for i in range(intervals(60)): yield start + datetime.timedelta(minutes=i) * step elif interval == ''second'': for i in range(intervals(1)): yield start + datetime.timedelta(seconds=i) * step elif interval == ''millisecond'': for i in range(intervals(1 / 1000)): yield start + datetime.timedelta(milliseconds=i) * step elif interval == ''microsecond'': for i in range(intervals(1e-6)): yield start + datetime.timedelta(microseconds=i) * step else: raise AttributeError("Interval must be ''week'', ''day'', ''hour'' ''second'', / ''microsecond'' or ''millisecond''.")


Esta es la solución más legible por humanos que se me ocurre.

import datetime def daterange(start, end, step=datetime.timedelta(1)): curr = start while curr < end: yield curr curr += step


Esta función tiene algunas características adicionales:

  • puede pasar una cadena que coincida con DATE_FORMAT para el inicio o el final y se convierte en un objeto de fecha
  • Puede pasar un objeto de fecha para inicio o fin
  • Comprobación de errores en caso de que el final sea anterior al inicio.

    import datetime from datetime import timedelta DATE_FORMAT = ''%Y/%m/%d'' def daterange(start, end): def convert(date): try: date = datetime.datetime.strptime(date, DATE_FORMAT) return date.date() except TypeError: return date def get_date(n): return datetime.datetime.strftime(convert(start) + timedelta(days=n), DATE_FORMAT) days = (convert(end) - convert(start)).days if days <= 0: raise ValueError(''The start date must be before the end date.'') for n in range(0, days): yield get_date(n) start = ''2014/12/1'' end = ''2014/12/31'' print list(daterange(start, end)) start_ = datetime.date.today() end = ''2015/12/1'' print list(daterange(start, end))


Esto podría ser más claro:

d = start_date delta = datetime.timedelta(days=1) while d <= end_date: print d.strftime("%Y-%m-%d") d += delta


La función arange de Numpy se puede aplicar a las fechas:

import numpy as np from datetime import datetime, timedelta d0 = datetime(2009, 1,1) d1 = datetime(2010, 1,1) dt = timedelta(days = 1) dates = np.arange(d0, d1, dt).astype(datetime)

El uso de astype es convertir de numpy.datetime64 a una matriz de objetos datetime.datetime .


No puedo creer * esta pregunta ha existido durante 9 años sin que nadie sugiera una función recursiva simple:

from datetime import datetime, timedelta def walk_days(start_date, end_date): if start_date <= end_date: print(start_date.strftime("%Y-%m-%d")) next_date = start_date + timedelta(days=1) walk_days(next_date, end_date) #demo start_date = datetime(2009, 5, 30) end_date = datetime(2009, 6, 9) walk_days(start_date, end_date)

Salida:

2009-05-30 2009-05-31 2009-06-01 2009-06-02 2009-06-03 2009-06-04 2009-06-05 2009-06-06 2009-06-07 2009-06-08 2009-06-09

Edición: * Ahora puedo creerlo. Ver ¿Python optimiza la recursión de la cola? . Gracias Tim


Pandas es ideal para series de tiempo en general y tiene soporte directo para rangos de fechas.

import pandas as pd daterange = pd.date_range(start_date, end_date)

A continuación, puede recorrer el daterange para imprimir la fecha:

for single_date in daterange: print (single_date.strftime("%Y-%m-%d"))

También tiene muchas opciones para hacer la vida más fácil. Por ejemplo, si solo deseaba entre semana, solo intercambiaría bdate_range. Consulte http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps

El poder de Pandas es realmente sus marcos de datos, que admiten operaciones vectorizadas (muy parecidas a las de números) que hacen que las operaciones en grandes cantidades de datos sean muy rápidas y fáciles.

EDITAR: También puede omitir completamente el bucle for y simplemente imprimirlo directamente, lo que es más fácil y más eficiente:

print(daterange)


Puede generar una serie de fechas entre dos fechas utilizando la biblioteca de pandas de manera simple y confiable

import pandas as pd print pd.date_range(start=''1/1/2010'', end=''1/08/2018'', freq=''M'')

Puede cambiar la frecuencia de generación de fechas configurando las frecuencias como D, M, Q, Y (diaria, mensual, trimestral, anual)


Tengo un problema similar, pero necesito iterar mensualmente en lugar de diario.

Esta es mi solucion

import calendar from datetime import datetime, timedelta def days_in_month(dt): return calendar.monthrange(dt.year, dt.month)[1] def monthly_range(dt_start, dt_end): forward = dt_end >= dt_start finish = False dt = dt_start while not finish: yield dt.date() if forward: days = days_in_month(dt) dt = dt + timedelta(days=days) finish = dt > dt_end else: _tmp_dt = dt.replace(day=1) - timedelta(days=1) dt = (_tmp_dt.replace(day=dt.day)) finish = dt < dt_end

Ejemplo 1

date_start = datetime(2016, 6, 1) date_end = datetime(2017, 1, 1) for p in monthly_range(date_start, date_end): print(p)

Salida

2016-06-01 2016-07-01 2016-08-01 2016-09-01 2016-10-01 2016-11-01 2016-12-01 2017-01-01

Ejemplo # 2

date_start = datetime(2017, 1, 1) date_end = datetime(2016, 6, 1) for p in monthly_range(date_start, date_end): print(p)

Salida

2017-01-01 2016-12-01 2016-11-01 2016-10-01 2016-09-01 2016-08-01 2016-07-01 2016-06-01


Un enfoque ligeramente diferente de los pasos reversibles al almacenar los argumentos de range en una tupla.

def date_range(start, stop, step=1, inclusive=False): day_count = (stop - start).days if inclusive: day_count += 1 if step > 0: range_args = (0, day_count, step) elif step < 0: range_args = (day_count - 1, -1, step) else: raise ValueError("date_range(): step arg must be non-zero") for i in range(*range_args): yield start + timedelta(days=i)


Usa la librería dateutil :

from datetime import date from dateutil.rrule import rrule, DAILY a = date(2009, 5, 30) b = date(2009, 6, 9) for dt in rrule(DAILY, dtstart=a, until=b): print dt.strftime("%Y-%m-%d")

Esta biblioteca de Python tiene muchas más características avanzadas, algunas muy útiles, como relative delta , y se implementa como un solo archivo (módulo) que se incluye fácilmente en un proyecto.


Mostrar los últimos n días a partir de hoy:

import datetime for i in range(0, 100): print((datetime.date.today() + datetime.timedelta(i)).isoformat())

Salida:

2016-06-29 2016-06-30 2016-07-01 2016-07-02 2016-07-03 2016-07-04


for i in range(16): print datetime.date.today() + datetime.timedelta(days=i)


> pip install DateTimeRange from datetimerange import DateTimeRange def dateRange(start, end, step): rangeList = [] time_range = DateTimeRange(start, end) for value in time_range.range(datetime.timedelta(days=step)): rangeList.append(value.strftime(''%m/%d/%Y'')) return rangeList dateRange("2018-09-07", "2018-12-25", 7) Out[92]: [''09/07/2018'', ''09/14/2018'', ''09/21/2018'', ''09/28/2018'', ''10/05/2018'', ''10/12/2018'', ''10/19/2018'', ''10/26/2018'', ''11/02/2018'', ''11/09/2018'', ''11/16/2018'', ''11/23/2018'', ''11/30/2018'', ''12/07/2018'', ''12/14/2018'', ''12/21/2018'']


import datetime def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False): # inclusive=False to behave like range by default if step.days > 0: while start < stop: yield start start = start + step # not +=! don''t modify object passed in if it''s mutable # since this function is not restricted to # only types from datetime module elif step.days < 0: while start > stop: yield start start = start + step if inclusive and start == stop: yield start # ... for date in daterange(start_date, end_date, inclusive=True): print strftime("%Y-%m-%d", date.timetuple())

Esta función hace más de lo que estrictamente requiere, apoyando el paso negativo, etc. Mientras que day_count su lógica de rango, entonces no necesita el day_count separado y, lo que es más importante, el código se vuelve más fácil de leer al llamar a la función desde múltiples lugares.


import datetime def daterange(start, stop, step_days=1): current = start step = datetime.timedelta(step_days) if step_days > 0: while current < stop: yield current current += step elif step_days < 0: while current > stop: yield current current += step else: raise ValueError("daterange() step_days argument must not be zero") if __name__ == "__main__": from pprint import pprint as pp lo = datetime.date(2008, 12, 27) hi = datetime.date(2009, 1, 5) pp(list(daterange(lo, hi))) pp(list(daterange(hi, lo, -1))) pp(list(daterange(lo, hi, 7))) pp(list(daterange(hi, lo, -7))) assert not list(daterange(lo, hi, -1)) assert not list(daterange(hi, lo)) assert not list(daterange(lo, hi, -7)) assert not list(daterange(hi, lo, 7))