私はArduinoとリアルタイムクロックチップを使用しています。チップはうるう年などを補正するので、常に正しい日付になりますが、夏時間には対応していません。地域の複雑さのためと思います。クロックは、日、月、年(1ベース)と曜日(日曜日= 0から土曜日= 6)を提供します。
ユーザーが入力した日時と比較する必要があるため、夏時間に調整された日時を知る必要があります。現在の日付が夏時間である場合、時計から時間に1時間を追加するだけで、必要なものが得られます。
難しいのは、年によって変わるため、夏時間かどうかを判断することです。私はそれが私の場所(Mountain Time)で機能することだけを気にします。私のプラットフォーム用の包括的な日付ライブラリはないようで、とにかくやりすぎだと思います。私が夏時間かどうかを判断する簡単な式はありますか?
これは実際には一見単純です。私たちを助けるいくつかの事実があります:
これらの事実により、次のコードが生成されます(C#ですが、ご使用のプラットフォームに移植可能です)。
_ 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;
}
_
曜日の値を信頼できる限り、これを行うために年を知る必要すらありません。
簡単な単体テストを作成し、このコードが1800から2200までのすべての日付でTimeZone.IsDayLightSavingsTime()
と一致することを確認しました。午前2時の規則は考慮していませんが、曜日が日曜日で、日付は8〜14(3月)または1〜7(11月)です。
中央ヨーロッパのコード(2014年から3000年の範囲で毎日テスト済み)
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
}
テスト機能
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();
}
現在の規則では、特定の場所の特定の日付がDSTに含まれるかどうかを簡単に計算できますが、DSTは政治家の気まぐれであり、いつでも変更される可能性があることに注意してください。私は2007年より前に製造された時計で夏時間を自動的に調整し、現在は1年に4回変更する必要があります。変更が発生し、古い日付でそれ自体が誤って変更されたときに2回発生します。
この場合、日付と時刻と共にユーザーにタイムゾーンを入力させるという簡単な方法で、DSTを完全に無視できる場合があります。または、ほとんどのコンシューマデバイスと同じようにして、ユーザーに年に2回ローカルタイムゾーンに時刻を調整させることができます。
しかし、本当にDSTを処理する必要があり、本当に正しく実行したい場合は、 zoneinfo database を使用して、何らかの方法で更新できることを確認してください。何らかの理由でそれができない場合は、少なくともユーザーがルールを上書きできるようにします。それでも難しい場合は、少なくとも自動調整をオフにするオプションをユーザーに提供してください(私の愚かな目覚まし時計とは異なります)。
私はこのアプローチで試しており、シンプルで正確だと思います:
// 3月の最初の日曜日はDoW = 1、日曜日は(month == 3 && day> = 8 && day <= 14 && DoW = 1)がTrueを返す場合
// 11月の第2日曜日はDoW = 1、日曜日は(month == 11 && day> = 1 && day <= 7 && DoW = 1)がTrueを返す場合
3月14日と11月7日は、常に米国で夏時間が行われる週の一部です。日曜日、土曜日、またはその間の曜日である場合もありますが、常にその週の一部です。以下のコードは、これら2つの日付が問題の日付が発生する年の一部である日曜日を見つけます。次に、この日付に2時間を追加して、夏時間が実際に発生する時刻を取得します。次に、問題の日付を夏時間の開始日と終了日と比較し、GMTオフセットによって時刻を調整します。このプロセスは、他の国の開始日と終了日で機能します。すべての国コードと郵便番号を含むテーブルを設定して、夏時間の終了日と開始日、および両方の期間のgmtオフセットを設定できます。日付は、月の第1日から第4日曜日までの7日、14日、21日、28日です。 9月30日または10月31日など、月の最後の日曜日の最大日を入力します。
3月の第2日曜日:
cdate("3/14/" & format(now(),"yyyy"))-format(cdate("3/14/" & format(now(),"yyyy")),"W")+1+2/24
11月第1日曜日:
cdate("11/7/" & format(now(),"yyyy"))-format(cdate("11/7/" & format(now(),"yyyy")),"W")+1+2/24
例.
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の例
CASE WHEN [date2check] <DATEADD(hh、2、CAST( '3/14 /' + CAST(DATEPART(yyyy、[date2check])AS nvarchar(4))AS datetime)+ 1-DATEPART(w、CAST( ' 3/14/'+ CAST(DATEPART(yyyy、[date2check])AS nvarchar(4))AS datetime)))THEN dateadd(hh、-DST_GMT_TM_ZN_DIFF、[date2check])ELSE CASE WHEN [date2check] <DATEADD(hh、 2、CAST('11/7/'+ CAST(DATEPART(yyyy、[date2check])AS nvarchar(4))AS datetime)+ 1-DATEPART(w、CAST('11/7 /' + CAST(DATEPART( yyyy、[date2check])AS nvarchar(4))AS datetime)))THEN dateadd(hh、-STD_GMT_TM_ZN_DIFF、[date2check])ELSE dateadd(hh、-DST_GMT_TM_ZN_DIFF、[date2check])END END
このコードは、mktimeを使用して曜日を取得します。曜日を使用して夏時間を計算しました。 mktimeを使用したくない場合は、second_sundayプログラムを使用できます。 2007年3月14日、水曜日です。曜日は、毎年1日進み、2004年以降は跳躍ごとに2日進みます。
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
int isDst(int month, int dayOfMonth, int hour, int dayOfWeek);
int main(int argc, char *argv[])
{
int isdst, dayOfWeek;
char buf[80];
struct tm tmData;
if( argc == 1 )
{
printf("\nsyntax: %s mm/dd/yyyy_hh:mm:00", argv[0]);
return -1;
}
// 0123456789A12
// 03/12/2018_12
strcpy(buf, argv[1]);
tmData.tm_mon = atoi(&buf[0]) - 1; //month -1
tmData.tm_mday = atoi(&buf[3]); //day of month
tmData.tm_year = atoi(&buf[6]) - 1900; // year - 1900
tmData.tm_hour = atoi(&buf[11]); // hour
tmData.tm_min = 0; //minutes (not used)
tmData.tm_sec = 0; //seconds (not used)
//tmData.tm_min = atoi(&buf[14]);
//tmData.tm_sec = atoi(&buf[27]);
//day light saving time variable.
//NOT used in this calculation.
//Tells mktime the input date is in day light saving time
tmData.tm_isdst = 0; //
mktime(&tmData);
dayOfWeek = tmData.tm_wday;
printf("%02d/%02d/%2d_%02d dayWk=%d ",
tmData.tm_mon+1, tmData.tm_mday, tmData.tm_year, tmData.tm_hour, dayOfWeek);
isdst = isDst(tmData.tm_mon+1, tmData.tm_mday, tmData.tm_hour, dayOfWeek);
printf("isdst=%d\n", isdst);
return 0;
}
int isDst(int month, int dayOfMonth, int hour, int dayOfWeek)
{
int second_sunday, first_sunday;
if( month > 3 && month < 11 ) return 1; //4,5,6,7,8,9,10
if( month < 3 || month == 12 ) return 0; //1, 2 or 12
if( month == 3 )
{
//The 2nd Sunday in March is 8,9,10,11,12,13,14
if( dayOfMonth < 8 ) return 0;
if( dayOfMonth > 14 ) return 1;
//To get here dayOfMonth >= 8 && dayOfMonth <= 14
second_sunday = dayOfMonth - dayOfWeek;
if( second_sunday < 8 ) second_sunday += 7;
printf("2nd_Sunday=%2d ", second_sunday);
if( dayOfMonth > second_sunday ) return 1;
if( dayOfMonth < second_sunday ) return 0;
//To get here dayOfMonth = second_sunday
if( hour >= 2 ) return 1;
else return 0;
}
if( month == 11 )
{
//The 1st Sunday in Nov is 1,2,3,4,5,6,7
if( dayOfMonth > 7 ) return 0;
//To get here dayOfMonth >= 1 && dayOfMonth <= 7
first_sunday = dayOfMonth - dayOfWeek;
if( first_sunday < 1 ) first_sunday += 7;
printf("1st_Sunday=%2d ", first_sunday);
if( dayOfMonth > first_sunday ) return 0;
if( dayOfMonth < first_sunday ) return 1;
//To get here dayOfMonth = first_sunday
if( hour >= 2 ) return 0;
else return 1;
}
return -1;
}
/**************
Compile via cl.exe isDst.c
Begin and End dates for day light saving time
03/11/2007_01:00:00 11/04/2007_01:00:00
03/09/2008_01:00:00 11/02/2008_01:00:00
03/08/2009_01:00:00 11/01/2009_01:00:00
03/14/2010_01:00:00 11/07/2010_01:00:00
03/13/2011_01:00:00 11/06/2011_01:00:00
03/11/2012_01:00:00 11/04/2012_01:00:00
03/10/2013_01:00:00 11/03/2013_01:00:00
03/09/2014_01:00:00 11/02/2014_01:00:00
03/08/2015_01:00:00 11/01/2015_01:00:00
03/13/2016_01:00:00 11/06/2016_01:00:00
03/12/2017_01:00:00 11/05/2017_01:00:00
03/11/2018_01:00:00 11/04/2018_01:00:00
03/10/2019_01:00:00 11/03/2019_01:00:00
03/08/2020_01:00:00 11/01/2020_01:00:00
03/14/2021_01:00:00 11/07/2021_01:00:00
03/13/2022_01:00:00 11/06/2022_01:00:00
03/12/2023_01:00:00 11/05/2023_01:00:00
03/10/2024_01:00:00 11/03/2024_01:00:00
03/09/2025_01:00:00 11/02/2025_01:00:00
03/08/2026_01:00:00 11/01/2026_01:00:00
03/14/2027_01:00:00 11/07/2027_01:00:00
03/12/2028_01:00:00 11/05/2028_01:00:00
03/11/2029_01:00:00 11/04/2029_01:00:00
03/10/2030_01:00:00 11/03/2030_01:00:00
03/09/2031_01:00:00 11/02/2031_01:00:00
03/14/2032_01:00:00 11/07/2032_01:00:00
isDst.exe 03/11/2007_02:00:00 >> dst.txt
isDst.exe 03/09/2008_02:00:00 >> dst.txt
isDst.exe 03/08/2009_02:00:00 >> dst.txt
isDst.exe 03/14/2010_02:00:00 >> dst.txt
isDst.exe 03/13/2011_02:00:00 >> dst.txt
isDst.exe 03/11/2012_02:00:00 >> dst.txt
isDst.exe 03/10/2013_02:00:00 >> dst.txt
isDst.exe 03/09/2014_02:00:00 >> dst.txt
isDst.exe 03/08/2015_02:00:00 >> dst.txt
isDst.exe 03/13/2016_02:00:00 >> dst.txt
isDst.exe 03/12/2017_02:00:00 >> dst.txt
isDst.exe 03/11/2018_02:00:00 >> dst.txt
isDst.exe 03/10/2019_02:00:00 >> dst.txt
isDst.exe 03/08/2020_02:00:00 >> dst.txt
isDst.exe 03/14/2021_02:00:00 >> dst.txt
isDst.exe 03/13/2022_02:00:00 >> dst.txt
isDst.exe 03/12/2023_02:00:00 >> dst.txt
isDst.exe 03/10/2024_02:00:00 >> dst.txt
isDst.exe 03/09/2025_02:00:00 >> dst.txt
isDst.exe 03/08/2026_02:00:00 >> dst.txt
isDst.exe 03/14/2027_02:00:00 >> dst.txt
isDst.exe 03/12/2028_02:00:00 >> dst.txt
isDst.exe 03/11/2029_02:00:00 >> dst.txt
isDst.exe 03/10/2030_02:00:00 >> dst.txt
isDst.exe 03/09/2031_02:00:00 >> dst.txt
isDst.exe 03/14/2032_02:00:00 >> dst.txt
isDst.exe 11/04/2007_02:00:00 >> dst.txt
isDst.exe 11/02/2008_02:00:00 >> dst.txt
isDst.exe 11/01/2009_02:00:00 >> dst.txt
isDst.exe 11/07/2010_02:00:00 >> dst.txt
isDst.exe 11/06/2011_02:00:00 >> dst.txt
isDst.exe 11/04/2012_02:00:00 >> dst.txt
isDst.exe 11/03/2013_02:00:00 >> dst.txt
isDst.exe 11/02/2014_02:00:00 >> dst.txt
isDst.exe 11/01/2015_02:00:00 >> dst.txt
isDst.exe 11/06/2016_02:00:00 >> dst.txt
isDst.exe 11/05/2017_02:00:00 >> dst.txt
isDst.exe 11/04/2018_02:00:00 >> dst.txt
isDst.exe 11/03/2019_02:00:00 >> dst.txt
isDst.exe 11/01/2020_02:00:00 >> dst.txt
isDst.exe 11/07/2021_02:00:00 >> dst.txt
isDst.exe 11/06/2022_02:00:00 >> dst.txt
isDst.exe 11/05/2023_02:00:00 >> dst.txt
isDst.exe 11/03/2024_02:00:00 >> dst.txt
isDst.exe 11/02/2025_02:00:00 >> dst.txt
isDst.exe 11/01/2026_02:00:00 >> dst.txt
isDst.exe 11/07/2027_02:00:00 >> dst.txt
isDst.exe 11/05/2028_02:00:00 >> dst.txt
isDst.exe 11/04/2029_02:00:00 >> dst.txt
isDst.exe 11/03/2030_02:00:00 >> dst.txt
isDst.exe 11/02/2031_02:00:00 >> dst.txt
isDst.exe 11/07/2032_02:00:00 >> dst.txt
https://stackoverflow.com/questions/5590429/calculating-daylight-saving-time-from-only-date
***************/
/*****
The previous programs used mktime to compute day_of_week.
It used day_of_week to compute 2nd_sunday in march and
1st_sunday in Nov.
If you don't want to use mktime, you can use this program to
compute 2nd_sunday. The same technique will compute 1st_sunday.
On 03/14/2007, the day of the week is Wed, or 3.
Every year after 2007, the day of the week advances 1 day.
on leap years, the day of the week advances 2 days.
Must include the no. of leap years sinc 2004.
******/
#include <stdio.h>
#include <string.h>
#include <time.h>
int secondSunday(year);
int main(int argc, char *argv[])
{
int year, second_sunday;
if( argc == 1 )
{
printf("\nsyntax: %s year, with year >= 2007.\n", argv[0]);
return -1;
}
year = atoi(argv[1]);
if( year < 2007 )
{
printf("\nsyntax: %s year, with year >= 2007.\n", argv[0]);
return -1;
}
second_sunday = secondSunday(year);
printf("second_sunday=%d\n", second_sunday);
return 0;
}
int secondSunday(year)
{
//On 03/14/2007, the day of the week is Wed, or 3.
int no_years, no_leaps, day_of_week, second_sunday;
no_years = year - 2007;
no_leaps = (year - 2004)/4;
day_of_week = 3 + (no_years + no_leaps) % 7;
second_sunday = 14 - day_of_week;
if( second_sunday < 8 ) second_sunday += 7;
//printf("no_years=%d,no_leaps=%d,day_of_week=%d, second_sunday=%d\n",
//no_years, no_leaps, day_of_week, second_sunday);
return second_sunday;
}
/**************
Compile via cl.exe second_sunday.c
second_sunday.exe 2007
second_sunday.exe 2008
second_sunday.exe 2009
second_sunday.exe 2010
second_sunday.exe 2011
second_sunday.exe 2012
second_sunday.exe 2013
second_sunday.exe 2014
second_sunday.exe 2015
second_sunday.exe 2016
second_sunday.exe 2017
second_sunday.exe 2018
second_sunday.exe 2019
second_sunday.exe 2020
second_sunday.exe 2021
second_sunday.exe 2022
second_sunday.exe 2023
second_sunday.exe 2024
second_sunday.exe 2025
second_sunday.exe 2026
second_sunday.exe 2027
second_sunday.exe 2028
second_sunday.exe 2029
second_sunday.exe 2030
second_sunday.exe 2031
second_sunday.exe 2032
***************/
誰かがこれをPythonで探しているなら、ここに素晴らしい記事があります: http://code.activestate.com/recipes/425607-findng-the-xth-day-in-a-month/ =
次のコードが含まれています。
from calendar import monthrange
def dow_date_Finder(which_weekday_in_month=FIRST,day=MONDAY,month=JANUARY,year=2000):
bom, days = monthrange(year, month)
firstmatch = (day - bom) % 7 + 1
return xrange(firstmatch, days+1, 7)[which_weekday_in_month]
ユーザーフレンドリーにするためには、上のリンクにあるページの上部からすべての変数を表示する必要があることに注意してください。
以下は2007年以降に適しています。
def find_dt_of_daylight_savings_time(yr=date.today().year):
dst = {}
spring = dow_date_Finder(SECOND, SUNDAY, MARCH, yr)
spring_dt = datetime(int(yr), 3, spring, 3, 0)
dst['spring'] = spring_dt
fall = dow_date_Finder(FIRST, SUNDAY, NOVEMBER, yr)
fall_dt = datetime(int(yr), 11, fall, 3, 0)
dst['fall'] = fall_dt
return dst
これはすべてとても役に立ちました。ブラジルには南半球の特別な問題があり、カーニバルが秋の変更日と重なる場合があります。
これらの場合、議会は夏時間を1週間遅らせます。米国海軍天文台の計算では、イースターを見つけることができます(- http://aa.usno.navy.mil/faq/docs/easter.php 、2017年1月3日取得)。カーニバルは正確な日数、灰の水曜日の前の週末(マルディグラは「太っている火曜日」を意味します)。
したがって、Cでは:
static const uint8_t carnival[] = {
0x04, 0x24, 0x24, 0x21, // 2000... 2031
0x01, 0x09, 0x48, 0x09, // 2032... 2063
0x4a, 0x40, 0x4a, 0x52, // 2064... 2095
0x02, 0x90, 0x12, 0x94 // 2096... 2127
}
/* Returns the current time offset. */
int dst(struct tm *tm_ptr)
{
int st = 0;
int dst = 60;
int mon = tm_ptr->tm_mon;
int mday, previous_sunday;
int gmt_offset = tm_ptr->gmt_offset;
// If not Brasilia or Amazon time, no DST.
if(gmt_offset != -240 && gmt_offset != -300)
return st;
if(NOV < mon || FEB > mon) // Summer?
return dst;
else if(NOV > mon && FEB < mon) // Winter?
return st;
mday = tm_ptr->tm_mday;
previous_sunday = mday - tm_ptr->tm_wday;
// Begin DST on first Sunday of November.
if(NOV == mon) // If it's November... i.e. spring, so forward
{
if(previous_sunday < 1) // Before Sunday, week 1?
{
return st;
} else { // After or during Sunday, week 1
return dst;
}
// End DST in February, accounting for Carnival.
} else { // It has to be February, i.e. fall, so backward.
int year, week_start;
year = tm_ptr->tm_year;
if(0 == (carnival[year/8] & (1 << (year%8))))
week_start = 15; // 3rd Sunday is not in Carnival.
else
week_start = 22; // Use 4th Sunday, 1 week after Carnival.
if(previous_sunday < (week_start-1))
return dst;
if(previous_sunday < week_start) {
if(tm_ptr->tm_isdst == st) // if it already fell backward, stay.
return st;
return dst;
}
// On or after the correct Sunday?
return st;
}
}
これが私の答えです。修正があれば歓迎します。年は2000年から2099年の間にあると想定されています。詳細については、参照リンクから入手できます。
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++;
}
曜日の計算リファレンス: 月、日、年を指定して曜日を決定する方法
DSTテストの参照は、この記事を介して captncraig によって回答されたものであり、彼の回答に対する私自身の推論と解釈です。