2010-08-10 37 views
8

Necesito de alguna manera determinar si algún valor TDateTime está dentro del rango de horario de verano para mi zona horaria o no (en C# lo mismo hace el método DateTime.IsDaylightSavingTime()).Delphi - equivalente al método C# DateTime.IsDaylightSavingTime() necesario

Sé en Delphi que no hay una función similar, porque Delphi TDateTime no contiene información sobre la zona horaria, pero supongo que hay alguna forma de hacerlo con la API de Win32.

He examinado las funciones de Win32 API GetTimeZoneInformation y GetTimeZoneInformationForYear, pero no entiendo muy bien cómo usarlas, así que me gustaría pedirle ayuda. Gracias de antemano por cualquier consejo.

Editar:

Ejemplo:

En mi zona horaria (Central Europeo) el horario de verano comenzó este año en 28a marzo a las 2 de la mañana y termina el 31 de octubre de 2010 a las 03 a.m..

Necesito una función con cabecera:

function IsDaylightSavingTime(input: TDateTime): boolean; 

que devolverá true si la fecha de entrada está entre el 28 de marzo de 2010 2:00 y 31 de octubre 2010 03:00 false y en caso contrario.

(El ejemplo es sólo para el año 2010, pero que había necesidad de que funcione para todos los años.)

Una vez más, sé que la información guardada en TDateTime por sí sola no es suficiente, pero creo que con alguna función API de Win32 que debería ser capaz de obtener, por ejemplo información sobre la zona horaria actual desde la configuración de Windows.

Respuesta

3

No es tan fácil como parece, ya que:

1) La fecha de conmutación entre DST y la hora estándar no es lo mismo para todos los países

2) La fecha de conmutación entre DST y hora estándar no es el mismo algoritmo para el mismo país para todos los años (por ejemplo, en Europa Central anteriormente Domingo de abril, IIRC, ahora es el último domingo de marzo). Estados Unidos cambió del primer domingo de abril al segundo domingo de marzo de 2007 en adelante.

Entonces, una fecha simple no es suficiente, también necesitará una ubicación geográfica.

Pero, si se puede vivir con el hecho de que usted se limita a las fechas de conmutación que se pueden calcular a partir de la algoritmo actual para la ACTUAL año para el ACTUAL configuración regional (país) y que esto puede ser malo para las fechas tanto en el futuro y en el pasado, entonces se puede utilizar la información de TIME_ZONE_INFORMATION para calcular las fechas de conmutación:

USES Windows,SysUtils,DateUtils; 

FUNCTION GetDaylightSavingsSwitchOverDates(Year : Cardinal ; VAR Start,Stop : TDateTime) : BOOLEAN; 

    VAR 
    TZ : TTimeZoneInformation; 

    FUNCTION DecodeSwitchOverDate(Year : Cardinal ; CONST Time : TSystemTime) : TDateTime; 
    VAR 
     I : Cardinal; 

    BEGIN 
     Result:=EncodeDateTime(Year,Time.wMonth,1,Time.wHour,Time.wMinute,Time.wSecond,0); 
     IF Time.wDay=5 THEN BEGIN 
     Result:=DateOf(EndOfTheMonth(Result))+TimeOf(Result); 
     WHILE PRED(DayOfWeek(Result))<>Time.wDayOfWeek DO 
      Result:=IncDay(Result,-1) 
     END 
     ELSE BEGIN 
     WHILE PRED(DayOfWeek(Result))<>Time.wDayOfWeek DO Result:=IncDay(Result); 
     FOR I:=1 TO PRED(Time.wDay) DO Result:=IncWeek(Result) 
     END 
    END; 

    BEGIN 
    IF GetTimeZoneInformation(TZ)=TIME_ZONE_ID_UNKNOWN THEN 
     Result:=FALSE 
    ELSE BEGIN 
     Start:=DecodeSwitchOverDate(Year,TZ.DaylightDate); 
     Stop:=DecodeSwitchOverDate(Year,TZ.StandardDate); 
     Result:=TRUE 
    END 
    END; 

FUNCTION StartOfDST(Year : Cardinal) : TDateTime; 
    VAR 
    Stop : TDateTime; 

    BEGIN 
    IF NOT GetDaylightSavingsSwitchOverDates(Year,Result,Stop) THEN Result:=0 
    END; 

FUNCTION EndOfDST(Year : Cardinal) : TDateTime; 
    VAR 
    Start : TDateTime; 

    BEGIN 
    IF NOT GetDaylightSavingsSwitchOverDates(Year,Start,Result) THEN Result:=0 
    END; 

bucle a través de los años 2000 a 2020 sobre mi PC (Zona horaria de Europa Central), obtengo las siguientes fechas:

