海外のある時点の日付・時間がサマータイム(夏時間)かどうかの判別をしたいと思っています。

言語はなんでもいいのですが、サマータイムの判別アルゴリズムをご存知の方、ポイントはずみますのでよろしくお願いします。

なお、使える関数は、年の取得関数・月の取得関数・日の取得関数・時の取得関数・分の取得関数・秒の取得関数・曜日の取得関数のみです。

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:2007/08/19 14:21:13
  • 終了:2007/08/19 23:11:37

ベストアンサー

id:noocyte No.5

noocyte回答回数21ベストアンサー獲得回数32007/08/19 19:51:15

ポイント300pt

四則演算と剰余は使っていいんですか?


夏時間についてはよく知りませんが,

Wikipedia を参考に判定プログラムを書いてみました.


  • 現在日時:month 月 day 日 (w曜日) hour 時
  • 夏時間開始:startMonth 月 startN 回目の startW 曜日 startHour 時
  • 夏時間終了:endMonth 月 endN 回目の endW 曜日 endHour 時

であれば,次のようにして判定できると思います.ただし,


  • 現在日時がUTCか地方時かは,当該国の夏時間の定義に応じて調整済とする.
  • USA 等のように夏時間の期間中時計を1時間進める国では,現在日時は

その補正を行う前の値とする.(そうでないと期間終了時の1時間が判定不能となる.)

  • 曜日番号は一応日=0,~,土=6とするが,0~6の範囲であれば何曜から始めてもよい.

Bool IsSummerTime(int month, int day, int w, int hour,
                  int startMonth, int startN, int startW, int startHour,
                  int endMonth, int endN, int endW, int endHour)
{
  int w1;     // month月1日の曜日
  int dstDay; // 夏時間開始または終了日付

  // デバッグ用
  printf("\n%d月%d日(%d曜) %d時は夏時間?\n", month, day, w, hour);
  assert((1 <= day) && (day <= 31));
  assert((0 <= w) && (w <= 6));
  assert((0 <= startW) && (startW <= 6));
  assert((0 <= endW) && (endW <= 6));

  if((month < startMonth) || (endMonth < month)) goto NotDST;

  // month月1日の曜日w1を求める.day=1 ならば w1=w で,
  // dayが1日増えるごとにw1は1日前にずれるので,数学的には
  //   w1 = (w - (day - 1)) mod 7
  // しかしC言語の場合は被除数が負になるとまずいので,
  // 負にならないようにするための最小の7の倍数35を足して
  w1 = (w + 36 - day) % 7;

  // デバッグ用
  printf("%d月%d日(%d曜) → 1日は%d曜\n", month, day, w, w1);

  if(month == startMonth) {
    // month月のstartN回目のstartW曜日の日付dstDayを求める.
    dstDay = NthDayOfWeekToDay(startN, startW, w1);

    // デバッグ用
    printf("夏時間開始:%d月%d日(%d曜) %d時\n",
           startMonth, dstDay, startW, startHour);

    // (day, hour) が (dstDay, startHour) より前ならば夏時間ではない.
    if((day < dstDay) || ((day == dstDay) && (hour < startHour)))
      goto NotDST;
  }
  if(month == endMonth) {
    // month月のendN回目のendW曜日の日付dstDayを求める.
    dstDay = NthDayOfWeekToDay(endN, endW, w1);

    // デバッグ用
    printf("夏時間終了:%d月%d日(%d曜) %d時\n",
           endMonth, dstDay, endW, endHour);

    // (day, hour) が (dstDay, startHour) 以後ならば夏時間ではない.
    if((day > dstDay) || ((day == dstDay) && (hour >= endHour)))
      goto NotDST;
  }

  printf("夏時間だよ.\n");         // デバッグ用
  return TRUE;

NotDST:
  printf("夏時間じゃないよ.\n");   // デバッグ用
  return FALSE;
}

