2011-04-08 18 views
14

Estoy trabajando con un Arduino y un chip de reloj en tiempo real. El chip compensa los años bisiestos y tal, por lo que siempre tendrá la fecha correcta, pero no maneja el horario de verano, supongo que debido a complicaciones regionales. El reloj me puede dar el día, mes y año (1 base) y el día de la semana (domingo = 0 a sábado = 6).Cálculo del horario de verano a partir de la única fecha

Como necesito comparar las fechas y horas ingresadas por el usuario, necesito saber la fecha y la hora ajustadas para el horario de verano. Si la fecha actual está en horario de verano, simplemente puedo agregar una hora a la hora del reloj y tengo lo que necesito.

Lo difícil es determinar si estoy en horario de verano o no, porque cambia de año en año. Solo me importa que funcione en mi ubicación (Mountain Time). No parece haber ninguna biblioteca de fechas completa para mi plataforma, y ​​creo que eso sería excesivo de todos modos. ¿Hay una fórmula simple para determinar si estoy en horario de verano o no?

Respuesta

30

Esto es engañosamente simple. Hay algunos hechos que nos ayudarán:

  1. En la mayoría de los EE. UU., El horario de verano comienza el segundo domingo de marzo y finaliza el primer domingo de noviembre a las 2:00 AM en ambas ocasiones.
  2. El segundo domingo de marzo siempre será entre el 8 y el 14 inclusive.
  3. El primer domingo de noviembre siempre estará entre el 1 y el 7 inclusive.
  4. La numeración del día de la semana es bastante conveniente porque el día - día de la semana le dará el domingo anterior.

Estos hechos llevan a la siguiente código (C#, pero trivialmente portátil para su plataforma):

public bool IsDST(int day, int month, int dow) 
    { 
     //January, february, and december are out. 
     if (month < 3 || month > 11) { return false; } 
     //April to October are in 
     if (month > 3 && month < 11) { return true; } 
     int previousSunday = day - dow; 
     //In march, we are DST if our previous sunday was on or after the 8th. 
     if (month == 3) { return previousSunday >= 8; } 
     //In november we must be before the first sunday to be dst. 
     //That means the previous sunday must be before the 1st. 
     return previousSunday <= 0; 
    } 

Resulta que ni siquiera necesita saber el año de hacer esto, siempre como puede confiar en el valor del día de la semana.

Escribí una prueba rápida de unidad y verifiqué que este código concuerda con TimeZone.IsDayLightSavingsTime() para todas las fechas de 1800 a 2200. No contabilicé la regla de las 2 a. M., Pero podría hacerlo fácilmente si el día de la semana es domingo y la fecha es entre 8 y 14 (en marzo) o 1 y 7 (en noviembre).

+1

Si ese código realmente concuerda con 'TimeZone.IsDayLightSavingsTime()' para todas las fechas de 1800 a 2006, entonces 'TimeZone.IsDayLightSavingsTime()' está roto. El horario de verano no existía a nivel nacional en los EE. UU. Hasta 1966 (a excepción de ciertos períodos durante las dos guerras mundiales). Y hasta 2007, comenzó el primer domingo de abril y terminó el último domingo de octubre, a excepción de un período en 1974-1975 cuando el DST funcionó durante todo el año. – Anomie

+0

La documentación dice que no es histórico. Solo aplica la regla para la cultura actual. Hay otra instalación para obtener datos históricos precisos. – captncraig

+0

Entonces, al menos, el quebranto está documentado. – Anomie

2

Si bien es fácil calcular si una fecha en particular está en horario de verano para una ubicación particular bajo las reglas actuales, tenga en cuenta que el horario de verano es el capricho de los políticos y podría cambiar en cualquier momento. Tengo un reloj fabricado antes de 2007 que se ajusta automáticamente para el horario de verano, y ahora tengo que cambiarlo cuatro veces veces al año: dos veces cuando se produce el cambio real y dos veces cuando ahora se cambia incorrectamente en las fechas anteriores .

En este caso, es posible que pueda ignorar completamente el horario de verano por el simple hecho de que el usuario ingrese la zona horaria junto con la fecha y la hora. O puede hacer como la mayoría de los dispositivos de consumo y dejar que el usuario ajuste la hora a la zona horaria local dos veces al año.

Pero si realmente necesita manejar el horario de verano y realmente quiere hacer las cosas bien, use el zoneinfo database y asegúrese de que se puede actualizar de alguna manera. Si no puede hacer eso por alguna razón, al menos permita que el usuario anule las reglas. Y si incluso eso es demasiado difícil, al menos dale al usuario la opción de desactivar los ajustes automáticos (a diferencia de mi estúpida alarma).

+0

Tenga en cuenta que estoy trabajando con una plataforma incrustada, por lo que la memoria es un bien escaso. Esa base de datos es varios órdenes de magnitud más grande que mi memoria disponible sin expansión de hardware. Este proyecto es para una sola instalación, por lo que realmente solo me importa que se mantenga precisa dentro de un solo huso horario. Puedo reprogramarlo si las reglas del horario de verano cambian, pero preferiría no reprogramarlo dos veces al año para ajustar manualmente el tiempo si puedo evitarlo. – Muggz

6

Código de Europa central (probado por cada día en el rango de 2.014 a 3.000 años)

public static bool IsDst(int day, int month, int dow) 
    { 
     if (month < 3 || month > 10) return false; 
     if (month > 3 && month < 10) return true; 

     int previousSunday = day - dow; 

     if (month == 3) return previousSunday >= 25; 
     if (month == 10) return previousSunday < 25; 

     return false; // this line never gonna happend 
    } 

función de prueba

static void Main(string[] args) 
    { 
     TimeZoneInfo tzf2 = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time"); 

     var date = new DateTime(2014, 01, 1, 5, 0,0); 
     bool wasSummer = false; 

     while (date <= new DateTime(3000,1,1)) 
     {           
      var dow = (int) date.DayOfWeek; 

      var isDst = IsDst(date.Day, date.Month, dow);    

      DateTime f2 = TimeZoneInfo.ConvertTime(date, tzf2); 
      var isSummer = f2.IsDaylightSavingTime(); 

      if (isSummer != isDst) 
      { 
       Console.WriteLine("ERROR"); 
       Console.WriteLine(date); 
      } 

      if (isSummer != wasSummer) 
      { 
       Console.WriteLine(date.AddDays(-1).ToShortDateString()); 
      } 

      date = date.AddDays(1); 
      wasSummer = isSummer; 
     } 

     Console.ReadKey(); 

}

0

14to marzo y 7 de noviembre son siempre parte de la semana en que se producen los ahorros de luz en los Estados Unidos ... pueden ser el domingo o el sábado o el día de la semana en el medio, pero siempre son parte de esa semana. El siguiente código encontrará el domingo del que esas dos fechas forman parte en el año en el que aparece la fecha en cuestión. A continuación, agrega 2 horas a esta fecha para obtener el momento en que realmente ocurre el horario de verano. luego compara la fecha en cuestión con las fechas de inicio y finalización de ahorro de luz diurna y ajusta el tiempo por la compensación gmt. Este proceso puede funcionar para otras fechas de inicio y finalización en otros países. puede configurar una tabla que tenga todos los códigos de país y código postal con el horario de verano y las fechas de inicio y la compensación de GMT para ambos períodos. las fechas serían el 7, 14, 21 y 28, para los domingos primero a cuarto de un mes. pondría el día máximo para el último domingo del mes, ex 30 de septiembre o 31 de octubre.

segundo domingo de marzo:

cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24 

domingo 1 de noviembre:

cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24 

Ex.

If(now() < cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-5,now()), if(now() < cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24, dateadd("H",-6,now()), dateadd("H",-5,now()))) 

