Marcar horas de horario de verano (DST) en la columna de fecha y hora de Pandas

Creé un dataframe de fechas por hora, y ahora me gustaría crear una columna que marque si cada fila (hora) está en horario de verano o no. Por ejemplo, en las horas de verano, la bandera debe == 1, y en las horas de invierno, la bandera debe == 0.

# Localized dates dataframe dates = pd.DataFrame(data=pd.date_range('2018-1-1', '2019-1-1', freq='h', tz='America/Denver'), columns=['date_time']) # My failed attempt to create the flag column dates['dst_flag'] = np.where(dates['date_time'].dt.daylight_saving_time == True, 1, 0) 

Hay un bonito enlace en los comentarios que al menos te permite hacer esto manualmente. AFAIK, no hay una forma vectorizada de hacer esto.

 import pandas as pd import numpy as np from pytz import timezone # Generate data (as opposed to index) date_range = pd.to_datetime(pd.date_range('1/1/2018', '1/1/2019', freq='h', tz='America/Denver')) date_range = [date for date in date_range] # Localized dates dataframe df = pd.DataFrame(data=date_range, columns=['date_time']) # Map transition times to year for some efficiency gain tz = timezone('America/Denver') transition_times = tz._utc_transition_times[1:] transition_times = [t.astimezone(tz) for t in transition_times] transition_times_by_year = {} for start_time, stop_time in zip(transition_times[::2], transition_times[1::2]): year = start_time.year transition_times_by_year[year] = [start_time, stop_time] # If the date is in DST, mark true, else false def mark_dst(dates): for date in dates: start_dst, stop_dst = transition_times_by_year[date.year] yield start_dst <= date <= stop_dst df['dst_flag'] = [dst_flag for dst_flag in mark_dst(df['date_time'])] # Do a quick sanity check to make sure we did this correctly for year 2018 dst_start = df[df['dst_flag'] == True]['date_time'][0] # First dst time 2018 dst_end = df[df['dst_flag'] == True]['date_time'][-1] # Last dst time 2018 print(dst_start) print(dst_end) 

esto produce:

 2018-03-11 07:00:00-06:00 2018-11-04 06:00:00-07:00 

lo que es probablemente correcto. No hice las conversiones UTC a mano ni nada para comprobar que las horas son exactamente las adecuadas para la zona horaria dada. Puede al menos verificar que las fechas son correctas con una búsqueda rápida en Google.

Algunas trampas:

  1. pd.date_range genera un índice , no datos. Cambié un poco su código original para que sea información en lugar del índice. Supongo que ya tienes los datos.

  2. Hay algo ridículo en cómo se estructura tz._utc_transition_times . Es el inicio / parada de los tiempos de transición del horario de verano, pero hay algunas cosas tontas en las primeras fechas. Sin embargo, debería ser bueno desde 1965 en adelante. Si está haciendo fechas anteriores a ese cambio, tz._utc_transition_times[1:] a tz._utc_transition_times . Tenga en cuenta que no todos los años anteriores a 1965 están presentes.

  3. tz._utc_transition_times es "Python private". Es posible que cambie sin previo aviso o notificación, y puede o no funcionar para versiones futuras o pasadas de pytz . Estoy usando pytz verion 2017.3. Le recomiendo que ejecute este código para asegurarse de que la salida coincida, y si no, asegúrese de usar la versión 2017.3.

HTH, buena suerte con tu problema de investigación / regresión!

Esto es lo que terminé haciendo, y funciona para mis propósitos:

 import pandas as pd import pytz # Create dates table and flag Daylight Saving Time dates dates = pd.DataFrame(data=pd.date_range('2018-1-1', '2018-12-31-23', freq='h'), columns=['date_time']) # Create a list of start and end dates for DST in each year, in UTC time dst_changes_utc = pytz.timezone('America/Denver')._utc_transition_times[1:] # Convert to local times from UTC times and then remove timezone information dst_changes = [pd.Timestamp(i).tz_localize('UTC').tz_convert('America/Denver').tz_localize(None) for i in dst_changes_utc] flag_list = [] for index, row in dates['date_time'].iteritems(): # Isolate the start and end dates for DST in each year dst_dates_in_year = [date for date in dst_changes if date.year == row.year] spring = dst_dates_in_year[0] fall = dst_dates_in_year[1] if (row >= spring) & (row < fall): flag = 1 else: flag = 0 flag_list.append(flag) print(flag_list) dates['dst_flag'] = flag_list del(flag_list)