timesteps 0.9.6 → 0.9.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0feffe90d4bd2fdd8c478624bc78b67e3a7ab1c5905e236767c7734ba8c6b098
4
- data.tar.gz: 4e924fa6b145f2b7cb06836d408b05e8bb34e8133797a3b7702ba29033af4400
3
+ metadata.gz: bac0d24992c8693974390e294212ee592b8b1974486d8ffe95704550810ade9e
4
+ data.tar.gz: e1da906f7b7e7adc85e597dfc14dc8229ff3625f2be3652c599f56a476a10e95
5
5
  SHA512:
6
- metadata.gz: 52773368aa3a11f6ac7e6e2e71b56e8ebb84f78b2c6807ac509f77a8342566cae41fb4b93a1c0c03f4fd1fbed07ae698144fb208f433b5a663a08cbf80ff83cb
7
- data.tar.gz: 61d67459512fa64a4e7b8967b7300af0c9719b61c2bf4f66c478b5c08a064c38d889f0d2830bb7399d81ba02c516b194ccaab85406855a688204c06347cd6117
6
+ metadata.gz: e2e1b74b4052d4c16d2c4538c98f7328591a317494c6937a7a48d09c751037693d4235cd4a6cf73fb24f993f8a6d35a24cbcef0f4862b6b7e31ec965e552f725
7
+ data.tar.gz: c7beea6d38d54bfda596c8f5ea4b77f8ebc68660bc3cf1d7fe7c2305a6a63a7966a925caa8b579538010e14cee370934f7c68a115276a9c64b67d04fa0a69dc5
data/Note.ja.md CHANGED
@@ -1,4 +1,13 @@
1
1
  # メモ
2
+
3
+ masterブランチ -> gem リリース最新版
4
+ developブランチ -> 開発統合版
5
+ topicブランチ -> Feature開発版
6
+
7
+
8
+
9
+ "XX since YYY" と since: time を同時に指定下場合は、前者が採用される (sinceオプションは無視される)。
10
+
2
11
  ### 時間の単位 (udunits)
3
12
  * years, year
4
13
  * months,month
data/README.md CHANGED
@@ -58,9 +58,9 @@ ts.index_at("2001-01-02 00:00:00") ### => 8
58
58
  ts.time_at(0) ### => #<DateTime 2001-01-01T00:00:00 ...>
59
59
  ts.time_at(1) ### => #<DateTime 2001-01-01T03:00:00 ...>
60
60
  ts.time_at(8) ### => #<DateTime 2001-01-02T00:00:00 ...>
61
- ts.days_at(0) ### => 0 [Integer]
62
- ts.days_at(1) ### => (1/8) [Rational]
63
- ts.days_at(8) ### => 1 [Integer]
61
+ ts.duration_at(0) ### => 0 [Integer]
62
+ ts.duration_at(1) ### => (1/8) [Rational]
63
+ ts.duration_at(8) ### => 1 [Integer]
64
64
  ```
65
65
 
66
66
  #### Treatment of year and month units
@@ -137,14 +137,14 @@ ts = TimeStep.new("days since 2001-01-01", calendar: "allleap")
137
137
  ts.parse("2001-02-29") ### => #<DateTime::AllLeap 2001-02-29T ...>
138
138
  ```
139
139
 
140
- In the UDUNITS library, negative years are treated as BCs, and A.D. 0 is treated as non-existent. This is different from how it is handled in Ruby's DateTime class. To parse date-time string of UDUNITS type, `bc` option for `Date.parse_timestamp` method.
140
+ In the UDUNITS library, negative years are treated as BCs, and A.D. 0 is treated as non-existent. This is different from how it is handled in Ruby's DateTime class.
141
141
 
