when_exe 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. data/LICENSE.ja.txt +25 -25
  2. data/LICENSE.txt +31 -31
  3. data/bin/locales.rb +2 -1
  4. data/bin/when.rb.config +1 -1
  5. data/lib/when_exe.rb +70 -48
  6. data/lib/when_exe/basictypes.rb +99 -65
  7. data/lib/when_exe/calendartypes.rb +40 -178
  8. data/lib/when_exe/coordinates.rb +156 -62
  9. data/lib/when_exe/core/compatibility.rb +10 -0
  10. data/lib/when_exe/core/extension.rb +40 -0
  11. data/lib/when_exe/ephemeris.rb +112 -50
  12. data/lib/when_exe/icalendar.rb +125 -91
  13. data/lib/when_exe/inspect.rb +100 -48
  14. data/lib/when_exe/locales/ar.rb +48 -48
  15. data/lib/when_exe/locales/bg.rb +1 -1
  16. data/lib/when_exe/locales/bs.rb +4 -2
  17. data/lib/when_exe/locales/ca.rb +1 -1
  18. data/lib/when_exe/locales/en_CA.rb +3 -4
  19. data/lib/when_exe/locales/en_IE.rb +88 -0
  20. data/lib/when_exe/locales/en_US.rb +87 -0
  21. data/lib/when_exe/locales/es_CR.rb +84 -0
  22. data/lib/when_exe/locales/es_EC.rb +85 -0
  23. data/lib/when_exe/locales/es_PA.rb +85 -0
  24. data/lib/when_exe/locales/fr.rb +39 -39
  25. data/lib/when_exe/locales/hu.rb +15 -14
  26. data/lib/when_exe/locales/it.rb +1 -1
  27. data/lib/when_exe/locales/ja.rb +2 -2
  28. data/lib/when_exe/locales/locales.rb +7 -0
  29. data/lib/when_exe/locales/lt.rb +21 -19
  30. data/lib/when_exe/locales/ms.rb +84 -0
  31. data/lib/when_exe/locales/nl.rb +2 -2
  32. data/lib/when_exe/locales/ru.rb +1 -1
  33. data/lib/when_exe/locales/uk.rb +1 -1
  34. data/lib/when_exe/locales/ur.rb +84 -0
  35. data/lib/when_exe/mini_application.rb +44 -43
  36. data/lib/when_exe/parts/enumerator.rb +3 -3
  37. data/lib/when_exe/parts/geometric_complex.rb +6 -1
  38. data/lib/when_exe/parts/locale.rb +49 -18
  39. data/lib/when_exe/parts/method_cash.rb +61 -0
  40. data/lib/when_exe/parts/resource.rb +221 -106
  41. data/lib/when_exe/parts/timezone.rb +70 -33
  42. data/lib/when_exe/region/bahai.rb +2 -2
  43. data/lib/when_exe/region/balinese.rb +40 -43
  44. data/lib/when_exe/region/chinese.rb +93 -33
  45. data/lib/when_exe/region/chinese_calendar.rb +117 -1
  46. data/lib/when_exe/region/chinese_epoch.rb +65 -10
  47. data/lib/when_exe/region/christian.rb +97 -2
  48. data/lib/when_exe/region/ephemeric_notes.rb +353 -0
  49. data/lib/when_exe/region/french.rb +1 -1
  50. data/lib/when_exe/region/geologicalage.rb +171 -171
  51. data/lib/when_exe/region/indian.rb +18 -14
  52. data/lib/when_exe/region/iranian.rb +1 -1
  53. data/lib/when_exe/region/japanese.rb +49 -12
  54. data/lib/when_exe/region/japanese_notes.rb +838 -507
  55. data/lib/when_exe/region/japanese_residues.rb +724 -662
  56. data/lib/when_exe/region/javanese.rb +7 -7
  57. data/lib/when_exe/region/mayan.rb +19 -17
  58. data/lib/when_exe/region/nihon_shoki.rb +3 -3
  59. data/lib/when_exe/region/residue.rb +29 -28
  60. data/lib/when_exe/region/shire.rb +2 -2
  61. data/lib/when_exe/region/tibetan.rb +87 -5
  62. data/lib/when_exe/region/world.rb +1 -1
  63. data/lib/when_exe/timestandard.rb +85 -7
  64. data/lib/when_exe/tmobjects.rb +32 -4
  65. data/lib/when_exe/tmposition.rb +104 -55
  66. data/lib/when_exe/tmreference.rb +157 -60
  67. data/lib/when_exe/version.rb +2 -2
  68. data/test/examples/JapanHolidays.ics +3 -3
  69. data/test/examples/JapanHolidaysRFC6350.ics +499 -0
  70. data/test/examples/Residue.m17n +3 -2
  71. data/test/examples/Spatial.m17n +3 -3
  72. data/test/examples/USA-DST.ics +27 -27
  73. data/test/examples/today.rb +1 -1
  74. data/test/test.rb +4 -2
  75. data/test/test/basictypes.rb +40 -15
  76. data/test/test/coordinates.rb +9 -4
  77. data/test/test/icalendar.rb +24 -14
  78. data/test/test/inspect.rb +5 -3
  79. data/test/test/parts.rb +11 -2
  80. data/test/test/region/chinese.rb +4 -4
  81. data/test/test/region/civil.rb +124 -0
  82. data/test/test/region/geologicalage.rb +5 -2
  83. data/test/test/region/indian.rb +2 -0
  84. data/test/test/region/japanese.rb +156 -1
  85. data/test/test/region/jewish.rb +3 -3
  86. data/test/test/region/m17n.rb +9 -9
  87. data/test/test/region/mayan.rb +122 -5
  88. data/test/test/region/residue.rb +1 -1
  89. data/test/test/tmobjects.rb +27 -64
  90. data/test/test/tmposition.rb +48 -1
  91. data/test/test/tmreference.rb +66 -4
  92. data/when_exe.gemspec +1 -1
  93. metadata +15 -6
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  =begin
3
- Copyright (C) 2011-2013 Takashi SUGA
3
+ Copyright (C) 2011-2014 Takashi SUGA
4
4
 