DST in 2000: Sun 26 Mar 2000 02:00:00 through Sun 29 Oct 2000 03:00:00 
DST in 2001: Sun 25 Mar 2001 02:00:00 through Sun 28 Oct 2001 03:00:00 
DST in 2002: Sun 31 Mar 2002 02:00:00 through Sun 27 Oct 2002 03:00:00 
DST in 2003: Sun 30 Mar 2003 02:00:00 through Sun 26 Oct 2003 03:00:00 
DST in 2004: Sun 28 Mar 2004 02:00:00 through Sun 31 Oct 2004 03:00:00 
DST in 2005: Sun 27 Mar 2005 02:00:00 through Sun 30 Oct 2005 03:00:00 
DST in 2006: Sun 26 Mar 2006 02:00:00 through Sun 29 Oct 2006 03:00:00 
DST in 2007: Sun 25 Mar 2007 02:00:00 through Sun 28 Oct 2007 03:00:00 
DST in 2008: Sun 30 Mar 2008 02:00:00 through Sun 26 Oct 2008 03:00:00 
DST in 2009: Sun 29 Mar 2009 02:00:00 through Sun 25 Oct 2009 03:00:00 
DST in 2010: Sun 28 Mar 2010 02:00:00 through Sun 31 Oct 2010 03:00:00 
DST in 2011: Sun 27 Mar 2011 02:00:00 through Sun 30 Oct 2011 03:00:00 
DST in 2012: Sun 25 Mar 2012 02:00:00 through Sun 28 Oct 2012 03:00:00 
DST in 2013: Sun 31 Mar 2013 02:00:00 through Sun 27 Oct 2013 03:00:00 
DST in 2014: Sun 30 Mar 2014 02:00:00 through Sun 26 Oct 2014 03:00:00 
DST in 2015: Sun 29 Mar 2015 02:00:00 through Sun 25 Oct 2015 03:00:00 
DST in 2016: Sun 27 Mar 2016 02:00:00 through Sun 30 Oct 2016 03:00:00 
DST in 2017: Sun 26 Mar 2017 02:00:00 through Sun 29 Oct 2017 03:00:00 
DST in 2018: Sun 25 Mar 2018 02:00:00 through Sun 28 Oct 2018 03:00:00 
DST in 2019: Sun 31 Mar 2019 02:00:00 through Sun 27 Oct 2019 03:00:00 
DST in 2020: Sun 29 Mar 2020 02:00:00 through Sun 25 Oct 2020 03:00:00 

pero al menos algunos de estos años son incorrectos debido a que el algoritmo ha cambiado de mi configuración regional en los años enumerados.

Su función sería entonces:

FUNCTION IsDaylightSavingTime(Input : TDateTime) : BOOLEAN; 
    VAR 
    Start,Stop : TDateTime; 

    BEGIN 
    Result:=GetDaylightSavingsSwitchOverDates(YearOf(Input),Start,Stop) AND (Input>=Start) AND (Input<Stop) 
    END; 
+0

¿por qué no actualizar desde xml o base de datos? tal vez el servicio web? – none

0

Como dice usted mismo, esa información no se guarda junto con las fechas y fechas en Delphi, por lo que no puede simplemente portar esto. Cada rutina que proceda a una hora de tiempo tendría que agregar esta información, que no es el caso en Delphi.

tal vez debería explicar más lo que realmente están tratando de hacer, y describir el problema menos por analogía

+0

OK, he agregado un ejemplo, espero que esté un poco más claro ahora ... –

+0

La información tampoco se guarda en paquetes en .Net. Es solo que el tipo .Net tiene métodos que hacen que parezca que almacena más información que la que tiene. El método simplemente utiliza la misma API subyacente de Windows, razón por la cual los métodos .Net se comportan de manera diferente en Windows XP. –

0

que utiliza .NET Reflector para ver la implementación de esta función en .NET. Se define de la siguiente manera, ¿tal vez puedes convertir las matemáticas en Delphi? Si necesita profundizar en esto, le sugiero que abra Reflector por su cuenta. ¡Creo que te ayudará!

public static bool IsDaylightSavingTime(DateTime time, DaylightTime daylightTimes) 
{ 
    return (CalculateUtcOffset(time, daylightTimes) != TimeSpan.Zero); 
} 

internal static TimeSpan CalculateUtcOffset(DateTime time, DaylightTime daylightTimes) 
{ 
    if (daylightTimes != null) 
    { 
     DateTime time4; 
     DateTime time5; 
     if (time.Kind == DateTimeKind.Utc) 
     { 
      return TimeSpan.Zero; 
     } 
     DateTime time2 = daylightTimes.Start + daylightTimes.Delta; 
     DateTime end = daylightTimes.End; 
     if (daylightTimes.Delta.Ticks > 0L) 
     { 
      time4 = end - daylightTimes.Delta; 
      time5 = end; 
     } 
     else 
     { 
      time4 = time2; 
      time5 = time2 - daylightTimes.Delta; 
     } 
     bool flag = false; 
     if (time2 > end) 
     { 
      if ((time >= time2) || (time < end)) 
      { 
       flag = true; 
      } 
     } 
     else if ((time >= time2) && (time < end)) 
     { 
      flag = true; 
     } 
     if ((flag && (time >= time4)) && (time < time5)) 
     { 
      flag = time.IsAmbiguousDaylightSavingTime(); 
     } 
     if (flag) 
     { 
      return daylightTimes.Delta; 
     } 
    } 
    return TimeSpan.Zero; 
} 
0

