”あと何日”

Arduino に関するご質問などはこちらへ。

”あと何日”

投稿記事by OKA » 2012年8月20日(月) 20:08

引き続き・・・悩んでおります(^^;

”あと何日”というものを作ろうとRTCで時刻を管理し
300日など日数から引き算していくのは理解できるのですが
現在から設定した特定の日時までのカウントダウン値を求める場合は
うるう年のときは○○で、2月4月6月9月11月が30日と計算させる方法以外に
ライブラリーや関数などはあるものなのでしょうか?
何かいい方法がありましたらお教えください。

宜しくお願いします。
OKA
 
記事: 6
登録日時: 2012年8月18日(土) 16:47

Re: ”あと何日”

投稿記事by yamaguch » 2012年8月21日(火) 13:43

OKA さんが書きました:現在から設定した特定の日時までのカウントダウン値を求める場合は
うるう年のときは○○で、2月4月6月9月11月が30日と計算させる方法以外に
ライブラリーや関数などはあるものなのでしょうか?


来年の元旦までとか、リオのオリンピック(2016年8月5日)までの日数を簡単に計算したいということですね。
大の月と小の月が2か月ごとでないのと、閏年が4年に一回(と、100年、400年毎になかったりあったり)あって二月の日数が変わるという処理が面倒くさそうですが、ちょっとした工夫で簡単に計算できます。

まず、1年を三月から始まって二月(14月)に終わるように変更します。こうすると、二月の最終日をまたぐ処理をしなくてよくなるので、ずいぶん簡単になります。

次に、大の月と小の月の日数ですが、三月から一月までの各月の日数から 30 を引いた数を順に並べると
 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1
となり、5 が周期になっています。
これを積算した数列を作ると
 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7
となります。
この数列を表す式を求めてみましょう。大雑把には 5 進むと 3 増えるような数列です。
傾きが 3/5 の直線を使って、うまく近似できないでしょうか。
 y = (3/5) x + a
のような式です。少し試行錯誤をしてみると
 y = (3 * x) / 5 + 1
という式で, x を 0 から 10 まで変化させると上の数列が出てくることが分かります。
ここで割り算結果の小数部分は切り捨てです。

これを使うと、3月1日から(3 + x) 月末日までの日数が次のように求まります。
 30 * (x + 1) + (3 * x) / 5 + 1

そして 1900年3月1日から yyyy 年 mm 月 dd 日までの日数 days は次の式で計算できます。
(「年始」の 3月だけは上の寄与がありません。これは3項演算子を使って除外しています)
コード: 全て選択
   int y = yyyy - 1900, m = mm;
   if (m < 3) {
      m = 12 + mm;
      y = y - 1;
   }
   int x = m - 4;
   int days = 365 * y + y / 4 + ((x >= 0) ? 30 * (x + 1) + 3 * x / 5 + 1 : 0) + dd - 1;

2100年は閏年ではないので、この式は 2100年2月までしか使えません。

私はそんなに長生きしないのでこれで十分ですが、長生きしようと思っている人は、上の days に百年単位の補正項
 - y / 100 + y / 400
を追加してください。

山口
yamaguch
 
記事: 482
登録日時: 2010年7月06日(火) 17:37

Re: ”あと何日”

投稿記事by yamaguch » 2012年8月21日(火) 15:02

おまけ(検算用)です。
1900年3月1日からの日数(days)から yyyy 年 mm 月 dd 日を知るには次のようにします。
コード: 全て選択
#define min(x, y) ((x) < (y) ? (x) : (y))

     int y1 = days / 1461;
     int y2 = min(days % 1461 / 365, 3);
     
     int d1 = days - 1461 * y1 - 365 * y2;
     int d2 = d1 % 153;
     
     int m1 = d1 / 153;
     int m2 = 5 * d2 / 153;
     
     int dd = d2 - (153 * m2 + 2) / 5 + 1;
     int mm = 5 * m1 + m2 + 3;
     int yyyy = 1900 + 4 * y1 + y2;
     
     if (mm > 12) {
         yyyy += 1;
         mm -= 12;
     }

前と同じように閏年の処理を簡単にするために3月から始めています。
このとき大小の月の出現パターンは 5 か月周期になりますが、日に直すと 153 日です。

これも2100年2月までで、長生きしたい人は補正が必要です ;)

山口
最後に編集したユーザー yamaguch [ 2012年8月24日(金) 17:27 ], 累計 1 回
yamaguch
 
記事: 482
登録日時: 2010年7月06日(火) 17:37

Re: ”あと何日”

投稿記事by OKA » 2012年8月22日(水) 15:31

山口さん
ありがとうございます。

数百行はあるのかと覚悟していたのですが驚きです。
やはり数学からやり直さないと太刀打ちできないようです。
何となく理解できるので時間をかけてやってみます。
OKA
 
記事: 6
登録日時: 2012年8月18日(土) 16:47

Re: ”あと何日”

投稿記事by yamaguch » 2013年11月05日(火) 13:29

昨日、MFT の会場で OKA さんと久しぶりに話して、ふと思い出しました。
虫の知らせというやつです。

前のコードを使った方、いらしたらごめんなさい。
int m2 = (5 * d2 + 2) / 153;
の計算が間違っていました。

こちらが訂正版です。

days を 1900 年 3 月 1 日からの経過日数とするとき、年月日は yyyy/mm/dd になります。

コード: 全て選択
#define min(x, y) ((x) < (y) ? (x) : (y))

     int y1 = days / 1461;
     int y2 = min(days % 1461 / 365, 3);
     
     int d1 = days - 1461 * y1 - 365 * y2;
     int d2 = d1 % 153;
     
     int m1 = d1 / 153;
     int m2 = (5 * d2 + 2) / 153;
     
     int dd = d2 - (153 * m2 + 2) / 5 + 1;
     int mm = 5 * m1 + m2 + 3;
     int yyyy = 1900 + 4 * y1 + y2;
     
     if (mm > 12) {
         yyyy += 1;
         mm -= 12;
     }

ちゃんとテストしてなかったことがバレバレですね :D

山口
yamaguch
 
記事: 482
登録日時: 2010年7月06日(火) 17:37


Return to Arduino 質問箱

cron