zakuro 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +9 -0
  3. data/.gitignore +69 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +14 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +19 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +44 -0
  10. data/Rakefile +20 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/images/12/346/234/210/344/273/245/351/231/215/343/201/256/345/205/245/345/256/232/346/260/227.png +0 -0
  14. data/images/12/346/234/210/344/273/245/351/231/215/343/201/256/346/234/210/343/201/256/351/200/262/351/200/200.png +0 -0
  15. data/images/source/description.numbers +0 -0
  16. data/images//345/205/245/345/256/232/346/260/227/343/201/256/350/265/267/347/256/227.png +0 -0
  17. data/images//345/220/204/346/234/210/343/201/256/346/261/202/343/202/201/346/226/271.png +0 -0
  18. data/images//345/244/252/351/231/275/343/201/250/346/234/210.png +0 -0
  19. data/lib/zakuro.rb +9 -0
  20. data/lib/zakuro/condition.rb +239 -0
  21. data/lib/zakuro/cycle/abstract_remainder.rb +457 -0
  22. data/lib/zakuro/cycle/zodiac.rb +103 -0
  23. data/lib/zakuro/era/gengou/set-001-until-south.yaml +375 -0
  24. data/lib/zakuro/era/gengou/set-002-from-north.yaml +166 -0
  25. data/lib/zakuro/era/gengou/set-003-modern.yaml +12 -0
  26. data/lib/zakuro/era/japan.rb +630 -0
  27. data/lib/zakuro/era/western.rb +412 -0
  28. data/lib/zakuro/merchant.rb +57 -0
  29. data/lib/zakuro/output/error.rb +10 -0
  30. data/lib/zakuro/output/logger.rb +64 -0
  31. data/lib/zakuro/output/response.rb +170 -0
  32. data/lib/zakuro/output/result.rb +219 -0
  33. data/lib/zakuro/output/stringifier.rb +62 -0
  34. data/lib/zakuro/version.rb +7 -0
  35. data/lib/zakuro/version/abstract_version.rb +29 -0
  36. data/lib/zakuro/version/genka/genka.rb +19 -0
  37. data/lib/zakuro/version/gihou/gihou.rb +19 -0
  38. data/lib/zakuro/version/gregorio/gregorio.rb +19 -0
  39. data/lib/zakuro/version/houryaku/houryaku.rb +19 -0
  40. data/lib/zakuro/version/joukyou/joukyou.rb +19 -0
  41. data/lib/zakuro/version/kansei/kansei.rb +19 -0
  42. data/lib/zakuro/version/senmyou/README.md +586 -0
  43. data/lib/zakuro/version/senmyou/base/era.rb +81 -0
  44. data/lib/zakuro/version/senmyou/base/gengou.rb +210 -0
  45. data/lib/zakuro/version/senmyou/base/remainder.rb +60 -0
  46. data/lib/zakuro/version/senmyou/base/solar_term.rb +66 -0
  47. data/lib/zakuro/version/senmyou/base/year.rb +58 -0
  48. data/lib/zakuro/version/senmyou/monthly/lunar_phase.rb +220 -0
  49. data/lib/zakuro/version/senmyou/monthly/month.rb +112 -0
  50. data/lib/zakuro/version/senmyou/senmyou.rb +34 -0
  51. data/lib/zakuro/version/senmyou/stella/lunar_orbit.rb +332 -0
  52. data/lib/zakuro/version/senmyou/stella/solar_average.rb +192 -0
  53. data/lib/zakuro/version/senmyou/stella/solar_orbit.rb +398 -0
  54. data/lib/zakuro/version/senmyou/stella/winter_solstice.rb +106 -0
  55. data/lib/zakuro/version/senmyou/summary/annual_data.rb +186 -0
  56. data/lib/zakuro/version/senmyou/summary/gengou_data.rb +294 -0
  57. data/lib/zakuro/version/taien/taien.rb +19 -0
  58. data/lib/zakuro/version/tenpou/tenpou.rb +19 -0
  59. data/lib/zakuro/version_factory.rb +59 -0
  60. data/zakuro.gemspec +31 -0
  61. metadata +106 -0
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../output/logger'
4
+
5
+ # :nodoc:
6
+ module Zakuro
7
+ # :nodoc:
8
+ module Senmyou
9
+ # :reek:TooManyConstants
10
+
11
+ #
12
+ # WinterSolstice 冬至
13
+ #
14
+ module WinterSolstice
15
+ # @return [Integer] 統法(1日=8400分)
16
+ DAY = 8400
17
+ # @return [Integer] 朔望月
18
+ SYNODIC_MONTH = 248_057
19
+ # @return [Integer] 一年
20
+ YEAR = 3_068_055
21
+ # @return [Integer] 通余: (YEAR - DAY * 12 * 30) = 44055
22
+ REMAINDER_ALL_YEAR = 44_055
23
+ # @return [Integer] 旬周(60日) 8400 * 60
24
+ SIXTY_DAYS = 504_000
25
+ # @return [Integer] 積年(甲子夜半朔旦冬至〜暦の開始前)
26
+ TOTAL_YEAR = 7_070_138
27
+ # @return [Integer] 暦の開始年(長慶2年)
28
+ BEGIN_YEAR = 822
29
+
30
+ # @return [Logger] ロガー
31
+ LOGGER = Logger.new(location: 'winter_solstice')
32
+
33
+ # :reek:TooManyStatements { max_statements: 6 }
34
+
35
+ #
36
+ # 対象年の前年の冬至を求める
37
+ #
38
+ # @param [Integer] western_year 西暦年
39
+ #
40
+ # @return [Remainder] 前年の冬至
41
+ #
42
+ def self.calc(western_year:)
43
+ # 積年の開始から対象年までの年数
44
+ total = TOTAL_YEAR + western_year - BEGIN_YEAR
45
+ remainder_year = total % SIXTY_DAYS
46
+
47
+ LOGGER.debug("[a01]:#{remainder_year}")
48
+
49
+ # 通余を使う
50
+ winter_solstice_minute = (remainder_year * REMAINDER_ALL_YEAR) % SIXTY_DAYS
51
+
52
+ LOGGER.debug("[a02]:#{winter_solstice_minute}")
53
+
54
+ Remainder.new(total: winter_solstice_minute)
55
+ end
56
+
57
+ # :reek:TooManyStatements { max_statements: 7 }
58
+
59
+ #
60
+ # 対象年の天正閏余(冬至より前にある11月経朔との差 = 月齢)を算出する
61
+ # 太陽と月の運動による補正値を算出し、その補正結果を返す
62
+ #
63
+ # @param [Integer] western_year 西暦年
64
+ #
65
+ # @return [Remainder] 天正閏余
66
+ #
67
+ def self.calc_moon_age(western_year:)
68
+ # 積年の開始から対象年までの年数
69
+ total = TOTAL_YEAR + western_year - BEGIN_YEAR
70
+
71
+ # 12朔望月に対する1年の余り(単位:分)
72
+ remainder_minute = YEAR - (SYNODIC_MONTH * 12)
73
+
74
+ # 朔望月に含まれなかった余り(単位:年)
75
+ remainder_year = total % SYNODIC_MONTH
76
+
77
+ LOGGER.debug("[b01]: #{remainder_year}")
78
+
79
+ # 天正閏余
80
+ winter_solstice_age = remainder_minute * remainder_year % SYNODIC_MONTH
81
+
82
+ LOGGER.debug("[b02]: #{winter_solstice_age}")
83
+
84
+ # 大余・小余に変換する
85
+ Remainder.new(total: winter_solstice_age)
86
+ end
87
+
88
+ #
89
+ # 11月経朔(冬至が含まれる月の1日)を求める
90
+ #
91
+ # @param [Integer] western_year 西暦年
92
+ #
93
+ # @return [Remainder] 11月経朔
94
+ #
95
+ def self.calc_averaged_last_november_1st(western_year:)
96
+ # 冬至
97
+ winter_solstice = calc(western_year: western_year)
98
+ # 天正閏余
99
+ winter_solstice_age = calc_moon_age(western_year: western_year)
100
+
101
+ # 11月経朔
102
+ winter_solstice.sub(winter_solstice_age)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../output/logger'
4
+ require_relative '../base/remainder'
5
+ require_relative '../monthly/month'
6
+ require_relative '../base/solar_term'
7
+ require_relative '../monthly/lunar_phase'
8
+ require_relative '../stella/solar_orbit'
9
+ require_relative '../stella/solar_average'
10
+ require_relative '../stella/lunar_orbit'
11
+
12
+ # :nodoc:
13
+ module Zakuro
14
+ # :nodoc:
15
+ module Senmyou
16
+ # AnnualData 年間データ
17
+ module AnnualData
18
+ # @return [Logger] ロガー
19
+ LOGGER = Logger.new(location: 'annual_data')
20
+
21
+ # :reek:TooManyStatements { max_statements: 6 }
22
+
23
+ #
24
+ # 11月定朔(冬至が含まれる月の1日:補正済)を求める
25
+ #
26
+ # @param [Integer] western_year 西暦年
27
+ #
28
+ # @return [Remainder] 11月定朔
29
+ #
30
+ def self.calc_last_november_1st(western_year:)
31
+ # 天正閏余
32
+ winter_solstice_age = \
33
+ WinterSolstice.calc_moon_age(western_year: western_year)
34
+ # 11月経朔
35
+ november_1st = \
36
+ WinterSolstice.calc_averaged_last_november_1st(western_year: western_year)
37
+ # 11月定朔
38
+
39
+ # 補正
40
+ correction_value = correction_value_on_last_november_1st(
41
+ winter_solstice_age: winter_solstice_age,
42
+ western_year: western_year
43
+ )
44
+
45
+ result = november_1st.add(Remainder.new(day: 0, minute: correction_value,
46
+ second: 0))
47
+ # 進朔
48
+ result.up_on_new_moon!
49
+ result
50
+ end
51
+
52
+ # :reek:TooManyStatements { max_statements: 6 }
53
+
54
+ #
55
+ # 一覧取得する
56
+ #
57
+ # * 対象年に対して、前年11月-当年11月までを出力する
58
+ # * 対象年(西暦)と計算年(元号x年)の紐付けは行わない
59
+ #
60
+ # @param [Integer] western_year 西暦年
61
+ #
62
+ # @return [Array<Month>] 1年データ
63
+ #
64
+ def self.collect_annual_data_after_last_november_1st(western_year:)
65
+ annual_data = initialized_annual_data(western_year: western_year)
66
+
67
+ apply_big_and_small_of_the_month(annual_data: annual_data)
68
+
69
+ SolarAverage.set_solar_terms_into_annual_data(western_year: western_year,
70
+ annual_data: annual_data)
71
+
72
+ # 月間隔を取得するためだけの末尾要素を削除
73
+ annual_data.pop
74
+
75
+ adjust_leap_month(annual_data: annual_data)
76
+
77
+ annual_data
78
+ end
79
+
80
+ #
81
+ # 11月定朔の補正値を求める
82
+ #
83
+ # @param [Remainder] winter_solstice_age 天正閏余
84
+ # @param [Integer] western_year 西暦年
85
+ #
86
+ # @return [Integer] 補正値
87
+ #
88
+ def self.correction_value_on_last_november_1st(winter_solstice_age:, western_year:)
89
+ # 補正
90
+ solar_term = SolarTerm.new(
91
+ remainder: winter_solstice_age
92
+ )
93
+ solar_term = \
94
+ SolarOrbit.calc_solar_term_by_remainder(
95
+ solar_term: solar_term
96
+ )
97
+
98
+ moon_remainder, is_forward = LunarOrbit.calc_moon_point(
99
+ remainder: winter_solstice_age, western_year: western_year
100
+ )
101
+
102
+ SolarOrbit.calc_sun_orbit_value(solar_term: solar_term) +
103
+ LunarOrbit.calc_moon_orbit_value(remainder_month: moon_remainder,
104
+ is_forward: is_forward)
105
+ end
106
+ private_class_method :correction_value_on_last_november_1st
107
+
108
+ #
109
+ # 1年データを取得する
110
+ #
111
+ # @param [Integer] western_year 西暦年
112
+ #
113
+ # @return [Array<Month>] 1年データ
114
+ #
115
+ def self.initialized_annual_data(western_year:)
116
+ result = []
117
+ lunar_phase = LunarPhase.new(western_year: western_year)
118
+
119
+ is_last_year = true
120
+
121
+ monthes = [11, 12] + [*1..12]
122
+
123
+ monthes.each do |month|
124
+ LOGGER.debug('---', "month: #{month}", "is_last_year: #{is_last_year}")
125
+
126
+ adjusted = lunar_phase.next_month
127
+
128
+ result.push(
129
+ Month.new(is_last_year: is_last_year, number: month,
130
+ remainder: adjusted, phase_index: 0)
131
+ )
132
+ is_last_year = false if month == 12
133
+ end
134
+ result
135
+ end
136
+ private_class_method :initialized_annual_data
137
+
138
+ #
139
+ # 1年データの各月に月の大小を設定する
140
+ #
141
+ # @param [Array<Month>] annual_data 1年データ
142
+ #
143
+ def self.apply_big_and_small_of_the_month(annual_data:)
144
+ size = annual_data.size - 1
145
+ (0...size).each do |idx|
146
+ current_month = annual_data[idx]
147
+ next_month = annual_data[idx + 1]
148
+ current_month.is_many_days = \
149
+ current_month.remainder.same_remainder_divided_by_ten?(
150
+ other: next_month.remainder.day
151
+ )
152
+ end
153
+ end
154
+ private_class_method :apply_big_and_small_of_the_month
155
+
156
+ # :reek:TooManyStatements { max_statements: 10 }
157
+
158
+ #
159
+ # 閏月が存在した場合に以降の月を1つずつ減らす
160
+ # @example 7,8,9 と続く月の8月が閏の場合、7, 閏7, 8 となる
161
+ #
162
+ # @param [Array<Month>] annual_data 1年データ
163
+ #
164
+ def self.adjust_leap_month(annual_data:)
165
+ # 閏による月の再調整を行う
166
+ leaped = false
167
+ annual_data.each do |month|
168
+ if month.even_term.invalid?
169
+ month.leaped = true
170
+ leaped = true
171
+ end
172
+ next unless leaped
173
+
174
+ # NOTE: 常気法では閏月は2-3年に一度のため、1年に二度発生しない前提
175
+ number = month.number - 1
176
+ if number <= 0
177
+ month.is_last_year = true
178
+ number = 12
179
+ end
180
+ month.number = number
181
+ end
182
+ end
183
+ private_class_method :adjust_leap_month
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,294 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../era/western'
4
+ require_relative 'annual_data'
5
+ require_relative '../../../output/response'
6
+ require_relative '../base/era'
7
+ require_relative '../base/gengou'
8
+ require_relative '../base/year'
9
+
10
+ # :nodoc:
11
+ module Zakuro
12
+ # :nodoc:
13
+ module Senmyou
14
+ #
15
+ # GengouData 元号の年情報
16
+ #
17
+ module GengouData
18
+ def self.get_ancient_date(date: Western::Calendar.new)
19
+ SingleDay.get_ancient_date(date: date)
20
+ end
21
+
22
+ #
23
+ # SingleDay 1日データ
24
+ #
25
+ module SingleDay
26
+ #
27
+ # 1日データを取得する
28
+ #
29
+ # @param [Western::Calendar] date 年月日情報(西暦)
30
+ #
31
+ # @return [Response::SingleDay] 1日データ
32
+ #
33
+ def self.get_ancient_date(date: Western::Calendar.new)
34
+ raise ArgumentError, 'invalid senmyou date' unless Era.include?(date: date)
35
+
36
+ target = get_target(year_list: get_year_list(date: date), date: date)
37
+
38
+ Response::SingleDay.save_single_day(
39
+ param: Response::SingleDay::Param.new(
40
+ year: target.year, month: target.month,
41
+ date: target.date, days: target.days
42
+ )
43
+ )
44
+ end
45
+
46
+ #
47
+ # Target 対象
48
+ #
49
+ class Target
50
+ # @return [Year] 年
51
+ attr_reader :year
52
+ # @return [Month] 月
53
+ attr_reader :month
54
+ # @return [Western::Calendar] 日
55
+ attr_reader :date
56
+ # @return [Integer] 月初からの日数
57
+ attr_reader :days
58
+
59
+ #
60
+ # 初期化
61
+ #
62
+ # @param [Year] year 年
63
+ # @param [Month] month 月
64
+ # @param [Western::Calendar] date 日
65
+ # @param [Integer] days 月初からの日数
66
+ #
67
+ def initialize(year:, month:, date:, days:)
68
+ @year = year
69
+ @month = month
70
+ @date = date
71
+ @days = days
72
+ end
73
+ end
74
+
75
+ #
76
+ # MonthSearchResult 1年内に該当する月を検索した結果
77
+ #
78
+ class MonthSearchResult
79
+ # @return [Month] 月
80
+ attr_reader :target_month
81
+ # @return [Western::Calendar] 月初日
82
+ attr_reader :first_date
83
+ # @return [True] 該当データ引き当て済
84
+ # @return [False] 該当データ引き当てなし
85
+ attr_reader :breaked
86
+
87
+ #
88
+ # 初期化
89
+ #
90
+ # @param [Month] target_month 月
91
+ # @param [Western::Calendar] first_date 月初日
92
+ # @param [True, False] breaked 該当データ引き当て
93
+ #
94
+ def initialize(target_month: Month.new,
95
+ first_date: Western::Calendar.new, breaked:)
96
+ @target_month = target_month
97
+ @first_date = first_date
98
+ @breaked = breaked
99
+ end
100
+ end
101
+
102
+ #
103
+ # 指定日に関連する暦の範囲だけ年データを生成する
104
+ #
105
+ # @param [Western::Calendar] date 西暦日
106
+ #
107
+ # @return [Array<Year>] 年データ
108
+ #
109
+ def self.get_year_list(date:)
110
+ # 元号の最初の年月日を取る
111
+ gengou = Gengou.new(date: date.clone)
112
+
113
+ # 南北朝には元号が2つ並び、必ずしも範囲が重ならないケースもある
114
+ # 対象日付に重なる元号から論理和の範囲を取り、処理漏れがないようにする
115
+ start_date = gengou.first_date.clone
116
+ # 最後の年は翌年から今年の冬至を求めるため範囲を1年広げる
117
+ newest_date = gengou.choise_newest_gengou_date.next_year
118
+
119
+ Series.get_year_list(
120
+ start_gengou: gengou, start_date: start_date, end_date: newest_date
121
+ )
122
+ end
123
+
124
+ #
125
+ # 対象を引き当てる
126
+ #
127
+ # @param [Array<Year>] year_list 年データ
128
+ # @param [Western::Calendar] date 西暦日
129
+ #
130
+ # @return [Target] 対象
131
+ #
132
+ def self.get_target(year_list:, date:)
133
+ # 対象年/対象月を引き当てる
134
+ year_list.each do |year|
135
+ result = get_target_month(date: date, year: year)
136
+
137
+ next unless result.breaked
138
+
139
+ return Target.new(
140
+ year: year, month: result.target_month, date: date,
141
+ # 対象日に調整する
142
+ days: date - result.first_date
143
+ )
144
+ end
145
+
146
+ raise ArgumentError, "invalid range: #{date.format}"
147
+ end
148
+
149
+ # :reek:TooManyStatements { max_statements: 6 }
150
+
151
+ #
152
+ # 対象の月を引き当てる
153
+ #
154
+ # @param [Western::Calendar] date 西暦日
155
+ # @param [Year] year 年
156
+ #
157
+ # @return [MonthSearchResult] 1年内に該当する月を検索した結果
158
+ #
159
+ def self.get_target_month(date:, year:)
160
+ first_date = year.gengou.first_date.clone
161
+ year.months.each do |month|
162
+ # 月末の日付を超える場合
163
+ next_first_date = first_date.clone + month.days
164
+ if date < next_first_date
165
+ return MonthSearchResult.new(target_month: month,
166
+ first_date: first_date, breaked: true)
167
+ end
168
+ first_date = next_first_date
169
+ end
170
+ MonthSearchResult.new(breaked: false)
171
+ end
172
+ end
173
+
174
+ #
175
+ # Series 和暦時系列データ
176
+ #
177
+ module Series
178
+ # :reek:TooManyInstanceVariables { max_instance_variables: 6 }
179
+
180
+ #
181
+ # TimeAdvance 時間進行
182
+ #
183
+ class TimeAdvance
184
+ #
185
+ # 初期化
186
+ #
187
+ # @param [Gengou] start_gengou 開始元号
188
+ # @param [Western::Calendar] start_date 開始日
189
+ # @param [Western::Calendar] end_date 終了日
190
+ #
191
+ def initialize(start_gengou:, start_date:, end_date:)
192
+ @current_date = start_date.clone
193
+ @end_date = end_date
194
+ @year_list = []
195
+ @western_year = start_date.year
196
+ @year = Year.new(gengou: start_gengou)
197
+ end
198
+
199
+ #
200
+ # 年データを収集する
201
+ #
202
+ # @return [Array<Year>] 年データ
203
+ #
204
+ def collect_year_list
205
+ push_current_year
206
+
207
+ # 2年目以降
208
+ while @current_date <= @end_date
209
+ compensate_last_year
210
+
211
+ push_current_year
212
+ end
213
+
214
+ @year_list
215
+ end
216
+
217
+ private
218
+
219
+ #
220
+ # 1年データを収集する
221
+ #
222
+ # @return [Array<Month>] 1年データ
223
+ #
224
+ def collect_annual_data
225
+ AnnualData.collect_annual_data_after_last_november_1st(
226
+ western_year: @western_year
227
+ )
228
+ end
229
+
230
+ #
231
+ # 昨年データの不足分を補完する
232
+ # 昨年11月1日から今年1月1日の前日のデータを1年前データとする
233
+ #
234
+ # @return [<Type>] <description>
235
+ #
236
+ def compensate_last_year
237
+ last_year = @year_list[@year_list.size - 1]
238
+ collect_annual_data.each do |month|
239
+ next unless month.is_last_year
240
+
241
+ # 昨年の月データのみ
242
+ last_year.push(month: month)
243
+ @current_date += month.days
244
+ end
245
+ end
246
+
247
+ # :reek:TooManyStatements { max_statements: 7 }
248
+
249
+ #
250
+ # 当年データを生成する
251
+ #
252
+ def push_current_year
253
+ next_year
254
+ collect_annual_data.each do |month|
255
+ next if month.is_last_year
256
+
257
+ @year.push(month: month)
258
+ @current_date += month.days
259
+ end
260
+ @year_list.push(@year)
261
+
262
+ @western_year += 1
263
+ end
264
+
265
+ #
266
+ # 次の元号年を取得する
267
+ #
268
+ def next_year
269
+ return if @year_list.empty?
270
+
271
+ @year = Year.new(gengou: @year.gengou.next_year)
272
+ end
273
+ end
274
+
275
+ #
276
+ # 期間内の年データ全てを返す
277
+ #
278
+ # @param [Gengou] start_gengou 開始元号
279
+ # @param [Western::Calendar] start_date 開始日
280
+ # @param [Western::Calendar] end_date 終了日
281
+ #
282
+ # @return [Array<Year>] 年データ
283
+ #
284
+ def self.get_year_list(start_gengou:, start_date:, end_date:)
285
+ advance = TimeAdvance.new(
286
+ start_gengou: start_gengou, start_date: start_date, end_date: end_date
287
+ )
288
+
289
+ advance.collect_year_list
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end