2011-11-02 23 views
10

ACTUALIZACIÓN: he agregado una solución al final de esta pregunta.¿Cuál es la manera más elegante de obtener el final del día (fecha y hora)?

Actualmente estoy escribiendo un código de informe que permite a los usuarios especificar opcionalmente un rango de fechas. La forma en que funciona (simplificado) es:

  • Un usuario (opcionalmente) especifica un año.
  • Un usuario (opcionalmente) especifica un mes.
  • Un usuario (opcionalmente) especifica un día.

He aquí un fragmento de código, junto con los comentarios que describen lo que había como hacer:

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. 

Estoy tratando de encontrar la manera más elegante de escribir el código anterior - I He estado tratando de encontrar una manera de hacerlo con timedelta, pero parece que no puedo resolverlo. Cualquier consejo sería apreciado.

Gracias.

EDITAR, SOLUCIÓN agregó:

Después de ver algunas de las respuestas aquí, y en realidad no encontrar nada muy elegante, hice un poco de hurgar la biblioteca estándar, y encontré mi solución actual (que me gusta bastante bien): dateutil.

Así es como yo implementé:

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 ... 

Lo que me gusta de esta aplicación, es dateutil.relativedata.relativedata del pitón que funciona muy bien en casos extremos. Hace que los días/meses/años sean correctos. Si tengo month = 12 y do datos relativos (meses = 1), aumentará el año y establecerá el mes en 1 (funciona bien).

También: en la implementación anterior, si el usuario no especifica ninguna de las fechas opcionales (año, mes o día), recurriremos a un buen valor predeterminado (start_time = esta mañana, stop_time = esta noche), De manera predeterminada, haremos cosas solo para el día actual.

Gracias a todos por sus respuestas, fueron útiles en mi investigación.

+4

El fin de cualquier día dado siempre sería '23: 59: 59.999' –

+0

@MarcB Eso no es cierto durante los segundos intercalares, que son a las 23:59:60. –

+1

Cierto, pero luego es bastante fácil agregar cheques para junio/diciembre: son lo suficientemente raros como para no preocupar a la mayoría de los sistemas. O ajuste la lógica de fecha para que sea '

Respuesta

4

El uso de dict.get puede simplificar su código. Es un poco más limpio que usar datetime.replace y timedelta objetos.

aquí hay algo para empezar:

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) 
+0

Esto fue útil, sin embargo, después de jugar, Encontré dateutil.relativedelta para ser más elegante. Actualicé mi respuesta con ese método en caso de que tuviera algún comentario. – rdegges

4

Para establecer el stop_time, avanzar start_time un año, mes o día, según proceda, a continuación, reste un 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) 
1
today = datetime.date.today() 
    begintime = today.strftime("%Y-%m-%d 00:00:00") 
    endtime = today.strftime("%Y-%m-%d 23:59:59") 
Cuestiones relacionadas