/*--------------------------------------------------------------------------
IsSummerTime() のテスト.
--------------------------------------------------------------------------*/
void IsSummerTimeTest(void)
{
  // USA の夏時間
  const int startMonth = 3;
  const int startN = 2;
  const int startW = 0;
  const int startHour = 2;
  const int endMonth = 11;
  const int endN = 1;
  const int endW = 0;
  const int endHour = 1; // 夏時間補正前の時刻

#define ISSUMMERTIME(month, day, w, hour, isDST) \
  assert(IsSummerTime((month), (day), (w), (hour), \
                      startMonth, startN, startW, startHour, \
                      endMonth, endN, endW, endHour) \
         == (isDST))

  // 2007年の場合
  ISSUMMERTIME( 2, 28, 3, 23, FALSE); // 開始月の前月
  ISSUMMERTIME( 3, 10, 6, 23, FALSE); // 開始月,開始日より前
  ISSUMMERTIME( 3, 11, 0,  1, FALSE); // 開始日,開始時刻より前
  ISSUMMERTIME( 3, 11, 0,  2, TRUE ); // 開始日開始時刻ピッタリ
  ISSUMMERTIME( 3, 11, 0,  3, TRUE ); // 開始日,開始時刻より後
  ISSUMMERTIME( 3, 12, 1,  0, TRUE ); // 開始月,開始日より後
  ISSUMMERTIME( 4,  1, 0,  0, TRUE ); // 開始月の翌月
  ISSUMMERTIME(10, 31, 3, 23, TRUE ); // 終了月の前月
  ISSUMMERTIME(11,  3, 6, 23, TRUE ); // 終了月,終了日より前
  ISSUMMERTIME(11,  4, 0,  0, TRUE ); // 終了日,終了時刻より前
  ISSUMMERTIME(11,  4, 0,  1, FALSE); // 終了日,終了時刻ピッタリ
  ISSUMMERTIME(11,  4, 0,  2, FALSE); // 終了日,終了時刻より後
  ISSUMMERTIME(11,  5, 1,  0, FALSE); // 終了月,終了日より後
  ISSUMMERTIME(12,  1, 6,  0, FALSE); // 終了月の翌月
}

/*--------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------*/
2月28日(3曜) 23時は夏時間?
夏時間じゃないよ.

