mk-mode BLOG

このブログは自作の自宅サーバに構築した Debian GNU/Linux で運用しています。
PC・サーバ構築等の話題を中心に公開しております。(クローンサイト: GitHub Pages

ブログ開設日2009-01-05
サーバ連続稼働時間
Reading...
Page View 合計
Reading...
今日
Reading...
昨日
Reading...

Ruby - フリーゲルの公式で日数計算!

[ プログラミング ] [ Ruby, カレンダー ]

こんばんは。

2年ぐらい前に、「フリーゲルの公式」を使って異なる2つの日付の「修正ユリウス日」を算出し2つの日付の日数差を求める方法について紹介しました。

今日は、その公式を使用して異なる2つの日付の日数差(第2日付の第1日付からの経過日数)を求める Ruby スクリプトを紹介します。(実際、単純に公式を当てはめて計算しているだけです)

0. 前提条件

  • Ruby 2.0.0-p247 で作業・動作確認済。
  • ユリウス日については「* 日数計算の方法!」でも説明している。
    • ユリウス日 … 紀元前4713年01月01日正午からの経過日数
    • 修正ユリウス日 … 西暦1858年11月17日午前0時からの経過日数(= ユリウス日 - 2400000.5 日した日数)
  • 計算対象の日付は西暦の日付に限定する。
  • 時・分・秒は考慮しない。

1. Ruby スクリプト

以下のように Ruby スクリプトを作成した。

calc_num_of_days.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
require 'date'

# 使用方法
USAGE = <<"EOS"
USAGE : ruby calc_num_of_days.rb DATE_FROM DATE_TO
        - The formart of DATE_FROM and DATE_TO is "YYYYMMDD".
EOS

# [CLASS] 引数
class Arg
  def initialize
    @date_f = ""
    @date_t = ""
  end

  def check
    begin
      # 引数の数が2個以外はエラー
      if ARGV.length == 2
        # 日付桁数チェック
        return false unless ARGV[0].to_s =~ /^\d{8}$/
        return false unless ARGV[1].to_s =~ /^\d{8}$/

        # 日付妥当性チェック ( FROM )
        y_f = ARGV[0].to_s[0,4].to_i
        m_f = ARGV[0].to_s[4,2].to_i
        d_f = ARGV[0].to_s[6,2].to_i
        if Date.valid_date?(y_f, m_f, d_f)
          @date_f = ARGV[0].to_s
        else
          return false
        end

        # 日付妥当性チェック ( TO )
        y_t = ARGV[1].to_s[0,4].to_i
        m_t = ARGV[1].to_s[4,2].to_i
        d_t = ARGV[1].to_s[6,2].to_i
        if Date.valid_date?(y_t, m_t, d_t)
          @date_t = ARGV[1].to_s
        else
          return false
        end
      else
        return false
      end

      return true
    rescue => e
      STDERR.puts "[EXCEPTION][#{self.class.name}.check] #{e}"
      exit! 1
    end
  end

  def get_date(n)
    return n == 1 ? @date_f : @date_t
  end
end

# [CLASS] 計算
class Calc
  def initialize(date_f, date_t)
    @date_f = date_f
    @date_t = date_t
  end

  def calc
    begin
      # 日付 ( FROM )
      y = @date_f[0,4].to_i
      m = @date_f[4,2].to_i
      d = @date_f[6,2].to_i
      jd_f  = calc_jd(0, y, m, d)
      jd2_f = calc_jd(1, y, m, d)
      puts "[ #{y}#{sprintf("%02d", m)}#{sprintf("%02d", d)}日 ]"
      puts "\t    ユリウス日 : #{sprintf("%9.1f", jd_f)} 日"
      puts "\t修正ユリウス日 : #{sprintf("%9.1f", jd2_f)} 日"

      # 日付 ( TO )
      y = @date_t[0,4].to_i
      m = @date_t[4,2].to_i
      d = @date_t[6,2].to_i
      jd_t  = calc_jd(0, y, m, d)
      jd2_t = calc_jd(1, y, m, d)
      puts "[ #{y}#{sprintf("%02d", m)}#{sprintf("%02d", d)}日 ]"
      puts "\t    ユリウス日 : #{sprintf("%9.1f", jd_t)} 日"
      puts "\t修正ユリウス日 : #{sprintf("%9.1f", jd2_t)} 日"

      # 日数
      puts "[[ 経過日数 ]]\n\t#{jd_t - jd_f} 日"
    rescue => e
      STDERR.puts "[EXCEPTION][#{self.class.name}.calc] #{e}"
      exit 1
    end
  end

private

  # ユリウス日計算
  #   kbn : 0 ( ユリウス日 )
  #         1 ( 修正ユリウス日 )
  def calc_jd(kbn, y, m, d)
    jd  = (365.25 * y).truncate
    jd += (y / 400).truncate
    jd -= (y / 100).truncate
    jd += (30.59 * (m - 2)).truncate
    jd += d
    if kbn == 0
      jd += 1721088.5
    else
      jd -= 678912
    end
  end
end

#### MAIN ####

if __FILE__ == $0
  # 引数チェック( エラーなら終了 )
  obj_arg = Arg.new
  unless obj_arg.check
    puts USAGE
    exit!
  end

  # 日付取得
  date_f = obj_arg.get_date(1)
  date_t = obj_arg.get_date(2)

  # 日数計算
  obj_calc = Calc.new(date_f, date_t)
  obj_calc.calc
end

2. Ruby スクリプト実行

作成した Ruby スクリプトを実行してみる。

1
2
3
4
5
6
7
8
9
$ ruby calc_num_of_days.rb 20000101 20130817
[ 2000年01月01日 ]
            ユリウス日 : 2451544.5 日
        修正ユリウス日 :   51544.0 日
[ 2013年08月17日 ]
            ユリウス日 : 2456521.5 日
        修正ユリウス日 :   56521.0 日
[[ 経過日数 ]]
        4977.0 日

第2日付が第1日付から何日経過しているかの計算なので、2013/01/01 と 2013/01/02 の場合の計算結果は以下のように「1日」となる。
2013/01/02 が 今年の何日目にあたるかを計算したければ、計算結果に 1 を加算するか、第1日付を 2012/12/31 にする。

1
2
3
4
5
6
7
8
9
$ ruby calc_num_of_days.rb 20130101 20130102
[ 2013年01月01日 ]
            ユリウス日 : 2456292.5 日
        修正ユリウス日 :   56292.0 日
[ 2013年01月02日 ]
            ユリウス日 : 2456293.5 日
        修正ユリウス日 :   56293.0 日
[[ 経過日数 ]]
        1.0 日

参考サイト

当方の過去記事、過去に作成したカレンダー類スクリプト内のユリウス日計算ロジックを参照のこと。


日数計算メソッド等を使用すれば日数の計算は簡単にできますが、今回は基本に戻って忠実に計算(実際にメソッド内部で行なっている日数計算アルゴリズムで計算)してみました。

ちなみに、表計算ソフト等での日数計算も、内部ではこのアルゴリズム(ユリウス日を使った考え方)を使用して計算しているはずです。

以上。

Comments