コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2009年11月16日 >>> 2009年11月3日
link もっと後

2009年11月16日

経過日を年月日に変換

ある時点からの経過日(0基点)を年月日にする、なんていかにも学校の授業で出てきそうなアルゴリズムですが、世間では需要がないのか、名前が付いているようなアルゴリズムがありません。

練習問題にちょうど良かったので、いっちょ考えてみました。鈴木アルゴリズム完成!なーんて言えれば良かったんですけど、大した物はできませんでした。

その代わりといってはなんですが、考え方を細かくメモっていきたいと思います。今日は月日の変換、後日に年の変換を扱います。

文章をうだうだ読むのが面倒くせえ!って人のために、実装例も載せる予定です。ライセンスを特に明示していなければ、修正BSDライセンスを適用します。

グレゴリオ歴の性質

年月日のルールを決めるのは暦です。現在採用されている暦の主流は1582年頃に採用が始まった「グレゴリオ歴」です。下記の性質があります。

  • 1年は365日(平年)か366日(閏年)
  • 1年は12ヶ月
  • 1月は28日(平年2月)29日(閏年2月)30日(4,6,9,11月)31日(1,3,5,7,8,10,12月)のどれか
  • 1日は24時間
  • 1時間は60分
  • 1分は60秒

閏年は暦と地球の季節がずれないようにする仕組み(※1)です。もっと簡単に言うと、平年より1日長い年のことです。平年/閏年は下記のルールで決まります。

  1. 西暦が4で割り切れる年は閏年(1年が366日)
  2. 西暦が100で割り切れる年は平年(1年が365日)
  3. 西暦が400で割り切れる年は閏年(1年が366日)

変なルールに見えるかもしれませんが、人類が苦労して「時間とはなんぞや?暦とはなんぞや?」を定義してきた証なのでしょう。

(※1)地球の公転周期(1年)は自転周期(1日)の365倍と1/4日くらいです。単純に暦を1年 = 365日としてしまうと4年で1日分、季節と暦がずれてしまいます。その差を補正するために閏年が作られました。

邪魔な2月

初めに経過日→月日の変換を考えます。その際に邪魔なのが年によって長さが変わる2月です。

例として「1/1から60日後は何月何日か?」を考えてみます。平年(2月が28日間)ならば答えは3/2、閏年(2月が29日間)ならば答えは3/1です。2月の存在によって、経過日と月の対応関係がずれてしまうのです。

年によって計算方法を変えるのは面倒です。以下のように経過日の基準点を変えて一通りの計算方法で、経過日→月日を計算できるようにします。


3/1を経過日0日とし、2月を最後に持って行く

つまり3月を一年の始まり(正確には3/1を経過日0日とする)とし、来年の2月を当年の最終月14月として考えます。こうすると経過日と各月の対応関係が固定されるため、2月の長さが変化しても計算に影響が出ません(後ほどまた説明します)

経過日から月日への変換

では実際に経過日から月日を計算してみます。使うのは下記のテーブル(※2)です。

各月1日の経過日月の長さ
3 0 31日
4 31 30日
5 61 31日
6 92 30日
7 12231日
8 15331日
9 18430日
1021431日
1124530日
1227531日
1330631日
1433728 or 29日

テーブルの2列目は「その月の1日を表す経過日」を表しています。調べたい経過日と、2列目で大小比較すれば、調べたい経過日が何月なのかがわかります。何月なのかわかれば何日か?も簡単に分かります。

例として100を考えましょう。
まず92(6/1)< 100 < 122(7/1)から6月であることがわかります。
さらに100 - 92(6/1)= 8ですから6/1から8日後、つまり6/9だとわかります。

この計算方法の利点は、各月の1日に対応する経過日だけ考えれば計算できることです。さきほど2月の長さは気にしなくて良いと言った理由はここにあります。

2月は最終月のため、長さが変化しても各月の1日に対応する経過日は変化しません。というより各月の1日に対応する経過日を変化させないようにするために、わざわざ2月を最後に回したのです。

実装例

実装する関数の仕様は下記の通りです。

date
3/1からの経過日を渡します。値域は0〜365です。
month
変換後の月を返します。値域は3〜14(翌年2月)です。
days
変換後の日を返します。値域は1〜31です。
返り値
成功ならば0を返します。エラーが起きた場合は -1を返します。

この処理をC言語で書くと下記のようになります。

経過日から月日への変換、実装例