3月10日(6曜) 23時は夏時間?
3月10日(6曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間じゃないよ.

3月11日(0曜) 1時は夏時間?
3月11日(0曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間じゃないよ.

3月11日(0曜) 2時は夏時間?
3月11日(0曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間だよ.

3月11日(0曜) 3時は夏時間?
3月11日(0曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間だよ.

3月12日(1曜) 0時は夏時間?
3月12日(1曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間だよ.

4月1日(0曜) 0時は夏時間?
4月1日(0曜) → 1日は0曜
夏時間だよ.

10月31日(3曜) 23時は夏時間?
10月31日(3曜) → 1日は1曜
夏時間だよ.

11月3日(6曜) 23時は夏時間?
11月3日(6曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間だよ.

11月4日(0曜) 0時は夏時間?
11月4日(0曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間だよ.

11月4日(0曜) 1時は夏時間?
11月4日(0曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間じゃないよ.

11月4日(0曜) 2時は夏時間?
11月4日(0曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間じゃないよ.

11月5日(1曜) 0時は夏時間?
11月5日(1曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間じゃないよ.

12月1日(6曜) 0時は夏時間?
夏時間じゃないよ.
id:wizemperor

質問が曖昧、条件が厳しい中、コードまで書いていただきまして、ありがとうございます。四則演算と剰余は使えます。

第何曜日かを求める関数がなく悩んでいましたが、NthDayOfWeekToDay関数は助かりました。

とても汎用的なコードを教えていただけたので、問題なく実装できそうです。ありがとうございました。

2007/08/19 23:06:00

その他の回答(5件)

id:xxxatsxxx No.1

xxxatsxxx回答回数45ベストアンサー獲得回数22007/08/19 15:05:21

ポイント10pt

localtime_r(またはlocaltime)

の戻り値の構造体内のtm_isdstがその名の通り、サマータイムフラグです。

http://www.linux.or.jp/JM/html/LDP_man-pages/man3/ctime.3.html

C標準でいろんなLL等からも利用できます。

id:wizemperor

説明が足りなかったかもしれませんが、あくまでアルゴリズムでお願いします。

if構文やwhileループ・forループなどの基本的なCの文は使えますが、質問の関数以外の組み込み関数や構造体は使えません。

変数、1次元配列は使えます。

2007/08/19 15:17:52
id:wasisan No.2

wasisan回答回数86ベストアンサー獲得回数72007/08/19 15:07:41

ポイント13pt

正直私もまだよくわかっていませんが,とりあえず

サマータイムの判定について -OKWave:

http://okwave.jp/qa2332703.html

を張っておきます.

id:wizemperor

なるほど、ParseDateという関数では第n曜日というのが取得できるようですね。

ただ、今回は質問の関数しか使えないのです。

ParseDate関数のような第n曜日かを返す関数を実装するためのアルゴリズムでもかまいません。

2007/08/19 15:34:24
id:hiramatsu_kg No.3

hiramatsu_kg回答回数424ベストアンサー獲得回数32007/08/19 15:18:10

ポイント8pt

http://ja.wikipedia.org/wiki/%E5%A4%8F%E6%99%82%E9%96%93

アルゴリズムではなく、3月の第四日曜日からという風に日が各国別に決まってるんです。

id:wizemperor

それはわかっていますが、それをどうやって機械的に判別するかが今回の質問になります。

一般的な言語では、TZやサマータイムかどうかを表す定数などがありますが、今回は使えないのです(Cをかなりシンプルにしたような言語のため)。

第n曜日という決まりがあるので、第n曜日を取得するアルゴリズムがわかれば実装できそうなんですけどねぇ…。

2007/08/19 15:25:31
id:j1960 No.4

j1960回答回数322ベストアンサー獲得回数212007/08/19 16:32:58

ポイント15pt

要件があいまいなのですが、何かのマシンにソフトを走らせて取得した日付と時間からそのマシンの置いてある国を特定して今サマータイムかどうかを判定するということですよね?

なお、使える関数は、年の取得関数・月の取得関数・日の取得関数・時の取得関数・分の取得関数・秒の取得関数・曜日の取得関数のみです。

使える関数が上記だけなら不可能です。

TZが取得できないということであれば現在「どこ」なの分からないということですから。

それとも、JST(TZ-9)の時計が動いているマシンがあって、国名とかを指定するとその国ではサマータイム中がどうかを教えてくれる、ということでしょうか?

だとするとアルゴリズムでは無理です。

まず、TZの指定だけではサマータイムかどうかは判定できません。

サマータイムを決めるのは国家です。

国家にひとつのタイムゾーンとは限りません。

例えば、オーストラリアはタイムゾーンを3ないしは4持っています。

しかも州によって夏時間があったり無かったりします。さらに夏時間の定義は国によって異なりますし、政治的な都合でしばしば変更されます。

これらをテーブルを引く方法以外に何らかのアルゴリズムで定義することは不可能です。


http://www.linux.or.jp/JM/html/LDP_man-pages/man2/gettimeofday.2...

http://man.cx/settimeofday(2)/ja

id:wizemperor

そうですね、なので質問してみました。ただ、ちょっと質問が曖昧でしたね。

想定しているタイムゾーンはアメリカの東部時間(EST, EDT)です。

基本的に自分で使うものなので、今から100年前だとか、今後数十年とかまで通用するアルゴリズムではなくてもいいです。

国・タイムゾーンは自分で指定するので、1つの国・タイムゾーンに固定になります。

アメリカ(GMT-5、夏はGMT-4)の日付時間を渡すと、夏時間かどうか判別したいということになります。

アメリカ東部であれば、第何曜日というのが決まっているので、ある日付が月の第何曜日かわかれば、できると思ったんですが…。

2007/08/19 22:46:45
id:noocyte No.5

noocyte回答回数21ベストアンサー獲得回数32007/08/19 19:51:15ここでベストアンサー

ポイント300pt

四則演算と剰余は使っていいんですか?


夏時間についてはよく知りませんが,

Wikipedia を参考に判定プログラムを書いてみました.


  • 現在日時:month 月 day 日 (w曜日) hour 時
  • 夏時間開始:startMonth 月 startN 回目の startW 曜日 startHour 時
  • 夏時間終了:endMonth 月 endN 回目の endW 曜日 endHour 時

であれば,次のようにして判定できると思います.ただし,


  • 現在日時がUTCか地方時かは,当該国の夏時間の定義に応じて調整済とする.
  • USA 等のように夏時間の期間中時計を1時間進める国では,現在日時は

その補正を行う前の値とする.(そうでないと期間終了時の1時間が判定不能となる.)

  • 曜日番号は一応日=0,~,土=6とするが,0~6の範囲であれば何曜から始めてもよい.

Bool IsSummerTime(int month, int day, int w, int hour,
                  int startMonth, int startN, int startW, int startHour,
                  int endMonth, int endN, int endW, int endHour)
{
  int w1;     // month月1日の曜日
  int dstDay; // 夏時間開始または終了日付

  // デバッグ用
  printf("\n%d月%d日(%d曜) %d時は夏時間?\n", month, day, w, hour);
  assert((1 <= day) && (day <= 31));
  assert((0 <= w) && (w <= 6));
  assert((0 <= startW) && (startW <= 6));
  assert((0 <= endW) && (endW <= 6));

  if((month < startMonth) || (endMonth < month)) goto NotDST;

  // month月1日の曜日w1を求める.day=1 ならば w1=w で,
  // dayが1日増えるごとにw1は1日前にずれるので,数学的には
  //   w1 = (w - (day - 1)) mod 7
  // しかしC言語の場合は被除数が負になるとまずいので,
  // 負にならないようにするための最小の7の倍数35を足して
  w1 = (w + 36 - day) % 7;

  // デバッグ用
  printf("%d月%d日(%d曜) → 1日は%d曜\n", month, day, w, w1);

  if(month == startMonth) {
    // month月のstartN回目のstartW曜日の日付dstDayを求める.
    dstDay = NthDayOfWeekToDay(startN, startW, w1);

    // デバッグ用
    printf("夏時間開始:%d月%d日(%d曜) %d時\n",
           startMonth, dstDay, startW, startHour);

    // (day, hour) が (dstDay, startHour) より前ならば夏時間ではない.
    if((day < dstDay) || ((day == dstDay) && (hour < startHour)))
      goto NotDST;
  }
  if(month == endMonth) {
    // month月のendN回目のendW曜日の日付dstDayを求める.
    dstDay = NthDayOfWeekToDay(endN, endW, w1);

    // デバッグ用
    printf("夏時間終了:%d月%d日(%d曜) %d時\n",
           endMonth, dstDay, endW, endHour);

    // (day, hour) が (dstDay, startHour) 以後ならば夏時間ではない.
    if((day > dstDay) || ((day == dstDay) && (hour >= endHour)))
      goto NotDST;
  }

  printf("夏時間だよ.\n");         // デバッグ用
  return TRUE;

NotDST:
  printf("夏時間じゃないよ.\n");   // デバッグ用
  return FALSE;
}

/*--------------------------------------------------------------------------
IsSummerTime() のテスト.
--------------------------------------------------------------------------*/
void IsSummerTimeTest(void)
{
  // USA の夏時間
  const int startMonth = 3;
  const int startN = 2;
  const int startW = 0;
  const int startHour = 2;
  const int endMonth = 11;
  const int endN = 1;
  const int endW = 0;
  const int endHour = 1; // 夏時間補正前の時刻

#define ISSUMMERTIME(month, day, w, hour, isDST) \
  assert(IsSummerTime((month), (day), (w), (hour), \
                      startMonth, startN, startW, startHour, \
                      endMonth, endN, endW, endHour) \
         == (isDST))

  // 2007年の場合
  ISSUMMERTIME( 2, 28, 3, 23, FALSE); // 開始月の前月
  ISSUMMERTIME( 3, 10, 6, 23, FALSE); // 開始月,開始日より前
  ISSUMMERTIME( 3, 11, 0,  1, FALSE); // 開始日,開始時刻より前
  ISSUMMERTIME( 3, 11, 0,  2, TRUE ); // 開始日開始時刻ピッタリ
  ISSUMMERTIME( 3, 11, 0,  3, TRUE ); // 開始日,開始時刻より後
  ISSUMMERTIME( 3, 12, 1,  0, TRUE ); // 開始月,開始日より後
  ISSUMMERTIME( 4,  1, 0,  0, TRUE ); // 開始月の翌月
  ISSUMMERTIME(10, 31, 3, 23, TRUE ); // 終了月の前月
  ISSUMMERTIME(11,  3, 6, 23, TRUE ); // 終了月,終了日より前
  ISSUMMERTIME(11,  4, 0,  0, TRUE ); // 終了日,終了時刻より前
  ISSUMMERTIME(11,  4, 0,  1, FALSE); // 終了日,終了時刻ピッタリ
  ISSUMMERTIME(11,  4, 0,  2, FALSE); // 終了日,終了時刻より後
  ISSUMMERTIME(11,  5, 1,  0, FALSE); // 終了月,終了日より後
  ISSUMMERTIME(12,  1, 6,  0, FALSE); // 終了月の翌月
}

/*--------------------------------------------------------------------------
実行結果
--------------------------------------------------------------------------*/
2月28日(3曜) 23時は夏時間?
夏時間じゃないよ.

3月10日(6曜) 23時は夏時間?
3月10日(6曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間じゃないよ.

3月11日(0曜) 1時は夏時間?
3月11日(0曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間じゃないよ.

3月11日(0曜) 2時は夏時間?
3月11日(0曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間だよ.

3月11日(0曜) 3時は夏時間?
3月11日(0曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間だよ.

3月12日(1曜) 0時は夏時間?
3月12日(1曜) → 1日は4曜
夏時間開始:3月11日(0曜) 2時
夏時間だよ.

4月1日(0曜) 0時は夏時間?
4月1日(0曜) → 1日は0曜
夏時間だよ.

10月31日(3曜) 23時は夏時間?
10月31日(3曜) → 1日は1曜
夏時間だよ.

11月3日(6曜) 23時は夏時間?
11月3日(6曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間だよ.

11月4日(0曜) 0時は夏時間?
11月4日(0曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間だよ.

11月4日(0曜) 1時は夏時間?
11月4日(0曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間じゃないよ.

11月4日(0曜) 2時は夏時間?
11月4日(0曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間じゃないよ.

11月5日(1曜) 0時は夏時間?
11月5日(1曜) → 1日は4曜
夏時間終了:11月4日(0曜) 1時
夏時間じゃないよ.

12月1日(6曜) 0時は夏時間?
夏時間じゃないよ.
id:wizemperor

質問が曖昧、条件が厳しい中、コードまで書いていただきまして、ありがとうございます。四則演算と剰余は使えます。

第何曜日かを求める関数がなく悩んでいましたが、NthDayOfWeekToDay関数は助かりました。

とても汎用的なコードを教えていただけたので、問題なく実装できそうです。ありがとうございました。

2007/08/19 23:06:00
id:tezcello No.6

tezcello回答回数460ベストアンサー獲得回数692007/08/19 20:13:37

ポイント10pt

第1日目の曜日を求める事が出来れば、当月の第n曜日との関係は求まりますよね?

曜日を求めるって事で検索したら、以下の様なのが見つかりました。

http://sapporo.cool.ne.jp/daturyoku/HSP/module/index.html

http://www2u.biglobe.ne.jp/~MAS/perl/waza/getwday.html


特定の地域で、サマータイムの期間が判っているという前提なんですよね?

去年まではサマータイムを使っていなかったのに今年から採用する(及びその逆)とか、期間が変更になるとかいろいろある様ですから、過去のある時点がサマータイム期間中であったかどうかは、テーブルを作らないとどうにもならないかも知れません。

将来の変更に関しては、その都度ソースの変更が必要ですね。

id:wizemperor

曜日の取得関数はあるので問題ないです。

ただ、その曜日が第何曜日なのか?というのが問題ですね。

遡って調べるとかも考えたのですが、いまいち効率が悪い気がしました。

その都度のソースの修正は問題ないです。

2007/08/19 22:51:20
  • id:wizemperor

    あらかじめサマータイムの日付テーブルを配列で用意しておくなど考えたのですが、面倒です。(配列は1次元配列に限って使えます)

    年・月・日・時・分・秒・曜日のみを使って、ある日付時間が第n曜日かを取得するためのアルゴリズムがわかれば、うまくいきそうなんですが…。

    日付型もあるC言語に近い言語になります。
    質問の関数以外には、if, while, for, switch, 変数, 1次元配列などが使えます。
  • id:kk_solanet
    サマータイム自体が結構流動的な制度だったりするので、機械的なロジックで求めるのはかなり面倒な気が…

    http://www.ibm.com/support/alerts/jp/daylightsavingstimealert.html
  • id:wizemperor
    2007年以降については、条件分岐すればなんとかなるかなあとも思ったのですが…。
    あまり古い日付は扱わないので、2007年以前、2007移行でも十分なんですけど、地道に曜日をたどって調べるしかないですかねえ…。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません