Мне нужно как-то определить, находится ли какое-то значение TDateTime
в пределах диапазона летнего времени для моего часового пояса или нет (в C # то же самое делает метод DateTime.IsDaylightSavingTime ()
).
Я знаю, что в Delphi подобной функции нет, потому что Delphi TDateTime
не содержит информации о часовом поясе, но я полагаю, что есть какой-то способ сделать это с помощью Win32 API.
Я посмотрел в Win32 API GetTimeZoneInformation
и GetTimeZoneInformationForYear
, но я не совсем понимаю, как их использовать, поэтому я хотел бы попросить вас о помощи. Заранее благодарим за любые советы.
Редактировать:
Пример:
В моем часовом поясе (в Центральной Европе) переход на летнее время начался в этом году
Я знаю, что в Delphi нет подобной функции, потому что Delphi TDateTime
не содержит информации о часовом поясе, но я предполагаю, что есть какой-то способ, как это сделать с помощью Win32 API.
Я смотрел на Win32 Функции API GetTimeZoneInformation
и GetTimeZoneInformationForYear
, но я не совсем понимаю, как их использовать, поэтому я хотел бы попросить вас о помощи. Заранее благодарим за любые советы.
Редактировать:
Пример:
В моем часовом поясе (в Центральной Европе) переход на летнее время начался в этом году
Я знаю, что в Delphi нет подобной функции, потому что Delphi TDateTime
не содержит информации о часовом поясе, но я предполагаю, что есть какой-то способ, как это сделать с помощью Win32 API.
Я смотрел на Win32 Функции API GetTimeZoneInformation
и GetTimeZoneInformationForYear
, но я не совсем понимаю, как их использовать, поэтому я хотел бы попросить вас о помощи. Заранее благодарим за любые советы.
Редактировать:
Пример:
В моем часовом поясе (в Центральной Европе) переход на летнее время начался в этом году Мы рассмотрели функции Win32 API GetTimeZoneInformation
и GetTimeZoneInformationForYear
, но я не совсем понимаю, как их использовать, поэтому я хотел бы попросить вас о помощи. Заранее благодарим за любые советы.
Редактировать:
Пример:
В моем часовом поясе (в Центральной Европе) переход на летнее время начался в этом году Мы рассмотрели функции 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.
Это не так просто, как кажется, потому что:
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;
Как вы сами говорите, эта информация не хранится вместе с датами в Delphi, поэтому вы не можете просто перенести это. Каждая процедура, выполняющая tdatetime, должна будет добавить эту информацию, чего нет в Delphi.
Может быть, вам стоит больше объяснить, что вы на самом деле пытаетесь сделать, и меньше описывать проблему по аналогии
Я использовал отражатель .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;
}
Ондра К. -
Да, вы правы. Вам необходимо:
Установить переменную Delphi TDateTime на желаемую дату / время
Преобразовать ее в Windows SystemTime
Вызвать GetTimeZoneInformation (), чтобы получить TTimeZoneInformation
Вызвать GetTimeZoneInformationForYear () с вашей структурой TTimeZoneInformation, чтобы получить информацию о летнем времени для вашего часового пояса (я не уверен, где вы можете получить TTimeZoneInformation для некоторого произвольного часового пояса, но вы сможете найти его в MSDN).
Выполните арифметические действия, чтобы узнать, происходит ли ваше системное время ПОСЛЕ TTZI.StandardDate (в этом случае это стандартное время) или ПОСЛЕ TTZI.DaylightDate (в этом случае это летнее время).
В качестве альтернативы ...
Возможно, вы могли бы просто преобразовать это в таблицу Delphi:
http://www.twinsun.com/tz/tz-link.htm
Для любого datetime in, любого часовой пояс, просто посмотрите, попадает ли данное datetime в DST или за его пределы. Вуаля! Никаких Microsoft API - только простой поиск по таблице или блокировка if / else!
'Надеюсь, что это поможет ... pSM
Вы можете найти пример этого в Библиотеке кода JEDI (с открытым исходным кодом) в модуле JclDateTime.pas в функции LocalDateTimeToDateTime. Информация о переходе на летнее время извлекается и используется для преобразования в и из времени UTC.