T_SQL ejemplo

CASO CUANDO [date2check] < DATEADD (hh, 2, CAST ('3/14 /' + CAST (DATEPART (aaaa, [date2check]) AS nvarchar (4)) AS datetime) + 1 - DATEPART (w, CAST ('3/14 /' + CAST (DATEPART (aaaa, [date2check]) AS nvarchar (4)) AS datetime))) ENTONCES dateadd (hh, - DST_GMT_TM_ZN_DIFF, [date2check]) CASE ELSE Cuando [date2check] < DATEADD (hh, 2, CAST ('11/7 /' + CAST (DATEPART (aaaa, [date2check]) AS nvarchar (4)) AS de fecha y hora) + 1 - DATEPART (w, CAST ('11/7/'+ CAST (DATEPART (aaaa, [date2check]) AS nvarchar (4)) AS de fecha y hora))) ENTONCES dateadd (hh, - STD_GMT_TM_ZN_DIFF, [date2check]) ELSE dateadd (hh, - DST_GMT_TM_ZN_DIFF, [date2check]) FIN FIN

+2

Agregue un texto que describa lo que está sucediendo aquí. ¡Gracias! –

+0

Debe poner la explicación en la respuesta misma. Además, es posible que desee formatear su último fragmento de código para que sea vagamente legible. –

0

que estoy tratando con este enfoque y creo que es simple y preciso:

// para el primer domingo de marzo como si DOW = 1 para el domingo si (mes == 3 días & &> = 8 & & día < = 14 & & DOW = 1) devolver True

// para el segundo domingo de noviembre como si DoW = 1 para el domingo si (mes == 11 días & &> = 1 & & día < = 7 & & DOW = 1) de vuelta verdad

0

aquí está mi respuesta, y la bienvenida a cualquier corrección. Se supone que los años son entre 2000 y 2099 inclusive. Más detalles disponibles a través del enlace de referencia.

int timezone = 0; // Set to correct initial value depending on where you are (or via GPS if you like). 
    // Calculate day of week for Daylight savings time. 
    int day_of_week = (day_of_month + int(2.6 * (((month + 12 - 3) % 12) + 1) - 0.2) - 40 + 
       (month < 3 ? year-1 : year) + int((month < 3 ? year-1 : year)/4) + 5) % 7; 
    // Adjust timezone based on Daylight savings time for northern hemisphere, USA 
    if ((month > 3 && month < 11) || 
     (month == 3 && day_of_month >= 8 && day_of_week == 0 && hour >= 2) || // DST starts 2nd Sunday of March; 2am 
     (month == 11 && day_of_month < 8 && day_of_week > 0) || 
     (month == 11 && day_of_month < 8 && day_of_week == 0 && hour < 2)) { // DST ends 1st Sunday of November; 2am 
    timezone++; 
    } 

día de la semana de referencia de cálculo: How to determine the day of the week, given the month, day and year
referencia de la prueba DST es a través de este artículo como respondidas por captncraig y mi propio razonamiento y la interpretación de su respuesta.

Cuestiones relacionadas