142
142
  ```ruby
143
143
  DateTime.parse_timestamp("-0001-01-01")
144
144
  # => #<DateTime: -0001-01-01T00:00:00+00:00 ...>
145
145
  # B.C. 2
146
146
 
147
- DateTime.parse_timestamp("-0001-01-01", bc: true)
147
+ DateTime.parse_timestamp("0000-01-01")
148
148
  # => #<DateTime: 0000-01-01T00:00:00+00:00 ...>
149
149
  # B.C. 1
150
150
 
@@ -12,3 +12,5 @@ require "timesteps/timestep"
12
12
  require "timesteps/timestep_pair"
13
13
  require "timesteps/timestep_converter"
14
14
  require "timesteps/timeperiod"
15
+ require "timesteps/timestep_range"
16
+ require "timesteps/datetime_timestep"
@@ -11,11 +11,10 @@ class DateTime
11
11
  # `DateTime._parse()` is called internally.
12
12
  #
13
13
  # @param spec [String]
14
- # @option bc [Boolean]
15
14
  #
16
15
  # @return [DateTimeFixedDPY]
17
16
 
18
- def self.parse_timestamp (spec, calendar: "standard", bc: false, format: nil)
17
+ def self.parse_timestamp (spec, format: nil, offset: nil, calendar: "standard", tz: nil)
19
18
  case calendar.downcase.intern
20
19
  when :standard, :gregorian
21
20
  klass = DateTime
@@ -40,27 +39,37 @@ class DateTime
40
39
  hash = DateTime._strptime(spec, format)
41
40
  raise "date-time string '#{spec}' doesn't match with the given format '#{format}'" unless hash
42
41
  else
43
- if spec =~ /\A([+\-]?\d{4})(\-(\d{1,2}))?\z/
44
- hash = { year: $1.to_i, mon: $3 ? $3.to_i : 1, mday: 1 }
42
+ if spec =~ /\A([+\-]?\d{1,4})(\-(\d{1,2}))?(\s+(\w{3}|[+\-]\d{1,2}(:?\d{1,2})))?\z/
43
+ year = $1.to_i
44
+ month = $3 ? $3.to_i : 1
45
+ mday = 1
46
+ rest = $4
47
+ hash = DateTime._parse("#{year}-#{month}-#{mday} 00:00:00 #{rest}")
45
48
  else
46
49
  hash = DateTime._parse(spec)
47
50
  end
48
51
  end
49
- year, month, day, hour, minute, second, sec_fraction, offset =
52
+ year, month, day, hour, minute, second, sec_fraction, offset_ =
50
53
  hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset)
51
- if bc and year < 0
52
- year = year + 1
53
- end
54
54
  hour ||= 0
55
55
  minute ||= 0
56
56
  second ||= 0.0
57
57
  sec_fraction ||= 0.0
58
- offset ||= 0
59
- if hour == 24 && minute == 0 && second == 0.0
60
- klass.new(year, month, day, 23, minute, second + sec_fraction, offset.quo(86400), start) + 1.quo(24)
58
+ if offset_
59
+ offset = offset_.quo(86400)
60
+ else
61
+ offset ||= 0
62
+ end
63
+ if tz
64
+ time = tz.local_datetime(year, month, day, hour, minute, second.to_i, sec_fraction.to_r)
61
65
  else
62
- klass.new(year, month, day, hour, minute, second + sec_fraction, offset.quo(86400), start)
66
+ if hour == 24 && minute == 0 && second == 0.0
67
+ time = klass.new(year, month, day, 23, minute, second + sec_fraction, offset, start) + 1.quo(24)
68
+ else
69
+ time = klass.new(year, month, day, hour, minute, second + sec_fraction, offset, start)
70
+ end
63
71
  end
72
+ return time
64
73
  end
65
74
 
66
75
  end
@@ -0,0 +1,64 @@
1
+
2
+ class DateTime
3
+
4
+ def timestep (interval_spec, tz: nil)
5
+ case start
6
+ when Date::ITALY
7
+ calendar = "standard"
8
+ when Date::GREGORIAN
9
+ calendar = "proleptic_gregorian"
10
+ when Date::JULIAN
11
+ calendar = "proleptic_julian"
12
+ end
13
+ return TimeStep.new(interval_spec, since: self, calendar: calendar, tz: tz)
14
+ end
15
+
16
+ def timeperiod (interval_spec, tz: nil, ends: "[]")
17
+ case start
18
+ when Date::ITALY
19
+ calendar = "standard"
20
+ when Date::GREGORIAN
21
+ calendar = "proleptic_gregorian"
22
+ when Date::JULIAN
23
+ calendar = "proleptic_julian"
24
+ end
25
+ return TimePeriod.new(interval_spec, since: self, calendar: calendar, ends: ends, tz: tz)
26
+ end
27
+
28
+ end
29
+
30
+ class DateTime::NoLeap
31
+
32
+ def timestep (interval_spec, tz: nil)
33
+ return TimeStep.new(interval_spec, since: self, clanedar: "noleap", tz: tz)
34
+ end
35
+
36
+ def timeperiod (interval_spec, tz: nil, ends: "[]")
37
+ return TimePeriod.new(interval_spec, since: self, calendar: "noleap", ends: ends, tz: tz)
38
+ end
39
+
40
+ end
41
+
42
+ class DateTime::AllLeap
43
+
44
+ def timestep (interval_spec, tz: nil)
45
+ return TimeStep.new(interval_spec, since: self, clanedar: "allleap", tz: tz)
46
+ end
47
+
48
+ def timeperiod (interval_spec, tz: nil, ends: "[]")
49
+ return TimePeriod.new(interval_spec, since: self, calendar: "allleap", ends: ends, tz: tz)
50
+ end
51
+
52
+ end
53
+
54
+ class DateTime::Fixed360Day
55
+
56
+ def timestep (interval_spec, tz: nil)
57
+ return TimeStep.new(interval_spec, since: self, clanedar: "360day", tz: tz)
58
+ end
59
+
60
+ def timeperiod (interval_spec, tz: nil, ends: "[]")
61
+ return TimePeriod.new(interval_spec, since: self, calendar: "360day", ends: ends, tz: tz)
62
+ end
63
+
64
+ end
@@ -398,29 +398,18 @@ class DateTimeLike
398
398
  #
399
399
  # @return [Integer]
400
400
  def difference_in_years (other)
401
- extra = 0
402
- if self.year > other.year && self.compare_md(other) < 0
403
- extra = -1
404
- end
405
- if self.year < other.year && self.compare_md(other) > 0
406
- extra = 1
407
- end
408
- return self.year - other.year + extra
401
+ my = self.new_offset(0)
402
+ other = other.new_offset(0)
403
+ return my.year - other.year + my.compare_md(other).quo(2)
409
404
  end
410
405
 
411
406
  # Calculate difference between the object and other object in months.
412
407
  #
413
408
  # @return [Integer]
414
409
  def difference_in_months (other)
415
- extra = 0
416
- if self.month > other.month && self.compare_d(other) < 0
417
- extra = -1
418
- end
419
- if self.month < other.month && self.compare_d(other) > 0
420
- extra = 1
421
- end
422
- return 12*(self.year - other.year) + self.month - other.month + extra
410
+ my = self.new_offset(0)
411
+ other = other.new_offset(0)
412
+ return 12*(my.year - other.year) + my.month - other.month + my.compare_d(other).quo(2)
423
413
  end
424
-
425
414
  end
426
415
 
@@ -1,6 +1,24 @@
1
- require_relative "../timesteps"
1
+ require "timesteps"
2
2
 
3
- class TimeStep
3
+ class DateTime
4
+
5
+ def self.parse_grads_time (time_string)
6
+ if time_string.strip =~ /\A(((\d{2})?(:(\d{2}))?Z)?(\d{2}))?(\w{3})(\d{4})\z/i
7
+ hour = $3 || "00"
8
+ min = $5 || "00"
9
+ day = $6 || "01"
10
+ mon = $7
11
+ year = $8
12
+ time = DateTime.parse("#{year}#{mon}#{day} #{hour}:#{min}:00")
13
+ else
14
+ raise "invalid time format"
15
+ end
16
+ return time
17
+ end
18
+
19
+ end
20
+
21
+ class TimeStep::Range
4
22
 
5
23
  # @private
6
24
  REGEXP_GRADS_TDEF = /\A(?:TDEF\s+)?(\d+)\s+LINEAR\s+([^\s]*?)\s+([^\s]*?)\s*\z/i
@@ -13,8 +31,17 @@ class TimeStep
13
31
  "mo" => "months",
14
32
  "yr" => "years",
15
33
  }
16
-
17
- def self.parse_grads_tdef (tdef_string)
34
+
35
+ module GrADSMixin
36
+
37
+ def parse_grads_time (time_string)
38
+ return DateTime.parse_grads_time(time_string)
39
+ end
40
+
41
+ end
42
+
43
+ def self.from_grads_tdef (tdef_string)
44
+
18
45
  if tdef_string.strip =~ REGEXP_GRADS_TDEF
19
46
  count = $1.to_i
20
47
  time = $2
@@ -22,22 +49,16 @@ class TimeStep
22
49
  else
23
50
  raise "invalid grads tdef string"
24
51
  end
25
- if time =~ /(((\d{2})?(:(\d{2}))?Z)?(\d{2}))?(\w{3})(\d{4})/i
26
- hour = $3 || "00"
27
- min = $5 || "00"
28
- day = $6 || "01"
29
- mon = $7
30
- year = $8
31
- origin = DateTime.parse("#{year}#{mon}#{day} #{hour}:#{min}:00")
32
- else
33
- raise "invalid time format"
34
- end
52
+
53
+ origin = DateTime.parse_grads_time(time)
35
54
 
36
55
  increment = increment.sub(/(mn|hr|dy|mo|yr)/i) {|s| GRADS_INCREMENT[s.downcase]}
37
-
38
- spec = format("%s since %s", increment, origin.strftime("%F %T"))
39
-
40
- return TimeStep.new(spec, count: count)
56
+
57
+ range = TimeStep.new(increment, since: origin).range(count)
58
+ range.extend GrADSMixin
59
+
60
+ return range
41
61
  end
42
62
 
63
+
43
64
  end
@@ -1,16 +1,17 @@
1
1
 
2
+
2
3
  class TimePeriod < TimeStep
3
-
4
- def initialize (spec, since: nil, format: nil, calendar: "standard", bc: false, boundary: "[)" )
5
- super(spec, since: since, format: format, count: 1, calendar: calendar, bc: bc)
6
- raise "invalid boundary specification" unless boundary =~ /\A[\[\(][\]\)]\z/
7
- @boundary = boundary
8
- @include_start = ( boundary[0] == "[" ) ? true : false
9
- @include_last = ( boundary[1] == "]" ) ? true : false
4
+
5
+ def initialize (spec, since: nil, format: nil, offset: nil, calendar: "standard", tz: nil, ends: "[]" )
6
+ super(spec, since: since, format: format, offset: offset, calendar: calendar, tz: tz)
7
+ raise "invalid ends specification" unless ends =~ /\A[\[\(][\]\)]\z/
8
+ @ends = ends
9
+ @include_start = ( ends[0] == "[" ) ? true : false
10
+ @include_last = ( ends[1] == "]" ) ? true : false
10
11
  @last = time_at(1)
11
12
  end
12
13
 
13
- attr_reader :last
14
+ attr_reader :last, :ends
14
15
 
15
16
  def start
16
17
  return @origin
@@ -27,40 +28,40 @@ class TimePeriod < TimeStep
27
28
  # Returns the value as a string for inspection.
28
29
  #
29
30
  def inspect
31
+ options = ""
32
+ case @calendar.name
33
+ when "standard", "gregorian"
34
+ else
35
+ options << " calendar='#{calendar.name}'"
36
+ end
30
37
  left_paren = @include_start ? "[" : "("
31
38
  right_paren = @include_last ? "]" : ")"
32
- "#<TimePeriod '#{interval_spec}' #{left_paren}#{start.to_s}, #{last.to_s}#{right_paren} calendar='#{calendar.name}'>"
33
- end
34
-
35
- def boundary
36
- left_paren = @include_start ? "[" : "("
37
- right_paren = @include_last ? "]" : ")"
38
- return left_paren + right_paren
39
+ "#<TimePeriod '#{interval_spec}' #{left_paren}#{start.to_s}, #{last.to_s}#{right_paren} #{options}>"
39
40
  end
40
41
 
41
42
  def include? (other)
42
43
  case other
43
44
  when TimePeriod
44
45
  if ( other.origin < @origin ) ||
45
- ( other.origin == @origin && ( not include_start? ) && other.include_start? )
46
+ ( other.origin == @origin && ( not @include_start ) && other.include_start? )
46
47
  left = false
47
48
  else
48
49
  left = true
49
50
  end
50
51
  if ( @last < other.last ) ||
51
- ( @last == other.last && ( not include_last? ) && other.include_last? )
52
+ ( @last == other.last && ( not @include_last ) && other.include_last? )
52
53
  right = false
53
54
  else
54
55
  right = true
55
56
  end
56
57
  return left & right
57
58
  else
58
- if include_start?
59
+ if @include_start
59
60
  left = @origin <= other
60
61
  else
61
62
  left = @origin < other
62
63
  end
63
- if include_last?
64
+ if @include_last
64
65
  right = other <= @last
65
66
  else
66
67
  right = other < @last
@@ -73,13 +74,13 @@ class TimePeriod < TimeStep
73
74
  case other
74
75
  when TimePeriod
75
76
  if ( @origin < other.last ) ||
76
- ( @origin == other.last && include_start? && other.include_last? )
77
+ ( @origin == other.last && @include_start && other.include_last? )
77
78
  left = true
78
79
  else
79
80
  left = false
80
81
  end
81
82
  if ( other.origin < @last ) ||
82
- ( other.origin == @last && include_last? && other.include_start? )
83
+ ( other.origin == @last && @include_last && other.include_start? )
83
84
  right = true
84
85
  else
85
86
  right = false
@@ -116,26 +117,26 @@ class TimePeriod < TimeStep
116
117
  if ( other.origin < @origin ) ||
117
118
  ( other.origin == @origin && ( not include_start? ) && other.include_start? )
118
119
  left = other.origin
119
- left_boundary = other.include_start? ? "[" : "("
120
+ left_end = other.include_start? ? "[" : "("
120
121
  else
121
122
  left = @origin
122
- left_boundary = include_start? ? "[" : "("
123
+ left_end = include_start? ? "[" : "("
123
124
  end
124
125
  if ( @last < other.last ) ||
125
126
  ( @last == other.last && ( not include_last? ) && other.include_last? )
126
127
  right = other.last
127
- right_boundary = other.include_last? ? "]" : ")"
128
+ right_end = other.include_last? ? "]" : ")"
128
129
  else
129
130
  right = @last
130
- right_boundary = include_last? ? ")" : "]"
131
+ right_end = include_last? ? ")" : "]"
131
132
  end
132
133
  ridx = index_at(right)
133
134
  lidx = index_at(left)
134
135
  numeric = (ridx - lidx) * @numeric
135
136
  origin = left
136
137
  interval_spec = format("%g %s", numeric, @symbol)
137
- boundary = left_boundary + right_boundary
138
- return TimePeriod.new(interval_spec, since: origin, calendar: @calendar.name, bc: @bc, boundary: boundary)
138
+ ends = left_end + right_end
139
+ return TimePeriod.new(interval_spec, since: origin, calendar: @calendar, ends: ends)
139
140
  end
140
141
 
141
142
  def clip (other)
@@ -143,60 +144,62 @@ class TimePeriod < TimeStep
143
144
  if ( @origin < other.origin ) ||
144
145
  ( @origin == other.origin && include_start? && ( not other.include_start? ) )
145
146
  left = other.origin
146
- left_boundary = other.include_start? ? "[" : "("
147
+ left_end = other.include_start? ? "[" : "("
147
148
  else
148
149
  left = @origin
149
- left_boundary = include_start? ? "[" : "("
150
+ left_end = include_start? ? "[" : "("
150
151
  end
151
152
  if ( other.last < @last ) ||
152
153
  ( other.last == @last && include_last? && ( not other.include_last? ) )
153
154
  right = other.last
154
- right_boundary = other.include_last? ? "]" : ")"
155
+ right_end = other.include_last? ? "]" : ")"
155
156
  else
156
157
  right = @last
157
- right_boundary = include_last? ? ")" : "]"
158
+ right_end = include_last? ? ")" : "]"
158
159
  end
159
160
  ridx = index_at(right)
160
161
  lidx = index_at(left)
161
162
  numeric = (ridx - lidx) * @numeric
162
163
  origin = left
163
164
  interval_spec = format("%g %s", numeric, @symbol)
164
- bndry = left_boundary + right_boundary
165
- return TimePeriod.new(interval_spec, since: origin, calendar: @calendar.name, bc: @bc, boundary: bndry)
165
+ ends = left_end + right_end
166
+ return TimePeriod.new(interval_spec, since: origin, calendar: @calendar, ends: ends)
166
167
  end
167
168
 
168
- def split (time, boundary: ")[")
169
+ def split (time, ends: ")[")
169
170
  raise "can't split period without contacted time" unless contact?(time)
170
171
  # left
171
172
  numeric = index_at(time) * @numeric
172
173
  interval_spec = format("%g %s", numeric, @symbol)
173
- bndry = (@include_start ? "[" : "(") + boundary[0]
174
- left_period = TimePeriod.new(interval_spec, since: @origin, calendar: @calendar.name, bc: @bc, boundary: bndry)
174
+ ends = (@include_start ? "[" : "(") + ends[0]
175
+ left_period = TimePeriod.new(interval_spec, since: @origin, calendar: @calendar, ends: ends)
175
176
  # right
176
177
  numeric = (1 - index_at(time)) * @numeric
177
178
  interval_spec = format("%g %s", numeric, @symbol)
178
- bndry = boundary[1] + (@include_last ? "]" : ")")
179
- right_period = TimePeriod.new(interval_spec, since: @origin, calendar: @calendar.name, bc: @bc, boundary: bndry)
179
+ ends = ends[1] + (@include_last ? "]" : ")")
180
+ right_period = TimePeriod.new(interval_spec, since: @origin, calendar: @calendar, ends: ends)
180
181
  return left_period, right_period
181
182
  end
182
183
 
183
184
  def next
184
- return TimePeriod.new(interval_spec, since: @last, calendar: @calendar.name, bc: @bc, boundary: boundary)
185
+ return TimePeriod.new(interval_spec, since: @last, calendar: @calendar, ends: ends)
185
186
  end
186
187
 
187
188
  def prev
188
189
  origin = index_at(-1)
189
- return TimePeriod.new(interval_spec, since: origin, calendar: @calendar.name, bc: @bc, boundary: boundary)
190
+ return TimePeriod.new(interval_spec, since: origin, calendar: @calendar, ends: ends)
190
191
  end
191
192
 
192
- def shift (index)
193
- return TimePeriod.new(interval_spec, since: time_at(index), calendar: @calendar.name, bc: @bc, boundary: boundary)
194
- end
193
+ def shift_origin (index, with: "index")
194
+ case with
195
+ when :index, "index"
196
+ return TimePeriod.new(interval_spec, since: time_at(index), calendar: @calendar, ends: ends)
197
+ when :duration, "duration", :days, "days"
198
+ time = @origin + index
199
+ return TimePeriod.new(interval_spec, since: time, calendar: @calendar, ends: ends)
200
+ end
195
201
 
196
- def shift_with_duration (duration)
197
- time = @origin + duration
198
- return TimePeriod.new(interval_spec, since: time, calendar: @calendar.name, bc: @calendar.bc?, boundary: boundary)
199
202
  end
200
-
203
+
201
204
  end
202
205