Delphi - требуется эквивалент C # DateTime.IsDaylightSavingTime () - PullRequest
8 голосов
/ 10 августа 2010

Мне нужно как-то определить, находится ли какое-либо значение TDateTime в пределах диапазона летнего времени для моего часового пояса или нет (в C # то же самое делает метод DateTime.IsDaylightSavingTime()).

Я знаю в Delphiнет подобной функции, потому что Delphi TDateTime не содержит информации о часовом поясе, но я предполагаю, что есть какой-то способ сделать это с помощью Win32 API.

Я смотрел на Win32 API GetTimeZoneInformation и GetTimeZoneInformationForYearфункций, но я не совсем понимаю, как их использовать, поэтому я хотел бы попросить вас о помощи.Заранее благодарим за любые советы.

Редактировать:

Пример:

В моем часовом поясе (центральноевропейском) переход на летнее время начался в этом году 28-го числаМарт в 2 часа ночи и заканчивается 31 октября 2010 года в 3 часа утра.

Мне нужна функция с заголовком:

function IsDaylightSavingTime(input: TDateTime): boolean;

, которая будет возвращать true, если дата ввода находится между 28 марта2010 2:00 и 31 октября 2010 3:00 и false, если нет.

(Пример только для 2010 года, но мне нужно, чтобы он работал все годы.)

Опять же, я знаю, что информации, сохраненной только в TDateTime, недостаточно, но я думаю, что с помощью некоторой функции Win32 API я смогу получить, например, информацию о текущем часовом поясе из настроек Windows.

Ответы [ 7 ]

3 голосов
/ 10 августа 2010

Это не так просто, как кажется, потому что:

1) Дата переключения между летним и летним временем не одинакова для всех стран

2) ПереключениеДля даты между летним и летним временем не одинаковый алгоритм для одной и той же страны для всех лет (например, в Центральной Европе это было первое первое воскресенье апреля, IIRC, сейчас последнее воскресенье марта).США изменились с первого воскресенья апреля на второе воскресенье марта 2007 года и далее.

Итак, простой даты недостаточно, вам также понадобится географическое местоположение.

Но, если вы можете согласиться с тем фактом, что вы ограничиваетесь датами переключения, которые можно рассчитать по алгоритму CURRENT для года CURRENT для CURRENT языковой стандарт (страна) и то, что это может быть неверно для дат как в будущем, так и в прошлом, тогда вы можете использовать информацию в TIME_ZONE_INFORMATION для вычисления дат переключения:

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;

ЦиклВ период с 2000 по 2020 годы на моем ПК (часовой пояс Центральной Европы) я получаю следующие даты:

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

, но по крайней мере некоторые из этих лет неверны из-за того, что алгоритм изменился с моего языка вперечисленные годы.

Ваша функция будет тогда:

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

  BEGIN
    Result:=GetDaylightSavingsSwitchOverDates(YearOf(Input),Start,Stop) AND (Input>=Start) AND (Input<Stop)
  END;
3 голосов
/ 21 ноября 2010

Возможно, это излишне для вашего конкретного приложения, но проект с открытым исходным кодом " База данных часовых поясов Олсона для Delphi " позволяет получить доступ к всем часовым поясам , поддерживаемым tzбаза данных проект.База данных tz регулярно обновляется с учетом изменений или исправлений перехода на летнее время .

TZDB можно скомпилировать в Delphi 6, 7, 8, 9, 10, 2007, 2009, 2010и XE или FreePascal 2.0 и выше.TZDB лучше всего использовать с Delphi XE, который вводит класс TTimeZone в RTL.

1 голос
/ 16 августа 2010

Ондра С. -

Да, вы правы.Вам необходимо:

  1. Установить переменную Delphi TDateTime на желаемую дату / время

  2. Преобразовать ее в Windows SystemTime

  3. Вызовите GetTimeZoneInformation () для получения TTimeZoneInformation

  4. Вызовите GetTimeZoneInformationForYear () со своей структурой TTimeZoneInformation, чтобы получить информацию о летнем времени для вашего часового пояса (я не уверен, гдевы получите TTimeZoneInformation для некоторого произвольного часового пояса - но вы сможете найти его в MSDN).

  5. Выполните арифметику, чтобы увидеть, происходит ли системное время ПОСЛЕ TTZI.StandardDate (вв этом случае это стандартное время), или ПОСЛЕ TTZI.DaylightDate (в этом случае это DST).

В качестве альтернативы ...

Возможно, вы могли бы просто преобразовать это вТаблица Delphi:

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

Для любой даты и времени в любом часовом поясе, просто посмотрите, попадает ли данная дата и время в летнее время или за его пределы.Вуаля!Никаких API-интерфейсов Microsoft - просто просмотр таблицы или блок case if / else!

'Надеюсь, это поможет .. pSM

0 голосов
/ 10 октября 2010

Я знаю, что это не ответ на ваш вопрос, но вас могут заинтересовать следующие две функции: SystemTimeToTzSpecificLocalTime() и TzSpecificLocalTimeToSystemTime(). Первый преобразует универсальное время в соответствующее время для указанного часового пояса (где ноль означает ваш местный часовой пояс). Другой работает наоборот, но включен только в Windows XP и выше, как говорится в справке Borland. Если вы собираетесь выполнять только преобразование времени в зависимости от часового пояса, оно должно быть в порядке для вас. И хорошо знать, что они проверяют, является ли указанное время UTC летним или стандартным временем. Однако я нигде не читал. Я только что проверил это сам, поэтому, пожалуйста, поправьте меня, если я ошибаюсь.

Пожалуйста, смотрите следующую функцию. Я не уверен, буду ли я использовать это где-нибудь, потому что я не уверен в его правильности, но это могло бы стоить видеть. И, пожалуйста, не говорите мне, что это глупый метод, потому что я знаю, что: -).

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;
0 голосов
/ 13 августа 2010

Пример этого можно найти в библиотеке кодов JEDI (с открытым исходным кодом) в блоке JclDateTime.pas в функции LocalDateTimeToDateTime. Информация о летнем времени извлекается и используется для преобразования в и из времени UTC.

0 голосов
/ 13 августа 2010

Я использовал отражатель .net для просмотра реализации этой функции в .net. Это определяется следующим образом, может быть, вы можете преобразовать математику в Delphi? Если вам нужно углубиться в это, я предлагаю открыть Reflector для себя. Я думаю, что это поможет вам!

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 голосов
/ 10 августа 2010

Как вы сами сказали, эта информация не хранится в связке с датами и временем в Delphi, поэтому вы не можете просто перенести это.Каждая процедура, выполняющая tdatetime, должна будет добавить эту информацию, чего нет в Delphi.

Возможно, вам следует больше объяснить, что вы действительно пытаетесь сделать, и описать проблему меньше по аналогии

...