5
5
  You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
6
  =end
@@ -86,7 +86,7 @@ module When::V
86
86
 
87
87
  #
88
88
  # iCalendar クラス群の属性
89
- # @return [Hash] { String => When::Parts::Resource::Element }
89
+ # @return [Hash] { String => When::Parts::Resource::ContentLine }
90
90
  #
91
91
  attr_reader :property
92
92
 
@@ -128,9 +128,27 @@ module When::V
128
128
  raise ArgumentError, "No enumerator exists" if (enumerators.length==0)
129
129
 
130
130
  # Enumerator の生成
131
- return enumerators[0] if (enumerators.length==1 && exdate.node.size==0)
132
- options[:exdate] = exdate
133
- return When::Parts::Enumerator::Integrated.new(self, enumerators, *args)
131
+ enumerator =
132
+ if (enumerators.length==1 && exdate.node.size==0)
133
+ enumerators[0]
134
+ else
135
+ options[:exdate] = exdate
136
+ When::Parts::Enumerator::Integrated.new(self, enumerators, *args)
137
+ end
138
+ if ::Object.const_defined?(:Date) && (args[0].kind_of?(Range) ? args[0].first : args[0]).kind_of?(::Date)
139
+ enumerator.instance_eval %Q{
140
+ alias :_succ_of_super :succ
141
+ def succ
142
+ result = _succ_of_super
143
+ case result
144
+ when When::TM::DateAndTime ; result.to_date_time
145
+ when When::TM::CalDate ; result.to_date
146
+ else ; result
147
+ end
148
+ end
149
+ }
150
+ end
151
+ enumerator
134
152
  end
135
153
  alias :to_enum :_enumerator
136
154
  alias :enum_for :_enumerator
@@ -147,7 +165,14 @@ module When::V
147
165
  @_pool['..'] = options['..']
148
166
 
149
167
  # parsed 部の属性化
150
- _parsed(options)
168
+ @property = {}
169
+ @namespace = @_pool['..'].respond_to?(:namespace) ? @_pool['..'].namespace : {}
170
+ if options['.']
171
+ _parse_from_file(options)
172
+ else
173
+ _parse_from_code(options)
174
+ end
175
+ _set_variables
151
176
 
152
177
  # 属性の存在チェック & 設定
153
178
  _initialize_attributes(_attribute_appearance(self.class::Properties)) # .const_get(:Properties)))
@@ -156,29 +181,47 @@ module When::V
156
181
  _child(options, self.class::Classes) #.const_get(:Classes))
157
182
  end
158
183
 
159
- # parsed 部の属性化
160
- def _parsed(options)
161
- @property = {}
162
- if options['.']
163
- options['.'].each do |v|
164
- v = When::Parts::Resource._parse(v)
165
- next unless (v.kind_of?(Element))
166
- key = v.predicate
167
- case @property[key]
168
- when nil ; @property[key] = v
169
- when Array ; @property[key] << v
170
- else ; @property[key] = [@property[key], v]
171
- end
184
+ # ファイルからの属性読み込み
185
+ def _parse_from_file(options)
186
+ options['.'].each do |v|
187
+ v = When::Parts::Resource._parse(v)
188
+ _parse_altid(@property, v) if v.kind_of?(ContentLine)
189
+ end
190
+
191
+ keys = @property.keys
192
+ if keys.delete('namespace')
193
+ content = @property['namespace'][0]
194
+ @property['namespace'] = content if @property['namespace'].size == 1
195
+ if content.attribute['prefix']
196
+ begin
197
+ @namespace[content.attribute['prefix'].object] = content.object
198
+ end while (content = content.same_altid)
199
+ else
200
+ @namespace.update(When::Parts::Locale._namespace(content.object))
172
201
  end
173
- else
174
- options.each_pair do |key, value|
175
- @property[key] = Element.new(key, value) if key.kind_of?(String)
202
+ end
203
+
204
+ keys.each do |key|
205
+ @property[key].each do |content|
206
+ content.object = When::BasicTypes::M17n.new(content, @namespace, []) if content.same_altid
176
207
  end
177
- @property['dtstamp'] ||= Element.new('dtstamp',
178
- When.now.to_s.gsub(/[-:]/,'')) if self.class::Properties[0].index('dtstamp')
179
- @property['uid'] ||= Element.new('uid',
180
- @property['dtstamp'].object + '-auto') if self.class::Properties[0].index('uid')
208
+ @property[key] = @property[key][0] if @property[key].size == 1
209
+ end
210
+ end
211
+
212
+ # コードからの属性読み込み
213
+ def _parse_from_code(options)
214
+ options.each_pair do |key, value|
215
+ @property[key] = ContentLine.new(key, value) if key.kind_of?(String)
181
216
  end
