when_exe 0.2.100 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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,1955 @@
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
+ module When::TM
9
+ #
10
+ # (5.4) Temporal Position Package
11
+ #
12
+ #
13
+
14
+ # 列挙データ型である When::TM::IndeterminateValue は,不確定な位置のために
15
+ # 6つの値を規定する.このうち Min と Max は 本ライブラリでの拡張である.
16
+ #
17
+ # These values are interpreted as follows:
18
+ #- “Unknown” 時間位置を示す値が与えられていないことを示す(本ライブラリでは“Unknown”は使用しない)
19
+ #- “Now” 常に現時点の時間位置を示す(オブジェクト生成時の時間位置でないことに注意)
20
+ #- “Before” 実際の時間位置は未知だが、指定した値よりも前であることを示す(本ライブラリでは“Before”は無視される)
21
+ #- “After” 実際の時間位置は未知だが、指定した値よりも後であることを示す(本ライブラリでは“After”は無視される)
22
+ #- “Min” 無限の過去を示す
23
+ #- “Max” 無限の未来を示す
24
+ #
25
+ # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimeIndeterminateValueType]
26
+ #
27
+ module IndeterminateValue
28
+
29
+ After = :After
30
+ Before = :Before
31
+ Now = :Now
32
+ Unknown = :Unknown
33
+ # additional value for this library
34
+ Min = :Min
35
+ # additional value for this library
36
+ Max = :Max
37
+
38
+ S = {'After'=>After, 'Before'=>Before, 'Now'=>Now, 'Unknown'=>Unknown, '-Infinity'=>Min, '+Infinity'=>Max}
39
+ I = S.invert
40
+ end
41
+
42
+ # 時間位置共用体
43
+ # union of
44
+ # When::TM::TemporalPosition
45
+ # When::BasicTypes::Date
46
+ # When::BasicTypes::Time
47
+ # When::BasicTypes::DateTime
48
+ #
49
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporal.xsd#TimePositionType gml schema}
50
+ #
51
+ class Position
52
+
53
+ include IndeterminateValue
54
+ include When::Parts::Resource
55
+
56
+ # @private
57
+ HashProperty =
58
+ [:indeterminated_position, :frame,
59
+ [:precision, When::SYSTEM], :events, :options, :trans, :query,
60
+ :location, [:time_standard, When::TimeStandard::UniversalTime], [:rate_of_clock, 1.0]]
61
+
62
+ # 代表文字列
63
+ #
64
+ # @return [When::BasicTypes::DateTime]
65
+ #
66
+ attr_reader :date_time8601
67
+ alias :dateTime8601 :date_time8601
68
+
69
+ # 時間位置
70
+ #
71
+ # @return [When::TM::TemporalPosition]
72
+ #
73
+ attr_reader :any_other
74
+ alias :anyOther :any_other
75
+
76
+ # 諸々のオブジェクトから When::TM::TemporalPosition を取り出す
77
+ #
78
+ # @param [Object] position 変換元の時間位置
79
+ # @param [Hash] options
80
+ # see {When::TM::TemporalPosition._instance}
81
+ #
82
+ # @return [When::TM::TemporalPosition] position の型により下記を返す
83
+ # - {When::BasicTypes::Date}, {When::BasicTypes::Time}, {When::BasicTypes::DateTime} - When::TM::Calendar#jul_trans による変換結果
84
+ # - {When::TM::TemporalPosition} - そのまま position ( optionは無視 )
85
+ # - {When::TM::Position} - position.any_other ( optionは無視 )
86
+ # - {When::Parts::GeometricComplex} - position.first ( optionは無視 )
87
+ #
88
+ def self.any_other(position, options={})
89
+ case position
90
+ when TemporalPosition ; position
91
+ when Position ; position.any_other
92
+ when When::Parts::GeometricComplex ; position.first
93
+ else ; When.Calendar(options[:frame] || 'Gregorian').jul_trans(position, options) || position
94
+ end
95
+ end
96
+
97
+ # オブジェクトの生成
98
+ #
99
+ # @param [String] specification When.exe Standard Representation として解釈して生成する
100
+ # @param [Hash] options 暦法や時法などの指定
101
+ # see {When::TM::TemporalPosition._instance}
102
+ #
103
+ # @note
104
+ # specification が String 以外の場合、そのオブジェクトの代表的な日時
105
+ # (When::TM::CalendarEra#reference_dateなど)により解釈する
106
+ #
107
+ def initialize(specification, options={})
108
+
109
+ case specification
110
+ when String
111
+ @date_time8601 = specification
112
+ @any_other = TemporalPosition._instance(specification, options)
113
+ when Position
114
+ @date_time8601 = specification.date_time8601
115
+ @any_other = specification.any_other
116
+ return
117
+ else
118
+ @any_other = specification
119
+ end
120
+
121
+ klass = specification.class
122
+ message = "Irregal specification type: #{klass}"
123
+
124
+ case specification
125
+ when CalDate ; @date_time8601 = When::BasicTypes::Date.new(specification.to_s)
126
+ when ClockTime ; @date_time8601 = When::BasicTypes::Time.new(specification.to_s)
127
+ when TemporalPosition ; @date_time8601 = When::BasicTypes::DateTime.new(specification.to_s)
128
+ when CalendarEra ; @date_time8601 = When::BasicTypes::Date.new(specification.reference_date.to_s)
129
+ when OrdinalEra ; @date_time8601 = When::BasicTypes::Date.new(specification.begin.to_s)
130
+ when When::BasicTypes::Date ; raise TypeError, message unless klass == CalDate
131
+ when When::BasicTypes::Time ; raise TypeError, message unless klass == ClockTime
132
+ when String ;
133
+ else ; raise TypeError, message
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ # その他のメソッド
140
+ #
141
+ # @note
142
+ # When::TM::Position で定義されていないメソッドは
143
+ # 処理を @date_time8601 (type: When::BasicTypes::DateTime)
144
+ # または @any_other (type: When::TM::TemporalPosition) に委譲する
145
+ # (両方ともに有効なメソッドは@any_otherを優先する)
146
+ #
147
+ def method_missing(name, *args, &block)
148
+ union = @any_other.respond_to?(name.to_sym) ? @any_other : @date_time8601
149
+ union.send(name.to_sym, *args, &block)
150
+ end
151
+ end
152
+
153
+ # 「時間位置」の基底クラス
154
+ #
155
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TemporalPositionType gml schema}
156
+ #
157
+ class TemporalPosition < ::Object
158
+
159
+ #
160
+ # When::TM::JulianDate, When::TM::CalDate or DateAndTime への変換メソッドを提供
161
+ #
162
+ module Conversion
163
+ #
164
+ # 対応する When::TM::JulianDate を生成
165
+ #
166
+ # @param [Hash] options
167
+ # @option options [When::TM::Clock] :clock
168
+ # @option options [When::Parts::Timezone] :tz
169
+ #
170
+ # @return [When::TM::JulianDate]
171
+ #
172
+ def julian_date(options={})
173
+ When::TM::JulianDate.new(self, options)
174
+ end
175
+ alias :to_julian_date :julian_date
176
+
177
+ #
178
+ # 対応する When::TM::CalDate or DateAndTime を生成
179
+ #
180
+ # @param [Hash] options 暦法や時法などの指定
181
+ # see {When::TM::TemporalPosition._instance}
182
+ #
183
+ # @return [When::TM::CalDate, When::TM::DateAndTime]
184
+ #
185
+ def tm_position(options={})
186
+ When.Calendar(options[:frame] || 'Gregorian').jul_trans(self, options)
187
+ end
188
+ alias :to_tm_position :tm_position
189
+ end
190
+
191
+ include When
192
+ include Comparable
193
+ include IndeterminateValue
194
+ include Coordinates
195
+ include Parts::Resource
196
+ include Conversion
197
+
198
+ # @private
199
+ HashProperty = Position::HashProperty
200
+
201
+ # この時間位置の意味づけ
202
+ #
203
+ # @return [When::TM::IndeterminateValue]
204
+ #
205
+ attr_reader :indeterminated_position
206
+ alias :indeterminatedPosition :indeterminated_position
207
+
208
+ # この時間位置と関連付けられた時間参照系 (relation - Reference)
209
+ #
210
+ # The time reference system associated with the temporal position being described
211
+ #
212
+ # @return [When::TM::ReferenceSystem]
213
+ #
214
+ attr_accessor :frame
215
+ alias :clock :frame
216
+
217
+ # この時間位置と関連付けられたイベント - additional attribute
218
+ #
219
+ # @return [Array<When::Parts::Enumerator>]
220
+ #
221
+ attr_accessor :events
222
+ #protected :events=
223
+
224
+ # この時間位置の分解能 - additional attribute
225
+ #
226
+ # @return [Numeric]
227
+ #
228
+ attr_accessor :precision
229
+
230
+ # その他の属性 - additional attribute
231
+ #
232
+ # @return [Hash] { String=>Object }
233
+ #
234
+ attr_reader :options
235
+
236
+ # その他の属性 - additional attribute
237
+ #
238
+ # @return [Hash] { String=>Object }
239
+ #
240
+ attr_accessor :trans
241
+
242
+ # その他の属性 - additional attribute
243
+ #
244
+ # @return [Hash] { String=>When::BasicTypes::M17n }
245
+ #
246
+ attr_accessor :query
247
+
248
+ # その他の属性 - additional attribute
249
+ #
250
+ # @return [When::Coordinates::Spatial]
251
+ #
252
+ attr_accessor :location
253
+
254
+ class << self
255
+
256
+ include When
257
+ include Coordinates
258
+
259
+ # Temporal Objetct の生成
260
+ #
261
+ # @param [String] specification When.exe Standard Representation
262
+ # @param [Hash] options 下記の通り
263
+ # @option options [When::TM::ReferenceSystem] :frame 暦法の指定
264
+ # @option options [When::TM::Clock] :clock 時法の指定
265
+ # @option options [When::Parts::Timezone] :tz 時法の指定(時間帯を指定する場合 :clock の替わりに用いることができる)
266
+ # @option options [Array<Numeric>] :abbr ISO8601上位省略形式のためのデフォルト日付(省略時 指定なし)
267
+ # @option options [String] :wkst ISO8601週日形式のための暦週開始曜日(省略時 'MO')
268
+ # @option options [Integer] :precision 生成するオブジェクトの分解能
269
+ # @option options [When::TimeStandard::TimeStandard] :time_standard 時刻系の指定(省略時 When::TimeStandard::UnversalTime)
270
+ # @option options [When::Ephemeris::Spatial] :location 観測地の指定(省略時 指定なし)
271
+ # @option options [String] :era_name 暦年代
272
+ # @option options [Hash] :trans 暦年代の上下限
273
+ # - :count => 条件に合致する暦年代のうち何番目を採用するか
274
+ # - :lower => 暦年代適用の下限
275
+ # true - epoch_of_use の始め(省略時)
276
+ # :reference_date - 参照事象の日付
277
+ # - :upper => 暦年代適用の上限
278
+ # true - epoch_of_use の終わり(省略時)
279
+ # :reference_date - 参照事象の日付
280
+ # @option options [Hash] :query 暦年代の絞込み条件
281
+ # - :area => area による暦年代絞込み
282
+ # - :period => period による暦年代絞込み
283
+ # - :name => name による暦年代絞込み(epoch の attribute使用可)
284
+ #
285
+ # @return [When::TM::TemporalPosition] ISO8601 time point
286
+ # @return [When::TM::Duration] ISO8601 duration
287
+ # @return [When::Parts::GeometricComplex] ISO8601 repeating interval
288
+ #
289
+ def _instance(specification, options={})
290
+
291
+ # Suffix - Frame specification
292
+ rfc5545form, frame, *rest = specification.split(/\^{1,2}/)
293
+ return rest.inject(_instance(rfc5545form + '^' + frame, options)) {|p,c| When.Resource(c, '_c:').jul_trans(p)} if rest.size > 0
294
+
295
+ options[:frame] = When.Resource(frame, '_c:') if (frame)
296
+
297
+ # Prefix - RFC 5545 Options
298
+ if (rfc5545form =~ /^([^:]+[^-:\d]):([^:].+)$/)
299
+ rfc5545option, iso8601form = $~[1..2]
300
+ rfc5545option.split(/;/).each do |eq|
301
+ key, value = eq.split(/=/, 2)
302
+ case key
303
+ when 'VALUE' ; options[:precision] = value
304
+ when 'TZID' ; options[:clock] =
305
+ case When::V::Timezone[value]
306
+ when Array ; When::V::Timezone[value][-1]
307
+ when nil ; When::Parts::Timezone.new(value)
308
+ else ; When::V::Timezone[value]
309
+ end
310
+ else ; options[key] = value
311
+ end
312
+ end
313
+ else
314
+ iso8601form = rfc5545form
315
+ end
316
+ options = options.dup
317
+
318
+ # IndeterminateValue
319
+ if (iso8601form.sub!(/\/After$|^Before\/|^Now$|^Unknown$|^[-+]Infinity$/i, ''))
320
+ options[:indeterminated_position] = When::TimeValue::S[$&.sub(/\//,'')]
321
+ case options[:indeterminated_position]
322
+ when When::TimeValue::Now,
323
+ When::TimeValue::Unknown,
324
+ When::TimeValue::Max,
325
+ When::TimeValue::Min
326
+ return self.new(self._options(options))
327
+ end
328
+ end
329
+
330
+ # each specification
331
+ splitted = iso8601form.split(/\//)
332
+ if (splitted[0] =~ /^R(\d+)?$/)
333
+ repeat = $1 ? $1.to_i : true
334
+ splitted.shift
335
+ end
336
+ case splitted.length
337
+ when 1
338
+ when 2
339
+ if (splitted[0] !~ /^[-+]?P/ && splitted[1] =~ /^\d/ && splitted[1].length < splitted[0].length)
340
+ splitted[1] = splitted[0][0..(splitted[0].length-splitted[1].length-1)] + splitted[1]
341
+ end
342
+ else
343
+ raise ArgumentError, "Irregal ISO8601 Format: #{iso8601form}"
344
+ end
345
+ options = self._options(options)
346
+ element = splitted.map { |v| _date_time_or_duration(v, options.dup) }
347
+
348
+ # total result
349
+ case repeat
350
+ when nil
351
+ case element[1]
352
+ when nil
353
+ return element[0]
354
+ when Duration
355
+ case element[0]
356
+ when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
357
+ when self ; return When::Parts::GeometricComplex.new(*element)
358
+ else ; return When::Parts::GeometricComplex.new(element[0].first, element[1])
359
+ end
360
+ when self
361
+ case element[0]
362
+ when Duration ; return When::Parts::GeometricComplex.new([[element[1]-element[0],false], [element[1],true ]])
363
+ when self ; return When::Parts::GeometricComplex.new(element[0]..element[1])
364
+ else ; return When::Parts::GeometricComplex.new(element[0].first..element[1])
365
+ end
366
+ else
367
+ case element[0]
368
+ when Duration ; return When::Parts::GeometricComplex.new([[element[1].first-element[0],false],
369
+ [element[1].last-element[0]-1,true ]])
370
+ when self ; return When::Parts::GeometricComplex.new(element[0]...element[1].last)
371
+ else ; return When::Parts::GeometricComplex.new(element[0].first...element[1].last)
372
+ end
373
+ end
374
+ when 0 ; return []
375
+ when Integer ; return [element[0]] * repeat unless element[1]
376
+ end
377
+
378
+ case element[1]
379
+ when Duration
380
+ case element[0]
381
+ when Duration ; raise TypeError, "Duplicate Duration: #{element[0]} and #{element[1]}"
382
+ else ; duration = element[1]
383
+ end
384
+ when self
385
+ case element[0]
386
+ when Duration ; duration = -element[0]
387
+ when self ; duration = element[1] - element[0]
388
+ else ; duration = element[1] - element[0].first
389
+ end
390
+ else
391
+ case element[0]
392
+ when Duration ; duration = -element[0]
393
+ when self ; duration = element[1].first - element[0]
394
+ else ; duration = element[1].first - element[0].first
395
+ end
396
+ end
397
+ base = element[0].kind_of?(Duration) ? element[1] : element[0]
398
+
399
+ if repeat.kind_of?(Integer)
400
+ result = case base
401
+ when self ; (1..repeat-1).inject([base]) {|a,i| a << (a[-1] + duration) }
402
+ else ; (1..repeat-1).inject([base]) {|a,i| a << When::Parts::GeometricComplex.new(
403
+ a[-1].first+duration...a[-1].last+duration)}
404
+ end
405
+ result.reverse! if duration.sign < 0
406
+ return result
407
+
408
+ else
409
+ duration = -duration if duration.sign < 0
410
+ return case base
411
+ when self ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base})
412
+ else ; When::V::Event.new({'rrule'=>{'FREQ'=>duration}, 'dtstart'=>base.first,
413
+ 'dtend' =>base.last})
414
+ end
415
+ end
416
+ end
417
+
418
+ # option の正規化
419
+ # @private
420
+ def _options(options)
421
+ query = options.dup
422
+ main = {}
423
+ clock = Clock.get_clock_option(query)
424
+ main[:clock] = clock if clock
425
+ [:indeterminated_position, :frame, :events, :precision,
426
+ :era_name, :era, :abbr, :wkst, :time_standard, :location].each do |key|
427
+ main[key] = query.delete(key) if (query.key?(key))
428
+ end
429
+ trans = query.delete(:trans) || {}
430
+ [:lower, :upper, :count].each do |key|
431
+ trans[key] = query.delete(key) if (query.key?(key))
432
+ end
433
+ query = query.merge(query.delete(:query)) if (query.key?(:query))
434
+ main[:query] = query if (query.size > 0)
435
+ main[:trans] = trans if (trans.size > 0)
436
+ return main
437
+ end
438
+
439
+ private
440
+
441
+ # date_time_or_duration
442
+ def _date_time_or_duration(specification, options)
443
+ # IntervalLength
444
+ args = IntervalLength._to_array(specification)
445
+ return IntervalLength.new(*args) if args
446
+
447
+ # PeriodDuration
448
+ sign, *args = PeriodDuration._to_array(specification)
449
+ if (sign)
450
+ args << options
451
+ duration = PeriodDuration.new(*args)
452
+ return (sign >= 0) ? duration : -duration
453
+ end
454
+
455
+ # TemporalPosition
456
+ specification =~ /(.+?)(?:\[([-+]?\d+)\])?$/
457
+ options[:sdn] = $2.to_i if $2
458
+ f, d, t, z, e = When::BasicTypes::DateTime._to_array($1, options)
459
+ raise ArgumentError, "Timezone conflict: #{z} - #{options[:clock]}" if (z && options[:clock])
460
+ options.delete(:abbr)
461
+ z ||= options[:clock]
462
+ z = When.Clock(z) if (z =~ /^[A-Z]+$/)
463
+
464
+ unless (d)
465
+ # ClockTime
466
+ raise ArgumentError, "Timezone conflict: #{z} - #{options[:clock]}" if (z && options[:frame])
467
+ options[:frame] ||= z
468
+ options.delete(:clock)
469
+ return ClockTime.new(t, options)
470
+ end
471
+
472
+ options[:era_name] = e if e
473
+ options[:_format ] = f if f
474
+ d, w = d[0..0], d[1..-1] if (f == :week || f == :day)
475
+ position = z ? DateAndTime.new(d, t||[0], options.update({:clock => z})) :
476
+ t ? DateAndTime.new(d, t, options) :
477
+ CalDate.new(d, options)
478
+ case f
479
+ when :day
480
+ position += PeriodDuration.new(w[0]-1, DAY)
481
+ when :week
482
+ position = ((position + PeriodDuration.new(4, DAY)) & (Residue.day_of_week(options[:wkst]) << 1)) +
483
+ PeriodDuration.new((w[0]-1)*7 + (w[1]||1)-1, DAY)
484
+ position = When::Parts::GeometricComplex.new(position...(position+DurationP1W)) unless w[1]
485
+ end
486
+ return position
487
+ end
488
+ end
489
+
490
+ # 時刻系
491
+ #
492
+ # @return [When::TimeStandard]
493
+ #
494
+ def time_standard
495
+ return @time_standard if @time_standard.kind_of?(When::TimeStandard)
496
+ @time_standard = When.Resource(@time_standard ||
497
+ (clock ? clock.time_standard : nil) ||
498
+ (frame ? frame.time_standard : nil) ||
499
+ 'UniversalTime', '_t:')
500
+ end
501
+
502
+ # 時間の歩度
503
+ #
504
+ # @return [Numeric]
505
+ #
506
+ def rate_of_clock
507
+ time_standard.rate_of_clock
508
+ end
509
+
510
+ # 内部時間
511
+ #
512
+ # @return [Numeric]
513
+ #
514
+ # 1970-01-01T00:00:00Z からの Universal Time, Coordinated の経過時間 / 128秒
515
+ #
516
+ # 暦法によっては、異なる意味を持つことがある
517
+ #
518
+ def universal_time
519
+ case @indeterminated_position
520
+ when Now ; time_standard.from_time_object(Time.now)
521
+ when Max ; +Float::MAX/4
522
+ when Min ; -Float::MAX/4
523
+ else ; raise NameError, "Temporal Reference System is not defined"
524
+ end
525
+ end
526
+
527
+ # 外部時間
528
+ #
529
+ # @return [Numeric]
530
+ #
531
+ # 1970-01-01T00:00:00TT からの terrestrial time の経過時間 / 128秒
532
+ #
533
+ def dynamical_time
534
+ return @dynamical_time if @dynamical_time && @indeterminated_position != Now
535
+ @dynamical_time =
536
+ case @indeterminated_position
537
+ when Max ; +Float::MAX/4
538
+ when Min ; -Float::MAX/4
539
+ else ; time_standard.to_dynamical_time(universal_time)
540
+ end
541
+ end
542
+
543
+ # ユリウス日時(実数)
544
+ #
545
+ # @return [Float]
546
+ #
547
+ # universal time での経過日数を, ユリウス日と1970-01-01T00:00:00Zで時計あわせしたもの
548
+ #
549
+ def to_f
550
+ JulianDate._t_to_d(universal_time)
551
+ end
552
+
553
+ # ユリウス日(整数)
554
+ #
555
+ # @return [Integer]
556
+ #
557
+ # -4712-01-01T12:00:00Z からの経過日数に対応する通番(当該時間帯での午前0時に1進める)
558
+ #
559
+ def to_i
560
+ sd = universal_time
561
+ sd -= @frame.universal_time if @frame.kind_of?(Clock)
562
+ div, mod = sd.divmod(Duration::DAY)
563
+ div + JulianDate::JD19700101
564
+ end
565
+
566
+ # 剰余類化
567
+ #
568
+ # @param [Numeric] remainder 剰余
569
+ # @param [Integer] divisor 法(>0)
570
+ #
571
+ # @return [When::Coordinates::Residue]
572
+ #
573
+ # 当日を基準とする剰余類
574
+ #
575
+ def to_residue(remainder, divisor)
576
+ When::Coordinates::Residue.new(remainder, divisor, {'day'=>to_i})
577
+ end
578
+
579
+ # ユリウス日時(実数)
580
+ #
581
+ # @return [Float]
582
+ #
583
+ # dynamical time での経過日数を, ユリウス日と1970-01-01T00:00:00TTで時計あわせしたもの
584
+ #
585
+ def +@
586
+ JulianDate._t_to_d(dynamical_time)
587
+ end
588
+
589
+ # When::TM::ClockTime オブジェクトへの変換
590
+ #
591
+ # @return [When::TM::ClokTime]
592
+ #
593
+ def to_clock_time
594
+ raise TypeError, "Clock not assigned" unless clock
595
+ clk_time = clock.to_clk_time(universal_time - (to_i - JulianDate::JD19700101)*Duration::DAY)
596
+ clk_time.clk_time[0] += to_i
597
+ return clk_time
598
+ end
599
+
600
+ # 標準ライブラリの DateTime オブジェクトへの変換
601
+ #
602
+ # @param [Hash] option 時間の歩度が1.0でない場合のための option
603
+ # see {When::TM::TemporalPosition._instance}
604
+ #
605
+ # @return [::DateTime]
606
+ #
607
+ def to_date_time(option={:frame=>When.utc})
608
+ return JulianDate.dynamical_time(dynamical_time, option).to_date_time unless rate_of_clock == 1.0
609
+ raise TypeError, "Clock not assigned" unless clock
610
+ Rational
611
+ offset = Rational(-(clock.universal_time/Duration::SECOND).to_i, (Duration::DAY/Duration::SECOND).to_i)
612
+ clk_time = clock.to_clk_time(universal_time - (to_i - JulianDate::JD19700101)*Duration::DAY).clk_time
613
+ ::DateTime.jd(to_i, clk_time[1], clk_time[2], clk_time[3].to_i, offset, ::Date::GREGORIAN)
614
+ end
615
+
616
+ # 標準ライブラリの Date オブジェクトへの変換
617
+ #
618
+ # @param [Hash] option 時間の歩度が1.0でない場合のための option
619
+ # see {When::TM::TemporalPosition._instance}
620
+ #
621
+ # @return [::Date]
622
+ #
623
+ def to_date(option={})
624
+ return JulianDate.dynamical_time(dynamical_time, option).to_date unless rate_of_clock == 1.0
625
+ ::Date.jd(to_i, ::Date::GREGORIAN)
626
+ end
627
+
628
+ # 組み込みライブラリの Time オブジェクトへの変換
629
+ #
630
+ # @return [::Time]
631
+ #
632
+ def to_time
633
+ time_standard.to_time_object(universal_time)
634
+ end
635
+
636
+ # 要素の参照
637
+ #
638
+ # @param [Integer, String] index 参照する要素の指定
639
+ #
640
+ # @return [Numeric]
641
+ #
642
+ def [](index)
643
+ return value(index) if index.kind_of?(String) || !index.respond_to?(:inject)
644
+ index.inject([]) {|list, i| list << value(i) }
645
+ end
646
+
647
+ # 加算
648
+ #
649
+ # @param [Numeric, When::TM::Duration] other
650
+ #
651
+ # @return [When::TM::TemporalPosition]
652
+ #
653
+ def +(other)
654
+ case other
655
+ when Integer ; self + PeriodDuration.new(other, When::DAY)
656
+ when Numeric ; self + IntervalLength.new(other, 'day')
657
+ when PeriodDuration ; _plus(other)
658
+ when Duration ; @frame.jul_trans(JulianDate.dynamical_time(dynamical_time + other.duration), self._attr)
659
+ else ; raise TypeError, "The right operand should be Numeric or Duration"
660
+ end
661
+ rescue RangeError
662
+ (@frame ^ self) + other
663
+ end
664
+
665
+ # 減算
666
+ #
667
+ # @param [Numeric, When::TM::Duration, When::TM::TemporalPosition] other
668
+ #
669
+ # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::Duration
670
+ # @return [When::TM::Duration] if other is a When::TM::TemporalPosition
671
+ #
672
+ def -(other)
673
+ case other
674
+ when TimeValue ; self.rate_of_clock == other.rate_of_clock && [@precision, other.precision].min <= When::DAY ?
675
+ PeriodDuration.new(self.to_i - other.to_i, When::DAY) :
676
+ IntervalLength.new((self.dynamical_time - other.dynamical_time) / Duration::SECOND, 'second')
677
+ when Integer ; self - PeriodDuration.new(other, When::DAY)
678
+ when Numeric ; self - IntervalLength.new(other, 'day')
679
+ when PeriodDuration ; _plus(-other)
680
+ when Duration ; @frame.jul_trans(JulianDate.dynamical_time(dynamical_time - other.duration), self._attr)
681
+ else ; raise TypeError, "The right operand should be Numeric, Duration or TemporalPosition"
682
+ end
683
+ rescue RangeError
684
+ (@frame ^ self) - other
685
+ end
686
+
687
+ # 分解能に対応する Duration
688
+ #
689
+ # @return [When::TM::PeriodDuration]
690
+ #
691
+ def period
692
+ return @period if @period
693
+ period_name = When::Coordinates::PERIOD_NAME[@precision]
694
+ raise ArgumentError, "Presicion not defined" unless period_name
695
+ @period = When.Duration(period_name)
696
+ end
697
+
698
+ # 前の日時
699
+ #
700
+ # @return [When::TM::TemporalPosition]
701
+ #
702
+ # 分解能に対応する Duration だけ,日時を戻す
703
+ #
704
+ def prev
705
+ @precision==When::DAY ? _force_euqal_day(-1) : self-period
706
+ rescue RangeError
707
+ (When.Calendar('Gregorian') ^ self) - period
708
+ end
709
+
710
+ # 次の日時
711
+ #
712
+ # @return [When::TM::TemporalPosition]
713
+ #
714
+ # 分解能に対応する Duration だけ,日時を進める
715
+ #
716
+ def succ
717
+ @precision==When::DAY ? _force_euqal_day(+1) : self+period
718
+ rescue RangeError
719
+ (When.Calendar('Gregorian') ^ self) + period
720
+ end
721
+ alias :next :succ
722
+
723
+ #
724
+ # 前後の日時を取得可能か?
725
+ #
726
+ # @return [Boolean]
727
+ # [ true - 可能 ]
728
+ # [ false - 不可 ]
729
+ #
730
+ def has_next?
731
+ When::Coordinates::PERIOD_NAME[@precision] != nil
732
+ end
733
+
734
+ # 下位桁の切り捨て
735
+ #
736
+ # @param [Integer] digit これより下の桁を切り捨てる(省略すると When::DAY)
737
+ #
738
+ # @param [Integer] precision 切り捨て結果の分解能
739
+ #
740
+ # @return [When::TM::TemporalPosition] (本 Class では、実際には切り捨てない)
741
+ #
742
+ def floor(digit=DAY, precision=digit)
743
+ self
744
+ end
745
+
746
+ # 分解能が時刻を持つか
747
+ #
748
+ # @return [Boolean]
749
+ #
750
+ def has_time?
751
+ (@precision > 0)
752
+ end
753
+
754
+ # 指定の日時を含むか?
755
+ #
756
+ # @param [When::TM::TemporalPosition] date チェックされる日時
757
+ #
758
+ # @return [Boolean]
759
+ # [ true - 含む ]
760
+ # [ false - 含まない ]
761
+ #
762
+ def include?(date)
763
+ return false if self.precision > date.precision
764
+ return self == date
765
+ end
766
+
767
+ # 大小比較
768
+ #
769
+ # @return [Integer] (-1, 0, 1)
770
+ #
771
+ # 分解能の低い方にあわせて比較を行う
772
+ #
773
+ # Ex. when?('2011-03') <=> when?('2011-03-10') -> 0
774
+ #
775
+ def <=>(other)
776
+ other = other.first if other.kind_of?(When::Parts::GeometricComplex)
777
+ [self.indeterminated_position, other.indeterminated_position].each do |position|
778
+ prec = SYSTEM if [TimeValue::Min, TimeValue::Max].include?(position)
779
+ end
780
+ prec = [self.precision, other.precision].min unless prec
781
+ case prec
782
+ when DAY ; return self.to_i <=> other.to_i
783
+ when SYSTEM ; src, dst = self, other
784
+ else
785
+ if prec < DAY && respond_to?(:most_significant_coordinate) &&
786
+ other.respond_to?(:most_significant_coordinate) && @frame.equal?(other.frame)
787
+ result = most_significant_coordinate <=> other.most_significant_coordinate
788
+ return result unless result == 0 && @cal_date.length + prec > 1
789
+ (@cal_date.length + prec - 2).times do |i|
790
+ result = @cal_date[i+1] <=> other.cal_date[i+1]
791
+ return result unless result == 0
792
+ end
793
+ return @cal_date[prec - 1] <=> other.cal_date[prec - 1]
794
+ end
795
+ src, dst =
796
+ [(prec >= self.precision ) ? self : self.floor(prec),
797
+ (prec >= other.precision) ? other : other.floor(prec)]
798
+ end
799
+ return src.to_i <=> dst.to_i if prec <= DAY
800
+ return src.universal_time <=> dst.universal_time if src.time_standard.equal?(dst.time_standard)
801
+ return src.dynamical_time <=> dst.dynamical_time
802
+ end
803
+
804
+ # 条件を満たすオブジェクトの抽出
805
+ #
806
+ # @param [Module, Array<Moduel>, When::Coordinates::Residue, When::TM::Duration, When::TM::Calendar, When::TM::CalendarEra] other
807
+ # @param [Boolean] leaf extract only leaf Objects.
808
+ # @param [Block] block If block is given, the specified block is yield.
809
+ #
810
+ # @return [Array of (element^self) for all When::Parts::Resource registered elements] (If other is Module)
811
+ # @return [Array of (self^element) for elemnt of other which belong to the specified module or class] (If other is [Array])
812
+ # @return [Enumerator which generates temporal position sequence begins from self with the specified duration] (If other is [When::TM::Duration])
813
+ # @return [Array of temporal position using the specified calendar as a frame] (If other is [When::TM::Calendar])
814
+ # @return [Array of temporal position using the specified calendar era as a calendarEraName] (If other is [When::TM::CalendarEra])
815
+ #
816
+ def scan(other, leaf=false, &block)
817
+ list = []
818
+ case other
819
+ when Numeric, TemporalPosition, Position
820
+ raise TypeError, "Operand should not be Numeric or (Temporal)Position"
821
+
822
+ when Module
823
+ objects = []
824
+ ObjectSpace.each_object(other) do |object|
825
+ objects << object if object.registered?
826
+ end
827
+ objects.each do |object|
828
+ element = (object ^ self)
829
+ if element && !(leaf && element.respond_to?(:leaf?) && !element.leaf?)
830
+ list << element
831
+ yield(element) if block_given?
832
+ end
833
+ end
834
+ return list
835
+
836
+ when Array
837
+ return other.map {|v| scan(v, leaf, &block)}
838
+
839
+ else
840
+ if other.respond_to?(:_enumerator)
841
+ enumerator = other._enumerator(self)
842
+ return enumerator unless block_given?
843
+ return enumerator.each(&block)
844
+ end
845
+
846
+ element = (other ^ self)
847
+ if element && !(leaf && element.respond_to?(:leaf?) && !element.leaf?)
848
+ list << element
849
+ yield(element) if block_given?
850
+ end
851
+ if (other.respond_to?(:child) && other.child)
852
+ other.child.each do |object|
853
+ list += scan(object, leaf, &block)
854
+ end
855
+ end
856
+ return list
857
+ end
858
+ end
859
+
860
+ # 条件を満たすオブジェクトの抽出
861
+ #
862
+ # @param (see #scan)
863
+ # @return (see #scan)
864
+ #
865
+ def ^(other, leaf=true, &block)
866
+ scan(other, leaf, &block)
867
+ end
868
+
869
+ # 属性を変更したコピーを作る
870
+ #
871
+ # @param [Hash] options { 属性=>属性値 }
872
+ #
873
+ # @return [When::TM::TemporalPosition]
874
+ #
875
+ def copy(options={})
876
+ position = self.dup
877
+ position._copy(options)
878
+ position
879
+ end
880
+
881
+ # 属性の Hash
882
+ # @private
883
+ def _attr
884
+ attributes = {}
885
+ ['frame', 'events', 'precision', 'options', 'trans', 'query', 'time_standard'].each do |key|
886
+ attributes[key.to_sym] = instance_variable_get("@#{key}")
887
+ end
888
+ return attributes
889
+ end
890
+
891
+ protected
892
+
893
+ # 属性のコピー
894
+ # @private
895
+ def _copy(options={})
896
+ @frame = options[:frame] if (options.key?(:frame))
897
+ @events = options[:events] if (options.key?(:events))
898
+ @precision = options[:precision] if (options.key?(:precision))
899
+ @query = options[:query] if (options.key?(:query))
900
+ @location = options[:location] if (options.key?(:location))
901
+ @time_standard = options[:time_standard] if (options.key?(:time_standard))
902
+ @sdn = @universal_time = @dynamical_time = @period = nil
903
+ _normalize(options)
904
+ return self
905
+ end
906
+
907
+ private
908
+
909
+ # オブジェクトの生成
910
+ #
911
+ # @param [Hash] options see {When::TM::TemporalPosition._instance}
912
+ #
913
+ def initialize(options={})
914
+ options.reject! {|key,value| value == nil}
915
+ options.each_pair do |key,value|
916
+ self.instance_variable_set("@#{key}", value)
917
+ end
918
+ @keys = []
919
+ @location = When.Resource(@location, '_l:') if @location.kind_of?(String)
920
+ # @sdn = @universal_time = @dynamical_time = nil
921
+ _normalize(options)
922
+ end
923
+
924
+ # オブジェクトの正規化
925
+ def _normalize(options={})
926
+ @precision ||= SYSTEM
927
+ @period = nil
928
+ @keys |= @frame.keys if @frame
929
+ end
930
+
931
+ # 指定桁のチェック
932
+ def _digit(index)
933
+ digit = index.kind_of?(String) ? PRECISION[index.upcase] : index
934
+ raise RangeError, " wrong digit: #{index}" unless digit.kind_of?(Integer) && (!block_given? || yield(digit))
935
+ digit
936
+ end
937
+
938
+ # 指定の日を探す
939
+ def _force_euqal_day(diff)
940
+ jdn = self.to_i + diff
941
+ date = self
942
+ done = {}
943
+ loop do
944
+ case
945
+ when date.to_i == jdn
946
+ return date
947
+ when date.to_i > jdn
948
+ next_date = date - When::DurationP1D
949
+ date = (date.to_i == next_date.to_i) ? date - When::DurationP1D * 2 : next_date
950
+ when date.to_i < jdn
951
+ next_date = date + When::DurationP1D
952
+ date = (date.to_i == next_date.to_i) ? date + When::DurationP1D * 2 : next_date
953
+ end
954
+ raise RangeError, "can't find target date: #{self} -> #{jdn}" if done.key?(date.to_i)
955
+ done[date.to_i] = true
956
+ end
957
+ end
958
+
959
+ # その他のメソッド
960
+ #
961
+ # @note
962
+ # When::TM::TemporalPosition で定義されていないメソッドは
963
+ # 処理を @frame (type: When::TM::Calendar or When::TM::Clock) に委譲する
964
+ #
965
+ def method_missing(name, *args, &block)
966
+ @frame.send(name.to_sym, self, *args, &block)
967
+ end
968
+ end
969
+
970
+ #
971
+ # 時間座標 - 単一の時間間隔によって定義する連続な間隔尺度を基礎とする
972
+ #
973
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeCoordinateType gml schema}
974
+ #
975
+ class Coordinate < TemporalPosition
976
+
977
+ class << self
978
+ # 内部時間によるオブジェクトの生成
979
+ alias :universal_time :new
980
+
981
+ # 外部時間によるオブジェクトの生成
982
+ #
983
+ # @param [Numeric] dynamical_time 外部時間による時間座標値
984
+ #
985
+ # @param [Hash] options 下記の通り
986
+ # @option options [When::TimeStandard] :time_standard
987
+ #
988
+ # @return [When::TM::Coordinate]
989
+ #
990
+ def dynamical_time(dynamical_time, options={})
991
+ universal_time(When.Resource(options[:time_standard] || 'UniversalTime', '_t:').from_dynamical_time(dynamical_time), options)
992
+ end
993
+
994
+ # 他種の時間位置によるオブジェクトの生成
995
+ #
996
+ # @param [Numeric, When::TM::ClockTime, ::Time, ::Date, ::DateTime] time 他種の時間位置によるオブジェクト
997
+ #
998
+ # @param [Hash] options 下記の通り
999
+ # @option options [When::TM::Clock] :clock
1000
+ # @option options [When::Parts::Timezone] :tz
1001
+ #
1002
+ # @return [When::TM::Coordinate]
1003
+ #
1004
+ def new(time, options={})
1005
+ options = options.dup
1006
+ options[:frame] = Clock.get_clock_option(options)
1007
+ case time
1008
+ when Numeric
1009
+ options[:frame] ||= When.utc unless time.kind_of?(Integer)
1010
+ external_time = (2*time - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1011
+ when ClockTime
1012
+ options[:frame] ||= time.clock
1013
+ external_time = time.clk_time[0] + time.universal_time
1014
+ when ::Time
1015
+ options[:frame] ||= When.Clock(time.gmtoff)
1016
+ external_time = When.Resource('_t:UniversalTime').from_time_object(time)
1017
+ when TimeValue
1018
+ options[:frame] ||= time.clock
1019
+ external_time = time.universal_time
1020
+ else
1021
+ if ::Object.const_defined?(:Date) && time.respond_to?(:ajd)
1022
+ case time
1023
+ when ::DateTime
1024
+ options[:frame] ||= When.Clock((time.offset * 86400).to_i)
1025
+ external_time = (2*time.ajd - (2*JulianDate::JD19700101-1)) * Duration::DAY.to_i / 2.0
1026
+ when ::Date
1027
+ external_time = JulianDate._d_to_t(time.jd)
1028
+ end
1029
+ end
1030
+ end
1031
+ raise TypeError, "Can't create #{self} from #{time.class}" unless external_time
1032
+ universal_time(external_time, options)
1033
+ end
1034
+ end
1035
+
1036
+ # 内部時間による時間座標値
1037
+ #
1038
+ # @return [Numeric]
1039
+ #
1040
+ attr_accessor :universal_time
1041
+ alias :coordinateValue :universal_time
1042
+ protected :universal_time=
1043
+
1044
+ # CoordinateSystem による時間座標値
1045
+ #
1046
+ # @return [Numeric]
1047
+ #
1048
+ def coordinateValue
1049
+ (universal_time - frame.origin.universal_time) / frame.interval.to_f
1050
+ end
1051
+
1052
+ # 加算
1053
+ #
1054
+ # @param [Numeric, When::TM::IntervalLength] other
1055
+ #
1056
+ # @return [When::TM::TemporalPosition]
1057
+ #
1058
+ def +(other)
1059
+ raise TypeError,"The right operand should be IntervalLength" if other.kind_of?(PeriodDuration)
1060
+ super
1061
+ end
1062
+
1063
+ # 減算
1064
+ #
1065
+ # @param [When::TM::TemporalPosition, Numeric, When::TM::IntervalLength] other
1066
+ #
1067
+ # @return [When::TM::TemporalPosition] if other is a Numeric or When::TM::IntervalLength
1068
+ # @return [When::TM::IntervalLength] if other is a When::TM::TemporalPosition
1069
+ #
1070
+ def -(other)
1071
+ raise TypeError,"The right operand should be IntervalLength or (Temporal)Position" if other.kind_of?(PeriodDuration)
1072
+ super
1073
+ end
1074
+
1075
+ # オブジェクトの生成
1076
+ #
1077
+ # @param [Numeric] universal_time 内部時間による時間座標値
1078
+ #
1079
+ # @param [Hash] options 下記の通り
1080
+ # @option options [When::TM::CoordinateSystem] :frame
1081
+ #
1082
+ def initialize(universal_time, options={})
1083
+ super(options)
1084
+ @universal_time = universal_time
1085
+ end
1086
+ end
1087
+
1088
+ #
1089
+ # ユリウス日
1090
+ #
1091
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#JulianDateType gml schema}
1092
+ #
1093
+ class JulianDate < Coordinate
1094
+
1095
+ # Julian Day Number
1096
+ # 19700101T120000Z
1097
+ JD19700101 = 2440588
1098
+
1099
+ class << self
1100
+
1101
+ JD19700100_5 = JD19700101 - 0.5
1102
+
1103
+ #
1104
+ # 日時の内部表現をユリウス日に変換
1105
+ #
1106
+ # @param [Numeric] t
1107
+ #
1108
+ # @return [Numeric]
1109
+ #
1110
+ def _t_to_d(t)
1111
+ t / Duration::DAY + JD19700100_5
1112
+ end
1113
+
1114
+ #
1115
+ # ユリウス日をに日時の内部表現変換
1116
+ #
1117
+ # @param [Numeric] d
1118
+ #
1119
+ # @return [Numeric]
1120
+ #
1121
+ def _d_to_t(d)
1122
+ (d - JD19700100_5) * Duration::DAY
1123
+ end
1124
+ end
1125
+
1126
+ # 加算
1127
+ #
1128
+ # @param [Numeric, When::TM::IntervalLength] other
1129
+ #
1130
+ # @return [When::TM::TemporalPosition]
1131
+ #
1132
+ def +(other)
1133
+ new_date = super
1134
+ if new_date.frame && new_date.frame._need_validate
1135
+ new_date.frame = new_date.frame._daylight(self.class.dynamical_time(new_date.dynamical_time, {:frame=>When.utc}))
1136
+ end
1137
+ return new_date
1138
+ end
1139
+
1140
+ # ユリウス日が指定の剰余となる日
1141
+ #
1142
+ # @param [When::Coordinates::Residue] other
1143
+ #
1144
+ # @return [When::TM::TemporalPosition]
1145
+ #
1146
+ def &(other)
1147
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1148
+ raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1149
+ jdn = to_i
1150
+ new_date = self.dup
1151
+ new_date.universal_time += ((other & jdn) - jdn) * Duration::DAY
1152
+ return new_date
1153
+ end
1154
+
1155
+ # ユリウス日の剰余
1156
+ #
1157
+ # @param [When::Coordinates::Residue] other
1158
+ #
1159
+ # @return [Numeric]
1160
+ #
1161
+ def %(other)
1162
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1163
+ raise ArgumentError,"The right operand should have a unit 'day'" unless other.event == 'day'
1164
+ other % to_i
1165
+ end
1166
+
1167
+ private
1168
+
1169
+ # オブジェクトの生成
1170
+ #
1171
+ # @param [Numeric] universal_time 内部時間による時間座標値
1172
+ #
1173
+ # @param [Hash] options 以下の通り
1174
+ # @option options [When::TM::Clock] :frame
1175
+ # @option options [Integer] :precision
1176
+ #
1177
+ def initialize(universal_time, options={})
1178
+ @frame = options.delete(:frame)
1179
+ @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1180
+ @frame = @frame._daylight(self.class.universal_time(universal_time, {:frame=>When.utc})) if @frame && @frame._need_validate
1181
+ precision = options.delete(:precision)
1182
+ precision ||= DAY unless @frame.kind_of?(Clock)
1183
+ @precision = Index.precision(precision)
1184
+ super
1185
+ end
1186
+ end
1187
+
1188
+ #
1189
+ # 順序時間参照系で参照する位置
1190
+ #
1191
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#TimeOrdinalPositionType gml schema}
1192
+ #
1193
+ class OrdinalPosition < TemporalPosition
1194
+
1195
+ # この順序位置と関連付けられた順序年代 (relation - Reference)
1196
+ #
1197
+ # The ordinal era associated with the ordinal position being described
1198
+ #
1199
+ # @return [When::TM::OrdinalEra]
1200
+ #
1201
+ attr_reader :ordinal_position
1202
+ alias :ordinalPosition :ordinal_position
1203
+
1204
+ # オブジェクトの生成
1205
+ #
1206
+ # @param [When::TM::OrdinalEra] ordinal_position この順序位置と関連付けられた順序年代
1207
+ # @param [Hash] options see {When::TM::TemporalPosition._instance}
1208
+ #
1209
+ def initialize(ordinal_position, options={})
1210
+ super(options)
1211
+ @ordinal_position = ordinal_position
1212
+ end
1213
+ end
1214
+
1215
+ #
1216
+ # 時刻
1217
+ #
1218
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#ClockTimeType gml schema}
1219
+ #
1220
+ class ClockTime < TemporalPosition
1221
+
1222
+ # 時刻要素
1223
+ #
1224
+ # @return [Array<Numeric>]
1225
+ #
1226
+ attr_reader :clk_time
1227
+ alias :clkTime :clk_time
1228
+
1229
+ # 内部時間
1230
+ #
1231
+ # @return [Numeric]
1232
+ #
1233
+ # T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1234
+ #
1235
+ # 時法によっては、異なる意味を持つことがある
1236
+ #
1237
+ def universal_time
1238
+ raise NameError, "Temporal Reference System is not defined" unless @frame
1239
+ @universal_time ||= @frame.to_universal_time(@clk_time)
1240
+ end
1241
+
1242
+ # 繰り上がり
1243
+ #
1244
+ # @return [Numeric]
1245
+ #
1246
+ # 日付の境界が午前0時でない場合、clk_time の最上位桁に 0 以外が入ることがある
1247
+ #
1248
+ def carry
1249
+ return @clk_time[0]
1250
+ end
1251
+
1252
+ # 要素の参照
1253
+ #
1254
+ # @param [Integer, String] index 参照する要素の指定
1255
+ #
1256
+ # @return [Numeric]
1257
+ #
1258
+ def value(index)
1259
+ @clk_time[_digit(index) {|digit| digit >= DAY}]
1260
+ end
1261
+
1262
+ #protected
1263
+ # 属性のコピー
1264
+ # @private
1265
+ def _copy(options={})
1266
+ @clk_time = options[:time] if (options.key?(:time))
1267
+ @frame = options[:clock] if (options.key?(:clock))
1268
+ if (options.key?(:tz_prop))
1269
+ @frame = @frame.dup
1270
+ @frame.tz_prop = options[:tz_prop]
1271
+ end
1272
+ return super
1273
+ end
1274
+
1275
+ # オブジェクトの生成
1276
+ #
1277
+ # @param [String] time ISO8601形式の時刻表現
1278
+ # @param [Array<Numeric>] time (日, 時, 分, 秒)
1279
+ #
1280
+ #
1281
+ # @param [Hash] options 以下の通り
1282
+ # @option options [When::TM::Clock] :frame
1283
+ # @option options [Integer] :precision
1284
+ #
1285
+ def initialize(time, options={})
1286
+ # 参照系の取得
1287
+ @frame = (options[:frame] || Clock.local_time || When.utc)
1288
+ @frame = When.Clock(@frame) if (@frame.kind_of?(String))
1289
+ options.delete(:frame)
1290
+
1291
+ # 時刻表現の解読 ( Time Zone の解釈 )
1292
+ if (time.kind_of?(String))
1293
+ case time
1294
+ when /^([-+])?(\d{2,}?):?(\d{2})?:?(\d{2}(\.\d+)?)?$/
1295
+ sign, hh, mm, ss = $~[1..4]
1296
+ time = @frame._validate([0,0,0,0],
1297
+ [0,
1298
+ -(sign.to_s + "0" + hh.to_s).to_i,
1299
+ -(sign.to_s + "0" + mm.to_s).to_i,
1300
+ Pair._en_number(-(sign.to_s + "0" + ss.to_s).to_f)])
1301
+ time[0] = Pair.new(0, time[0].to_i) if (time[0] != 0)
1302
+ when /^Z$/
1303
+ time = [0,0,0,0]
1304
+ else
1305
+ raise ArgumentError, "Invalid Time Format"
1306
+ end
1307
+ end
1308
+ @clk_time = time
1309
+
1310
+ # 分解能
1311
+ @precision = @frame._precision(time, options.delete(:precision))
1312
+
1313
+ super(options)
1314
+ end
1315
+
1316
+ private
1317
+
1318
+ # オブジェクトの正規化
1319
+ def _normalize(options={})
1320
+ # strftime で使用する locale
1321
+ @keys |= @frame.keys
1322
+
1323
+ # 時刻の正規化
1324
+ @clk_time = @frame._validate(@clk_time) unless options[:validate]
1325
+ end
1326
+
1327
+ # 加減算共通処理
1328
+ def _plus(period)
1329
+ self.dup._copy({:time=>@frame._validate(@clk_time, @frame._arrange_length(period.time)),
1330
+ :events=>nil, :query=>nil, :validate=>:done})
1331
+ end
1332
+ end
1333
+
1334
+ #
1335
+ # 暦日
1336
+ #
1337
+ # see {gml schema}[link:http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#CalDateType]
1338
+ #
1339
+ class CalDate < TemporalPosition
1340
+
1341
+ # 検索オプション
1342
+ # @private
1343
+ SearchOption = {After=>[0, -2, Before], Before=>[-2, 0, After]}
1344
+
1345
+ # 日付要素
1346
+ #
1347
+ # @return [Array<Numeric>]
1348
+ #
1349
+ attr_reader :cal_date
1350
+ alias :calDate :cal_date
1351
+
1352
+ # 暦年代名
1353
+ #
1354
+ # @return [Array] ( name, epoch, reverse, go back )
1355
+ # - name [String] 暦年代名
1356
+ # - epoch [Integer] 使用する When::TM::Calendar で暦元に対応する年
1357
+ # - reverse [Boolean] 年数が昇順(false,nil)か降順(true)か
1358
+ # - go back [Boolean] 参照イベントより前の暦日か(true)、否か(false,nil)
1359
+ #
1360
+ attr_accessor :calendar_era_name
1361
+ alias :calendarEraName :calendar_era_name
1362
+
1363
+ # 暦年代
1364
+ #
1365
+ # @return [When::TM::CalendarEra]
1366
+ #
1367
+ attr_accessor :calendar_era
1368
+ alias :calendarEra :calendar_era
1369
+
1370
+ # 時法の取得 - ダミー
1371
+ def clock
1372
+ nil
1373
+ end
1374
+
1375
+ # 内部時間
1376
+ #
1377
+ # @return [Numeric]
1378
+ #
1379
+ # 当日正午の 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1380
+ #
1381
+ def universal_time
1382
+ return super if [Now, Max, Min].include?(@indeterminated_position)
1383
+ @universal_time ||= JulianDate._d_to_t(to_i)
1384
+ end
1385
+
1386
+ # ユリウス日
1387
+ #
1388
+ # @return [Integer]
1389
+ #
1390
+ # -4712-01-01T12:00:00Z からの経過日数に対応する通番(日の境界で1進める)
1391
+ #
1392
+ def to_i
1393
+ return @sdn if @sdn
1394
+ name, base = @calendar_era_name
1395
+ if base
1396
+ date = @cal_date.dup
1397
+ date[0] += base
1398
+ else
1399
+ date = @cal_date
1400
+ end
1401
+ @sdn = @frame.to_julian_date(date)
1402
+ end
1403
+
1404
+ # 剰余類化
1405
+ #
1406
+ # @param [Numeric] remainder 剰余
1407
+ # @param [Integer] divisor 法(>0)
1408
+ #
1409
+ # @return [When::Coordinates::Residue] 当日、当年を基準とする剰余類
1410
+ #
1411
+ def to_residue(remainder, divisor)
1412
+ When::Coordinates::Residue.new(remainder, divisor, {'day' => least_significant_coordinate,
1413
+ 'year' => most_significant_coordinate})
1414
+ end
1415
+
1416
+ # 要素の参照
1417
+ #
1418
+ # @param [Integer, String] index 参照する要素の指定
1419
+ #
1420
+ # @return [Numeric]
1421
+ #
1422
+ def value(index)
1423
+ @cal_date[(_digit(index) {|digit| digit <= DAY})-1]
1424
+ end
1425
+
1426
+ #
1427
+ # 最上位の要素
1428
+ #
1429
+ # @return [Numeric] 暦年代の epoch に関わらず暦法に従った年の通し番号を返す
1430
+ #
1431
+ def most_significant_coordinate
1432
+ coordinate = @cal_date[0]
1433
+ coordinate += @calendar_era_name[1] if @calendar_era_name
1434
+ @frame.index_of_MSC.times do |i|
1435
+ coordinate = +coordinate * @frame.indices[i].unit + @cal_date[i+1] - @frame.indices[i].base
1436
+ end
1437
+ coordinate
1438
+ end
1439
+
1440
+ #
1441
+ # 最下位の要素
1442
+ #
1443
+ # @return [Numeric] 剰余類の演算に用いる日の通し番号を返す
1444
+ #
1445
+ def least_significant_coordinate
1446
+ return to_i + @frame.indices[-1].shift
1447
+ end
1448
+
1449
+ # ユリウス日または通年が指定の剰余となる日
1450
+ #
1451
+ # @param [When::Coordinates::Residue] other
1452
+ #
1453
+ # @return [When::TM::CalDate]
1454
+ #
1455
+ def &(other)
1456
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1457
+ case other.event
1458
+ when 'day'
1459
+ # 指定の剰余となる日
1460
+ other -= @frame.indices[-1].shift unless @frame.indices[-1].shift == 0
1461
+ date = @frame.to_cal_date(other & to_i)
1462
+ date[0] -= @calendar_era_name[1] if @calendar_era_name
1463
+ return self.dup._copy({:events=>nil, :query=>@query, :validate=>:done, :date=>date})
1464
+
1465
+ when 'year'
1466
+ # 指定の剰余となる年
1467
+ date = @cal_date.dup
1468
+ date[0] = (other & (most_significant_coordinate + @frame._diff_to_CE)) - @frame._diff_to_CE
1469
+ date[0] -= @calendar_era_name[1] if @calendar_era_name
1470
+ return self.dup._copy({:date=>date, :events=>nil, :query=>@query})
1471
+
1472
+ else
1473
+ raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1474
+ end
1475
+ end
1476
+
1477
+ # ユリウス日または通年の剰余
1478
+ #
1479
+ # @param [When::Coordinates::Residue] other
1480
+ #
1481
+ # @return [Numeric]
1482
+ #
1483
+ def %(other)
1484
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1485
+ case other.event
1486
+ when 'day' ; other % least_significant_coordinate
1487
+ when 'year' ; other % (most_significant_coordinate + @frame._diff_to_CE)
1488
+ else ; raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1489
+ end
1490
+ end
1491
+
1492
+ # 下位桁の切り捨て
1493
+ #
1494
+ # @param [Integer] digit 切り捨てずに残す、最下位の桁
1495
+ #
1496
+ # @param [Integer] precision 切り捨て結果の分解能
1497
+ #
1498
+ # @return [When::TM::CalDate]
1499
+ #
1500
+ def floor(digit=DAY, precision=digit)
1501
+ options = {:date=>@cal_date[0..(digit-1)], :events=>nil, :query=>nil}
1502
+ options[:precision] = precision if precision
1503
+ self.dup._copy(options)
1504
+ end
1505
+
1506
+ # 下位桁の切り上げ
1507
+ #
1508
+ # @param [Integer] digit 切り上げずに残す、最下位の桁
1509
+ #
1510
+ # @param [Integer] precision 切り上げ結果の分解能
1511
+ #
1512
+ # @return [When::TM::CalDate]
1513
+ #
1514
+ def ceil(digit=DAY, precision=digit)
1515
+ (self + PeriodDuration.new(1, digit, (-@frame.indices.length)..0)).floor(digit, precision)
1516
+ end
1517
+
1518
+ # 要素数 ― 上位要素に含まれる下位要素の数
1519
+ #
1520
+ # @param [Integer] upper 上位要素のインデックス
1521
+ # @param [Integer] lower 下位要素のインデックス(DAY または MONTH)
1522
+ #
1523
+ # @return [Integer]
1524
+ #
1525
+ def length(upper, lower=DAY)
1526
+ range = [floor(upper).to_i, ceil(upper).to_i]
1527
+ range = range.map {|d| (Residue.mod(d) {|m| frame._new_month(m)})[0]} if lower == MONTH
1528
+ range[1] - range[0]
1529
+ end
1530
+
1531
+ # 時刻情報のない When::TM::CalDate を返す
1532
+ #
1533
+ # @return [When::TM::CalDate]
1534
+ #
1535
+ alias :to_cal_date :dup
1536
+ alias :to_CalDate :to_cal_date
1537
+
1538
+ # 暦年代が末端の参照であるか?
1539
+ #
1540
+ # @return [Boolean]
1541
+ #
1542
+ def leaf?
1543
+ name, = @calendar_era_name
1544
+ return true unless name.respond_to?(:_pool)
1545
+ era = name._pool['..']
1546
+ return true unless era.respond_to?(:leaf?)
1547
+ return era.leaf?
1548
+ end
1549
+
1550
+ # 属性の Hash
1551
+ # @private
1552
+ def _attr
1553
+ super.merge({:era_name=>@calendar_era_name, :era=>@calendar_era})
1554
+ end
1555
+ protected
1556
+
1557
+ # 属性のコピー
1558
+ # @private
1559
+ def _copy(options={})
1560
+ @cal_date = options[:date] if (options.key?(:date))
1561
+ return super
1562
+ end
1563
+
1564
+ # オブジェクトの生成
1565
+ #
1566
+ # @param [Array<Numeric>] date 日付表現
1567
+ #
1568
+ # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
1569
+ # @option options [When::TM::Calendar] :frame
1570
+ # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
1571
+ # (Integer は 当該年号の 0 年に相当する通年)
1572
+ # @option options [Integer] :precision
1573
+ #
1574
+ def initialize(date, options={})
1575
+ # 年号 & 日付
1576
+ @calendar_era_name = options[:era_name]
1577
+ @calendar_era = options[:era]
1578
+ @cal_date = date
1579
+
1580
+ super(options)
1581
+ end
1582
+
1583
+ private
1584
+
1585
+ # オブジェクトの正規化
1586
+ def _normalize(options={})
1587
+
1588
+ # 日付配列の長さ
1589
+ cal_date_index = @cal_date.index(nil) || @cal_date.length
1590
+
1591
+ # 日付の正規化
1592
+ if @calendar_era_name
1593
+ # Calendar Era がある場合
1594
+ trans_options = @trans || {} # TODO? 消す
1595
+ count = trans_options[:count] || 1
1596
+ query_options = (@query || {}).dup
1597
+ query_options[:label] = @calendar_era_name
1598
+ query_options[:count] = count
1599
+ era = date = nil
1600
+ if @calendar_era
1601
+ era, date = _search_era(@calendar_era, trans_options)
1602
+ else
1603
+ eras = CalendarEra._instance(query_options)
1604
+ raise ArgumentError, "CalendarEraName doesn't exist: #{query_options[:label]}" if (eras.size==0)
1605
+ eras.each do |e|
1606
+ era, date = _search_era(e, trans_options)
1607
+ next unless era
1608
+ count -= 1
1609
+ break unless count > 0
1610
+ end
1611
+ end
1612
+ raise RangeError, "Out of CalendarEra Range" unless era
1613
+ @calendar_era_name = date.calendar_era_name
1614
+ @calendar_era = era
1615
+ @cal_date = date.cal_date
1616
+ @frame = date.frame
1617
+ @query = (@query||{}).merge(date.query)
1618
+ @trans = (@trans||{}).merge(date.trans)
1619
+ @keys |= @calendar_era_name[0].keys | @frame.keys
1620
+ else
1621
+ # Calendar Era がない場合
1622
+ @frame = When.Resource(options[:frame] || @frame || 'Gregorian', '_c:')
1623
+ @cal_date = @frame._validate(@cal_date) unless options[:validate] == :done
1624
+ @keys |= @frame.keys
1625
+ end
1626
+
1627
+ # 分解能
1628
+ precision = options.delete(:precision) || @precision
1629
+ cal_date_index =
1630
+ case options.delete(:_format)
1631
+ when :century ; CENTURY
1632
+ when :week, :day ; DAY
1633
+ else ; cal_date_index - (@frame.indices.length + 1)
1634
+ end
1635
+ precision ||= cal_date_index if cal_date_index < DAY
1636
+ precision ||= @clk_time.precision if @clk_time
1637
+ @precision = Index.precision(precision || DAY)
1638
+ end
1639
+
1640
+ # 暦年代を探す
1641
+ def _search_era(era, trans_options)
1642
+ cal_date = @cal_date.dup
1643
+ cal_date[0] = -cal_date[0] if era.reverse?
1644
+ cal_date[0] += era.epoch_year
1645
+ date = era.trans(cal_date, trans_options)
1646
+ loop do
1647
+ case date
1648
+ when Before, After
1649
+ i0, i1, reverse = SearchOption[date]
1650
+ new_era = (date == Before) ? era.prev : era.succ
1651
+ break unless new_era
1652
+ date = new_era.trans(cal_date, trans_options)
1653
+ if date == reverse
1654
+ cal_date = new_era.epoch[i0].frame.to_cal_date(era.epoch[i1].frame.to_julian_date(cal_date))
1655
+ date = new_era.trans(cal_date, trans_options)
1656
+ break if date == reverse
1657
+ end
1658
+ era = new_era
1659
+ when TimeValue
1660
+ return era, date
1661
+ else
1662
+ break
1663
+ end
1664
+ end
1665
+ return nil
1666
+ end
1667
+
1668
+ # 加減算共通処理
1669
+ def _plus(period)
1670
+ self.dup._copy({:date=>_date_with_era(@frame._validate(_date_without_era,
1671
+ @frame._arrange_length(period.date))),
1672
+ :events=>nil, :query=>nil, :validate=>:done})
1673
+ end
1674
+
1675
+ # 年号を除外した @frame の暦法に対応する日付
1676
+ def _date_without_era
1677
+ date = @cal_date.dup
1678
+ date[0] += @calendar_era_name[1] if @calendar_era_name
1679
+ date
1680
+ end
1681
+
1682
+ # 年号に続く日付
1683
+ def _date_with_era(date)
1684
+ date[0] -= @calendar_era_name[1] if @calendar_era_name
1685
+ date
1686
+ end
1687
+ end
1688
+
1689
+ #
1690
+ # 時刻を伴った日付
1691
+ #
1692
+ # see {http://schemas.opengis.net/gml/3.1.1/base/temporalAppendix.xsd#DateAndTimeType gml schema}
1693
+ #
1694
+ class DateAndTime < CalDate
1695
+
1696
+ # 時刻要素
1697
+ #
1698
+ # @return [When::TM::ClockTime]
1699
+ #
1700
+ attr_reader :clk_time
1701
+ alias :clkTime :clk_time
1702
+
1703
+ # 時法の取得
1704
+ #
1705
+ # @return [When::TM::Clock]
1706
+ #
1707
+ def clock
1708
+ @clk_time.frame
1709
+ end
1710
+
1711
+ # 内部時間
1712
+ #
1713
+ # @return [Numeric]
1714
+ #
1715
+ # 1970-01-01T00:00:00Z からの Universal Coordinated Time の経過時間 / 128秒
1716
+ #
1717
+ # 暦法によっては、異なる意味を持つことがある
1718
+ #
1719
+ def universal_time
1720
+ return super if [Now, Max, Min].include?(@indeterminated_position)
1721
+ raise NameError, "Temporal Reference System is not defined" unless (@frame && clock)
1722
+ @universal_time ||= (to_i - +@clk_time.clk_time[0] - JulianDate::JD19700101) * Duration::DAY +
1723
+ @clk_time.universal_time
1724
+ end
1725
+
1726
+ # 要素の参照
1727
+ #
1728
+ # @param [Integer] index 参照する要素の指定
1729
+ #
1730
+ # @return [Numeric]
1731
+ #
1732
+ def value(index)
1733
+ digit = _digit(index)
1734
+ return (digit <= DAY) ? @cal_date[digit-1] : @clk_time.clk_time[digit]
1735
+ end
1736
+
1737
+ # ユリウス日または通年が指定の剰余となる日
1738
+ #
1739
+ # @param [When::Coordinates::Residue] other
1740
+ #
1741
+ # @return [When::TM::DateAndTime]
1742
+ #
1743
+ def &(other)
1744
+ raise TypeError,"The right operand should be When::Coordinates::Residue" unless other.kind_of?(Residue)
1745
+ case other.event
1746
+ when 'day'
1747
+ # 指定の剰余となる日
1748
+ other -= @frame.indices[-1].shift unless @frame.indices[-1].shift == 0
1749
+ return self.dup._copy({:events=>nil, :query=>@query, :validate=>:done,
1750
+ :date=>_date_with_era(@frame.to_cal_date(other & to_i)),
1751
+ :time=>@clk_time.clk_time.dup})
1752
+
1753
+ when 'year'
1754
+ # 指定の剰余となる年
1755
+ date = @cal_date.dup
1756
+ date[0] = (other & (most_significant_coordinate + @frame._diff_to_CE)) - @frame._diff_to_CE
1757
+ return self.dup._copy({:events=>nil, :query=>@query,
1758
+ :date=>_date_with_era(date),
1759
+ :time=>@clk_time.clk_time.dup})
1760
+
1761
+ else
1762
+ raise ArgumentError,"The right operand should have a unit 'day' or 'year'"
1763
+ end
1764
+ end
1765
+
1766
+ # 下位桁の切り捨て
1767
+ #
1768
+ # @param [Integer] digit 切り捨てずに残す、最下位の桁
1769
+ #
1770
+ # @param [Integer] precision 切り捨て結果の分解能
1771
+ #
1772
+ # @return [When::TM::DateAndTime]
1773
+ #
1774
+ def floor(digit=DAY, precision=digit)
1775
+ count = digit - clock.indices.length
1776
+ date = (digit>=DAY) ? @cal_date.dup : @frame._validate(@cal_date[0..(digit-1)])
1777
+ time = clock._validate(@clk_time.clk_time[0..((digit<=DAY) ? 0 : ((count>=0) ? -1 : digit))])
1778
+
1779
+ if (count >= 0)
1780
+ factor = 10**count
1781
+ time[-1] = (time[-1] * factor).floor.to_f / factor
1782
+ end
1783
+
1784
+ # オブジェクトの生成
1785
+ options = {:date=>date, :validate=>:done, :events=>nil, :query=>nil,
1786
+ :time=>(digit<=DAY) ? time : @clk_time.dup._copy({:time=>time})}
1787
+ options[:precision] = precision if precision
1788
+ return self.dup._copy(options)
1789
+ end
1790
+
1791
+ # 下位桁の切り上げ
1792
+ #
1793
+ # digit : Integer
1794
+ # @param [Integer] digit 切り上げずに残す、最下位の桁
1795
+ #
1796
+ # @param [Integer] precision 切り上げ結果の分解能
1797
+ #
1798
+ # @return [When::TM::DateAndTime]
1799
+ #
1800
+ def ceil(digit=DAY, precision=digit)
1801
+ length = clock.indices.length
1802
+ count = digit - length
1803
+ period = PeriodDuration.new((count<=0) ? 1 : 0.1**count, digit, (-@frame.indices.length)..length)
1804
+ result = floor(digit, precision) + period
1805
+ result += clock._tz_difference if (result.universal_time <= self.universal_time)
1806
+ return result
1807
+ end
1808
+
1809
+ # 時刻情報のない When::TM::CalDate を返す
1810
+ #
1811
+ # @return [When::TM::CalDate]
1812
+ #
1813
+ def to_cal_date
1814
+ options = _attr
1815
+ options.delete(:clock)
1816
+ options[:precision] = [When::DAY, options[:precision]].min
1817
+ CalDate.new(@cal_date, options)
1818
+ end
1819
+ alias :to_CalDate :to_cal_date
1820
+
1821
+ #protected
1822
+
1823
+ # 属性の Hash
1824
+ # @private
1825
+ def _attr
1826
+ super.merge({:clock=>clock})
1827
+ end
1828
+
1829
+ # 属性のコピー
1830
+ # @private
1831
+ def _copy(options={})
1832
+ # 夏時間の調整
1833
+ case options[:time]
1834
+ when Array
1835
+ if clock._need_validate
1836
+ new_clock = clock._daylight { |c| self.class.new(options[:date], options[:time], {:frame=>@frame, :clock=>c}) } || clock
1837
+ options[:time] = options[:time].map {|t| t * 1}
1838
+ else
1839
+ new_clock = clock
1840
+ end
1841
+ options[:time] = @clk_time.dup._copy(options.merge({:clock=>new_clock}))
1842
+ when nil
1843
+ options[:time] = @clk_time.dup._copy(options)
1844
+ end
1845
+
1846
+ return super(options)
1847
+ end
1848
+
1849
+ # オブジェクトの生成
1850
+ #
1851
+ # @param [Array<Numeric>] date 日付表現
1852
+ # @param [Array<Numeric>] time 時刻表現
1853
+ # @param [Hash] options 下記の通り (see also {When::TM::TemporalPosition._instance})
1854
+ # @option options [When::TM::Calendar] :frame
1855
+ # @option options [When::TM::Clock] :clock
1856
+ # @option options [When::TM::CalendarEra, When::BasicTypes::M17n, Array<When::BasicTypes::M17n, Integer>] :era_name
1857
+ # (Integer は 当該年号の 0 年に相当する通年)
1858
+ # @option options [Integer] :precision
1859
+ #
1860
+ def initialize(date, time, options={})
1861
+ options[:time] = time
1862
+ super(date, options)
1863
+ end
1864
+
1865
+ private
1866
+
1867
+ # オブジェクトの正規化
1868
+ def _normalize(options={})
1869
+
1870
+ # Clock
1871
+ unless options[:validate]
1872
+ clock = Clock.get_clock_option(options)
1873
+ clock ||= options[:time].frame if options[:time].kind_of?(ClockTime)
1874
+ clock ||= Clock.local_time || When.utc
1875
+ end
1876
+ clock = When.Clock(clock) if (clock.kind_of?(String))
1877
+ clock_is_timezone = clock.respond_to?(:daylight)
1878
+ clock = clock.daylight if clock_is_timezone
1879
+
1880
+ # ClockTime
1881
+ @clk_time =
1882
+ case options[:time]
1883
+ when ClockTime ; options[:time]
1884
+ when Array ; ClockTime.new(options[:time], {:frame=>clock, :precision=>options[:precision], :validate=>:done})
1885
+ else ; clock.to_clk_time(options[:time], {:precision=>options[:precision]})
1886
+ end
1887
+
1888
+ super
1889
+
1890
+ # 日付と時刻の正規化
1891
+ unless options[:validate]
1892
+ time = @clk_time.clk_time
1893
+ precision = @clk_time.precision
1894
+ second = time[clock.base.length-1]
1895
+ second -= clock.base[-1] if second
1896
+ if second && second != 0 && time_standard.has_leap?
1897
+ zero = DateAndTime.new(@cal_date, time[0..-2],
1898
+ {:frame=>@frame, :clock=>clock, :precision=>precision,
1899
+ :era_name=>@calendar_era_name, :era=>options[:era],
1900
+ :time_standard=>time_standard, :location=>@location})
1901
+ end
1902
+
1903
+ # 日付と時刻の関係の調整
1904
+ @cal_date = _date_with_era(@frame._validate(_date_without_era) {|jdn|
1905
+ time[0] += jdn
1906
+ time[0..-1] = clock._validate(time)
1907
+ jdn = time[0] * 1
1908
+ time[0] -= jdn
1909
+ jdn
1910
+ })
1911
+
1912
+ # 夏時間の調整
1913
+ if clock._need_validate
1914
+ clock = clock._daylight {|clock| self.class.new(@cal_date, time, {:frame=>@frame, :clock=>clock}) } || clock
1915
+ end
1916
+ time = time.map {|t| t * 1}
1917
+ @clk_time = ClockTime.new(time, {:frame=>clock, :precision=>precision, :validate=>:done}) if clock_is_timezone
1918
+
1919
+ # 閏秒
1920
+ if zero
1921
+ leap = ((dynamical_time - zero.dynamical_time) * clock.second - second).to_i
1922
+ if leap != 0 && leap.abs < clock.second
1923
+ @cal_date = zero.cal_date
1924
+ @clk_time = zero.clk_time
1925
+ @clk_time.clk_time[-1] += second
1926
+ leap /= clock.second
1927
+ @universal_time = When::Coordinates::LeapSeconds.new(@universal_time-leap, leap, 1/clock.second)
1928
+ @dynamical_time -= leap
1929
+ end
1930
+ end
1931
+ end
1932
+
1933
+ # 後処理
1934
+ @keys |= @clk_time.keys
1935
+ end
1936
+
1937
+ # 加減算共通処理
1938
+ def _plus(period)
1939
+ # 日時の加算
1940
+ time = @clk_time.clk_time.dup
1941
+ pdate = @frame._arrange_length(period.date)
1942
+ ptime = clock._arrange_length(period.time)
1943
+ date = _date_with_era(@frame._validate(_date_without_era, pdate) {|jdn|
1944
+ time[0] += jdn
1945
+ time = clock._validate(time, ptime)
1946
+ jdn = time[0] * 1
1947
+ time[0] -= jdn
1948
+ jdn
1949
+ })
1950
+
1951
+ # オブジェクトの生成
1952
+ self.dup._copy({:date=>date, :time=>time, :validate=>:done, :events=>nil, :query=>nil})
1953
+ end
1954
+ end
1955
+ end