zakuro 0.0.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/README.md +102 -42
  4. data/doc/operation.md +25 -0
  5. data/doc/operation/csv/month.csv +202 -0
  6. data/doc/operation/operation.xlsx +0 -0
  7. data/doc/operation/transfer.rb +77 -0
  8. data/lib/zakuro/calculation/base/multi_gengou.rb +101 -0
  9. data/lib/zakuro/calculation/base/multi_gengou_roller.rb +218 -0
  10. data/lib/zakuro/calculation/base/year.rb +107 -0
  11. data/lib/zakuro/calculation/cycle/abstract_remainder.rb +459 -0
  12. data/lib/zakuro/calculation/cycle/abstract_solar_term.rb +151 -0
  13. data/lib/zakuro/calculation/cycle/zodiac.rb +106 -0
  14. data/lib/zakuro/calculation/monthly/first_day.rb +45 -0
  15. data/lib/zakuro/calculation/monthly/initialized_month.rb +120 -0
  16. data/lib/zakuro/calculation/monthly/month.rb +184 -0
  17. data/lib/zakuro/calculation/monthly/month_label.rb +88 -0
  18. data/lib/zakuro/calculation/monthly/operated_month.rb +201 -0
  19. data/lib/zakuro/calculation/range/full_range.rb +210 -0
  20. data/lib/zakuro/calculation/range/operated_range.rb +134 -0
  21. data/lib/zakuro/calculation/range/operated_solar_terms.rb +201 -0
  22. data/lib/zakuro/calculation/range/transfer/western_date_allocation.rb +76 -0
  23. data/lib/zakuro/calculation/range/transfer/year_boundary.rb +142 -0
  24. data/lib/zakuro/calculation/specifier/single_day.rb +109 -0
  25. data/lib/zakuro/calculation/summary/single.rb +129 -0
  26. data/lib/zakuro/condition.rb +17 -13
  27. data/lib/zakuro/era/japan/gengou.rb +106 -0
  28. data/lib/zakuro/era/japan/gengou/parser.rb +167 -0
  29. data/lib/zakuro/era/japan/gengou/type.rb +178 -0
  30. data/lib/zakuro/era/japan/gengou/validator.rb +236 -0
  31. data/lib/zakuro/era/japan/reki.rb +91 -0
  32. data/lib/zakuro/era/{gengou → japan/yaml}/set-001-until-south.yaml +0 -0
  33. data/lib/zakuro/era/{gengou → japan/yaml}/set-002-from-north.yaml +0 -0
  34. data/lib/zakuro/era/{gengou → japan/yaml}/set-003-modern.yaml +1 -1
  35. data/lib/zakuro/era/western.rb +1 -1
  36. data/lib/zakuro/merchant.rb +2 -2
  37. data/lib/zakuro/operation/month/parser.rb +373 -0
  38. data/lib/zakuro/operation/month/type.rb +453 -0
  39. data/lib/zakuro/operation/month/validator.rb +802 -0
  40. data/lib/zakuro/operation/operation.rb +66 -0
  41. data/lib/zakuro/operation/yaml/month.yaml +6452 -0
  42. data/lib/zakuro/output/error.rb +7 -4
  43. data/lib/zakuro/output/logger.rb +50 -47
  44. data/lib/zakuro/output/response.rb +146 -143
  45. data/lib/zakuro/result/core.rb +52 -0
  46. data/lib/zakuro/result/data.rb +187 -0
  47. data/lib/zakuro/result/operation.rb +114 -0
  48. data/lib/zakuro/result/result.rb +37 -0
  49. data/lib/zakuro/{output → tools}/stringifier.rb +16 -9
  50. data/lib/zakuro/tools/typeof.rb +33 -0
  51. data/lib/zakuro/version.rb +1 -1
  52. data/lib/zakuro/version/abstract_version.rb +1 -1
  53. data/lib/zakuro/version/context.rb +23 -0
  54. data/lib/zakuro/version/senmyou/cycle/remainder.rb +63 -0
  55. data/lib/zakuro/version/senmyou/cycle/solar_term.rb +31 -0
  56. data/lib/zakuro/version/senmyou/monthly/lunar_phase.rb +186 -182
  57. data/lib/zakuro/version/senmyou/range/annual_range.rb +164 -0
  58. data/lib/zakuro/version/senmyou/senmyou.rb +10 -4
  59. data/lib/zakuro/version/senmyou/stella/lunar_orbit.rb +7 -7
  60. data/lib/zakuro/version/senmyou/stella/solar_average.rb +103 -138
  61. data/lib/zakuro/version/senmyou/stella/solar_location.rb +213 -0
  62. data/lib/zakuro/version/senmyou/stella/solar_orbit.rb +6 -191
  63. data/lib/zakuro/version/senmyou/stella/winter_solstice.rb +4 -4
  64. data/lib/zakuro/version/version_class_resolver.rb +62 -0
  65. data/lib/zakuro/version_factory.rb +3 -3
  66. metadata +49 -20
  67. data/lib/zakuro/cycle/abstract_remainder.rb +0 -457
  68. data/lib/zakuro/cycle/zodiac.rb +0 -103
  69. data/lib/zakuro/era/japan.rb +0 -660
  70. data/lib/zakuro/output/result.rb +0 -219
  71. data/lib/zakuro/version/senmyou/base/era.rb +0 -83
  72. data/lib/zakuro/version/senmyou/base/multi_gengou.rb +0 -98
  73. data/lib/zakuro/version/senmyou/base/multi_gengou_roller.rb +0 -205
  74. data/lib/zakuro/version/senmyou/base/remainder.rb +0 -60
  75. data/lib/zakuro/version/senmyou/base/solar_term.rb +0 -66
  76. data/lib/zakuro/version/senmyou/base/year.rb +0 -104
  77. data/lib/zakuro/version/senmyou/monthly/month.rb +0 -122
  78. data/lib/zakuro/version/senmyou/summary/annual_range.rb +0 -186
  79. data/lib/zakuro/version/senmyou/summary/full_range.rb +0 -216
  80. data/lib/zakuro/version/senmyou/summary/specifier.rb +0 -100
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../era/japan/gengou'
4
+ require_relative '../../era/western'
5
+
6
+ # :nodoc:
7
+ module Zakuro
8
+ # :nodoc:
9
+ module Calculation
10
+ # :nodoc:
11
+ module Base
12
+ #
13
+ # MultiGengou 複数元号
14
+ #
15
+ class MultiGengou
16
+ # @return [Japan::Gengou] 元号(1行目)
17
+ attr_reader :first_line
18
+ # @return [Japan::Gengou] 元号(2行目)
19
+ attr_reader :second_line
20
+ # @return [Western::Calendar] 元旦
21
+ attr_reader :new_year_date
22
+
23
+ def initialize(first_line: Japan::Gengou.new, second_line: Japan::Gengou.new,
24
+ new_year_date: Western::Calendar.new)
25
+ @first_line = first_line
26
+ @second_line = second_line
27
+ @new_year_date = new_year_date
28
+ end
29
+
30
+ # :reek:TooManyStatements { max_statements: 7 }
31
+
32
+ #
33
+ # 改元する
34
+ #
35
+ # @param [Japan::Gengou] first_line 元号(1行目)
36
+ # @param [Japan::Gengou] second_line 元号(2行目)
37
+ #
38
+ # @return [MultiGengou] 自身
39
+ #
40
+ def transfer(first_line: Japan::Gengou.new, second_line: Japan::Gengou.new)
41
+ cloned_first_line = first_line.clone
42
+ cloned_second_line = second_line.clone
43
+
44
+ if integrated?(first_line: cloned_first_line, second_line: cloned_second_line)
45
+ @first_line = @second_line.clone
46
+ @second_line = cloned_second_line
47
+ end
48
+
49
+ @first_line = cloned_first_line if @first_line.name != first_line.name
50
+ @second_line = cloned_second_line if @second_line.name != second_line.name
51
+
52
+ self
53
+ end
54
+
55
+ #
56
+ # 次年にする
57
+ #
58
+ # @param [Japan::Gengou] first_line 元号(1行目)
59
+ # @param [Japan::Gengou] second_line 元号(2行目)
60
+ #
61
+ # @return [MultiGengou] 自身
62
+ #
63
+ def next_year
64
+ @first_line.next_year
65
+ @second_line.next_year
66
+
67
+ self
68
+ end
69
+
70
+ #
71
+ # ディープコピー
72
+ #
73
+ # @param [MultiGengou] obj 自身
74
+ #
75
+ def initialize_copy(obj)
76
+ @first_line = obj.first_line.clone
77
+ @second_line = obj.second_line.clone
78
+ end
79
+
80
+ private
81
+
82
+ #
83
+ # 複数元号を統一するかどうか
84
+ #
85
+ # @param [Japan::Gengou] first_line 元号(1行目)
86
+ # @param [Japan::Gengou] second_line 元号(2行目)
87
+ #
88
+ # @return [True] 統一する
89
+ # @return [False] 統一しない
90
+ #
91
+ def integrated?(first_line: Japan::Gengou.new, second_line: Japan::Gengou.new)
92
+ return false if @second_line.name != first_line.name
93
+
94
+ return false unless second_line.invalid?
95
+
96
+ true
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './multi_gengou'
4
+
5
+ require_relative '../../era/japan/gengou'
6
+
7
+ # :nodoc:
8
+ module Zakuro
9
+ # :nodoc:
10
+ module Calculation
11
+ # :nodoc:
12
+ module Base
13
+ #
14
+ # MultiGengouRoller 改元処理
15
+ #
16
+ class MultiGengouRoller
17
+ # @return [MultiGengou] 複数元号
18
+ attr_reader :multi_gengou
19
+ # @return [Western::Calendar] 元旦(元号が2つある場合は再過去の日付になる)
20
+ attr_reader :oldest_date
21
+ # @return [Western::Calendar] 元号内での最未来日(元号が2つある場合は再未来の日付になる)
22
+ attr_reader :newest_date
23
+ # @return [Western::Calendar] 現在日
24
+ attr_reader :current_date
25
+
26
+ def initialize(start_date: Western::Calendar.new, end_date: Western::Calendar.new)
27
+ end_date = start_date if end_date.invalid?
28
+
29
+ @oldest_date = MultiGengouRoller.choise_oldest_gengou_date(
30
+ first_line: MultiGengouRoller.first_line(date: start_date),
31
+ second_line: MultiGengouRoller.second_line(date: start_date)
32
+ )
33
+ @current_date = @oldest_date.clone
34
+ @newest_date = MultiGengouRoller.choise_newest_gengou_date(
35
+ first_line: MultiGengouRoller.first_line(date: end_date),
36
+ second_line: MultiGengouRoller.second_line(date: end_date)
37
+ )
38
+
39
+ @multi_gengou = MultiGengou.new(
40
+ first_line: current_first_line,
41
+ second_line: current_second_line,
42
+ new_year_date: @oldest_date
43
+ )
44
+ end
45
+
46
+ #
47
+ # 現在日付を未来に進める
48
+ #
49
+ # @param [<Type>] days <description>
50
+ #
51
+ # @return [<Type>] <description>
52
+ #
53
+ def next(days: 0)
54
+ @current_date += days
55
+ end
56
+
57
+ #
58
+ # 現在日から元号(1行目)を取得する
59
+ #
60
+ # @return [Japan::Gengou] 元号(1行目)
61
+ #
62
+ def current_first_line
63
+ MultiGengouRoller.first_line(date: @current_date)
64
+ end
65
+
66
+ #
67
+ # 現在日から元号(2行目)を取得する
68
+ #
69
+ # @return [Japan::Gengou] 元号(2行目)
70
+ #
71
+ def current_second_line
72
+ MultiGengouRoller.second_line(date: @current_date)
73
+ end
74
+
75
+ #
76
+ # 改元する
77
+ #
78
+ # @return [MultiGengou] 複数元号
79
+ #
80
+ def transfer
81
+ MultiGengouRoller.transfer(multi_gengou: @multi_gengou, date: @current_date)
82
+ end
83
+
84
+ #
85
+ # 次年にする
86
+ #
87
+ # @return [MultiGengou] 複数元号
88
+ #
89
+ def next_year
90
+ @multi_gengou.next_year
91
+ end
92
+
93
+ #
94
+ # 元号(1行目)を取得する
95
+ #
96
+ # @return [Japan::Gengou] 元号(1行目)
97
+ #
98
+ def self.first_line(date: Western::Calender.new)
99
+ Japan::GengouResource.first_line(date: date)
100
+ end
101
+
102
+ #
103
+ # 元号(2行目)を取得する
104
+ #
105
+ # @return [Japan::Gengou] 元号(2行目)
106
+ #
107
+ def self.second_line(date: Western::Calender.new)
108
+ Japan::GengouResource.second_line(date: date)
109
+ end
110
+
111
+ #
112
+ # 改元する
113
+ #
114
+ # @param [MultiGengou] multi_gengou 複数元号
115
+ # @param [Western::Calendar] date 対象日
116
+ #
117
+ # @return [MultiGengou] 改元済み複数元号
118
+ #
119
+ def self.transfer(multi_gengou:, date:)
120
+ multi_gengou.transfer(
121
+ first_line: first_line(date: date),
122
+ second_line: second_line(date: date)
123
+ )
124
+
125
+ multi_gengou
126
+ end
127
+
128
+ #
129
+ # 現在日からみて直近の未来に対する元号の切替前日を求める
130
+ # @note 2つの元号が重複した場合はより過去の日となる
131
+ #
132
+ # @param [Japan::Gengou] first_line 元号(1行目)
133
+ # @param [Japan::Gengou] second_line 元号(2行目)
134
+ #
135
+ # @return [Western::Calendar] 元号の切替前日
136
+ #
137
+ def choise_nearest_end_date
138
+ condition = lambda do |first_date, second_date|
139
+ first_date < second_date ? first_date : second_date
140
+ end
141
+
142
+ second_line = current_second_line
143
+
144
+ MultiGengouRoller.choise_date(condition: condition, second_line: second_line,
145
+ first_date: current_first_line.end_date.clone,
146
+ second_date: second_line.end_date.clone)
147
+ end
148
+
149
+ #
150
+ # 最過去の元旦を求める
151
+ # @note 2つの元号が重複した場合はより過去の日となる
152
+ #
153
+ # @param [Japan::Gengou] first_line 元号(1行目)
154
+ # @param [Japan::Gengou] second_line 元号(2行目)
155
+ #
156
+ # @return [Western::Calendar] 最過去の元旦
157
+ #
158
+ def self.choise_oldest_gengou_date(first_line:, second_line:)
159
+ condition = lambda do |first_date, second_date|
160
+ first_date < second_date ? first_date : second_date
161
+ end
162
+ choise_date(condition: condition, second_line: second_line,
163
+ first_date: first_line.new_year_date.clone,
164
+ second_date: second_line.new_year_date.clone)
165
+ end
166
+
167
+ #
168
+ # 元号内での最未来日を求める
169
+ # @note 2つの元号が重複した場合はより未来の日となる
170
+ #
171
+ # @param [Japan::Gengou] first_line 元号(1行目)
172
+ # @param [Japan::Gengou] second_line 元号(2行目)
173
+ #
174
+ # @return [Western::Calendar] 最未来日
175
+ #
176
+ def self.choise_newest_gengou_date(first_line:, second_line:)
177
+ condition = lambda do |first_date, second_date|
178
+ first_date > second_date ? first_date : second_date
179
+ end
180
+ choise_date(condition: condition, second_line: second_line,
181
+ first_date: first_line.end_date.clone,
182
+ second_date: second_line.end_date.clone)
183
+ end
184
+
185
+ # :reek:LongParameterList {max_params: 4}
186
+
187
+ #
188
+ # 条件を元に日付を求める
189
+ #
190
+ # @param [Proc] condition 比較条件
191
+ # @param [Japan::Gengou] second_line 元号(2行目)
192
+ # @param [Western::Calendar] first_date 比較対象(1行目)
193
+ # @param [Western::Calendar] second_date 比較対象(2行目)
194
+ #
195
+ # @return [Western::Calendar] 比較結果
196
+ #
197
+ def self.choise_date(condition:, second_line:, first_date:, second_date:)
198
+ # first_lineは常に存在する
199
+ return first_date if second_line.invalid?
200
+
201
+ condition.call(first_date, second_date)
202
+ end
203
+
204
+ #
205
+ # ディープコピー
206
+ #
207
+ # @param [MultiGengou] obj 自身
208
+ #
209
+ def initialize_copy(obj)
210
+ @oldest_date = obj.oldest_date.clone
211
+ @current_date = obj.current_date.clone
212
+ @newest_date = obj.newest_date.clone
213
+ @multi_gengou = obj.multi_gengou.clone
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cycle/zodiac'
4
+
5
+ # :nodoc:
6
+ module Zakuro
7
+ # :nodoc:
8
+ module Calculation
9
+ # :nodoc:
10
+ module Base
11
+ #
12
+ # Year 年
13
+ #
14
+ class Year
15
+ # @return [Gengou] 元号
16
+ attr_reader :multi_gengou
17
+ # @return [Array<Month>] 年内の全ての月
18
+ attr_reader :months
19
+ # @return [Integer] 年の日数
20
+ attr_reader :total_days
21
+ # @return [Western::Calendar] 元旦
22
+ attr_reader :new_year_date
23
+
24
+ #
25
+ # 初期化
26
+ #
27
+ # @param [Gengou] multi_gengou 元号
28
+ #
29
+ def initialize(multi_gengou: MultiGengou.new, new_year_date: Western::Calendar.new,
30
+ months: [], total_days: 0)
31
+ @multi_gengou = multi_gengou
32
+ @months = months
33
+ @new_year_date = new_year_date
34
+ @total_days = total_days
35
+ end
36
+
37
+ #
38
+ # 年の日数を確定する
39
+ #
40
+ def commit
41
+ @total_days = 0
42
+ months.each do |month|
43
+ @total_days += month.days
44
+ end
45
+
46
+ self
47
+ end
48
+
49
+ #
50
+ # 次年にする
51
+ #
52
+ # @param [Japan::Gengou] first_line 元号(1行目)
53
+ # @param [Japan::Gengou] second_line 元号(2行目)
54
+ #
55
+ # @return [MultiGengou] 自身
56
+ #
57
+ def next_year
58
+ @multi_gengou.next_year
59
+
60
+ @new_year_date += @total_days
61
+ @total_days = 0
62
+
63
+ self
64
+ end
65
+
66
+ #
67
+ # 十干十二支を取得する
68
+ #
69
+ # @return [String] 十干十二支
70
+ #
71
+ def zodiac_name
72
+ Cycle::Zodiac.year_name(western_year: @new_year_date.year)
73
+ end
74
+
75
+ #
76
+ # 月を追加する
77
+ #
78
+ # @param [Month] month 月
79
+ #
80
+ def push(month:)
81
+ return if duplicated?(month: month)
82
+
83
+ @months.push(month)
84
+
85
+ nil
86
+ end
87
+
88
+ #
89
+ # すでに登録済みの月と重複しているか判定する
90
+ #
91
+ # @note 昨年11月1日から今年1月1日の前日までで、去年データと重複する場合は登録スキップする
92
+ #
93
+ # @param [Month] month 月
94
+ #
95
+ # @return [True] 重複している
96
+ # @return [True] 重複していない
97
+ #
98
+ def duplicated?(month:)
99
+ @months.each do |existed|
100
+ return true if existed.same?(other: month)
101
+ end
102
+ false
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,459 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ module Zakuro
5
+ # :nodoc:
6
+ module Calculation
7
+ # :nodoc:
8
+ module Cycle
9
+ #
10
+ # 大余小余(時刻情報)
11
+ # @abstract 大余小余計算に必要な処理を行う、暦に依存しない汎用的なクラス
12
+ #
13
+ class AbstractRemainder # rubocop:disable Metrics/ClassLength
14
+ # @return [Integer] 大余上限
15
+ LIMIT = 60
16
+
17
+ # @return [Integer] 大余("日"に相当)
18
+ attr_reader :day
19
+ # @return [Integer] 小余("分"に相当)
20
+ attr_reader :minute
21
+ # @return [Integer] 秒
22
+ attr_reader :second
23
+ # @return [Integer] 繰り上げなしの小余
24
+ attr_reader :base_limit
25
+ # @return [Integer] 1大余に必要な小余(暦によって基数が異なる)
26
+ attr_reader :base_day
27
+ # @return [Integer] 1小余に必要な秒(暦によって基数が異なる)
28
+ attr_reader :base_minute
29
+ # 繰り上げ有無
30
+ # @return [True] 繰り上げあり
31
+ # @return [False] 繰り上げなし
32
+ attr_reader :limited
33
+
34
+ # :reek:instanceVariableAssumption
35
+ # rubocop:disable Metrics/ParameterLists
36
+
37
+ #
38
+ # 初期化
39
+ #
40
+ # @param [Integer] base_day 1大余に必要な小余(暦によって基数が異なる)
41
+ # @param [Integer] base_mitune 1小余に必要な秒(暦によって基数が異なる)
42
+ # @param [Integer] day 大余("日"に相当)
43
+ # @param [Integer] minute 小余("分"に相当)
44
+ # @param [Integer] second 秒
45
+ # @param [Integer] total 繰り上げなしの小余
46
+ #
47
+ def initialize(base_day: -1, base_mitune: -1, day: -1, minute: -1, second: -1, total: -1)
48
+ @base_limit = LIMIT
49
+ @base_day = base_day
50
+ @base_minute = base_mitune
51
+ @limited = true
52
+
53
+ set(day: day, minute: minute, second: second, total: total)
54
+ end
55
+ # rubocop:enable Metrics/ParameterLists
56
+
57
+ #
58
+ # 値を設定する
59
+ #
60
+ # @param [Integer] day 大余("日"に相当)
61
+ # @param [Integer] minute 小余("分"に相当)
62
+ # @param [Integer] second 秒
63
+ # @param [Integer] total 繰り上げなしの小余
64
+ #
65
+ # @return [AbstractRemainder] 自身の参照
66
+ #
67
+ def set(day: -1, minute: -1, second: -1, total: -1)
68
+ @day = day
69
+ @minute = minute
70
+ @second = second
71
+
72
+ if total != -1
73
+ @day = (total.to_f / @base_day).floor
74
+ @minute = (total % @base_day)
75
+ @second = 0
76
+ end
77
+
78
+ self
79
+ end
80
+
81
+ #
82
+ # 繰り上げ処理を外す
83
+ #
84
+ # @return [AbstractRemainder] 自身の参照
85
+ #
86
+ def lift_limit
87
+ @limited = false
88
+
89
+ self
90
+ end
91
+
92
+ #
93
+ # 繰り上げ処理を設定する
94
+ #
95
+ # @return [AbstractRemainder] 自身の参照
96
+ #
97
+ def set_limit
98
+ @limited = true
99
+
100
+ self
101
+ end
102
+
103
+ #
104
+ # 無効かどうか
105
+ #
106
+ # @param [AbstractRemainder] param 自クラスのインスタンス(デフォルトは自身の参照)
107
+ #
108
+ # @return [True] 無効
109
+ # @return [False] 有効
110
+ #
111
+ def invalid?(param: self)
112
+ raise ArgumentError, 'unmatch parameter type' unless param.is_a?(AbstractRemainder)
113
+
114
+ param.day == -1 || param.minute == -1 || param.second == -1
115
+ end
116
+
117
+ #
118
+ # 十干十二支名
119
+ #
120
+ # @return [String] 十干十二支名
121
+ #
122
+ def zodiac_name
123
+ Cycle::Zodiac.day_name(day: @day)
124
+ end
125
+
126
+ #
127
+ # 日が同じ十干かどうか(大小の月判定)
128
+ #
129
+ # @param [Integer] other 大余
130
+ #
131
+ # @return [True] 当月朔日と翌月朔日が同じ十干(「大」の月)
132
+ # @return [False] 当月朔日と翌月朔日が異なる十干(「小」の月)
133
+ #
134
+ def same_remainder_divided_by_ten?(other:)
135
+ day = @day % LIMIT
136
+
137
+ (day % 10) == (other % 10)
138
+ end
139
+
140
+ #
141
+ # (非破壊的に)加算する
142
+ #
143
+ # @param [AbstractRemainder] other 他の大余小余
144
+ #
145
+ # @return [AbstractRemainder] 加算結果
146
+ #
147
+ def add(other)
148
+ invalid?(param: other)
149
+ day = @day + other.day
150
+ minute = @minute + other.minute
151
+ second = @second + other.second
152
+ day, minute, second = carry(day, minute, second)
153
+
154
+ clone.set(day: day, minute: minute, second: second)
155
+ end
156
+
157
+ #
158
+ # (破壊的に)加算する
159
+ #
160
+ # @param [AbstractRemainder] other 他の大余小余
161
+ #
162
+ # @return [AbstractRemainder] 加算結果
163
+ #
164
+ def add!(other)
165
+ result = add(other)
166
+ @day = result.day
167
+ @minute = result.minute
168
+ @second = result.second
169
+
170
+ self
171
+ end
172
+
173
+ #
174
+ # (非破壊的に)除算する
175
+ #
176
+ # @param [AbstractRemainder] other 他の大余小余
177
+ #
178
+ # @return [AbstractRemainder] 除算結果
179
+ #
180
+ def sub(other)
181
+ invalid?(param: other)
182
+ day = @day - other.day
183
+ minute = @minute - other.minute
184
+ second = @second - other.second
185
+ day, minute, second = carry(day, minute, second)
186
+
187
+ clone.set(day: day, minute: minute, second: second)
188
+ end
189
+
190
+ #
191
+ # (破壊的に)除算する
192
+ #
193
+ # @param [AbstractRemainder] other 他の大余小余
194
+ #
195
+ # @return [AbstractRemainder] 除算結果
196
+ #
197
+ def sub!(other)
198
+ result = sub(other)
199
+ @day = result.day
200
+ @minute = result.minute
201
+ @second = result.second
202
+
203
+ self
204
+ end
205
+
206
+ #
207
+ # 大小比較(>)
208
+ #
209
+ # @param [AbstractRemainder] other 他の大余小余
210
+ #
211
+ # @return [True] より大きい
212
+ # @return [False] 以下
213
+ #
214
+ def >(other)
215
+ up?(other)
216
+ end
217
+
218
+ #
219
+ # 大小比較(>=)
220
+ #
221
+ # @param [AbstractRemainder] other 他の大余小余
222
+ #
223
+ # @return [True] 以上
224
+ # @return [False] より小さい
225
+ #
226
+ def >=(other)
227
+ up?(other) || \
228
+ (@day == other.day && @minute == other.minute && @second == other.second)
229
+ end
230
+
231
+ #
232
+ # 大小比較(<)
233
+ #
234
+ # @param [AbstractRemainder] other 他の大余小余
235
+ #
236
+ # @return [True] より小さい
237
+ # @return [False] 以上
238
+ #
239
+ def <(other)
240
+ down?(other)
241
+ end
242
+
243
+ #
244
+ # 大小比較(<=)
245
+ #
246
+ # @param [AbstractRemainder] other 他の大余小余
247
+ #
248
+ # @return [True] 以下
249
+ # @return [False] より大きい
250
+ #
251
+ def <=(other)
252
+ down?(other) || \
253
+ (@day == other.day && @minute == other.minute && @second == other.second)
254
+ end
255
+
256
+ #
257
+ # 大小比較(==)
258
+ #
259
+ # @param [AbstractRemainder] other 他の大余小余
260
+ #
261
+ # @return [True] 等しい
262
+ # @return [False] 等しくない
263
+ #
264
+ def ==(other)
265
+ eql?(other)
266
+ end
267
+
268
+ #
269
+ # 大小比較(!=)
270
+ #
271
+ # @param [AbstractRemainder] other 他の大余小余
272
+ #
273
+ # @return [True] 等しくない
274
+ # @return [False] 等しい
275
+ #
276
+ def !=(other)
277
+ !eql?(other)
278
+ end
279
+
280
+ # 進朔
281
+ # @see 進朔 http://eco.mtk.nao.ac.jp/koyomi/wiki/C2C0B1A2C2C0CDDBCEF12FBFCABAF3.html
282
+ # 進朔とは、朔の瞬間が1日の3/4すなわち夕方以降となる場合に、その日ではなく1日進めて翌日を1日目にする方法のことです。
283
+ # 進朔するかどうかの基準時刻を進朔限といいます。宣明暦では1日8400分ですから、進朔限は6300分で、それ以降だと進朔されます。
284
+
285
+ #
286
+ # (非破壊的に)進朔する
287
+ #
288
+ # @see http://eco.mtk.nao.ac.jp/koyomi/wiki/C2C0B1A2C2C0CDDBCEF12FBFCABAF3.html
289
+ #
290
+ # 進朔とは、朔の瞬間が1日の3/4すなわち夕方以降となる場合に、その日ではなく1日進めて翌日を1日目にする方法のことです。
291
+ # 進朔するかどうかの基準時刻を進朔限といいます。宣明暦では1日8400分ですから、進朔限は6300分で、それ以降だと進朔されます。
292
+ #
293
+ # @return [AbstractRemainder] 進朔結果
294
+ #
295
+ def up_on_new_moon
296
+ cloned = clone
297
+ limit = (base_day * 3) / 4
298
+ if @minute >= limit
299
+ day, minute, second = carry((@day + 1), @minute, @second)
300
+ return cloned.set(day: day, minute: minute, second: second)
301
+ end
302
+
303
+ cloned
304
+ end
305
+
306
+ #
307
+ # (破壊的に)進朔する
308
+ #
309
+ # @see http://eco.mtk.nao.ac.jp/koyomi/wiki/C2C0B1A2C2C0CDDBCEF12FBFCABAF3.html
310
+ #
311
+ # 進朔とは、朔の瞬間が1日の3/4すなわち夕方以降となる場合に、その日ではなく1日進めて翌日を1日目にする方法のことです。
312
+ # 進朔するかどうかの基準時刻を進朔限といいます。宣明暦では1日8400分ですから、進朔限は6300分で、それ以降だと進朔されます。
313
+ #
314
+ # @return [AbstractRemainder] 進朔結果
315
+ #
316
+ def up_on_new_moon!
317
+ result = up_on_new_moon
318
+ @day = result.day
319
+ @minute = result.minute
320
+ @second = result.second
321
+
322
+ self
323
+ end
324
+
325
+ #
326
+ # 小余に揃えた結果を返す(秒は除外する)
327
+ #
328
+ # @return [Integer] 小余
329
+ #
330
+ def to_minute
331
+ @day * @base_day + @minute
332
+ end
333
+
334
+ #
335
+ # 大余に四捨五入した結果を返す(秒は除外する)
336
+ #
337
+ # @return [AbstractRemainder] 大余
338
+ #
339
+ def round
340
+ day = @day
341
+ day += 1 if @minute >= (@base_day / 2)
342
+
343
+ initialize(day, 0, 0)
344
+ end
345
+
346
+ #
347
+ # 文字化する
348
+ #
349
+ # @return [String] 文字化
350
+ #
351
+ def to_s
352
+ "大余(日): #{@day}, 小余(分): #{@minute}, 小余(秒): #{@second}"
353
+ end
354
+
355
+ #
356
+ # 特定の文字フォーマットにして出力する
357
+ #
358
+ # @param [String] form フォーマット(大余、小余、秒それぞれを%dで指定する)
359
+ #
360
+ # @return [String] フォーマットした結果
361
+ #
362
+ def format(form: '%d-%d')
363
+ return '' if invalid?
364
+
365
+ super(form, @day, @minute, @second)
366
+ end
367
+
368
+ private
369
+
370
+ def carry!(day, minute, second)
371
+ @day, @minute, @second = carry(day, minute, second)
372
+
373
+ self
374
+ end
375
+
376
+ # 繰り上げ、繰り下げ
377
+ def carry(param_day, param_minute, param_second)
378
+ # NOTE: 計算方法としてマイナスでも徐算・剰余算の結果をプラス同様に扱う
379
+ minute, second = carry_second(param_minute, param_second)
380
+
381
+ day, minute = carry_minute(param_day, minute)
382
+
383
+ day = carry_day(day, @limited)
384
+
385
+ [day, minute, second]
386
+ end
387
+
388
+ def carry_second(param_minute, param_second)
389
+ sign = param_second.negative? ? -1 : 1
390
+ abs = sign * param_second
391
+ minute = param_minute + (sign * (abs / @base_minute).floor)
392
+ second = sign * (abs % @base_minute)
393
+
394
+ if sign.negative?
395
+ second += @base_minute
396
+ minute -= 1
397
+ end
398
+
399
+ [minute, second]
400
+ end
401
+
402
+ def carry_minute(param_day, param_minute)
403
+ sign = param_minute.negative? ? -1 : 1
404
+ abs = sign * param_minute
405
+
406
+ day = param_day + (sign * (abs / @base_day).floor)
407
+ minute = sign * (abs % @base_day)
408
+
409
+ if sign.negative?
410
+ minute += @base_day
411
+ day -= 1
412
+ end
413
+
414
+ [day, minute]
415
+ end
416
+
417
+ def carry_day(day, limited)
418
+ sign = day.negative? ? -1 : 1
419
+ abs = sign * day
420
+ carried = sign * (abs % @base_limit) if limited
421
+ carried += @base_limit if sign.negative?
422
+ carried
423
+ end
424
+
425
+ def up?(other)
426
+ invalid?(param: other)
427
+ day = other.day
428
+ minute = other.minute
429
+ return true if @day > day
430
+ return false if @day < day
431
+
432
+ return true if @minute > minute
433
+ return false if @minute < minute
434
+
435
+ @second > other.second
436
+ end
437
+
438
+ def eql?(other)
439
+ invalid?(param: other)
440
+
441
+ (@day == other.day && @minute == other.minute && @second == other.second)
442
+ end
443
+
444
+ def down?(other)
445
+ invalid?(param: other)
446
+ day = other.day
447
+ minute = other.minute
448
+ return true if @day < day
449
+ return false if @day > day
450
+
451
+ return true if @minute < minute
452
+ return false if @minute > minute
453
+
454
+ @second < other.second
455
+ end
456
+ end
457
+ end
458
+ end
459
+ end