when_exe 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
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
  #