when_exe 0.2.100 → 0.3.1

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 (193) hide show
  1. data/LICENSE.ja.txt +25 -25
  2. data/LICENSE.txt +31 -31
  3. data/bin/irb.rc +5 -0
  4. data/bin/locales.rb +2 -2
  5. data/bin/when.rb +16 -0
  6. data/bin/when.rb.config +7 -0
  7. data/lib/when_exe.rb +616 -14
  8. data/lib/when_exe/basictypes.rb +615 -0
  9. data/lib/when_exe/calendartypes.rb +1700 -0
  10. data/lib/when_exe/coordinates.rb +1936 -0
  11. data/lib/when_exe/core/compatibility.rb +54 -0
  12. data/lib/when_exe/core/duration.rb +72 -72
  13. data/lib/when_exe/core/extension.rb +382 -0
  14. data/lib/when_exe/ephemeris.rb +1845 -0
  15. data/lib/when_exe/googlecalendar.rb +140 -0
  16. data/lib/when_exe/icalendar.rb +1587 -0
  17. data/lib/when_exe/inspect.rb +1237 -0
  18. data/lib/when_exe/locales/af.rb +90 -0
  19. data/lib/when_exe/locales/ar.rb +145 -0
  20. data/lib/when_exe/locales/az.rb +90 -0
  21. data/lib/when_exe/locales/bg.rb +90 -0
  22. data/lib/when_exe/locales/bn.rb +94 -0
  23. data/lib/when_exe/locales/bs.rb +121 -0
  24. data/lib/when_exe/locales/ca.rb +92 -0
  25. data/lib/when_exe/locales/cs.rb +107 -0
  26. data/lib/when_exe/locales/cy.rb +150 -0
  27. data/lib/when_exe/locales/da.rb +84 -0
  28. data/lib/when_exe/locales/de.rb +92 -0
  29. data/lib/when_exe/locales/de_AT.rb +92 -0
  30. data/lib/when_exe/locales/de_CH.rb +92 -0
  31. data/lib/when_exe/locales/el.rb +93 -0
  32. data/lib/when_exe/locales/en.rb +88 -0
  33. data/lib/when_exe/locales/en_AU.rb +88 -0
  34. data/lib/when_exe/locales/en_CA.rb +88 -0
  35. data/lib/when_exe/locales/en_GB.rb +88 -0
  36. data/lib/when_exe/locales/en_IN.rb +88 -0
  37. data/lib/when_exe/locales/en_NZ.rb +88 -0
  38. data/lib/when_exe/locales/eo.rb +89 -0
  39. data/lib/when_exe/locales/es.rb +84 -0
  40. data/lib/when_exe/locales/es_419.rb +84 -0
  41. data/lib/when_exe/locales/es_AR.rb +84 -0
  42. data/lib/when_exe/locales/es_CL.rb +84 -0
  43. data/lib/when_exe/locales/es_CO.rb +84 -0
  44. data/lib/when_exe/locales/es_MX.rb +84 -0
  45. data/lib/when_exe/locales/es_PE.rb +85 -0
  46. data/lib/when_exe/locales/es_VE.rb +84 -0
  47. data/lib/when_exe/locales/et.rb +94 -0
  48. data/lib/when_exe/locales/eu.rb +95 -0
  49. data/lib/when_exe/locales/fa.rb +80 -0
  50. data/lib/when_exe/locales/fi.rb +89 -0
  51. data/lib/when_exe/locales/fr.rb +88 -0
  52. data/lib/when_exe/locales/fr_CA.rb +88 -0
  53. data/lib/when_exe/locales/fr_CH.rb +88 -0
  54. data/lib/when_exe/locales/gl.rb +81 -0
  55. data/lib/when_exe/locales/he.rb +84 -0
  56. data/lib/when_exe/locales/hi.rb +80 -0
  57. data/lib/when_exe/locales/hi_IN.rb +84 -0
  58. data/lib/when_exe/locales/hr.rb +128 -0
  59. data/lib/when_exe/locales/hu.rb +84 -0
  60. data/lib/when_exe/locales/id.rb +89 -0
  61. data/lib/when_exe/locales/is.rb +89 -0
  62. data/lib/when_exe/locales/it.rb +87 -0
  63. data/lib/when_exe/locales/it_CH.rb +87 -0
  64. data/lib/when_exe/locales/ja.rb +78 -0
  65. data/lib/when_exe/locales/kn.rb +86 -0
  66. data/lib/when_exe/locales/ko.rb +78 -0
  67. data/lib/when_exe/locales/links.rb +2342 -0
  68. data/lib/when_exe/locales/lo.rb +123 -0
  69. data/lib/when_exe/locales/locales.rb +91 -0
  70. data/lib/when_exe/locales/lt.rb +111 -0
  71. data/lib/when_exe/locales/lv.rb +118 -0
  72. data/lib/when_exe/locales/mk.rb +93 -0
  73. data/lib/when_exe/locales/mn.rb +80 -0
  74. data/lib/when_exe/locales/nb.rb +81 -0
  75. data/lib/when_exe/locales/ne.rb +81 -0
  76. data/lib/when_exe/locales/nl.rb +92 -0
  77. data/lib/when_exe/locales/nn.rb +73 -0
  78. data/lib/when_exe/locales/or.rb +84 -0
  79. data/lib/when_exe/locales/pl.rb +128 -0
  80. data/lib/when_exe/locales/pt.rb +88 -0
  81. data/lib/when_exe/locales/pt_BR.rb +88 -0
  82. data/lib/when_exe/locales/rm.rb +143 -0
  83. data/lib/when_exe/locales/ro.rb +105 -0
  84. data/lib/when_exe/locales/ru.rb +128 -0
  85. data/lib/when_exe/locales/sk.rb +109 -0
  86. data/lib/when_exe/locales/sl.rb +122 -0
  87. data/lib/when_exe/locales/sr.rb +122 -0
  88. data/lib/when_exe/locales/sv.rb +83 -0
  89. data/lib/when_exe/locales/sw.rb +89 -0
  90. data/lib/when_exe/locales/th.rb +78 -0
  91. data/lib/when_exe/locales/tl.rb +99 -0
  92. data/lib/when_exe/locales/tr.rb +96 -0
  93. data/lib/when_exe/locales/uk.rb +128 -0
  94. data/lib/when_exe/locales/uz.rb +128 -0
  95. data/lib/when_exe/locales/vi.rb +94 -0
  96. data/lib/when_exe/locales/wo.rb +82 -0
  97. data/lib/when_exe/locales/zh_CN.rb +77 -0
  98. data/lib/when_exe/locales/zh_HK.rb +77 -0
  99. data/lib/when_exe/locales/zh_TW.rb +77 -0
  100. data/lib/when_exe/mini_application.rb +252 -0
  101. data/lib/when_exe/parts/enumerator.rb +472 -0
  102. data/lib/when_exe/parts/geometric_complex.rb +379 -0
  103. data/lib/when_exe/parts/locale.rb +513 -0
  104. data/lib/when_exe/parts/method_cash.rb +207 -0
  105. data/lib/when_exe/parts/resource.rb +806 -0
  106. data/lib/when_exe/parts/timezone.rb +182 -0
  107. data/lib/when_exe/region/bahai.rb +145 -0
  108. data/lib/when_exe/region/balinese.rb +627 -0
  109. data/lib/when_exe/region/chinese.rb +896 -0
  110. data/lib/when_exe/region/chinese_calendar.rb +919 -0
  111. data/lib/when_exe/region/chinese_epoch.rb +1245 -0
  112. data/lib/when_exe/region/christian.rb +644 -0
  113. data/lib/when_exe/region/far_east.rb +192 -0
  114. data/lib/when_exe/region/french.rb +66 -0
  115. data/lib/when_exe/region/geologicalage.rb +639 -0
  116. data/lib/when_exe/region/indian.rb +1066 -0
  117. data/lib/when_exe/region/iranian.rb +66 -0
  118. data/lib/when_exe/region/islamic.rb +105 -0
  119. data/lib/when_exe/region/japanese.rb +851 -0
  120. data/lib/when_exe/region/japanese_notes.rb +964 -0
  121. data/lib/when_exe/region/japanese_residues.rb +1149 -0
  122. data/lib/when_exe/region/javanese.rb +228 -0
  123. data/lib/when_exe/region/jewish.rb +127 -0
  124. data/lib/when_exe/region/korean.rb +267 -0
  125. data/lib/when_exe/region/m17n.rb +115 -0
  126. data/lib/when_exe/region/martian.rb +215 -0
  127. data/lib/when_exe/region/mayan.rb +122 -0
  128. data/lib/when_exe/region/moon.rb +333 -0
  129. data/lib/when_exe/region/nihon_shoki.rb +73 -0
  130. data/lib/when_exe/region/planets.rb +585 -0
  131. data/lib/when_exe/region/pope.rb +298 -0
  132. data/lib/when_exe/region/residue.rb +229 -0
  133. data/lib/when_exe/region/roman.rb +325 -0
  134. data/lib/when_exe/region/ryukyu.rb +98 -0
  135. data/lib/when_exe/region/shire.rb +254 -0
  136. data/lib/when_exe/region/sun.rb +210 -0
  137. data/lib/when_exe/region/thai.rb +227 -0
  138. data/lib/when_exe/region/tibetan.rb +233 -0
  139. data/lib/when_exe/region/v50.rb +111 -0
  140. data/lib/when_exe/region/vietnamese.rb +173 -0
  141. data/lib/when_exe/region/world.rb +197 -0
  142. data/lib/when_exe/timestandard.rb +547 -0
  143. data/lib/when_exe/tmduration.rb +330 -330
  144. data/lib/when_exe/tmobjects.rb +1295 -0
  145. data/lib/when_exe/tmposition.rb +1955 -0
  146. data/lib/when_exe/tmreference.rb +1547 -0
  147. data/lib/when_exe/version.rb +10 -3
  148. data/link_to_online_documents +4 -0
  149. data/test/examples/JapanHolidays.ics +456 -0
  150. data/test/examples/Millennium.ics +17 -0
  151. data/test/examples/NewYork.ics +61 -0
  152. data/test/examples/Residue.m17n +135 -0
  153. data/test/examples/Spatial.m17n +179 -0
  154. data/test/examples/Terms.m17n +39 -0
  155. data/test/examples/Test.ics +53 -0
  156. data/test/examples/USA-DST.ics +61 -0
  157. data/test/examples/geometric_complex.rb +41 -0
  158. data/test/examples/sample.xml +14 -0
  159. data/test/examples/today.rb +61 -0
  160. data/test/test.rb +54 -19
  161. data/test/test.rb.config +1 -0
  162. data/test/test/basictypes.rb +368 -0
  163. data/test/test/calendartypes.rb +57 -0
  164. data/test/test/coordinates.rb +380 -0
  165. data/test/test/ephemeris.rb +127 -0
  166. data/test/test/googlecalendar.rb +167 -0
  167. data/test/test/icalendar.rb +848 -0
  168. data/test/test/inspect.rb +115 -0
  169. data/test/test/parts.rb +480 -0
  170. data/test/test/region/chinese.rb +161 -0
  171. data/test/test/region/french.rb +33 -0
  172. data/test/test/region/geologicalage.rb +14 -0
  173. data/test/test/region/indian.rb +55 -0
  174. data/test/test/region/iran.rb +54 -0
  175. data/test/test/region/islamic.rb +18 -0
  176. data/test/test/region/japanese.rb +62 -0
  177. data/test/test/region/jewish.rb +61 -0
  178. data/test/test/region/m17n.rb +181 -0
  179. data/test/test/region/mayan.rb +78 -0
  180. data/test/test/region/moon.rb +14 -0
  181. data/test/test/region/planets.rb +14 -0
  182. data/test/test/region/residue.rb +123 -0
  183. data/test/test/region/sun.rb +14 -0
  184. data/test/test/region/thai.rb +94 -0
  185. data/test/test/region/tibetan.rb +30 -0
  186. data/test/test/tmobjects.rb +356 -57
  187. data/test/test/tmposition.rb +237 -0
  188. data/test/test/tmreference.rb +95 -0
  189. data/when_exe.gemspec +2 -2
  190. metadata +187 -7
  191. data/doc/COPYING +0 -31
  192. data/doc/COPYING.ja +0 -25
  193. data/doc/document_url +0 -1