int date_to_month(int date, int *month, int *days)
{
        static int m_date[] = {
                0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337,
        };
        int m, d;
        int i;

        if (date < 0 || 365 < date) {
                return -1;
        }

        for (i = 0; i < 12; i++) {
                if (date < m_date[i]) {
                        break;
                }
        }
        m = i + 2;
        d = date - m_date[i - 1] + 1;

        if (month) {
                *month = m;
        }
        if (days) {
                *days = d;
        }

        return 0;
}

負の値や、1年(最長366日)以上の日数に対しては正常に動作しません。エラー処理として366以上の値や負の値を渡したときにエラーを返すこととします。

きちんと変換できるか、異常値に対してエラーを返すかどうか、-3から369までの経過日を与えてテストします。

経過日から月日への変換、テスト関数

int main()
{
        int m, d;
        int result, i;

        for (i = -3; i < 370; i++) {
                result = date_to_month(i, &m, &d);
                if (result == -1) {
                        printf("date:%3d -> error\n", i);
                        continue;
                }
                printf("date:%3d -> %2d/%2d\n", i, m, d);
        }

        return 0;
}

実行結果は下記の通りです。適当に端折ってあります。

経過日から月日への変換、実行結果
date: -3 -> error
date: -2 -> error
date: -1 -> error
date:  0 ->  3/ 1
date:  1 ->  3/ 2
...(略)...
date:100 ->  6/ 9
date:101 ->  6/10
date:102 ->  6/11
...(略)...
date:364 -> 14/28
date:365 -> 14/29
date:366 -> error
date:367 -> error
date:368 -> error
date:369 -> error

この関数は月のパラメータに13月やら14月を返します。しかし後ほど翌年の1月、2月へ変換してつじつまを合わせますので、ここでは何も変換しません。

ある月の1日を表す経過日を求めるには、下記の漸化式を用います。
(ある月の1日を表す経過日) = (前月の1日を表す経過日) + (前月の長さ)
要はテーブルの2列目(各月1日の経過日)と3列目(月の長さ)を足すと、次の月の1日を表す経過日が計算できるということです。

また今度

経過日から月日へ変換できたところで、また今度。次は経過日から年への変換を書く予定です。

編集者:すずき(2009/11/23 02:32)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2009年11月12日

重いだけ牧場

以前からmixiアプリのサンシャイン牧場というゲームをやっています。ゲームを一言で言えば「重い」です。とにかく重い。重すぎる。

その他にも下記のような特徴があります。

  • 開発元が中国だからなのか、漢字が変、日本語も変。
  • 何をするのもとにかく時間がかかる。
  • 突如、操作を受け付けなくなる。
  • 虫の数、作物の状態の表示が更新されない。
  • 操作中に「長時間操作していない」と言われ、タイトルに戻される。

どうもまともな部分がないな…。

ついに牧場ご臨終

そんなサンシャイン牧場ですが、今日は新たな一面を見せてくれました。


無人の牧場

畑は真っ白、牧場は無人、名前は「nullさん」だとさ。なんとまあ…。

最近、有料アイテム制度を追加しているようですが、こんな状態のまま金取るの?無神経すぎる。

編集者:すずき(2009/11/12 22:03)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2009年11月3日

Automakeの不細工な出力

最近はMakefileを自作せずに、GNU Toolchainの勉強も兼ねてGNU Automakeを使ってMakefileを生成しています。大したプログラムでもないのに ./configureとやるのはめんどくさいけど…。

Automakeで作ったMakefileはコマンドの実行履歴が大量に出ます。コンパイラに大量のオプションを渡すのと、そのオプションを全てログとして出力するためです。そのせいでコンパイラの警告やエラーがすっ飛んでいって消えてしまいます。

大量のログを見直すのはつらいです。いちいちスクロールバックしたり、パイプしてページャで見なければならないので、非常に面倒です。

Linuxの簡素な出力

この点優秀なのがLinux Kernel 2.6のビルドスクリプトです。単純にmakeとすると簡潔なログ、make V=1とすると詳細なログが出ます。

Linuxのmakeは以下のようなログが出ます。どのファイルに何をしている(CC, LD, CHKなど)か?だけが表示されます。

Linux Kernel 2.6のmake
$ make
scripts/kconfig/conf -s arch/x86/Kconfig
  CHK     include/linux/version.h
  UPD     include/linux/version.h
(略)
  UPD     include/linux/compile.h
  CC      init/version.o
...

同じ部分の詳細出力(make V=1)は以下のようになります。