puede encontrar un ejemplo de esto en el JEDI Code Library (Open Source) en la unidad de JclDateTime.pas, en la función LocalDateTimeToDateTime. La información del horario de verano se recupera y se usa para convertir desde y hacia la hora UTC.

1

Ondra C. -

Sí, estás en lo correcto. Es necesario:

  1. establecer una variable de Delphi TDateTime a la fecha/hora que desea

  2. convertirlo a Windows SYSTEMTIME

  3. llamada GetTimeZoneInformation() para obtener TTimeZoneInformation

  4. Llame a GetTimeZoneInformationForYear(), con su estructura TTimeZoneInformation, para obtener información DST para su zona horaria (no estoy seguro de dónde obtendría TTimeZoneInformation para alguna zona horaria arbitraria, pero usted debería ser capaz de encontrarlo en MSDN).

  5. Realice la aritmética para ver si la hora del sistema se produce DESPUÉS de TTZI.StandardDate (en cuyo caso es la hora estándar) o DESPUÉS de TTZI.DaylightDate (en cuyo caso es DST).

Alternativamente ...

Tal vez usted podría convertir esto en una mesa de Delphi:

http://www.twinsun.com/tz/tz-link.htm

Para cualquier fecha y hora en cualquier zona horaria, simplemente ver si las caídas de fecha y hora indicados dentro de DST, o fuera de ella. Voila! No hay API de Microsoft, ¡solo una simple búsqueda de tabla o un bloque de caso if/else!

'Espero que ayude ..pSM

0

Sé que esta no es una respuesta a su pregunta, pero podría estar interesado en las siguientes dos funciones: SystemTimeToTzSpecificLocalTime() y TzSpecificLocalTimeToSystemTime(). El primero convierte la hora universal a la hora correspondiente para la zona horaria especificada (donde nil significa su zona horaria local). El otro funciona al revés, pero está incluido solo en Windows XP y superior, como dice Borland Help. Si va a hacer una conversión de tiempo dependiente de la zona horaria solamente, deberían estar bien para usted. Y es bueno saber que comprueban si la hora UTC dada es un horario de verano o horario estándar. Sin embargo, no lo leí en ningún lado. Lo comprobé solo, así que por favor corrígeme si me equivoco.

Por favor, consulte la siguiente función. No estoy seguro de si lo usaría en cualquier lugar, porque no estoy seguro de su corrección, pero valdría la pena verlo. Y por favor, no me digas que es un método estúpido, porque lo sé :-).

function IsDaylightSavingTime(lLocalTime: TDateTime): boolean; 
var 
    lUniversalSystemTime: TSystemTime; 
    lLocalSystemTime: TSystemTime; 
    lTimeZoneInfo: TTimeZoneInformation; 
begin 
    case GetTimeZoneInformation(lTimeZoneInfo) of 
    TIME_ZONE_ID_UNKNOWN: 
     begin 
     Result := False; 
     Exit; 
     end; 

    TIME_ZONE_ID_STANDARD, 
    TIME_ZONE_ID_DAYLIGHT: ; 

    else 
    //TIME_ZONE_ID_INVALID: 
    RaiseLastOSError(); 
    end; 

    DateTimeToSystemTime(lLocalTime, lLocalSystemTime); 
    if not TzSpecificLocalTimeToSystemTime(nil, lLocalSystemTime, lUniversalSystemTime) then 
    RaiseLastOSError(); 

    Result := SameTime(SystemTimeToDateTime(lUniversalSystemTime), 
    IncMinute(lLocalTime, lTimeZoneInfo.DaylightBias + lTimeZoneInfo.Bias)); 
end; 
3

tal vez es una exageración para su aplicación específica, pero el proyecto de código abierto "Olson Time Zone Database for Delphi" permite acceder a todas las zonas horarias apoyadas por el proyecto tz database. La base de datos tz recibe actualizaciones periódicas con los cambios de horario de verano más recientes o corrige.

TZDB puede ser compilado en Delphi 6, 7, 8, 9, 10, 2007, 2009, 2010 y XE o FreePascal 2,0 y superior. TZDB es mejor utilizado con Delphi XE que introduce la clase TTimeZone en RTL.

Cuestiones relacionadas