Delphi-эквивалент C# DateTime.IsDaylightSavingTime() метод нужен
мне нужно как-то определить, есть ли некоторые TDateTime
значение находится в диапазоне летнего времени для моего часового пояса или нет (в C# то же самое делает DateTime.IsDaylightSavingTime()
метод).
Я знаю, что в Delphi нет подобной функции, потому что Delphi TDateTime
Не содержит информации о часовом поясе, но я полагаю, что есть способ сделать это с помощью Win32 API.
Я посмотрел на Win32 API GetTimeZoneInformation
и GetTimeZoneInformationForYear
функции, но я не совсем понимаю, как их использовать, поэтому я бы прошу вас о помощи. Заранее спасибо за любые советы.
Edit:
пример:
в моем часовом поясе (Центрально-Европейском) летнее время началось в этом году 28 марта в 2 часа ночи и заканчивается 31 октября 2010 года в 3 часа ночи.
мне нужна функция с заголовком:
function IsDaylightSavingTime(input: TDateTime): boolean;
, что вернет true
если дата ввода находится между 28 марта 2010 2: 00 и 31 октября 2010 3: 00 и false
если не.
(The пример только для 2010 года, но мне нужно, чтобы он работал все годы.)
еще раз, я знаю, что информации, сохраненной только в TDateTime, недостаточно, но я думаю, что с некоторой функцией Win32 API я должен иметь возможность получить, например, информацию о текущем часовом поясе из настроек Windows.
7 ответов
это не так просто, как кажется, потому что:
1) Дата переключения между DST и стандартным временем не одинакова для всех стран
2) Дата переключения между DST и стандартным временем не является одним и тем же алгоритмом для одной и той же страны для всех лет (например, в Центральной Европе это было ранее первое воскресенье в апреле, IIRC, теперь это последнее воскресенье в марте). США изменились с первого воскресенья апреля на второе воскресенье марта 2007 года и на.
Итак-простой даты недостаточно, вам также понадобится географическое местоположение.
но, если вы можете жить с тем фактом, что вы ограничиваете себя датами переключения, которые могут быть рассчитаны из настоящее на настоящее год настоящее locale (country) и что это может быть неправильно для дат как в будущем, так и в прошлом, тогда вы можете использовать информацию в 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;
может быть, это перебор для вашего конкретного приложения, но проект с открытым исходным кодом "база данных часовых поясов Olson для Delphi" позволяет получить доступ к все часовые пояса при поддержке база данных tz. База данных регулярно обновляется с новейшими переход на летнее время изменения или исправления.
TZDB можно скомпилировать на Delphi 6, 7, 8, 9, 10 2007, , 2009, 2010 и XE или FreePascal 2.0 и выше. TZDB-это лучший используется с Delphi XE, который вводит класс TTimeZone в RTL.
Ondra C. -
Да, вы правы. Вам нужно:
установить TDateTime в Delphi переменной даты/времени
преобразовать его в Windows SystemTime
вызовите GetTimeZoneInformation (), чтобы получить TTimeZoneInformation
вызовите GetTimeZoneInformationForYear (), с вашей структурой TTimeZoneInformation, чтобы получить информацию DST для вашего часового пояса (я не уверен, где вы получите TTimeZoneInformation для некоторого произвольного часового пояса-но вы должны быть в состоянии найти его на MSDN).
сделайте арифметику, чтобы увидеть, происходит ли ваше системное время после TTZI.StandardDate (в этом случае это стандартное время) или после TTZI.DaylightDate (в этом случае это DST).
альтернативно ...
возможно, вы могли бы просто преобразовать это в Delphi таблица:
http://www.twinsun.com/tz/tz-link.htm
для любого datetime в любом часовом поясе, просто посмотрите, попадает ли данное datetime в DST или вне его. Вуаля! Нет Microsoft APIs-просто простой поиск таблицы или блок if / else case!
'надеюсь, это поможет .. pSM
Как вы сами говорите, эта информация не хранится в комплекте с датами в Delphi, поэтому вы не можете просто перенести это. Каждую процедуру, которая proceduces в tdatetime бы добавить эту информацию, чего не бывает в Дельфах.
может быть, вы должны объяснить больше, что вы действительно пытаетесь сделать, и описать проблему меньше по аналогии
я использовал .NET reflector для просмотра реализации этой функции .сеть. Он определяется следующим образом, может быть, вы можете преобразовать математику в Delphi? Если вам нужно копнуть глубже в это, я предлагаю открыть рефлектор для себя. Я думаю, это поможет вам!
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;
}
вы можете найти пример этого в библиотека кодов джедаев (С открытым исходным кодом) в JclDateTime.блок pas, в функции LocalDateTimeToDateTime. Информация о летнее время извлекается и используется для преобразования В и из времени UTC.
Я знаю, что это не ответ на ваш вопрос, но вас могут заинтересовать следующие две функции:SystemTimeToTzSpecificLocalTime()
и TzSpecificLocalTimeToSystemTime()
. Первый преобразует универсальное время в соответствующее время для указанного часового пояса (где nil означает локальный часовой пояс). Другой работает наоборот, но включен только в Windows XP и выше, как говорит Borland Help. Если вы собираетесь делать только преобразование времени в зависимости от часового пояса, они должны быть в порядке для вас. И это хорошо знать. что они проверяют, является ли время UTC заданным DST или стандартным временем. Я не читал нигде. Я только что проверил это сам, поэтому, пожалуйста, поправьте меня, если я ошибаюсь.
пожалуйста, смотрите следующую функцию. Я не уверен, буду ли я использовать его где-нибудь, потому что я не уверен в его правильности, но, возможно, стоит посмотреть. И, пожалуйста, не говорите мне, что это глупый метод, потому что я знаю это :-).
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;