アプリ版:「スタンプのみでお礼する」機能のリリースについて

2038年問題で困っています。
指定した日付から指定した日数後および前(マイナス日数)の日付を取得するCのプログラムがあります。
これまでmktime関数をして算出していましたが、2038年以降の日付を指定するとエラーになるようです。
上記関数をmktime関数を使用せず自前で作成しないといけないのですが、どう作っていいか思いつきません。どなたか参考になるようなサイト、およびサンプルプログラムがあれば教えていただきたいと思っています。
よろしくお願いします。

A 回答 (5件)

★アドバイス


・一般的に次の方法を取ります。
 (1)年月日を西暦1年1月1日からの経過日数に変換
 (2)経過日数に(期間の)日数を加算/減算
 (3)西暦1年1月1日からの経過日数を年月日に変換
 このうち(3)が最も面倒な作業になります。多分。
 下に(3)だけですがサンプルを載せておきます。

サンプル:
// 閏年の判定
int isLeapYear( int year )
{
 if ( !(year % 3200) ) return 0;
 if ( !(year % 400) ) return 1;
 if ( !(year % 100) ) return 0;
 return !(year & 0x3);
}

// 経過日数から年月日を求める
int sample3( int days, int *year, int *month, int *day )
{
 static int table[ 13 ] = {
  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
 }; int Y, M, D, ddd;
 
 // [日]コピー
 D = days;
 // [年]算出
 for ( Y = 1 ; D > (ddd = (isLeapYear(Y) ? 366 : 365)) ; Y++ ){
  D -= ddd;
 }
 // [月]算出
 table[ 2 ] = ((ddd == 365) ? 28 : 29);
 for ( M = 1 ; D > table[M] ; M++ ){
  D -= table[ M ];
 }
 // セット
 *year = Y;
 *month = M;
 *day = D;
 return days % 7; // 曜日を返す(0-6)
}

その他:
・上記のソースは『経過日数を年月日に変換』する考え方のサンプルです。
 そのため計算スピードが低速で実用にはなりません。
 その代わりに正しく変換してくれます。
 答え合わせようの関数です。
・実際に利用するためには『年の算出』を除算と剰余を使って計算させます。
 考え方は
 (1)経過日数から3200年間の日数(1168776日)を幾つ引けるかを除算で求める。
 (2)(1)のあまりの経過日数から400年間の日数(146097日)を幾つ引けるかを除算で求める。
 (3)(2)のあまりの経過日数から100年間の日数(36524日)を幾つ引けるかを除算で求める。
 (4)(3)のあまりの経過日数から4年間の日数(1461日)を幾つ引けるかを除算で求める。
 (5)(4)のあまりの経過日数から1年間の日数(365日)を幾つ引けるかを除算で求める。
 (6)(1)-(5)までで求めた除算の商と年数を掛けて加算すると『年』が求まる。
 (7)(5)のあまりが年内の1月1日からの通算日数となります。これを『月』『日』に分ける。
・詳しい解説などは下の『参考URL』をどうぞ。
 そこで紹介している『経過日数から年月日と曜日を求める』なら実用的なスピードで
 計算してくれます。ただし有効範囲は3199年までです。3200年以降は1日ずつずれます。
 これは3200年以降の閏年を考慮していないためです。でも誤差1日ですが。
・以上。

参考URL:http://cl.is.kyushu-u.ac.jp/Literacy/PP/H14/adp/ …
    • good
    • 0

#3 Oh-Orange さん.



> 3200年以降の閏年

グレゴリオ暦に「3200年ごとの閏年」は存在しません.
グレゴリオ暦は400年周期です.

閏年と旧暦について (国立天文台)
http://www.nao.ac.jp/koyomi/topics/html/topics19 …

グレゴリオ暦 (Wikipedia)
http://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AC% …


「3200年閏年説」の発端は,グレゴリオ暦と太陽年の誤差にあると
考えて間違いないでしょう.

1太陽年 (平均回帰年) は365.2422日ですが,グレゴリオ暦の1年は
平均で 365 + 1/4 - 1/100 + 1/400 = 365.2425 日なので少し
誤差があります.

