コグノスケ


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の出力を簡潔化する方法がわからない

目次: Linux

最近は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でログを抑制する方法はないのでしょうか?

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

編集者:すずき(2025/10/09 23:58)

コメント一覧

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



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

管理用メニュー

link 記事を新規作成

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

最近のコメント5件

  • link 25年10月6日
    すずきさん (10/10 13:14)
    「ですね。ccはもはやコンパイラというより...」
  • link 25年10月6日
    hdkさん (10/10 08:27)
    「ただのHello, worldでも試して...」
  • link 25年9月29日
    すずきさん (10/03 00:29)
    「なんと、メタパッケージ入れてなかったです...」
  • link 25年9月29日
    hdkさん (10/02 06:51)
    「あれ、dkmsは自動ビルドされるのが便利...」
  • link 20年8月24日
    すずきさん (08/30 22:06)
    「ですね、自分も今はPulseAudioを...」

最近の記事20件

  • link 25年10月15日
    すずき (10/19 16:54)
    「[PipeWireの音切れ問題 - サーバー側の設定確認と反映] 目次: ALSAPipeWireに変えてから音切れがなくなり...」
  • link 25年10月18日
    すずき (10/19 16:52)
    「[PipeWireの音切れ問題 - サーバー側PipeWireの設定] 目次: ALSAPipeWireに変えてから音切れがな...」
  • link 22年5月5日
    すずき (10/19 16:49)
    「[ALSA - まとめリンク] 目次: ALSAALSAの話。ALSAその1 - 使ってみようALSAその2 - カードとデバ...」
  • link 25年10月19日
    すずき (10/19 16:47)
    「[PipeWireの音切れ問題 - サーバー側pipewire-pulseの設定] 目次: ALSA未だにPipeWireの音...」
  • link 23年4月11日
    すずき (10/19 14:55)
    「[ブラウザー/メーラー - まとめリンク] 目次: ブラウザー/メーラー関係の深いまとめリンク。目次: Linuxブラウザー。...」
  • link 06年4月22日
    すずき (10/19 14:54)
    「[Seamonkey/Firefoxのメモリ使用量最小化] 目次: ブラウザー/メーラーGIGAZINE 2006年4月15日...」
  • link 25年10月17日
    すずき (10/19 14:53)
    「[Linux版Firefoxアドレスバーの黒線] 目次: ブラウザー/メーラーLinuxデスクトップマシンのFirefox(1...」
  • link 25年10月6日
    すずき (10/16 03:20)
    「[makeのデフォルトルールのリンクはLDを使わない] 目次: LinuxMakefileの達人には常識かもしれませんが、ma...」
  • link 25年10月13日
    すずき (10/16 03:19)
    「[PipeWireをPulseAudioサーバーの代わりにする、その2 - 音切れ多発] 目次: ALSA先日(2025年10...」
  • link 20年10月23日
    すずき (10/16 03:15)
    「[ARM - まとめリンク] 目次: ARMROCK64ブート周りの話のまとめ。ROCK64購入ROCK64とU-Bootのd...」
  • link 25年10月12日
    すずき (10/16 03:14)
    「[ROCK 5BのDebianをアップデート] 目次: ARMDebian StableがTrixieに変わったことをすっかり...」
  • link 20年8月29日
    すずき (10/16 02:36)
    「[ROCK64をRaspberry Pi 3のケースに無理やり格納] 目次: ARM先日KADHAS VIM2, VIM3を購...」
  • link 19年1月13日
    すずき (10/16 02:35)
    「[ROCK64とTinker BoardのU-Boot設定メモ] 目次: ARM完全に自分のためのメモですが、ROCK64とT...」
  • link 18年10月13日
    すずき (10/16 02:35)
    「[Welcome back ROCK64] 目次: ARMやっとROCK64を置けそうな場所を確保できて、Linuxカーネルで...」
  • link 18年9月3日
    すずき (10/16 02:35)
    「[ROCK64のカーネルだけ元に戻したい] 目次: ARMROCK64のカーネルを入れ替えていたら、動かないカーネルを書き込ん...」
  • link 18年8月20日
    すずき (10/16 02:35)
    「[ROCK64のU-Bootスクリプトを読む] 目次: ARM先日(2018年7月23日の日記参照)ROCK64のU-Boot...」
  • link 18年7月23日
    すずき (10/16 02:35)
    「[ROCK64とU-Bootのdistro boot] 目次: ARM昨日(2018年7月22日の日記参照)に引き続き、ROC...」
  • link 18年7月22日
    すずき (10/16 02:34)
    「[ROCK64購入] 目次: ARMRockchip RK3328搭載のシングルボードコンピュータ、ROCK64 4GB版を買...」
  • link 18年10月14日
    すずき (10/16 02:34)
    「[ROCK64のアナログオーディオ - その1 - I2Sとクロックレジスタ] 目次: ARMまた忘れてしまいそうなので、メモ...」
  • link 18年11月10日
    すずき (10/16 02:33)
    「[ROCK64のアナログオーディオ - その2 - I2SのDMA問題] 目次: ARM[ROCK64のI2Sが動かない] 先日(2...」
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 2025年
open/close 過去日記について

その他の情報

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

合計:  counter total
本日:  counter today

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

最終更新: 10/19 16:54