217
+ @property['dtstamp'] ||= ContentLine.new('dtstamp',
218
+ When.now.to_s.gsub(/[-:]/,'')) if self.class::Properties[0].index('dtstamp')
219
+ @property['uid'] ||= ContentLine.new('uid',
220
+ @property['dtstamp'].object + '-auto') if self.class::Properties[0].index('uid')
221
+ end
222
+
223
+ # @propertyの個別属性化
224
+ def _set_variables
182
225
  @property.each_key do |key|
183
226
  next if respond_to?(key)
184
227
  instance_eval %Q{
@@ -220,7 +263,7 @@ module When::V
220
263
 
221
264
  # REQUIRED but MUST NOT occur more than once
222
265
  require_unique.each do |key|
223
- unless @property[key].kind_of?(When::Parts::Resource::Element)
266
+ unless @property[key].kind_of?(When::Parts::Resource::ContentLine)
224
267
  raise ArgumentError, "The #{key.upcase.gsub(/_/,'-')} is REQUIRED but MUST NOT occur more than once"
225
268
  end
226
269
  end
@@ -230,7 +273,7 @@ module When::V
230
273
  unless @property[key]
231
274
  raise ArgumentError, "The #{key.upcase.gsub(/_/,'-')} is REQUIRED and MAY occur more than once"
232
275
  end
233
- @property[key] = [@property[key]] if (@property[key].kind_of?(When::Parts::Resource::Element))
276
+ @property[key] = [@property[key]] if (@property[key].kind_of?(When::Parts::Resource::ContentLine))
234
277
  end
235
278
 
236
279
  # OPTIONAL but MUST NOT occur more than once
@@ -249,7 +292,7 @@ module When::V
249
292
 
250
293
  # OPTIONAL and MAY occur more than once
251
294
  (optional + DefaultOptional).each do |key|
252
- @property[key] = [@property[key]] if (@property[key].kind_of?(When::Parts::Resource::Element))
295
+ @property[key] = [@property[key]] if (@property[key].kind_of?(When::Parts::Resource::ContentLine))
253
296
  end
254
297
 
255
298
  # Other Properties
@@ -269,10 +312,6 @@ module When::V
269
312
  @calscale = @property['calscale'].object if @property['calscale']
270
313
  @calscale = When.Resource((@calscale||'GREGORIAN').capitalize, '_c:') unless (@calscale.kind_of?(When::TM::Calendar))
271
314
 
272
- # namespace の登録
273
- @namespace = @_pool['..'].respond_to?(:namespace) ? @_pool['..'].namespace : {}
274
- @namespace.update(When::Parts::Locale._namespace(@property['namespace'].object)) if @property['namespace']
275
-
276
315
  # locale の登録
277
316
  @locale = @_pool['..'].respond_to?(:locale) ? @_pool['..'].locale : []
278
317
  @locale = When::Parts::Locale._locale(@property['locale'].object) if @property['locale']
@@ -367,7 +406,7 @@ module When::V
367
406
  @exdate = When::Parts::GeometricComplex.new()
368
407
  if (@property['rdate'])
369
408
  @rdate = @property['rdate'].inject([]) do |sum, v|
370
- if When::Parts::Resource::Element === v
409
+ if v.kind_of?(When::Parts::Resource::ContentLine)
371
410
  if new_zone == ''
372
411
  sum += When.when?(v.attribute['.'].split(/,/), date_options)
373
412
  else
@@ -388,7 +427,7 @@ module When::V
388
427
  # exdate の登録
389
428
  if @property['exdate']
390
429
  dates = @property['exdate'].inject([]) do |sum, v|
391
- if When::Parts::Resource::Element === v
430
+ if v.kind_of?(When::Parts::Resource::ContentLine)
392
431
  sum += When.when?(v.attribute['.'].split(/,/), date_options)
393
432
  else
394
433
  sum << v
@@ -416,7 +455,7 @@ module When::V
416
455
  @freebusy = []
417
456
  if (@property['freebusy'])
418
457
  @freebusy = @property['freebusy'].inject([]) do |sum, v|
419
- if When::Parts::Resource::Element === v
458
+ if v.kind_of?(When::Parts::Resource::ContentLine)
420
459
  sum += When.when?((v.attribute['.']||v.object).split(/,/), date_options)
421
460
  else
422
461
  sum << v
@@ -454,8 +493,10 @@ module When::V
454
493
  copy.child = @child.select {|ev|
455
494
  if ev.kind_of?(Event)
456
495
  keys.each_pair do |key, value|
457
- value = /#{value}/ if value.kind_of?(String)
458
- break unless (value === ev.property[key].object)
496
+ case value
497
+ when String ; break unless ev.property[key].object.index(value)
498
+ when Regexp ; break unless (ev.property[key].object =~ value)
499
+ end
459
500
  end
460
501
  else
461
502
  true
@@ -467,7 +508,7 @@ module When::V
467
508
  # @private
468
509
  def _enumerator_list(args)
469
510
  (@child.reject {|el| !el.kind_of?(Event)}).inject([]) { |sum, ev|
470
- sum += ev._enumerator_list(args)
511
+ sum += ev._enumerator_list(args)
471
512
  }
472
513
  end
473
514
  end
@@ -517,6 +558,12 @@ module When::V
517
558
  end
518
559
  end
519
560
 
561
+ # SUMMARY Property
562
+ #
563
+ # @return [String, When::BasicTypes::M17n]
564
+ #
565
+ attr_reader :summary
566
+
520
567
  # RRULE Property
521
568
  #
522
569
  # @return [Hash]
@@ -587,7 +634,7 @@ module When::V
587
634
  # @return [String]
588
635
  #
589
636
  def label
590
- @property['uid'].object
637
+ @label ||= @property['uid'].object
591
638
  end
592
639
 
593
640
  # 最後のイベント
@@ -679,7 +726,7 @@ module When::V
679
726
  # ユニーク識別名 - ACTION Property をユニーク識別名とする。
680
727
  # @return [String]
681
728
  def label
682
- @property['action'].object # TODO
729
+ @label ||= @property['action'].object # TODO
683
730
  end
684
731
 
685
732
  # @private
@@ -692,7 +739,7 @@ module When::V
692
739
  _parsed(options)
693
740
 
694
741
  # 属性の存在チェック
695
- case (@property['action'].kind_of?(When::Parts::Resource::Element) && @property['action'].object)
742
+ case (@property['action'].kind_of?(When::Parts::Resource::ContentLine) && @property['action'].object)
696
743
  when 'AUDIO'
697
744
  aware = _attribute_appearance([
698
745
  ['action', 'trigger'], [],
@@ -808,7 +855,7 @@ module When::V
808
855
  # ユニーク識別名 - DTSTART Property をユニーク識別名とする
809
856
  # @return [String]
810
857
  def label
811
- @property['dtstart'].attribute['.']
858
+ @label ||= @property['dtstart'].attribute['.']
812
859
  end
813
860
  end
814
861
 
@@ -838,22 +885,12 @@ module When::V
838
885
 
839
886
  class << self; include When::Parts::Resource::Pool; end
840
887
 
841
- # 標準時間帯の時計
842
- # @return [When::TM::Clock]
843
- attr_reader :standard
844
-
845
- # 夏時間帯の時計
846
- # @return [When::TM::Clock]
847
- attr_reader :daylight
848
-
849
- # 夏時間帯と標準時間帯の時間差
850
- # @return [When::TM:IntervalLength]
851
- attr_reader :difference
888
+ include When::Parts::Timezone::Base
852
889
 
853
890
  # ユニーク識別名 - TZID Property をユニーク識別名とする
854
891
  # @return [String]
855
892
  def label
856
- @property['tzid'].object
893
+ @label ||= @property['tzid'].object
857
894
  end
858
895
 
859
896
  # 同一の時間帯を用いた期間
@@ -866,24 +903,21 @@ module When::V
866
903
  #
867
904
  def current_period(current_date=Time.now)
868
905
  current_date = When.when?(current_date) unless current_date.kind_of?(When::TM::TemporalPosition)
869
- period = _tz_period(current_date)
906
+ period = _tz_period(current_date.universal_time)
870
907
  range = period[1]
871
908
  return range if range.kind_of?(Range)
872
909
  GeometricComplex.new([period], !range)
873
910
  end
874
911
 
875
912
  # @private
876
- def _daylight(zdate=nil)
913
+ def _daylight(time)
877
914
  raise ArgumentError, "Needless daylight saving time evaluation" unless _need_validate
878
- zdate = yield(@standard.dup.tap{|clock| clock.tz_prop = nil}) if block_given?
879
- ndate = _neighbor_event_date(zdate)
880
- nprop = ndate.clock.tz_prop
881
- if block_given? && !@standard.equal?(nprop.tzoffsetfrom)
882
- zdate = yield(nprop.tzoffsetfrom.dup.tap{|clock| clock.tz_prop = nil})
883
- end
884
- deltad = zdate.universal_time - ndate.universal_time
885
- return nprop.tzoffsetto if (deltad >= 0)
886
- return nprop.tzoffsetfrom
915
+ frame, cal_date, clk_time = time
916
+ time = frame.to_universal_time(cal_date, clk_time, @standard) if clk_time
917
+ ndate = _neighbor_event_date(time)
918
+ nprop = ndate.clock.tz_prop
919
+ time = frame.to_universal_time(cal_date, clk_time, nprop.tzoffsetfrom) if clk_time && !@standard.equal?(nprop.tzoffsetfrom)
920
+ (time >= ndate.universal_time) ? nprop.tzoffsetto : nprop.tzoffsetfrom
887
921
  end
888
922
 
889
923
  # @private
@@ -922,43 +956,42 @@ module When::V
922
956
 
923
957
  # 指定の日時に最も近い、時間帯変更イベントの日時
924
958
  #
925
- # @param [When::TM::TemporalPosition] current_date 捜索の基点の日時
959
+ # @param [Numeric] current_time 捜索の基点の日時の universal time
926
960
  #
927
961
  # @return [When::TM::TemporalPosition] 捜索の基点の日時に最も近い、時間帯変更イベントの日時
928
962
  #
929
- def _neighbor_event_date(current_date)
930
- _tz_period(current_date)[0]
963
+ def _neighbor_event_date(current_time)
964
+ _tz_period(current_time)[0]
931
965
  end
932
966
 
933
- def _tz_period(current_date)
934
- universal_time = current_date.universal_time
935
- return [@dtstart, false] if (universal_time <= @dtstart.universal_time)
967
+ def _tz_period(current_time)
968
+ return [@dtstart, false] if (current_time <= @dtstart.universal_time)
936
969
  return [@dtstop, true ] if (@dtstop.kind_of?(When::TimeValue) &&
937
- universal_time >= @dtstop.universal_time)
970
+ current_time >= @dtstop.universal_time)
938
971
 
939
972
  # Thread 要注意 - @range は生成後に更新
940
973
  synchronize do
941
974
  @range.each do |range|
942
- from = universal_time - range.first.universal_time
943
- to = range.last.universal_time - universal_time
975
+ from = current_time - range.first.universal_time
976
+ to = range.last.universal_time - current_time
944
977
  return [(from < to) ? range.first : range.last, range] if (from >= 0 && to > 0)
945
978
  end
946
- early = _neighbor(current_date, :reverse)
947
- late = _neighbor(current_date, :forward)
948
- from = universal_time - early.universal_time
949
- to = late.universal_time - universal_time
979
+ early = _neighbor(current_time, :reverse)
980
+ late = _neighbor(current_time, :forward)
981
+ from = current_time - early.universal_time
982
+ to = late.universal_time - current_time
950
983
  @range << (early...late)
951
984
  return [(from < to) ? early : late, @range[-1]]
952
985
  end
953
986
  end
954
987
 
955
- def _neighbor(current_date, direction)
988
+ def _neighbor(current_time, direction)
956
989
  event = nil
957
990
  minimum = nil
958
991
  @child.each do |prop|
959
- date = prop.enum_for(current_date, direction, 1).succ
992
+ date = prop.enum_for(current_time, direction, 1).succ
960
993
  if (date)
961
- diff = (date - current_date).duration.abs
994
+ diff = (date.universal_time - current_time).abs
962
995
  if (minimum == nil || minimum > diff)
963
996
  event = date
964
997
  minimum = diff
@@ -1020,14 +1053,14 @@ module When::V
1020
1053
 
1021
1054
  # オブジェクトの生成
1022
1055
  def initialize(*args)
1023
- @options = When::Parts::Enumerator._options(args)
1024
- @exdate = @options.delete(:exdate)
1025
- @exevent = @options.delete(:exevent)
1056
+ @options = When::Parts::Enumerator._options(args)
1057
+ @exdate = @options.delete(:exdate)
1058
+ @exevent = @options.delete(:exevent)
1026
1059
  @parent, @rule, @dtstart, @duration, *args = args
1027
- @dtstart = When.when?(@dtstart)
1028
- @rule = self.class._decode_rule(@rule, @dtstart) if (@rule.kind_of?(String))
1029
- @logics = @rule[:logics]
1030
- @tz_prop = nil
1060
+ @dtstart = When.when?(@dtstart)
1061
+ @rule = self.class._decode_rule(@rule, @dtstart) if (@rule.kind_of?(String))
1062
+ @logics = @rule[:logics]
1063
+ @tz_prop = nil
1031
1064
  if (@dtstart.kind_of?(When::TM::DateAndTime))
1032
1065
  clock = @dtstart.clock
1033
1066
  if (clock.kind_of?(When::TM::Clock) &&
@@ -1054,10 +1087,11 @@ module When::V
1054
1087
  else
1055
1088
  @interval = When::TM::PeriodDuration.new(@rule['INTERVAL'], FreqIndex[@rule['FREQ']])
1056
1089
  end
1057
- return dtstart if (target == dtstart)
1090
+ return dtstart if (dtstart == target)
1058
1091
  interval_time = (dtstart + @interval) - dtstart
1059
1092
  return dtstart if (interval_time == 0)
1060
- div, mod = (target - dtstart).duration.divmod(interval_time.duration)
1093
+ duration = target.kind_of?(Numeric) ? target - dtstart.universal_time : (target - dtstart).duration
1094
+ div, mod = duration.divmod(interval_time.duration)
1061
1095
  seed = dtstart + (@interval * div)
1062
1096
  case @direction
1063
1097
  when :reverse ; seed += @interval while (seed <= target)
@@ -11,29 +11,30 @@ module When
11
11
  #
12
12
  # オブジェクトの内容を Hash 化
13
13
  #
14
- # @param [String, Integer] options {When::TM::TemporalPosition#_to_hash}に渡す
14
+ # @param [String, Integer] options {When::TM::TemporalPosition#_to_h}に渡す
15
15
  # @param [Hash] options 下記のとおり
16
+ # @option options [Numeric] :precision 指定があれば「イベント名(イベント時刻)」出力の時刻を指定の精度に丸める
16
17
  # @option options [Boolean] :camel true ならシンボルを camel case にする
17
18
  # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
18
19
  # @option options [Boolean] :simple true ならIRI の先頭部分を簡約表現にする
19
20
  # @option options [Boolean] :residue true なら Residue をそのまま出力
20
- # @option options [Object] :その他 各クラス#_to_hash を参照
21
+ # @option options [Object] :その他 各クラス#_to_h を参照
21
22
  #
22
23
  # @return [Hash] (Whenモジュール内のクラスは文字列 or M17n化)
23
24
  #
24
- def to_hash(options={})
25
- _m17n_form(_to_hash(options), options.kind_of?(Hash) ? options : {})
25
+ def to_h(options={})
26
+ _m17n_form(_to_h(options), options.kind_of?(Hash) ? options : {})
26
27
  end
27
28
 
28
29
  #
29
30
  # オブジェクトの内容を JSON 化
30
31
  #
31
- # @param [Object] options #to_hash を参照
32
+ # @param [Object] options #to_h を参照
32
33
  #
33
- # @return [String] to_hash 結果を JSON文字列化したもの
34
+ # @return [String] to_h 結果を JSON文字列化したもの
34
35
  #
35
36
  def _to_json(options={})
36
- JSON.dump(to_hash(options))
37
+ JSON.dump(to_h(options))
37
38
  end
38
39
 
39
40
  #
@@ -47,14 +48,14 @@ module When
47
48
  #
48
49
  # 時間位置オブジェクトの内容を Hash 化
49
50
  #
50
- # @param [Object] options #to_hash を参照
51
+ # @param [Object] options #to_h を参照
51
52
  #
52
53
  # @return [Hash]
53
54
  # 各クラスの HashProperty に列挙した属性のうち値が false/nil でないものを
54
55
  # 属性 => 値
55
56
  # とする Hash
56
57
  #
57
- def _to_hash(options={})
58
+ def _to_h(options={})
58
59
  hash = {}
59
60
  self.class::HashProperty.each do |property|
60
61
  method, skip = property
@@ -69,19 +70,20 @@ module When
69
70
  #
70
71
  # @param [Object] element 変換元
71
72
  # @param [Hash] options 下記の通り
72
- # @option options [Boolean] :camel true ならシンボルを camel case にする
73
- # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
74
- # @option options [Boolean] :simple true ならIRI の先頭部分を簡約表現にする
75
- # @option options [Boolean] :residue true なら Residue をそのまま出力
73
+ # @option options [Numeric] :precision 指定があれば「イベント名(イベント時刻)」出力の時刻を指定の精度に丸める
74
+ # @option options [Boolean] :camel true ならシンボルを camel case にする
75
+ # @option options [String] :locale 文字列化の locale(指定なしは M17nオブジェクトに変換)
76
+ # @option options [Boolean] :simple true ならIRI の先頭部分を簡約表現にする
77
+ # @option options [Boolean] :residue true なら Residue をそのまま出力
76
78
  #
77
79
  # @return [Hash, Array] 変換結果
78
80
  #
79
81
  # @note element.events のある日付は _event_form で変換する
80
82
  #
81
83
  def _m17n_form(element, options={})
82
- result = element.respond_to?(:_event_form) ? element._event_form(self) :
83
- element.respond_to?(:_to_hash_value) ? element._to_hash_value(options) :
84
- element.respond_to?(:label) && element.label ? element.label :
84
+ result = element.respond_to?(:_event_form) ? element._event_form(self, options[:precision]) :
85
+ element.respond_to?(:_to_hash_value) ? element._to_hash_value(options) :
86
+ element.respond_to?(:label) && element.label ? element.label :
85
87
  case element
86
88
  when Hash ; Hash[*(element.keys.inject([]) { |s, k|
87
89
  s + [_m17n_form(k, options), _m17n_form(element[k], options)]
@@ -121,7 +123,7 @@ module When
121
123
  end
122
124
 
123
125
  #
124
- # to_hash のための要素生成
126
+ # to_h のための要素生成
125
127
  # @private
126
128
  def _to_hash_value(options={})
127
129
  options[:residue] ? self : to_m17n
@@ -130,7 +132,7 @@ module When
130
132
 
131
133
  class Pair
132
134
  #
133
- # to_hash のための要素生成
135
+ # to_h のための要素生成
134
136
  # @private
135
137
  def _to_hash_value(options={})
136
138
  to_s
@@ -319,6 +321,7 @@ module When
319
321
  begin
320
322
  date = it.next
321
323
  end while date.to_i == dates[-1].to_i
324
+ date = date.to_cal_date unless date.instance_of?(TM::CalDate)
322
325
  dates << date
323
326
  end
324
327
  end
@@ -326,6 +329,11 @@ module When
326
329
  else
327
330
  begun = self.floor(MONTH,DAY) + When::TM::PeriodDuration.new([0, first, 0])
328
331
  ended = begun + When::TM::PeriodDuration.new([0, length, 0])
332
+ loop do
333
+ last = ended.prev
334
+ break unless last.cal_date[MONTH-1] == ended.cal_date[MONTH-1]
335
+ ended = last
336
+ end
329
337
  if block_given?
330
338
  (begun...ended).map do |date|
331
339
  yield(date)
@@ -535,7 +543,7 @@ module When
535
543
  # - :dynamical dynamical_time / 秒
536
544
  # - :universal universal_time / 秒
537
545
  #
538
- def _to_hash(options={})
546
+ def _to_h(options={})
539
547
  hash = super.update({
540
548
  :sdn => to_i,
541
549
  :calendar => calendar_name,
@@ -596,9 +604,7 @@ module When
596
604
 
597
605
  def _to_uri(date)
598
606
  uri = date.gsub(/\./, '-').gsub(/%/, '@')
599
- unless @calendar_era_name || @frame == When.Calendar('Gregorian')
600
- uri += '^^' + @frame.iri.gsub(/\?/, '%3F').gsub('.', '@').split(/\//)[-1]
601
- end
607
+ uri += '^^' + @frame.iri.split(/\//)[-1] unless @calendar_era_name || @frame == When.Calendar('Gregorian')
602
608
  uri
603
609
  end
604
610
  private :_to_uri
@@ -772,7 +778,9 @@ module When
772
778
  #
773
779
  def to_m17n(precision=@precision)
774
780
  time = m17n('T' + _time_to_s(precision))
775
- time += @frame.zone if (@frame && !@frame.equal?(Clock.local_time))
781
+ if @frame
782
+ time += @frame.zone unless Clock.is_local_time_set? && @frame.equal?(Clock.local_time)
783
+ end
776
784
  return time
777
785
  end
778
786
 
@@ -784,7 +792,9 @@ module When
784
792
  #
785
793
  def to_s(precision=@precision)
786
794
  time = 'T' + _time_to_s(precision)
787
- time += @frame.zone if (@frame && !@frame.equal?(Clock.local_time))
795
+ if @frame
796
+ time += @frame.zone unless Clock.is_local_time_set? && @frame.equal?(Clock.local_time)
797
+ end
788
798
  return time
789
799
  end
790
800
 
@@ -846,9 +856,12 @@ module When
846
856
 
847
857
  # 秒
848
858
  digits = [precision - @clk_time.length + 1, STRING-SECOND].min
849
- if digits >= 0
850
- terms << @clk_time[-1]
851
- format += (digits == 0) ? "%02d" : "%02.#{digits}f"
859
+ if digits == 0
860
+ format += "%02d"
861
+ elsif digits > 0
862
+ factor = 10**digits
863
+ terms[-1] = ((@clk_time[-1] + 1E-6) * factor).floor.to_f / factor # 切り捨て(10で割る丸めガードあり)
864
+ format += "%02.#{digits}f"
852
865
  end
853
866
 
854
867
  # 結果
@@ -857,7 +870,7 @@ module When
857
870
  end
858
871
 
859
872
  #
860
- # to_hash のための要素生成
873
+ # to_h のための要素生成
861
874
  # @private
862
875
  def _to_hash_value(options={})
863
876
  clk_time.map {|e| _m17n_form(e, options) }
@@ -923,7 +936,7 @@ module When
923
936
  # - :clk_time to_clock_time の結果 ( 日, 時, 分, 秒 )
924
937
  # - :notes Hash (の Array (の Array)) - _notes(options)
925
938
  #
926
- def _to_hash(options={})
939
+ def _to_h(options={})
927
940
  super.update({:cal_date=>@cal_date})
928
941
  end
929
942
 
@@ -1090,32 +1103,37 @@ module When
1090
1103
  # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
1091
1104
  #
1092
1105
  # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1106
+ # @param [false] round 常に切り捨てる(DateAndTimeとの互換性のためのダミーの引数)
1093
1107
  #
1094
1108
  # @return [When::BasicTypes::M17n]
1095
1109
  #
1096
- def to_m17n(precision=@precision)
1097
- era, = @calendar_era_name
1098
- date = _date_to_s(precision)
1099
- return m17n(date) unless era
1100
- return m17n(era) + date
1110
+ def to_m17n(precision=@precision, round=false)
1111
+ date = m17n(_date_to_s(precision))
1112
+ return date unless @calendar_era
1113
+ return _parent_labels.inject(m17n(@calendar_era_name[0])) {|era_name, parent|
1114
+ era_name.prefix(m17n(parent) + '::')
1115
+ } + date
1101
1116
  end
1102
1117
 
1103
1118
  # 文字列化 - When.exe Standard Representation により文字列化する
1104
1119
  #
1105
1120
  # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1121
+ # @param [false] round 常に切り捨てる(DateAndTimeとの互換性のためのダミーの引数)
1106
1122
  #
1107
1123
  # @return [String]
1108
1124
  #
1109
- def to_s(precision=@precision)
1110
- era, = @calendar_era_name
1125
+ def to_s(precision=@precision, round=false)
1111
1126
  date = _date_to_s(precision)
1112
- return date unless era
1113
- return era.to_s + date
1127
+ return date unless @calendar_era
1128
+ return _parent_labels.inject(@calendar_era_name[0].to_s) {|era_name, parent|
1129
+ parent.to_s + '::' + era_name
1130
+ } + date
1114
1131
  end
1115
1132
 
1116
1133
  # event を 文字列化 - 日時で与えられた event を文字列化する
1117
1134
  #
1118
1135
  # @param [When::TM::TemporalPosition] other 時系の歩度を比較する基準(nilは比較しない)
1136
+ # @param [Numeric] round_precision イベント名(イベント)出力の場合の時刻の丸め位置(nilなら丸めない)
1119
1137
  #
1120
1138
  # @return [String]
1121
1139
  #
@@ -1124,15 +1142,36 @@ module When
1124
1142
  # 日時の精度が日より細かい - イベント名(イベント時刻)
1125
1143
  # 日時の精度が日 - イベント名(当日までの経過日数)
1126
1144
  #
1127
- def _event_form(other=nil)
1145
+ def _event_form(other=nil, round_precision=nil)
1128
1146
  return to_m17n unless events
1129
- return events[0] + '(' + clk_time.to_s[/[:*=0-9]+/] + ')' if precision > When::DAY
1147
+ return events[0] + '(' + _clk_time_for_inspect(round_precision).to_s(round_precision || precision)[/[:*=0-9]+/] + ')' if precision > When::DAY
1130
1148
  return events[0] unless other
1131
1149
  other = JulianDate.dynamical_time(other.dynamical_time,
1132
1150
  {:time_standard=>time_standard}) unless time_standard.rate_of_clock == other.time_standard.rate_of_clock
1133
1151
  events[0] + '(' + (other.to_i - to_i).to_s + ')'
1134
1152
  end
1135
1153
 
1154
+ private
1155
+
1156
+ # 日付の年号に曖昧性がある場合の親年号の label の Array
1157
+ #
1158
+ # @return [Array<String>]
1159
+ #
1160
+ def _parent_labels
1161
+ return [] unless (area = When::TM::CalendarEra[nil]) &&
1162
+ (period = area[nil])
1163
+ list = []
1164
+ era = @calendar_era
1165
+ while (labels = period[era.label.to_s]) &&
1166
+ (epoch = labels[era.epoch_year]) &&
1167
+ (epoch.size > 1) &&
1168
+ (parent = era.parent).respond_to?(:epoch_year)
1169
+ list << parent.label
1170
+ era = parent
1171
+ end
1172
+ list
1173
+ end
1174
+
1136
1175
  # 日付の年号以外の部分を文字列化する
1137
1176
  #
1138
1177
  # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
@@ -1144,23 +1183,23 @@ module When
1144
1183
  precision = [precision, 1 - @cal_date.length].max
1145
1184
  precision = [precision, DAY].min
1146
1185
  terms = []
1147
- format = ""
1186
+ ext_dg = [(@extra_year_digits||1).to_i, 0].max
1148
1187
 
1149
1188
  # 年
1150
1189
  year_by_epoch = @cal_date[0]
1151
- if (@calendar_era_name)
1190
+ if @calendar_era_name
1152
1191
  era, epoch, reverse = @calendar_era_name
1153
1192
  year_in_term = reverse ? -year_by_epoch : year_by_epoch
1154
1193
  year_by_calendar = epoch + year_by_epoch if epoch
1155
1194
  terms << year_in_term
1156
- format += (0..99) === (year_in_term * 1) ? "%02d." : "%04d."
1195
+ format = (0..99) === (year_in_term * 1) ? "%02d." : "%04d."
1157
1196
  if year_by_calendar && year_by_calendar != year_in_term
1158
1197
  terms << (year_by_calendar * 1)
1159
1198
  format += "(%04d)"
1160
1199
  end
1161
1200
  else
1162
1201
  terms << year_by_epoch
1163
- format += (0..9999) === (year_by_epoch * 1) ? "%04d." : "%06d."
1202
+ format = (0..9999) === (year_by_epoch * 1) ? "%04d." : "%+0#{5+ext_dg}d."
1164
1203
  end
1165
1204
 
1166
1205
  # 月日
@@ -1196,22 +1235,35 @@ module When
1196
1235
  # 多言語対応文字列化 - When.exe Standard Representation により多言語対応文字列化する
1197
1236
  #
1198
1237
  # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1238
+ # @param [true, false] round 指定の桁までで丸める(true)か, 切り捨てる(false)か
1239
+ # @note 丸めるのは precision が When::DAY よりも高精度の場合のみである
1199
1240
  #
1200
1241
  # @return [When::BasicTypes::M17n]
1201
1242
  #
1202
- def to_m17n(precision=@precision)
1203
- super + @clk_time.to_m17n(precision)
1243
+ def to_m17n(precision=@precision, round=false)
1244
+ super + _clk_time_for_inspect(round ? precision : nil).to_m17n(precision)
1204
1245
  end
1205
1246
 
1206
1247
  # 文字列化 -When.exe Standard Representation により文字列化する
1207
1248
  #
1208
1249
  # @param [Integer] precision どの桁まで多言語対応文字列化するか、分解能で指定する
1250
+ # @param [true, false] round 指定の桁までで丸める(true)か, 切り捨てる(false)か
1251
+ # @note 丸めるのは precision が When::DAY よりも高精度の場合のみである
1209
1252
  #
1210
1253
  # @return [String]
1211
1254
  #
1212
- def to_s(precision=@precision)
1213
- super + @clk_time.to_s(precision)
1255
+ def to_s(precision=@precision, round=false)
1256
+ super + _clk_time_for_inspect(round ? precision : nil).to_s(precision)
1257
+ end
1258
+
1259
+ # 出力に使用する clk_time の作成
1260
+ def _clk_time_for_inspect(precision)
1261
+ return @clk_time unless precision && precision > When::DAY
1262
+ base = self + When::TM::Duration.new(@clk_time.frame._round_value(precision))
1263
+ base.clk_time.clk_time[When::HOUR] = @clk_time.clk_time[When::HOUR] + 1 unless self.to_i == base.to_i
1264
+ return base.clk_time
1214
1265
  end
1266
+ private :_clk_time_for_inspect
1215
1267
 
1216
1268
  # 時
1217
1269
  #