そこでグレゴリオ暦の平均年を太陽年に一致させるために,3200年
および80000年ごとの閏年の規則を追加すると「仮定すると」,
365 + 1/4 - 1/100 + 1/400 - 1/3200 + 1/80000 = 365.2422
となってピッタリになります.

ただし以上はあくまでも仮定の上での計算ですし,実際には地球の
自転はわずかずつ遅くなっているので,1太陽年も次第に長くなって
いるそうです.だから仮に3200年の規則があったとしても,それが
発動する頃には無意味になってしまっているどころか,発動すること
で却って誤差を大きくしてしまうでしょう.
(3200年+80000年規則により太陽年を逆に「縮めてしまう」わけですから.)


実際には,グレゴリオ暦と実際の太陽年の間の調整は「閏秒」で行います.
原子時計の時刻 (協定世界時 UTC) と地球の自転に基づく世界時 (UT1)
との間の誤差が常に±0.9秒以内になるように,必要に応じて1秒を挿入
または削除します.

閏秒は必要に応じて随時挿入/削除されるため,事前に計算することは
できません.当然,暦の規則とも無関係です.
(2004年のスマトラ島沖大地震も,地球の自転速度に影響を与えたそうです.)

国立天文台 アストロ・トピックス (167)
7年ぶりの「うるう秒」
http://www.nao.ac.jp/nao_topics/data/000167.html

閏秒 (Wikipedia)
http://ja.wikipedia.org/wiki/%E9%96%8F%E7%A7%92


そういうわけで,#3 の参考 URL のアルゴリズムに間違いはないはずです.
(細かくは見てませんが.)

ただし,せっかくアルゴリズムの説明にガウス記号 [x] (=床(floor)関数) を
使って正しい計算式を示しているにもかかわらず,Cのソースではそれが
単なる / 演算子になっているので,y<0 のとき結果が正しいかどうかは
処理系依存です (おそらく正しくない場合が多い).

床関数 (Wikipedia)
http://ja.wikipedia.org/wiki/%E5%BA%8A%E9%96%A2% …
    • good
    • 0

2038年問題は、time_tが従来の32bitの場合に発生する問題です。


処理系依存で、すでに64bitに以降したものも多くあるようです。
たとえば
MS VisualC++では、2005以降はすでに64ビットに以降済みだそうです。

質問者の処理系がなにかはわかりませんが、最新版にアップグレード
するか、有償のものであればベンダーに問い合わせみてはいかがでしょうか?

参考URL:http://ja.wikipedia.org/wiki/2038%E5%B9%B4%E5%95 …
    • good
    • 0

ユリウス日 (または姉妹品) ⇔ グレゴリオ暦相互変換の計算式やソースを


公開しているサイトはいくつもあるけど,そのアルゴリズムを説明している
サイトってほとんど見あたらないんですよね….
(私の探し方が悪いだけ?
 まあ,昔自分で考えたからそこまで必死に探す気もないんだけど.)


> 2038年以降の日付

ということなので今回の質問には関係ないんですが,
公開されているソースの中には紀元前の日付を正しく計算できない
(作者がそこまで仕様に入れていない?) ものも多いので,
そういう用途に使う場合には注意してくださいね.
(検索する場合にはキーワード "floor" を追加するといいと思います.)


> 参考になるようなサイト、

アルゴリズムの説明ならば,

グレゴリオ暦/ユリウス暦 ⇔ ユリウス日 (または一般の通算日数) 変換アルゴリズム
http://www5d.biglobe.ne.jp/~noocyte/Programming/ …


> およびサンプルプログラム

上のページのリンク集のいくつかにあったはず….
    • good
    • 0

わかりやすいのは, 「ある日からの経過日数」(通日) を計算することでしょうね.


天文学的には「ユリウス通日」(JD) とか「修正ユリウス通日 (あるいは準ユリウス通日)」(MJD) を使うのが普通で, 検索すればいろいろあると思います. wikipedia にもあるので特に URL は示しません.
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!