Linux Kernel 2.6のmake V=1(80文字折り返し)
$ make V=1
make -f /home/katsuhiro/usr/src/linux-2.6.30/Makefile silentoldconfig
make -f scripts/Makefile.build obj=scripts/basic
mkdir -p include/linux include/config
make -f scripts/Makefile.build obj=scripts/kconfig silentoldconfig
  gcc  -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.
  o -lncurses
scripts/kconfig/conf -s arch/x86/Kconfig
rm -f include/config/kernel.release
echo 2.6.30 > include/config/kernel.release
set -e; : '  CHK     include/linux/version.h'; mkdir -p include/linux/;        (
echo \#define LINUX_VERSION_CODE 132638; echo '#define KERNEL_VERSION(a,b,c) ((
(a) << 16) + ((b) << 8) + (c))';) < /home/katsuhiro/usr/src/linux-2.6.30/Makefil
e > include/linux/version.h.tmp; if [ -r include/linux/version.h ] && cmp -s inc
lude/linux/version.h include/linux/version.h.tmp; then rm -f include/linux/versi
 on.h.tmp; else : '  UPD     include/linux/version.h'; mv -f include/linux/versi
 on.h.tmp include/linux/version.h; fi
(略)
  UPD     include/linux/compile.h
  gcc -Wp,-MD,init/.version.o.d  -nostdinc -isystem /usr/lib/gcc/i486-linux-gnu/
4.3.2/include -Iinclude  -I/home/katsuhiro/usr/src/linux-2.6.30/arch/x86/include
 -include include/linux/autoconf.h -D__KERNEL__ -Wall -Wundef -Wstrict-prototype
s -Wno-trigraphs -fno-strict-aliasing -fno-common -Werror-implicit-function-decl
aration -Os -m32 -msoft-float -mregparm=3 -freg-struct-return -mpreferred-stack-
boundary=2 -march=i686 -mtune=generic -Wa,-mtune=generic32 -ffreestanding -DCONF
IG_AS_CFI=1 -DCONFIG_AS_CFI_SIGNAL_FRAME=1 -pipe -Wno-sign-compare -fno-asynchro
nous-unwind-tables -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -fno-stack-protector -
fno-omit-frame-pointer -fno-optimize-sibling-calls -Wdeclaration-after-statement
 -Wno-pointer-sign -fwrapv   -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(
version)"  -D"KBUILD_MODNAME=KBUILD_STR(version)"  -c -o init/version.o init/ver
sion.c
...

普段はmakeのシンプルなログで異常がないかどうかだけをチェックします。ビルドエラーに遭遇したらmake V=1で詳細なコンパイルオプションを眺めて原因を考えます。便利ですねえ。

Automakeではできないの?

良いモノは見習いましょう。Automakeでログを抑制する方法はないのでしょうか?

残念ながら、ネットでちょこっと調べた限りではやり方がわかりませんでした。誰かご存じないですか?

編集者:すずき(2009/11/03 17:43)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2009年11月16日 >>> 2009年11月3日
link もっと後

管理用メニュー

link 記事を新規作成

<2009>
<<<11>>>
1234567
891011121314
15161718192021
22232425262728
2930-----

最近のコメント5件

  • link 24年4月22日
    hdkさん (04/24 08:36)
    「うちのHHFZ4310は15年突破しまし...」
  • link 24年4月22日
    すずきさん (04/24 00:37)
    「ちゃんと数えてないですけど蛍光管が10年...」
  • link 24年4月22日
    hdkさん (04/23 20:52)
    「おお... うちのHHFZ4310より後...」
  • link 20年6月19日
    すずきさん (04/06 22:54)
    「ディレクトリを予め作成しておけば良いです...」
  • link 20年6月19日
    斎藤さん (04/06 16:25)
    「「Preferencesというメニューか...」

最近の記事3件

  • link 24年2月7日
    すずき (04/24 02:52)
    「[複数の音声ファイルのラウドネスを統一したい] PCやデジタル音楽プレーヤーで音楽を聞いていると、曲によって音量の大小が激しく...」
  • link 24年4月22日
    すずき (04/23 20:13)
    「[仕事部屋の照明が壊れた] いきなり仕事部屋のシーリングライトが消えました。蛍光管の寿命にしては去年(2022年10月19日の...」
  • link 24年4月17日
    すずき (04/18 22:44)
    「[VSCodeとMarkdownとPlantUMLのローカルサーバー] 目次: LinuxVSCodeのPlantUML Ex...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 04/24 08:36