@@ -0,0 +1,1936 @@
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2013 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ #
9
+ # 座標の記述に用いる諸オブジェクト
10
+ #
11
+ module When::Coordinates
12
+ # 変換テーブル
13
+ PRECISION = {'YEAR'=>When::YEAR, 'MONTH' =>When::MONTH, 'WEEK' =>When::WEEK, 'DAY' =>When::DAY,
14
+ 'HOUR'=>When::HOUR, 'MINUTE'=>When::MINUTE, 'SECOND'=>When::SECOND, 'SYSTEM'=>When::SYSTEM}
15
+ PERIOD = {'P1Y' =>When::YEAR, 'P1M' =>When::MONTH, 'P1W' =>When::WEEK, 'P1D' =>When::DAY,
16
+ 'PT1H'=>When::HOUR, 'PT1M' =>When::MINUTE, 'PT1S' =>When::SECOND,
17
+ '1Y' =>When::YEAR, '1M' =>When::MONTH, '1W' =>When::WEEK, '1D' =>When::DAY,
18
+ '1h' =>When::HOUR, '1m' =>When::MINUTE, '1s' =>When::SECOND}
19
+ VALUE = {'DATE'=>When::DAY, 'TIME' =>When::SYSTEM, 'DATE-TIME'=>When::SYSTEM} # RFC 5545
20
+ PRECISION_NAME = PRECISION.invert
21
+ PERIOD_NAME = {When::YEAR=>'P1Y' , When::MONTH=>'P1M', When::WEEK =>'P1W', When::DAY=>'P1D',
22
+ When::HOUR=>'PT1H', When::MINUTE=>'PT1M', When::SECOND=>'PT1S'}
23
+ MATCH = {'NS'=>/(N|S|北緯|南緯)/, 'EW'=>/(E|W|東経|西経)/}
24
+
25
+ # 60進->10進変換
26
+ #
27
+ # @param [String] src 60進法で表した方向付きの数値
28
+ # @param [String] dir 方向 ('NS' または 'EW')
29
+ #
30
+ # @return [Numeric] 10進変換した数値 (src が nil なら0.0を、Numeric ならそのままsrcを返す)
31
+ #
32
+ def self.to_deg(src, dir)
33
+ case src
34
+ when String
35
+ src = src.gsub(/_+/,'').gsub(/@/, '.')
36
+ return src.to_f unless (src =~ MATCH[dir])
37
+ sign = ($1 == dir[1..1]) ? -1 : +1
38
+ value = src.gsub(MATCH[dir], '').strip
39
+ if ((value + "00000") =~ /^(\d+)\.(\d{2})(\d{2})(\d+)$/)
40
+ deg, min, sec, frac = $~[1..4]
41
+ sec += "." + frac
42
+ else
43
+ deg, min, sec = value.split(/[^\d.]+/)
44
+ end
45
+ return sign * (deg.to_i + (min||0).to_f/60 + (sec||0).to_f/3600)
46
+ when NilClass
47
+ 0.0
48
+ when Numeric
49
+ src
50
+ else
51
+ raise TypeError, "Invalid Location Type"
52
+ end
53
+ end
54
+
55
+ # 10進->60進変換
56
+ #
57
+ # @param [Numeric] src 数値
58
+ # @param [String] dir 方向 ('NS' または 'EW')
59
+ #
60
+ # @return [String] 60進変換した数値
61
+ #
62
+ def self.to_dms(src, dir)
63
+ dir = (src >= 0) ? dir[0..0] : dir[1..1]
64
+ deg, min = src.abs.divmod(1)
65
+ min, sec = (60*min).divmod(1)
66
+ sec = (60*sec).floor
67
+ (['N','S'].include?(dir) ? "%02d.%02d%02d%s" : "%03d.%02d%02d%s") % [deg, min, sec, dir]
68
+ end
69
+
70
+ #
71
+ # 剰余類
72
+ #
73
+ class Residue < When::BasicTypes::Object
74
+
75
+ LabelProperty = 'label'
76
+
77
+ class << self
78
+ #
79
+ # 曜日(剰余類)
80
+ #
81
+ # @param [Numeric] day 月曜を 0 とする七曜(剰余類)を返します
82
+ # @param [String] day 最初の3文字から決定した七曜(剰余類)を返します。
83
+ # 一致する七曜(剰余類)がない場合、名前の一致するその他の剰余類を探して返します。
84
+ #
85
+ # @return [When::Coordinates::Residue]
86
+ #
87
+ def day_of_week(day)
88
+ return day if day.kind_of?(self)
89
+
90
+ day ||= 0
91
+ week = When.Resource('_co:CommonResidue::Week').child
92
+ case day
93
+ when Numeric ; return week[day]
94
+ when String ;
95
+ else ; return nil
96
+ end
97
+
98
+ match = day[/^...|^.{1,2}$/]
99
+ if match
100
+ week.size.times do |i|
101
+ return week[i] if week[i].label.=~(/^#{match}/i)
102
+ end
103
+ end
104
+
105
+ ObjectSpace.each_object(self) do |object|
106
+ return object if object.registered? && object.label.=~(/^#{day}$/)
107
+ end
108
+
109
+ return nil
110
+ end
111
+ alias :to_residue :day_of_week
112
+
113
+ # 汎用の mod
114
+ #
115
+ # @param [Numeric] nn 被除数
116
+ # @param [Block] dd 手続き
117
+ #
118
+ # nn = rem + dd(quot)
119
+ #
120
+ # div = dd(quot+1)-dd(quot)
121
+ #
122
+ # となるように rem, div, quot を決めたい手続き
123
+ #
124
+ # @return [Array<Numeric>] [ quot, rem, div ]
125
+ # nn を dd で「割った」商(quot), 余り(rem)および除数(div)を返します。
126
+ # remは非負、quotは Integer。
127
+ #
128
+ def mod(nn, &dd)
129
+ u = dd.call(0)
130
+ y = ((nn-u)*256).divmod(dd.call(256)-u)[0] - 1
131
+ w1, w2 = dd.call(y), dd.call(y+1)
132
+ until w1 <= nn && nn < w2
133
+ if w2 <= nn then y, w1, w2 = y+1, w2, dd.call(y+2)
134
+ else y, w1, w2 = y-1, dd.call(y-1), w1
135
+ end
136
+ end
137
+ return y, nn-w1, w2-w1
138
+ end
139
+
140
+ # 中国剰余
141
+ #
142
+ # @param [Array<Numeric>] a [ num, den ] den で割って num 余ることを示す配列
143
+ # @param [Array<Numeric>] b [ num, den ] den で割って num 余ることを示す配列
144
+ #
145
+ # @return [Array<Numeric>] a と b の条件をともに満たす [ num, den ]
146
+ #
147
+ def _china(a, b)
148
+ b, a = a, b if (a[1] <= b[1])
149
+ g, p, q = _gcd(a[1], b[1])
150
+ return [((b[0]*a[1]*q-a[0]*b[1]*p)*(a[1]*q-b[1]*p)) % (a[1]*b[1]), a[1]*b[1]] if (g == 1)
151
+ r = a[0] % g
152
+ s = b[0] % g
153
+ return nil unless (r == s)
154
+ m = _china([(a[0]-r).div(g), a[1].div(g)], [(b[0]-s).div(g), b[1].div(g)])
155
+ return nil unless (m)
156
+ return [m[0]*g+r, m[1]*g]
157
+ end
158
+
159
+ private
160
+
161
+ # 最大公約数
162
+ def _gcd(a,b)
163
+ c, x = a.divmod(b)
164
+ g = [[a, 1, 0],
165
+ [b, c, 1]]
166
+
167
+ until (x == 0) do
168
+ k = g[1][0].div(x)
169
+ g << [x, k * g[1][1] + g[0][1], k * g[1][2] + g[0][2]]
170
+ g.shift
171
+ x = g[0][0] % g[1][0]
172
+ end
173
+
174
+ return [g[1][0]] + g[0][1..2]
175
+ end
176
+ end
177
+
178
+ # 剰余
179
+ #
180
+ # @return [Numeric]
181
+ #
182
+ attr_accessor :remainder
183
+ protected :remainder=
184
+
185
+ # 法
186
+ #
187
+ # @return [Integer] (>0)
188
+ #
189
+ attr_reader :divisor
190
+
191
+ # 繰り上がり
192
+ #
193
+ # @return [Integer]
194
+ #
195
+ attr_accessor :carry
196
+ protected :carry=
197
+
198
+ # 単位
199
+ #
200
+ # @return [Hash] { String=>Numeric }
201
+ #
202
+ # Example : { 'day'=>11, 'year'=4 }
203
+ #
204
+ # 通日に適用するときは、11 ずらして計算 - 甲子日 = ユリウス日 11 + 60n
205
+ #
206
+ # 通年に適用するときは、 4 ずらして計算 - 甲子年 = 西暦 4 + 60n
207
+ #
208
+ attr_reader :units
209
+
210
+ # units の指定
211
+ #
212
+ # @param [String] arg あらかじめ units に指定した単位を用いる
213
+ #
214
+ # @return [When::Coordinates::Residue]
215
+ #
216
+ def to(arg)
217
+ return nil unless @units[arg]
218
+ self.class.new(@remainder, @divisor, @carry, {arg=>@units[arg]})
219
+ end
220
+ alias :/ :to
221
+
222
+ # オブジェクトの単位
223
+ #
224
+ # @return [String] 現在使用中の単位を返す
225
+ #
226
+ def event
227
+ keys = @units.keys
228
+ return (keys.size == 1) ? keys[0] : 'day'
229
+ end
230
+
231
+ # remainder の指定
232
+ #
233
+ # @param [Numeric] remainder 指定値を@remainderとする
234
+ # @param [String] remainder When::Parts::Resource の has-a 関係によりString に対応する子供オブジェクトを取得
235
+ #
236
+ # @return [When::Coordinates::Residue]
237
+ #
238
+ def [](remainder)
239
+ return super if !remainder.kind_of?(Numeric) || (child && child.length == @divisor)
240
+ return self if remainder == 0 && !child
241
+ remainder *= 1
242
+ return self.class.new(@remainder+remainder, @divisor, @carry, @label, @format, @units) unless (child && child.length > 0)
243
+ carry, remainder = (@remainder+remainder).divmod(@divisor)
244
+ base = child.reverse.each do |residue|
245
+ break residue if residue.remainder <= remainder
246
+ end
247
+ raise ArgumentError, "remainder out of range: #{remainder}" unless base.kind_of?(self.class)
248
+ base = base.dup
249
+ base.remainder = remainder
250
+ base.carry += carry
251
+ return base
252
+ end
253
+
254
+ # 典型的なイベントの発生間隔
255
+ #
256
+ # @param [String] event
257
+ #
258
+ # @return [When::TM::PeriodDuration]
259
+ #
260
+ def duration(event=self.event)
261
+ When::TM::PeriodDuration.new(@divisor, When::Coordinates::PRECISION[event.upcase])
262
+ end
263
+
264
+ # 派生オブジェクトと元オブジェクトの remainder の差
265
+ # (派生オブジェクトとは、元オブジェクトに[]演算を施して @remainder を変えたオブジェクト)
266
+ #
267
+ # @return [Integer]
268
+ #
269
+ # 派生オブジェクトでない場合 : 自身の remainder
270
+ #
271
+ # 派生オブジェクトである場合 : 派生オブジェクトと元オブジェクトの remainder の差
272
+ #
273
+ def difference
274
+ @difference ||= (registered? || iri !~ /:/) ? @remainder : @remainder - When.Resource(iri).remainder
275
+ end
276
+
277
+ # remainderの加算
278
+ #
279
+ # other : Numeric
280
+ #
281
+ # @return [When::Coordinates::Residue]
282
+ #
283
+ def +(other)
284
+ carry, remainder = (@remainder + other).divmod(@divisor)
285
+ return self.class.new(remainder, @divisor, @carry+carry, @units)
286
+ end
287
+
288
+ # remainderの減算
289
+ #
290
+ # @param [Numeric] other
291
+ #
292
+ # @return [When::Coordinates::Residue]
293
+ #
294
+ def -(other)
295
+ carry, remainder = (@remainder - other).divmod(@divisor)
296
+ return self.class.new(remainder, @divisor, @carry+carry, @units)
297
+ end
298
+
299
+ # carryの加算
300
+ #
301
+ # @param [Numeric] other
302
+ #
303
+ # @return [When::Coordinates::Residue]
304
+ #
305
+ def >>(other)
306
+ return self.class.new(@remainder, @divisor, @carry+other, @units)
307
+ end
308
+
309
+ # carryの減算
310
+ #
311
+ # @param [Numeric] other
312
+ #
313
+ # @return [When::Coordinates::Residue]
314
+ #
315
+ def <<(other)
316
+ return self.class.new(@remainder, @divisor, @carry-other, @units)
317
+ end
318
+
319
+ # 剰余類の共通集合
320
+ #
321
+ # @param [When::Coordinates::Residue] other
322
+ #
323
+ # @return [When::Coordinates::Residue]
324
+ #
325
+ def &(other)
326
+ case other
327
+ when Residue
328
+ if self.units == other.units
329
+ m = self.class._china([@remainder, @divisor], [other.remainder, other.divisor])
330
+ u = units.dup
331
+ else
332
+ keys = units.keys & other.units.keys
333
+ keys = ['day'] if keys == []
334
+ return nil unless (keys.size==1)
335
+ self_base = self.units[keys[0]] || 0
336
+ other_base = other.units[keys[0]] || 0
337
+ m = self.class._china([@remainder, @divisor],
338
+ [(other.remainder+other_base-self_base) % other.divisor, other.divisor])
339
+ u = {keys[0]=>self_base}
340
+ end
341
+ return nil unless (m)
342
+ return self.class.new(m[0], m[1], @carry, u)
343
+ when Pair
344
+ return Pair.new(self & other.trunk, other.branch)
345
+ when Numeric
346
+ keys = @units.keys
347
+ d = (keys.size == 1) ? @units[keys[0]] : (@units['day']||0)
348
+ c, m = (other-d).divmod(@divisor)
349
+ c += 1 if (m > @remainder)
350
+ return (c + @carry) * @divisor + @remainder + d
351
+ else
352
+ position = When::TM::Position.any_other(other)
353
+ raise TypeError, "Can't convert #{other.class} to When::TM::TemporalPosition" unless position
354
+ return position & self
355
+ end
356
+ end
357
+
358
+ # 剰余
359
+ #
360
+ # @param [When::TM::TemporalPosition, Numeric, When::Coordinates::Pair] other
361
+ #
362
+ # @return ['other' と同じクラス]
363
+ #
364
+ def %(other)
365
+ case other
366
+ when Pair
367
+ return Pair.new(self % other.trunk, other.branch)
368
+ when Numeric
369
+ keys = @units.keys
370
+ d = (keys.size == 1) ? @units[keys[0]] : (@units['day']||0)
371
+ return (other-d) % @divisor
372
+ else
373
+ position = When::TM::Position.any_other(other)
374
+ raise TypeError, "Can't convert #{other.class} to When::TM::TemporalPosition" unless position
375
+ return self[position % self]
376
+ end
377
+ end
378
+
379
+ # Enumerator の生成
380
+ #
381
+ # @overload initialize()
382
+ #
383
+ # @overload initialize(range, count_limit=nil)
384
+ # @param [Range, When::Parts::GeometricComplex] range 始点-range.first, 終点-range.last
385
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
386
+ #
387
+ # @overload initialize(first, direction, count_limit)
388
+ # @param [When::TM::TemporalPosition] first 始点
389
+ # @param [Symbol] direction :forward - 昇順, :reverse - 降順
390
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
391
+ #
392
+ # @return [When::Coordinates::Residue::BestRationalApproximations] 引数なしの場合
393
+ # @return [When::Coordinates::Residue::Enumerator] 引数ありの場合
394
+ #
395
+ def _enumerator(*args)
396
+ length = args.length
397
+ length -= 1 if args[-1].kind_of?(Hash)
398
+ args.unshift(self)
399
+ (length==0) ? BestRationalApproximations.new(self, *args) : Enumerator.new(*args)
400
+ end
401
+ alias :to_enum :_enumerator
402
+ alias :enum_for :_enumerator
403
+
404
+ # オブジェクトの生成
405
+ #
406
+ # @overload initialize(remainder, divisor, carry=0, label=nil, format=nil, units={})
407
+ # @param [Numeric] remainder 剰余
408
+ # @param [Integer] divisor 法
409
+ # @param [Integer] carry 繰り上がり
410
+ # @param [String, When::BasicTypes::M17n] label 名前
411
+ # @param [String, When::BasicTypes::M17n] format 名前の書式
412
+ # @param [Hash] units 単位(下記の通り)
413
+ # @option units [Numeric] 'day' 通日に対して使用する場合のオフセット
414
+ # @option units [Numeric] 'year' 通年に対して使用する場合のオフセット
415
+ #
416
+ def initialize(*args)
417
+ # units の取得
418
+ options =_get_options(args).dup
419
+ units = {}
420
+ list = []
421
+ options.each_pair do |key, value|
422
+ if (PRECISION[key.upcase])
423
+ list << key
424
+ units[key.downcase] = _to_num(value)
425
+ end
426
+ end
427
+ list.each do |key|
428
+ options.delete(key)
429
+ end
430
+ options['units'] ||= {}
431
+ options['units'].update(units)
432
+ _set_variables(options)
433
+ @units ||= {}
434
+
435
+ # その他の変数
436
+ remainder, divisor, carry, label, format = args
437
+ @label = label || @label
438
+ @label = m17n(@label, nil, nil, options) if (@label)
439
+ @format = format || @format
440
+ @format = m17n(@format, nil, nil, options) if (@format)
441
+ _sequence
442
+ @remainder = _to_num(remainder || @remainder)
443
+ @divisor = _to_num(divisor || @divisor )
444
+ @carry = _to_num(carry || @carry )
445
+ raise RangeError, "Divisor shoud be Positive Numeric" if (@divisor <= 0)
446
+ carry, @remainder = @remainder.divmod(@divisor)
447
+ @carry += carry
448
+ end
449
+
450
+ private
451
+
452
+ # その他のメソッド
453
+ #
454
+ # When::Coordinate::Residue で定義されていないメソッドは
455
+ # 指定の桁での剰余算とみなす
456
+ #
457
+ def method_missing(name, *args, &block)
458
+ self[args[0] % self.to(name.to_s.downcase)]
459
+ end
460
+
461
+ # 数値化
462
+ def _to_num(s)
463
+ case s
464
+ when Numeric ; return s
465
+ when nil ; return 0
466
+ when /\.|E/ ; return s.to_f
467
+ else ; return s.to_i
468
+ end
469
+ end
470
+
471
+ #
472
+ # 最良近似分数の系列を生成する Enumerator
473
+ #
474
+ class BestRationalApproximations < When::Parts::Enumerator
475
+
476
+ # Enumerator の巻き戻し
477
+ #
478
+ # @return [void]
479
+ #
480
+ def _rewind
481
+ @z = @x/@y
482
+ @k = @z.floor
483
+ @p = [1,@k]
484
+ @q = [0, 1]
485
+ super
486
+ end
487
+
488
+ # 最良近似分数を生成する
489
+ #
490
+ # @return [Array<Numeric>] ( remainder, divisor, error )
491
+ # [ remainder (Integer) 分子 ]
492
+ # [ divisor (Integer) 分母 ]
493
+ # [ error (Float) 誤差 ]
494
+ #
495
+ def succ
496
+ value = @current
497
+ if (@z==@k) ||
498
+ (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ||
499
+ (@error.kind_of?(Numeric) && @e && @error >= @e.abs)
500
+ @current = nil
501
+ else
502
+ @z = 1.0/(@z-@k)
503
+ @k = @z.floor
504
+ @e = @p[1].to_f/@q[1]-@x.to_f/@y
505
+ @current = [@p[1], @q[1], @e]
506
+ @p = [@p[1], @p[1]*@k + @p[0]]
507
+ @q = [@q[1], @q[1]*@k + @q[0]]
508
+ @count += 1
509
+ end
510
+ return value
511
+ end
512
+
513
+ # オブジェクトの生成
514
+ #
515
+ # @overload initialize(parent, options={})
516
+ # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
517
+ # @param [Hash] options 下記の通り
518
+ # @option options [Numeric] :error 収束とみなす誤差(デフォルト 1E-5)
519
+ # @option options [Integer] :count_limit 最大繰り返し回数
520
+ #
521
+ def initialize(*args)
522
+ @y = args[0].divisor
523
+ @x = args[0].remainder + @y * args[0].carry
524
+ super
525
+ @error = @options[:error] || 1e-15
526
+ @count_limit = @options[:count_limit]
527
+ end
528
+ end
529
+
530
+ #
531
+ # 指定の剰余となる通日or通年を生成する Enumerator
532
+ #
533
+ class Enumerator < When::Parts::Enumerator
534
+
535
+ #
536
+ # 次の通日or通年を得る
537
+ #
538
+ # @return [When::TM::TemporalPosition]
539
+ #
540
+ def succ
541
+ value = @current
542
+ @current = (@count_limit.kind_of?(Numeric) && @count >= @count_limit) ? nil :
543
+ (@current==:first) ? @first :
544
+ (@direction==:forward) ? @first & @parent >> @count : @first & @parent << @count
545
+ @count += 1
546
+ return value
547
+ end
548
+
549
+ # オブジェクトの生成
550
+ #
551
+ # @overload initialize(parent, range, count_limit=nil)
552
+ # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
553
+ # @param [Range, When::Parts::GeometricComplex] range 始点-range.first, 終点-range.last
554
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
555
+ #
556
+ # @overload initialize(parent, first, direction, count_limit)
557
+ # @param [When::Coordinates::Residue] parent 生成元の剰余類オブジェクト
558
+ # @param [When::TM::TemporalPosition] first 始点
559
+ # @param [Symbol] direction :forward - 昇順, :reverse - 降順
560
+ # @param [Integer] count_limit 繰り返し回数(デフォルトは指定なし)
561
+ #
562
+ def initialize(*args)
563
+ case args[1]
564
+ when When::TimeValue
565
+ first = args[1] & args[0]
566
+ first = args[1] & (args[0] << 1) if args[2] == :reverse && first > args[1]
567
+ args[1] = first
568
+ when Range
569
+ first = args[1].first & args[0]
570
+ args[1] = (args[1].exclude_end?) ? (first...args[1].last) : (first..args[1].last)
571
+ else
572
+ raise TypeError, "Second Argument should be 'When::TM::(Temporal)Position'"
573
+ end
574
+ @period = When::TM::PeriodDuration.new(args[0].divisor,
575
+ When::Coordinates::PRECISION[args[0].units] || When::DAY)
576
+ super(*args)
577
+ end
578
+ end
579
+ end
580
+
581
+ # 暦座標
582
+ #
583
+ # 暦座標の特性を定義する
584
+ #
585
+ class Index < When::BasicTypes::Object
586
+
587
+ # 日時要素の要素数
588
+ #
589
+ # @return [Integer]
590
+ #
591
+ # 「時」なら 24, 「分」なら 60
592
+ #
593
+ # 日数や太陰太陽暦の月数のように不定の場合は nil
594
+ #
595
+ attr_reader :unit
596
+
597
+ # 日時要素の下限
598
+ #
599
+ # @return [Integer]
600
+ #
601
+ # 年月日は 1, 時分秒は 0
602
+ #
603
+ attr_reader :base
604
+
605
+ # 日時要素名(幹)
606
+ #
607
+ # @return [When::BasicTypes::M17n]
608
+ #
609
+ # 月の名称の配列などに用いる
610
+ #
611
+ attr_reader :trunk
612
+
613
+ # 座標値のシフト
614
+ #
615
+ # @return [Integer](デフォルト 0)
616
+ #
617
+ # 月 - 名称を trunk 配列の何番目(0オリジン)からとるかを指定
618
+ #
619
+ # 日 - 剰余類のシフトを指定
620
+ #
621
+ attr_reader :shift
622
+
623
+ # 日時要素名(枝)
624
+ #
625
+ # @return [Hash] { Numeric=>When::BasicTypes::M17n }
626
+ #
627
+ # When::Coordinates::Pair の branch の値からprefix
628
+ #
629
+ # (「閏」などの文字列)を引くための Hash
630
+ #
631
+ attr_reader :branch
632
+
633
+ # 日時要素名(幹枝)
634
+ #
635
+ # @return [Hash] { When::Coordinates::Pair=>When::BasicTypes::M17n }
636
+ #
637
+ # When::Coordinates::Pair から 日時要素名を引くための Hash
638
+ #
639
+ # 初期化時に @trunk, @branch から自動的に生成する
640
+ #
641
+ attr_reader :trunk_branch
642
+
643
+ # インデクス(幹枝)
644
+ #
645
+ # @return [Hash] { String=>When::Coordinates::Pair }
646
+ #
647
+ # 日時要素名から When::Coordinates::Pair を引くための Hash
648
+ #
649
+ # 初期化時に @trunk, @branch から自動的に生成する
650
+ #
651
+ attr_reader :index
652
+
653
+ # @private
654
+ def self.precision(specification)
655
+ return specification.to_i if (specification.kind_of?(Numeric))
656
+ return (PRECISION[specification] || VALUE[specification] || When::SYSTEM)
657
+ end
658
+
659
+ private
660
+
661
+ def _normalize(args=[], options={})
662
+ label, unit, base, trunk, shift, branch = args
663
+ label ||= @label
664
+ unit ||= @unit
665
+ base ||= @base || 1
666
+ shift ||= @shift || 0
667
+ @label = m17n(label, nil, nil, @options) if label
668
+ @unit = unit.to_i if unit
669
+ @base = base.to_i
670
+ @trunk = trunk if trunk
671
+ @shift = shift.to_i
672
+ @branch = branch if branch
673
+ @keys = []
674
+ if (@trunk)
675
+ if (@trunk.kind_of?(Array))
676
+ m17n = {}
677
+ @trunk.length.times do |i|
678
+ m17n[(i-@shift) % @trunk.length + @base] = @trunk[i]
679
+ end
680
+ @trunk = m17n
681
+ end
682
+ raise TypeError, "Trunk must be Hash" unless @trunk.kind_of?(Hash)
683
+ @trunk.values.each do |v|
684
+ @keys |= v.keys if v.kind_of?(When::Parts::Locale)
685
+ end
686
+ end
687
+ if (@branch)
688
+ if (@branch.kind_of?(Array))
689
+ m17n = {}
690
+ @branch.length.times do |i|
691
+ m17n[i] = @branch[i]
692
+ end
693
+ @branch = m17n
694
+ end
695
+ raise TypeError, "Branch must be Hash" unless @branch.kind_of?(Hash)
696
+ @branch.values.each do |v|
697
+ @keys |= v.keys if v.kind_of?(When::Parts::Locale)
698
+ end
699
+ end
700
+ @trunk_branch = {}
701
+ @index = {}
702
+ if @trunk
703
+ if @branch
704
+ @trunk.keys.each do |t|
705
+ @branch.keys.each do |b|
706
+ @trunk_branch[Pair._en_pair(t,b)] = @trunk[t].prefix(@branch[b])
707
+ end
708
+ end
709
+ else
710
+ @trunk_branch = @trunk
711
+ end
712
+ @trunk_branch.each_pair do |k,v|
713
+ v.values.each do |s|
714
+ @index[s] = k
715
+ end
716
+ end
717
+ end
718
+ end
719
+ end
720
+
721
+ # 暦座標値
722
+ #
723
+ # 暦座標の値を表現する
724
+ #
725
+ class Pair < Numeric
726
+ DL0 = {'-'=> 0, '.'=> 0, ':'=> 0, ','=> 0, ' '=> 0, '@'=>-2 }
727
+ DL1 = {'!'=>-2.5, '%'=>-2, '&'=>-1.5, '*'=>-1, '+'=>-0.5,
728
+ '<'=> 0.5, '='=> 1, '>'=> 1.5, '?'=> 2 }
729
+ DL2 = DL1.invert
730
+
731
+ class << self
732
+ #
733
+ # source を Numeric に変換する
734
+ #
735
+ # @param [Object] source
736
+ # @param [Numeric] default source が nil の場合の代替値
737
+ #
738
+ # @return [Numeric]
739
+ #
740
+ def _en_number(source, default=0)
741
+ return default unless(source)
742
+ integer = source.to_i
743
+ float = source.to_f
744
+ return (integer==float) ? integer : float
745
+ end
746
+
747
+ #
748
+ # (source, branch) を When::Coordinates::Pair に変換する
749
+ #
750
+ # @param [Object] source
751
+ # @param [Numeric] branch
752
+ #
753
+ # @return [When::Coordinates::Pair]
754
+ #
755
+ def _force_pair(source, branch=nil)
756
+ case source
757
+ when self
758
+ return source
759
+ when Numeric
760
+ return new(_en_number(source), branch)
761
+ when String
762
+ tail = source[-1..-1]
763
+ branch = DL0[tail] || DL1[tail]
764
+ source = (branch) ? source[0..-2] : source.dup
765
+ trunk = (source =~ /\.|E/i) ? source.to_f : source.to_i
766
+ return new(trunk, branch)
767
+ else
768
+ raise TypeError, "Irregal type for #{self} :#{source}"
769
+ end
770
+ end
771
+
772
+ #
773
+ # branch が有効なら (source, branch) を When::Coordinates::Pair に変換する。
774
+ #
775
+ # @param [Object] trunk
776
+ # @param [Numeric, String] branch
777
+ #
778
+ # @return [trunk自体] branch が無効
779
+ # @return [When::Coordinates::Pair] branch が有効(非0)
780
+ #
781
+ def _en_pair(trunk, branch=nil)
782
+ return trunk if (trunk.kind_of?(self))
783
+ branch = DL0[branch] || DL1[branch] || branch
784
+ trunk = _en_number(trunk) if (trunk)
785
+ return trunk unless (branch && branch != 0)
786
+ return new(trunk, branch)
787
+ end
788
+
789
+ #
790
+ # When::Coordinates::Pair の trunk と branch の和をとって Numeric 化する
791
+ #
792
+ # @param [When::Coordinates::Pair] source trunk と branch の和を返す
793
+ # @param [Array] source 各要素を _de_pair した Arrayを返す
794
+ # @param [その他 Numeric] source をそのまま返す
795
+ #
796
+ # @return [Numeric, Array<Numeric>]
797
+ #
798
+ def _de_pair(source)
799
+ case source
800
+ when Array
801
+ return (source.map!{|v| _de_pair(v)})
802
+ when self
803
+ return source.sum
804
+ when Numeric
805
+ return source
806
+ else
807
+ raise TypeError, "Irregal type for #{self}"
808
+ end
809
+ end
810
+
811
+ #
812
+ # 文字列を When::Coordinates::Pair のArray 化する
813
+ #
814
+ # @param [String] source
815
+ #
816
+ # @return [Array<When::Coordinates::Pair>]
817
+ #
818
+ def _en_pair_array(source)
819
+ source = $1 if (source=~/^\s*\[?(.+?)\]?\s*$/)
820
+ source.split(/,/).map {|v|
821
+ v =~ /^\s*(.+?)([^\d\s])?\s*$/
822
+ _en_pair($1, $2)
823
+ }
824
+ end
825
+
826
+ #
827
+ # 日付を When::Coordinates::Pair のArray 化する
828
+ #
829
+ # @param [String] source
830
+ #
831
+ # @return [Array<When::Coordinates::Pair>]
832
+ #
833
+ def _en_pair_date_time(source)
834
+ source = $1 if source =~ /^\s*\[(.+)\]\s*$/
835
+ trunk, branch, *rest = source.strip.split(/([^\d])/)
836
+ if trunk == ''
837
+ sign = branch
838
+ trunk, branch, *rest = rest
839
+ trunk = sign + trunk if trunk
840
+ end
841
+ pairs = []
842
+ while (trunk)
843
+ pairs << _en_pair(trunk, branch)
844
+ trunk, branch, *rest = rest
845
+ end
846
+ return pairs
847
+ end
848
+
849
+ #
850
+ # branch文字を意識して、書式文字列により When::Coordinates::Pair の Array を文字列化
851
+ #
852
+ # @overload _format(format, list)
853
+ # @param [String] format 書式文字列
854
+ # @param [Array] list 書式に従って文字列化されるオブジェクトの Array
855
+ #
856
+ # @return [String]
857
+ #
858
+ def _format(m17ns)
859
+ index = 1
860
+ m17ns[0].scan(/(%(?:(\d)\$)?[-+0# ]?[*\d.$]*[cspdiuboxXfgeEG])([-.:])?|(%%|.)/).inject('') { |form, m17n|
861
+ t,i,s,c = m17n
862
+ case t
863
+ when '%0s'
864
+ m17n[index..index] = nil
865
+ when nil
866
+ form += c
867
+ else
868
+ suffix = DL2[m17ns[i ? i.to_i : index]*0] || s if s
869
+ form += t + ({nil=>'', '%'=>'%%'}[suffix] || suffix)
870
+ index += t.length - t.gsub(/\*/,'').length + 1
871
+ end
872
+ form
873
+ } % m17ns[1..-1].map { |v| v.kind_of?(self) ? v.trunk : v }
874
+ end
875
+ end
876
+
877
+ #
878
+ # 暦要素の幹
879
+ #
880
+ # @return [Numeric] 年番号,月番号,日番号など暦要素の幹となる部分
881
+ #
882
+ attr_accessor :trunk
883
+
884
+ #
885
+ # 暦要素の枝
886
+ #
887
+ # @return [Numeric] 暦要素のうち(閏であるかなど)幹で表現しきれない枝の部分
888
+ #
889
+ attr_accessor :branch
890
+
891
+ #
892
+ # 暦要素の幹と枝の和
893
+ #
894
+ # @return [Numeric]
895
+ #
896
+ # @note
897
+ # 個別の実装において、本変数が When::TM::Calendar や When::TM::Clock が扱うべき
898
+ # 座標値を表すように配慮されている。
899
+ #
900
+ # 例: 会計年度など年の変わり目が暦年と異なる場合、trunk+=1, branch-=1 として、
901
+ # trunk が会計年度, sum が暦年を表現するようにできる。この場合、trunk は表記上の
902
+ # 年、branch は会計年度と暦年にずれがあるという情報を表現していることになる。
903
+ #
904
+ attr_reader :sum
905
+
906
+ #
907
+ # trunk の更新
908
+ #
909
+ # @param [Numeric] trunk 新しい trunk
910
+ #
911
+ # @return [When::Coordinates::Pair] 更新結果
912
+ #
913
+ def trunk=(trunk)
914
+ @trunk = trunk
915
+ _normalize
916
+ self
917
+ end
918
+
919
+ #
920
+ # branch の更新
921
+ #
922
+ # @param [Numeric] branch新しい branch
923
+ #
924
+ # @return [When::Coordinates::Pair] 更新結果
925
+ #
926
+ def branch=(branch)
927
+ @branch = branch
928
+ _normalize
929
+ self
930
+ end
931
+
932
+ # 属性 @sum を取得する
933
+ #
934
+ # @return [Numeric]
935
+ #
936
+ # @note
937
+ # When::Coordinates::Pair 以外の Numeric では、単項演算 + は恒等変換になる。
938
+ # このため、When::TM::Calendar や When::TM::Clock の実装は、暦要素が When::Coordinates::Pair か
939
+ # 否かを判断することなく、暦要素に単項演算 + を施すことによって、必要な暦要素を取得できる。
940
+ def +@
941
+ return @sum
942
+ end
943
+
944
+ # trunk の符号を反転する
945
+ #
946
+ # @return [When::Coordinates::Pair] Pair.new(-@trunk, @branch)
947
+ #
948
+ def -@
949
+ return self.class.new(-(@trunk||0), @branch)
950
+ end
951
+
952
+ # @trunk, @branch を取得する
953
+ #
954
+ # @param [Integer] other (1, 0, -1)
955
+ #
956
+ # @return [Numeric]
957
+ # [ other == 1 - @trunk ]
958
+ # [ other == 0 - @branch ]
959
+ # [ other == -1 - -@trunk ]
960
+ #
961
+ # @note
962
+ # When::Coordinates::Pair 以外の Numeric では、1 による乗算は恒等変換になる。
963
+ # また、0 による乗算は恒に 0になる。
964
+ # このため、When::TM::Calendar や When::TM::Clock の実装は、暦要素が When::Coordinates::Pair か
965
+ # 否かを判断することなく、暦要素に 1 による乗算を施すことによって、trunk に相当する値を、
966
+ # 0 による乗算を施すことによって、branch に相当する値を取得できる。
967
+ def *(other)
968
+ case other
969
+ when 1 ; @trunk
970
+ when 0 ; @branch
971
+ when -1 ; -@trunk
972
+ else ; raise ArgumentError, "Irregal designation : #{other}"
973
+ end
974
+ end
975
+
976
+ # 加算
977
+ #
978
+ # @param [Numeric] other
979
+ #
980
+ # @return [When::Coordinates::Pair]
981
+ # other が When::Coordinates::Pair でない場合、trunk に対する加算となる
982
+ #
983
+ def +(other)
984
+ return self.class.new((@trunk||0) + other, @branch) unless other.kind_of?(self.class)
985
+ return self.class.new((@trunk||0) + (other.trunk||0), (@branch||0) + (other.branch||0))
986
+ end
987
+
988
+ # 減算
989
+ #
990
+ # @param [Numeric] other
991
+ #
992
+ # @return [When::Coordinates::Pair]
993
+ # other が When::Coordinates::Pair でない場合、trunk に対する減算となる
994
+ #
995
+ def -(other)
996
+ return self.class.new((@trunk||0) - other, @branch) unless other.kind_of?(self.class)
997
+ return self.class.new((@trunk||0) - (other.trunk||0), (@branch||0) - (other.branch||0))
998
+ end
999
+
1000
+ # 商と剰余
1001
+ #
1002
+ # @param [Numeric] other
1003
+ #
1004
+ # @return [When::Coordinates::Pair]
1005
+ # trunk に対する divmod となる
1006
+ #
1007
+ def divmod(other)
1008
+ div, mod = (@trunk||0).divmod(other)
1009
+ return div, self.class.new(mod, @branch)
1010
+ end
1011
+
1012
+ # 剰余
1013
+ #
1014
+ # @param [Numeric] other
1015
+ #
1016
+ # @return [When::Coordinates::Pair]
1017
+ # trunk に対する % となる
1018
+ #
1019
+ def %(other)
1020
+ self.class.new((@trunk||0) % other, @branch)
1021
+ end
1022
+
1023
+ # 比較
1024
+ #
1025
+ # @param [Numeric] other
1026
+ #
1027
+ # @return [Integer] (負,0,正)
1028
+ # trunk の比較が優先される
1029
+ #
1030
+ def <=>(other)
1031
+ other = self.class._force_pair(other)
1032
+ (@trunk <=> other.trunk).nonzero? || ((@branch||0) <=> (other.branch||0))
1033
+ end
1034
+
1035
+ # 文字列化
1036
+ #
1037
+ # @param [String] zero @branch == 0 を表現する文字列
1038
+ #
1039
+ # @return [String]
1040
+ #
1041
+ def to_s(zero='')
1042
+ return @trunk.to_s + (((@branch||0)==0) ? zero : DL2[@branch])
1043
+ end
1044
+
1045
+ # 強制型変換
1046
+ # @private
1047
+ def coerce(other)
1048
+ [self.class._force_pair(other), self]
1049
+ end
1050
+
1051
+ private
1052
+
1053
+ # オブジェクトの生成
1054
+ #
1055
+ # @param [Numeric] trunk 幹
1056
+ # @param [Numeric] branch 枝
1057
+ #
1058
+ def initialize(trunk, branch=nil)
1059
+ @trunk = trunk
1060
+ @branch = branch
1061
+ _normalize
1062
+ end
1063
+
1064
+ def _normalize
1065
+ @trunk = @trunk.to_i if (@trunk.kind_of?(Float) && @trunk.to_i == @trunk.to_f)
1066
+ @sum = @trunk
1067
+ if (@branch.kind_of?(Numeric))
1068
+ @branch = @branch.to_i if (@branch.to_i == @branch.to_f)
1069
+ @sum += @branch if (@trunk)
1070
+ end
1071
+ end
1072
+
1073
+ # その他のメソッド
1074
+ # When::Coordinates:Pair で定義されていないメソッドは
1075
+ # 処理を @sum (type:Numeric) に委譲する
1076
+ #
1077
+ def method_missing(name, *args, &block)
1078
+ @sum.send(name.to_sym, *args, &block)
1079
+ end
1080
+ end
1081
+
1082
+ # 暦座標値
1083
+ #
1084
+ # 閏秒のある暦座標の値を表現する
1085
+ #
1086
+ class LeapSeconds < Pair
1087
+
1088
+ #
1089
+ # 閏秒の単位 / When::TM::Duration::SYSTEM
1090
+ #
1091
+ # @return [Numeric]
1092
+ #
1093
+ attr_reader :second
1094
+
1095
+ #
1096
+ # trunk の整数部
1097
+ #
1098
+ # @return [Integer]
1099
+ #
1100
+ attr_reader :int
1101
+
1102
+ #
1103
+ # trunk の小数部
1104
+ #
1105
+ # @return [Float]
1106
+ #
1107
+ attr_reader :frac
1108
+
1109
+ # 加算
1110
+ #
1111
+ # @param [Numeric] other
1112
+ #
1113
+ # @return [When::Coordinates::LeapSeconds]
1114
+ # other が When::Coordinates::LeapSeconds でない場合、trunk に対する加算となる
1115
+ #
1116
+ def +(other)
1117
+ return self.class.new(@trunk + +other, @branch, @second) unless other.kind_of?(self.class)
1118
+ return self.class.new(@trunk + other.trunk, @branch + other.branch, @second)
1119
+ end
1120
+
1121
+ # 減算
1122
+ #
1123
+ # @param [Numeric] other
1124
+ #
1125
+ # @return [When::Coordinates::LeapSeconds]
1126
+ # other が When::Coordinates::LeapSeconds でない場合、trunk に対する減算となる
1127
+ #
1128
+ def -(other)
1129
+ return self.class.new(@trunk - +other, @branch, @second) unless other.kind_of?(self.class)
1130
+ return self.class.new(@trunk - other.trunk, @branch - other.branch, @second)
1131
+ end
1132
+
1133
+ # 商と剰余
1134
+ #
1135
+ # @param [Numeric] other
1136
+ #
1137
+ # @return [When::Coordinates::LeapSeconds]
1138
+ # trunk に対する divmod となる
1139
+ #
1140
+ def divmod(other)
1141
+ div, mod = @trunk.divmod(other)
1142
+ return div, self.class.new(mod, @branch, @second)
1143
+ end
1144
+
1145
+ # 比較
1146
+ #
1147
+ # @param [Numeric] other
1148
+ #
1149
+ # @return [Integer] (負,0,正)
1150
+ # trunk の整数部, branch, trunk の小数部の順に比較する
1151
+ #
1152
+ def <=>(other)
1153
+ other = self.class.new(+other, 0, @second) unless other.kind_of?(self.class)
1154
+ raise ArgumentError, "length of unit 'second' mismatch" unless @second == other.second
1155
+ (@int <=> other.int).nonzero? || (@branch <=> other.branch).nonzero? || (@frac <=> other.frac)
1156
+ end
1157
+
1158
+ # 文字列化
1159
+ #
1160
+ # @param [String] zero @branch==0 を表現する文字列
1161
+ #
1162
+ # @return [String]
1163
+ #
1164
+ def to_s(zero='')
1165
+ return @trunk.to_s + (@branch==0 ? zero : DL2[(@branch/@second).floor])
1166
+ end
1167
+
1168
+ # 強制型変換
1169
+ # @private
1170
+ def coerce(other)
1171
+ [self.class.new(other, 0, @second), self]
1172
+ end
1173
+
1174
+ # オブジェクトの生成
1175
+ #
1176
+ # @param [Numeric] trunk 幹
1177
+ # @param [Numeric] branch 枝
1178
+ # @param [Numeric] second 閏秒の単位
1179
+ #
1180
+ def initialize(trunk, branch, second)
1181
+ @second = second
1182
+ @int, @frac = trunk.divmod(second)
1183
+ super(trunk, branch)
1184
+ end
1185
+ end
1186
+
1187
+ #
1188
+ # 空間位置
1189
+ #
1190
+ class Spatial < When::BasicTypes::Object
1191
+
1192
+ LabelProperty = 'label'
1193
+
1194
+ include When::Ephemeris
1195
+
1196
+ # Degree / Internal Location Unit(16")
1197
+ #
1198
+ # (3600 を 2 の因数で割りつくした値を単位とする)
1199
+ DEGREE = 225
1200
+
1201
+ # 黄道座標 (ecliptic coordinate system)
1202
+ ECLIPTIC = 0
1203
+
1204
+ # 赤道座標 (equatorial coordinate system)
1205
+ EQUATORIAL = 1
1206
+
1207
+ # 地平座標 (horizontal coordinate system)
1208
+ HORIZONTAL = 2
1209
+
1210
+ # 惑星中心の高度
1211
+ CENTER = :center
1212
+
1213
+ # 北緯を正とする緯度 / 16秒
1214
+ #
1215
+ # @return [Numeric]
1216
+ #
1217
+ attr_reader :lat
1218
+
1219
+ # 東経を正とする経度 / 16秒
1220
+ #
1221
+ # @return [Numeric]
1222
+ #
1223
+ attr_reader :long
1224
+
1225
+ # 高度 / m
1226
+ #
1227
+ # @return [Numeric]
1228
+ # @return [:Center] 天体の中心の場合
1229
+ #
1230
+ attr_reader :alt
1231
+
1232
+ # 座標系
1233
+ #
1234
+ # @return [When::Ephemeris::Datum]
1235
+ #
1236
+ attr_reader :datum
1237
+
1238
+ # 参照
1239
+ #
1240
+ # @return [String] URI
1241
+ #
1242
+ attr_reader :ref
1243
+
1244
+ # 時間帯(オプショナル)
1245
+ #
1246
+ # @return [TZInfo::CountryTimezone]
1247
+ #
1248
+ # TZInfoライブラリから経緯度を取得して使用する
1249
+ #
1250
+ attr_reader :tz
1251
+
1252
+ # 観測地の地心距離 / kmを返します。
1253
+ #
1254
+ # @return [Numeric]
1255
+ #
1256
+ def obserber_distance
1257
+ l = PI / (90 * DEGREE) * @lat
1258
+ @datum.surface_radius * (@datum.shape[0]+@datum.shape[1]*cos(l)+@datum.shape[2]*cos(2*l))
1259
+ end
1260
+
1261
+ # 観測地での‘大地’の視半径
1262
+ #
1263
+ # @return [Numeric]
1264
+ #
1265
+ def horizon
1266
+ # 地面以下なら 90度とみなす
1267
+ return 0.25 if @alt == :Center || @alt <= 0
1268
+
1269
+ # 観測地の地心距離 / m
1270
+ r = obserber_distance * 1000.0
1271
+
1272
+ # 大気効果
1273
+ air_effect = @datum.air[1] * @alt / (@datum.air[2] * @alt + r)
1274
+
1275
+ # ‘大地’の視半径
1276
+ asin((1.0+air_effect) * r / (r+@alt)) / CIRCLE
1277
+ end
1278
+
1279
+ # 観測地の地方恒星時 / 時を返します。
1280
+ #
1281
+ # @param [Numeric] t ユリウス日(Terrestrial Time)
1282
+ # @param [When::TM::TemporalPosition] t
1283
+ #
1284
+ # @return [Numeric]
1285
+ #
1286
+ def local_sidereal_time(t)
1287
+ t = +t
1288
+ c = julian_century_from_2000(t)
1289
+ result = @datum.sid[0] + c * (@datum.sid[1] + c * @datum.sid[2]) + @long / (15.0 * DEGREE)
1290
+ result += (cosc(obl(c)) * delta_p(c) +
1291
+ (t-When::TimeStandard.delta_t_observed(t)/86400+0.5) % 1) * 24 if @datum.kind_of?(Earth)
1292
+ result
1293
+ end
1294
+
1295
+ # 観測地の日心三次元座標(黄道座標)
1296
+ #
1297
+ # @param [Numeric] t ユリウス日(Terrestrial Time)
1298
+ # @param [When::TM::TemporalPosition] t
1299
+ #
1300
+ # @return [Numeric]
1301
+ #
1302
+ def _coords(t)
1303
+ t = +t
1304
+ @datum._coords(t) + _coords_diff(t)
1305
+ end
1306
+
1307
+ private
1308
+
1309
+ # 要素の正規化
1310
+ def _normalize(args=[], options={})
1311
+ @lat = When::Coordinates.to_deg(@lat, 'NS') * DEGREE if @lat
1312
+ @long = When::Coordinates.to_deg(@long, 'EW') * DEGREE if @long
1313
+ @alt = (@alt) ? @alt.to_f : 0.0
1314
+ @datum = When.Resource(@datum || 'Earth', '_ep:')
1315
+ if @tz.kind_of?(String)
1316
+ @label ||= @tz
1317
+ @tz = When::Parts::Timezone.tz_info[@tz]
1318
+ end
1319
+ if @tz
1320
+ @lat ||= @tz.latitude * DEGREE
1321
+ @long ||= @tz.longitude * DEGREE
1322
+ end
1323
+ @lat ||= 0.0
1324
+ @long ||= 0.0
1325
+ end
1326
+
1327
+ # 観測地の惑星中心を原点とする三次元座標
1328
+ #
1329
+ # @param [Numeric] t ユリウス日(Terrestrial Time)
1330
+ # @param [When::TM::TemporalPosition] t
1331
+ # @param [Integer] system : 座標系
1332
+ # [ ECLIPTIC = 黄道座標 ]
1333
+ # [ EQUATORIAL = 赤道座標 ]
1334
+ # [ HORIZONTAL = 地平座標 ]
1335
+ #
1336
+ def _coords_diff(t, system=ECLIPTIC)
1337
+ return Coords.polar(0,0,0) if alt == :Center
1338
+ t = +t
1339
+ lat = @lat.to_f / DEGREE
1340
+ coords = Coords.polar(
1341
+ local_sidereal_time(t) / 24.0,
1342
+ (lat + @datum.shape[3] * sind(2*lat)) / 360.0,
1343
+ (obserber_distance + @alt) / AU)
1344
+ case system
1345
+ when ECLIPTIC ; coords.r_to_y(t, @datum)
1346
+ when EQUATORIAL ; coords
1347
+ when HORIZONTAL ; coords.r_to_h(t, self)
1348
+ end
1349
+ end
1350
+
1351
+ # その他のメソッド
1352
+ # When::Coordinates::Spatial で定義されていないメソッドは
1353
+ # 処理を @datum (type: When::Ephemeris::Datum) に委譲する
1354
+ #
1355
+ def method_missing(name, *args, &block)
1356
+ @datum.send(name.to_sym, self, *args, &block)
1357
+ end
1358
+
1359
+ module Normalize
1360
+
1361
+ private
1362
+
1363
+ # 位置情報
1364
+ #
1365
+ # @return [When::Coordinates::Spatial]
1366
+ #
1367
+ attr_reader :location
1368
+
1369
+ # 時差情報
1370
+ #
1371
+ # @return [Array<Numeric>]
1372
+ #
1373
+ attr_reader :timezone
1374
+
1375
+ # 日時要素の境界オブジェクト
1376
+ #
1377
+ # @return [When::CalendarTypes::Border]
1378
+ #
1379
+ attr_reader :border
1380
+
1381
+ # 境界計算用の計算オブジェクト
1382
+ #
1383
+ # @return [When::Ephemeris::Formula]
1384
+ #
1385
+ attr_reader :formula
1386
+
1387
+ #
1388
+ # Temporal Module の Spatial Parts の初期化
1389
+ #
1390
+ def _normalize_spatial
1391
+
1392
+ # Location
1393
+ if ((@location||@long||@lat).kind_of?(String))
1394
+ @location ||= "_l:long=#{@long||0}&lat=#{@lat||0}"
1395
+ @location = When.Resource(@location)
1396
+ end
1397
+
1398
+ # Timezone
1399
+ if respond_to?(:timezone)
1400
+ @timezone ||= @location ? @location.long / (Spatial::DEGREE * 15) : 9.0
1401
+ @timezone = @timezone.kind_of?(String) ? @timezone.split(/,/) : Array(@timezone)
1402
+ @timezone = @timezone.map {|v| v.to_f / 24}
1403
+ (1...@timezone.size).each do |i|
1404
+ @timezone[i] += @timezone[i-1]
1405
+ end
1406
+ end
1407
+
1408
+ # Border
1409
+ if (@border.kind_of?(String))
1410
+ @border = When.Calendar(
1411
+ case @border
1412
+ when /\([-\d]+?\)/ ; "_c:MultiBorder?borders=#{@border}"
1413
+ when /^[^A-Z_]/i ; "_c:Border?border=#{@border}"
1414
+ else ; @border
1415
+ end)
1416
+ end
1417
+
1418
+ # Formula
1419
+ instance_eval('class << self; attr_reader :formula; end') # if @location && @border
1420
+ if respond_to?(:formula)
1421
+ instance_eval('class << self; include When::Ephemeris::Formula::Methods; end')
1422
+ @formula ||= When::Ephemeris::Formula.new({:location=>@location})
1423
+ @formula = When.Resource(Array(@formula), '_ep:')
1424
+ end
1425
+ end
1426
+ end
1427
+ end
1428
+
1429
+ #
1430
+ # 暦座標を扱う処理をまとめたモジュール
1431
+ #
1432
+ # When::TM::Calendar と When::TM::Clock に共通する処理だが、ISO 19108 で両者の
1433
+ # 直接の superclass である、When::TM::ReferenceSystem は、これらの処理を持たない
1434
+ # こととなっているため、When::TM::Calendar と When::TM::Clock の共通部分を
1435
+ # モジュールとしてまとめた。
1436
+ #
1437
+ module Temporal
1438
+
1439
+ include When::Parts::MethodCash
1440
+ include When::Coordinates::Spatial::Normalize
1441
+
1442
+ # @private
1443
+ HashProperty =
1444
+ [[:origin_of_MSC, 0], [:origin_of_LSC, 0], [:index_of_MSC, 0], [:_diff_to_CE, 0],
1445
+ :unit, :base, :pair, :note,
1446
+ :location, :timezone, :border, :formula]
1447
+
1448
+ # 年/日の原点(origin of most significant coordinate)
1449
+ #
1450
+ # @return [Integer]
1451
+ #
1452
+ attr_reader :origin_of_MSC
1453
+
1454
+ # 日/秒の原点(origin of least significant coordinate)
1455
+ #
1456
+ # @return [Integer]
1457
+ #
1458
+ attr_reader :origin_of_LSC
1459
+
1460
+ # インデクスオブジェクト
1461
+ #
1462
+ # @return [Array<When::Coordinates::Index>]
1463
+ #
1464
+ attr_reader :indices
1465
+
1466
+ # 年/日のインデクス(index of most significant coordinate)
1467
+ #
1468
+ # @return [Integer]
1469
+ #
1470
+ attr_reader :index_of_MSC
1471
+
1472
+ # 日時要素の要素数
1473
+ #
1474
+ # @return [Array<Integer, nil>]
1475
+ #
1476
+ # Ex. [nil, 12], [nil, 24, 60, 60]
1477
+ #
1478
+ # 初期化時に indices から自動生成する
1479
+ #
1480
+ attr_reader :unit
1481
+
1482
+ # 日時要素の下限
1483
+ #
1484
+ # @return [Array<Integer, nil>]
1485
+ #
1486
+ # Ex. [nil, 1, 1], [nil, 0, 0, 0]
1487
+ #
1488
+ # 初期化時に indices から自動生成する
1489
+ #
1490
+ attr_reader :base
1491
+
1492
+ # 日時要素がPairであるべきか
1493
+ #
1494
+ # @return [Array<Boolean>]
1495
+ #
1496
+ # Ex. [false] * 3, [false] * 4
1497
+ #
1498
+ # 初期化時に indices から自動生成する
1499
+ #
1500
+ attr_reader :pair
1501
+
1502
+ # 代表暦注
1503
+ #
1504
+ # @return [When::CalendarTypes::CalendarNote]
1505
+ # @return [Array<Array<klass, Array<klass, method, block>>>] 最外側のArray要素は年・月・日に対応
1506
+ #
1507
+ # klass [String, When::CalendarTypes::CalendarNote, When::Coordinates::Residue]
1508
+ #
1509
+ # method [String, Symbol] (デフォルト 'day', 'month' or 'year' (対応する桁による))
1510
+ #
1511
+ # block [Block] (デフォルト なし)
1512
+ #
1513
+ def note
1514
+ @note = When.CalendarNote(@note) if @note.kind_of?(String)
1515
+ @note
1516
+ end
1517
+
1518
+ #
1519
+ # 日時要素の正規化
1520
+ #
1521
+ # @param [Array<Numeric>] source 正規化しようとしている日時要素の Array
1522
+ # @param [Array<Numeric>] other 日時要素ごとに加減算を行う場合、加減算量の Array を指定する
1523
+ # @param [Block] block
1524
+ # @note
1525
+ # 日付要素と時刻要素に関連がある場合、block を指定して、両者の
1526
+ # 情報をやり取りする( yield で通日を渡し、通日を返してもらう)。
1527
+ #
1528
+ # 例1: 夏時間制を採用している場合、日付によって時刻の正規化の仕方が影響を受ける
1529
+ #
1530
+ # 例2: 日の境界が日没の場合、当該時刻が日没の前か後かで日付が変わる
1531
+ #
1532
+ # @return [Array<Numeric>] 正規化された日時要素の Array
1533
+ #
1534
+ # 日時要素は、それぞれの When::TM::Calendar や When::TM::Clock の実装に応じて有効な値となっている。
1535
+ #
1536
+ def _validate(source, other=nil, &block)
1537
+ return _encode(_decode(source, other, &block))
1538
+ end
1539
+
1540
+ # 期間指定用 Array の桁数合わせ
1541
+ #
1542
+ # @param [Array<Numeric>] period
1543
+ #
1544
+ # @return [Array<Numeric>] 桁数合わせをした Array
1545
+ #
1546
+ def _arrange_length(period)
1547
+ return period unless period.kind_of?(Array)
1548
+ diff = @indices.length - period.length + 1
1549
+ return period if (diff == 0)
1550
+ return (diff > 0) ? Array.new(diff, 0) + period : period[(-diff)..-1]
1551
+ end
1552
+
1553
+ # 西暦との差
1554
+ #
1555
+ # @return [Integer] 暦法の年の桁と西暦とのずれ
1556
+ #
1557
+ def _diff_to_CE
1558
+ @_diff_to_CE ||= @epoch_in_CE ? @epoch_in_CE - @origin_of_MSC : 0
1559
+ end
1560
+
1561
+ # protected
1562
+
1563
+ #
1564
+ # 日時要素の encode
1565
+ #
1566
+ # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
1567
+ #
1568
+ # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
1569
+ #
1570
+ def _encode(source, border=@border)
1571
+ # source は非破壊
1572
+ date = source.dup
1573
+
1574
+ # 外部表現に戻す
1575
+ date[0] = +date[0]
1576
+ (@base.length-1).downto(@unit.length-1) do |i|
1577
+ date[i] = _from_index(date[0..i]) || date[i] + (@base[i]||0)
1578
+ end
1579
+ date[0] = source[0]
1580
+
1581
+ # 結果を反映
1582
+ date = border._adjust_epoch(date, self) if border
1583
+ _encode_upper_structure(date)
1584
+ end
1585
+
1586
+ private
1587
+
1588
+ #
1589
+ # 日時要素の decode
1590
+ #
1591
+ # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
1592
+ #
1593
+ # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
1594
+ #
1595
+ def _decode(source, other=nil)
1596
+
1597
+ # other の正規化
1598
+ period = (other) ? other.dup : Array.new(@indices.length+1,0)
1599
+
1600
+ # 上の位の集約
1601
+ date = _decode_upper_structure(source.dup)
1602
+ if ((@index_of_MSC > 0) && other)
1603
+ u = 1
1604
+ s = period[@index_of_MSC]
1605
+ (@index_of_MSC-1).downto(0) do |i|
1606
+ u *= @indices[i].unit
1607
+ s += u * period[i]
1608
+ end
1609
+ period = [s] + period[(@index_of_MSC+1)..-1]
1610
+ end
1611
+
1612
+ # 下の位の既定値
1613
+ unless date[1] || !@border
1614
+ date[0...@base.length] = @border.border([date[0]], self)
1615
+ end
1616
+
1617
+ # 要素数固定部分の正規化(上 -> 下) - ISO8601 の 小数要素(ex. "T23:20.8")の処理
1618
+ carry = 0
1619
+ @unit.length.times do |i|
1620
+ if carry == 0
1621
+ break unless date[i]
1622
+ else
1623
+ date[i] ||= 0
1624
+ date[i] += carry * unit[i]
1625
+ end
1626
+ if (date[i].kind_of?(Integer))
1627
+ carry = 0
1628
+ else
1629
+ if i < @unit.length-1
1630
+ carry = (date[i].kind_of?(Pair) ? date[i].trunk : date[i]) % 1
1631
+ date[i] -= carry
1632
+ end
1633
+ date[i] = date[i].to_i if date[i].kind_of?(Float) && date[i] == date[i].to_i
1634
+ end
1635
+ end
1636
+
1637
+ # 要素数固定部分の正規化(下 -> 上)
1638
+ carry = 0
1639
+ (@unit.length-1).downto(1) do |i|
1640
+ if date[i]
1641
+ digit = date[i] + ((other || date[i]>=0) ? period[i]-@base[i] : @unit[i])
1642
+ else
1643
+ digit = period[i]
1644
+ end
1645
+ carry, date[i] = (digit + carry).divmod(@unit[i])
1646
+ end
1647
+ year = date[0] + period[0] + carry
1648
+
1649
+ # 要素数可変部分の正規化
1650
+ limit = @base.length-1
1651
+ count = nil
1652
+ date[0] = +year
1653
+ if (@base.length > @unit.length)
1654
+ @unit.length.upto(limit) do |i|
1655
+ len = _length(date[0...i])
1656
+ if date[i]
1657
+ plus = date[i]>=0
1658
+ digit = !plus ? +date[i] : _to_index(date[0..i]) || +date[i] - @base[i]
1659
+ digit += (plus || other) ? period[i] : len
1660
+ else
1661
+ digit = period[i]
1662
+ end
1663
+
1664
+ if (i==limit) then
1665
+ # 最下位
1666
+ if ((0...len) === digit)
1667
+ # 要素が範囲内
1668
+ date[i] = digit
1669
+ elsif other && other[i] == 0
1670
+ # 要素が範囲外で、加算自体はあるが“日”の加算なし
1671
+ date[i] = len-1
1672
+ else
1673
+ # 要素が範囲外で、加算自体がないか“日”の加算あり
1674
+ date[i] = 0
1675
+ count = _coordinates_to_number(*date)+digit
1676
+ count = yield(count) if block_given?
1677
+ date = _number_to_coordinates(count)
1678
+ end
1679
+
1680
+ else
1681
+ # 最下位以外
1682
+ # 要素が大きすぎる場合
1683
+ while (digit >= len) do
1684
+ digit -= len
1685
+ carry = 1
1686
+ (i-1).downto(1) do |k|
1687
+ if (date[k] >= _length(date[0...k])-1)
1688
+ date[k] = 0
1689
+ carry = 1
1690
+ else
1691
+ date[k] += 1
1692
+ carry = 0
1693
+ break
1694
+ end
1695
+ end
1696
+ date[0] += carry
1697
+ len = _length(date[0...i])
1698
+ end
1699
+
1700
+ # 要素が小さすぎる場合
1701
+ while (digit < 0) do
1702
+ date[i-1] -= 1
1703
+ digit += _length(date[0...i])
1704
+ (i-1).downto(1) do |k|
1705
+ break if (date[k] >= 0)
1706
+ date[k-1] -= 1
1707
+ date[k] = _length(date[0...k]) - 1
1708
+ end
1709
+ end
1710
+
1711
+ # 要素が範囲内
1712
+ date[i] = digit
1713
+ end
1714
+ end
1715
+ end
1716
+ date[0] = year + (date[0] - +year)
1717
+
1718
+ # 時刻部分による補正が入る場合
1719
+ if (block_given? && !count)
1720
+ count = _coordinates_to_number(*date)
1721
+ modified = yield(count)
1722
+ date = _number_to_coordinates(modified) unless (count == modified)
1723
+ end
1724
+
1725
+ # 結果を返す
1726
+ return date
1727
+ end
1728
+
1729
+ #
1730
+ # 日時要素の下限を取得する
1731
+ #
1732
+ # @param [Array<Numeric>] date 日時要素
1733
+ #
1734
+ # @return [Integer] 日時要素の下限
1735
+ #
1736
+ def _base(date)
1737
+ return @base[date.length]
1738
+ end
1739
+
1740
+ #
1741
+ # 日時要素の番号から要素を取得する配列を返す
1742
+ #
1743
+ #def _ids(date)
1744
+ # return nil
1745
+ #end
1746
+
1747
+ #
1748
+ # 日時要素の要素数を取得する(直下の要素)
1749
+ #
1750
+ #def _length(date)
1751
+ # return @unit[date.length]
1752
+ #end
1753
+
1754
+ #
1755
+ # 日時要素の要素数を取得する(全要素)
1756
+ #
1757
+ # @param [Array<Numeric>] date 日時要素(内部表現)
1758
+ #
1759
+ # @return [Integer] 日時要素の要素数
1760
+ # @note 一般に date が 1要素ならその要素の年の日数、2要素ならその要素の年月の日数になる。
1761
+ #
1762
+ def _sum_(date)
1763
+ return @unit[date.length..-1].inject(1) {|p, u| p * u }
1764
+ end
1765
+
1766
+ def _normalize_temporal
1767
+
1768
+ # method cash
1769
+ @_m_cash_lock_ = Mutex.new if When.multi_thread
1770
+
1771
+ # Spatial Parts
1772
+ _normalize_spatial
1773
+
1774
+ # Origin and Upper Digits
1775
+ @origin_of_MSC ||= - +@border.behavior if @border
1776
+ @origin_of_MSC = Pair._en_number(@origin_of_MSC)
1777
+ @origin_of_LSC = Pair._en_number(@origin_of_LSC)
1778
+ @index_of_MSC = Pair._en_number(@index_of_MSC)
1779
+ if (@index_of_MSC != 0)
1780
+ class << self; include OriginAndUpperDigits; end
1781
+ elsif (@origin_of_MSC != 0)
1782
+ class << self; include OriginOnly; end
1783
+ end
1784
+
1785
+ # unit
1786
+ @unit = [nil]
1787
+ (@index_of_MSC...@indices.length).each do |i|
1788
+ break unless @indices[i].unit
1789
+ @unit << @indices[i].unit
1790
+ end
1791
+
1792
+ # base & pair
1793
+ @base = [nil]
1794
+ @pair = [false]
1795
+ (@index_of_MSC...@indices.length).each do |i|
1796
+ raise ArgumentError, "Base not defined" unless @indices[i].base
1797
+ @base << @indices[i].base
1798
+ @pair << (@indices[i].branch != nil)
1799
+ end
1800
+ class << self; include IndexConversion; end unless @pair.uniq == [false]
1801
+
1802
+ # note
1803
+ @note ||= 'DefaultNotes'
1804
+
1805
+ # keys
1806
+ @keys = @indices.inject(label.instance_of?(When::Parts::Locale) ? label.keys : []) {|key, index| key |= index.keys}
1807
+ end
1808
+
1809
+ # 何もしない
1810
+ def _return_nil(source); nil end
1811
+ alias :_from_index :_return_nil
1812
+ alias :_to_index :_return_nil
1813
+
1814
+ def _do_nothing(source); source end
1815
+ alias :_encode_upper_structure :_do_nothing
1816
+ alias :_decode_upper_structure :_do_nothing
1817
+
1818
+ # @private
1819
+ module IndexConversion
1820
+ #
1821
+ # indexのPair化
1822
+ #
1823
+ # @param [Array<Numeric>] date 最下位が index になっている日時要素
1824
+ #
1825
+ # @return [When::Coordinates::Pair] 最下位の index に対応する When::Coordinates::Pair
1826
+ #
1827
+ def _from_index(date)
1828
+ return nil unless @pair[date.size-1]
1829
+ ids = _ids(date[0..-2])
1830
+ m = ids[date[-1]] if (ids)
1831
+ return Pair._force_pair(m) if (ids && m)
1832
+ return Pair.new(+date[-1]+@base[date.length-1], 0)
1833
+ end
1834
+
1835
+ #
1836
+ # Pairのindex化
1837
+ #
1838
+ # @param [Array<Numeric>] date 最下位が When::Coordinates::Pair になっている日時要素
1839
+ #
1840
+ # @return [When::Coordinates::Pair] 最下位の When::Coordinates::Pair に対応する index
1841
+ #
1842
+ def _to_index(date)
1843
+ return nil unless @pair[date.size-1]
1844
+ ids = _ids(date[0..-2])
1845
+ i = ids.index(date[-1]) if ids
1846
+ return i if i
1847
+ return nil unless ids && date[-1].kind_of?(Pair)
1848
+ digit = Pair.new(date[-1].trunk, date[-1].branch)
1849
+ while digit.branch > 0
1850
+ digit.branch -= 1
1851
+ i = ids.index(digit)
1852
+ return i + date[-1].branch - digit.branch if i
1853
+ end
1854
+ return nil
1855
+ end
1856
+ end
1857
+
1858
+ alias :_method_missing :method_missing
1859
+
1860
+ # その他のメソッド
1861
+ # When::Coordinates::Temporal で定義されていないメソッドは
1862
+ # 処理を @note or @formula[0] (When::Ephemeris::Formula) に委譲する
1863
+ #
1864
+ def method_missing(name, *args, &block)
1865
+ return @note.send(name.to_sym, *(args + [self]), &block) if note.respond_to?(name)
1866
+ forward = forwarded_formula(name, args[0]) if self.respond_to?(:forwarded_formula, true)
1867
+ return forward.send(name.to_sym, *args, &block) if forward
1868
+ _method_missing(name, *args, &block)
1869
+ end
1870
+
1871
+ # @private
1872
+ module OriginOnly
1873
+ # 上の位の付加
1874
+ #
1875
+ # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
1876
+ #
1877
+ # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
1878
+ #
1879
+ def _encode_upper_structure(source)
1880
+ date = source.dup
1881
+ date[0] += @origin_of_MSC
1882
+ return date
1883
+ end
1884
+
1885
+ # 上の位の除去
1886
+ #
1887
+ # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
1888
+ #
1889
+ # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
1890
+ #
1891
+ def _decode_upper_structure(source)
1892
+ date = source.dup
1893
+ date[0] -= @origin_of_MSC
1894
+ return date
1895
+ end
1896
+ end
1897
+
1898
+ # @private
1899
+ module OriginAndUpperDigits
1900
+ # 上の位の付加
1901
+ #
1902
+ # @param [Array<Numeric>] source 日時要素の内部表現に対応する Array
1903
+ #
1904
+ # @return [Array<Numeric>] 日時要素の外部表現に対応する Array
1905
+ #
1906
+ def _encode_upper_structure(source)
1907
+ date = source.dup
1908
+ date[0] += @origin_of_MSC
1909
+ @index_of_MSC.downto(1) do |i|
1910
+ carry, date[0] = (+date[0]).divmod(@indices[i-1].unit)
1911
+ date[0] += @indices[i-1].base
1912
+ date.unshift(carry)
1913
+ end
1914
+ return date
1915
+ end
1916
+
1917
+ # 上の位の除去
1918
+ #
1919
+ # @param [Array<Numeric>] source 日時要素の外部表現に対応する Array
1920
+ #
1921
+ # @return [Array<Numeric>] 日時要素の内部表現に対応する Array
1922
+ #
1923
+ def _decode_upper_structure(source)
1924
+ date = source.dup
1925
+ u = 1
1926
+ s = 0
1927
+ @index_of_MSC.downto(1) do |i|
1928
+ s += u * (+date[i] - @indices[i-1].base) if (date[i])
1929
+ u *= @indices[i-1].unit
1930
+ end
1931
+ date[@index_of_MSC] = s + u * (+date[0]) - @origin_of_MSC
1932
+ return date[@index_of_MSC..-1]
1933
+ end
1934
+ end
1935
+ end
1936
+ end