source: ruby/branches/0.5/lib/open_ehr/assumed_library_types.rb @ 282

Last change on this file since 282 was 282, checked in by KOBAYASHI, Shinji, 11 years ago

date and time are seeds of headache

File size: 14.9 KB
Line 
1# This module is related to the ticket #36
2require 'date'
3require 'time'
4
5module OpenEHR
6  module AssumedLibraryTypes
7    class Any < Object
8     
9    end # of Any
10
11    class Interval < Any
12      attr_reader :lower, :upper
13     
14      def initialize(args = {})
15        check_lower_upper(args[:lower], args[:upper])
16        self.lower_included = args[:lower_included]
17        self.upper_included = args[:upper_included]
18      end
19
20      def lower=(lower)
21        check_lower_upper(lower, @upper)
22      end
23
24      def upper=(upper)
25        check_lower_upper(@lower, upper)
26      end
27
28      def lower_included?
29        return @lower_included
30      end
31
32      def lower_included=(lower_included)
33        if (lower == nil) && (lower_included != nil)
34          raise ArgumentError, "lower is not set"
35        end
36        @lower_included = lower_included
37      end
38     
39      def lower_unbounded?
40        return @lower.nil?
41      end
42
43      def upper_included?
44        return @upper_included
45      end
46
47      def upper_included=(upper_included)
48        if (@upper.nil?) && (upper_included != nil)
49          raise ArgumentError, "upper is not set"
50        end
51        @upper_included = upper_included
52      end
53
54      def upper_unbounded?
55        return @upper.nil?
56      end
57
58      def has?(value)
59        if ((@lower.nil?||@lower < value||((@lower_included == true) && (@lower == value)))&&
60            (@upper.nil?||value < @upper||((@upper_included == true) && (@upper == value))))
61          true
62        else
63          false
64        end
65      end
66
67      private
68
69      def check_lower_upper(lower, upper)
70        if lower.nil? && upper.nil?
71          raise ArgumentError, "Either lower or upper must be assigned"
72        end
73        unless (lower.nil? || upper.nil?)
74          if lower > upper
75            raise ArgumentError, "Upper must be larger than lower."
76          end
77        end
78        @lower = lower
79        @upper = upper
80      end
81    end # end of Interval
82 
83    class TimeDefinitions < Any
84      DAYS_IN_LEAP_YEAR = 366
85      DAYS_IN_WEEK = 7
86      DAYS_IN_YEAR = 365
87      HOURS_IN_DAY = 24
88      MAX_DAYS_IN_MONTH = 31
89      MAX_DAYS_IN_YEAR = 366
90      MINUTES_IN_HOUR = 60
91      MONTH_IN_YEAR = 12
92      NOMINAL_DAYS_IN_MONTH = 30.42
93      NOMINAL_DAYS_IN_YEAR = 365.24
94      SECONDS_IN_MINUTE = 60
95
96      def self.valid_year?(year)
97        return !year.nil? && year >= 0
98      end
99
100      def self.valid_day?(y, m, d)
101        unless y.nil? || m.nil? || d.nil?
102          return Date.valid_date?(y,m,d)
103        end
104        if (y.nil?) || (m.nil? && !d.nil?)
105          return false
106        end
107        return self.valid_year?(y) && self.valid_month?(m)
108      end
109
110      def self.valid_hour?(h,m = nil, s = nil)
111        if !m.nil? and !valid_minute?(m)
112          return false
113        end
114        if !s.nil? and (!m.nil? and !valid_second?(s))
115          return false
116        end
117        (h >= 0 and h < HOURS_IN_DAY) or (h == HOURS_IN_DAY and m == 0 and s == 0)
118      end
119
120      def self.valid_minute?(mi)
121        mi >= 0 and mi < MINUTES_IN_HOUR
122      end
123
124      def self.valid_second?(s)
125        s >= 0 and s < SECONDS_IN_MINUTE
126      end
127
128      def self.valid_month?(mo)
129        mo >= 1 and mo <= MONTH_IN_YEAR
130      end
131    end # end of TimeDefinitions
132
133    module ISO8601DateModule
134      attr_reader :year, :month, :day
135
136      def year=(year)
137        unless ISO8601Date.valid_year?(year)
138          raise ArgumentError, "Year is not valid"
139        end
140        @year = year
141      end
142
143      def month=(month)
144        raise ArgumentError, "Month is not valid" unless month.nil? or ISO8601Date.valid_month?(month)
145        @month = month
146      end
147
148      def day=(day)
149       
150        raise ArgumentError, "Day is not valid" unless day.nil? or ISO8601Date.valid_day?(@year, @month, day)
151        @day = day
152      end
153
154      def as_string
155        if (!@year.nil? and !@month.nil? and !@day.nil?)
156          Date.new(@year, @month, @day).to_s
157        elsif (!@year.nil? and !@month.nil? and @day.nil?)
158          Date.new(@year, @month).to_s[0,7]
159        elsif (!@year.nil? and @month.nil? and @day.nil?)
160          Date.new(@year).to_s[0,4]
161        end         
162      end
163
164      def month_unknown?
165        @month.nil?
166      end
167
168      def day_unknown?
169        @day.nil?
170      end
171
172      def is_extended?
173        true
174      end
175
176      def is_partial?
177        month_unknown? or day_unknown?
178      end
179
180      protected
181      def leapyear?(year)
182        case
183        when year % 400 == 0: true
184        when year % 100 == 0: false
185        else year % 4 == 0
186        end
187      end
188    end
189
190    class ISO8601Date < TimeDefinitions
191      include ISO8601DateModule
192      def initialize(string)
193        /(\d{4})(?:-(\d{2})(?:-(\d{2})?)?)?/ =~ string
194        if $1.nil?
195          raise ArgumentError, 'data invalid'
196        else
197          self.year = $1.to_i
198        end
199        if $2.nil?
200          self.month = nil
201        else
202          self.month = $2.to_i
203        end
204        if $3.nil?
205          self.day = nil
206        else
207          self.day = $3.to_i
208        end
209      end
210
211      def self.valid_iso8601_date?(string)
212        begin
213          Date.parse(string)
214        rescue
215          return false
216        end
217        true
218      end
219    end # end of ISO8601_DATE
220
221    module ISO8601TimeModule
222      attr_reader :hour, :minute, :second, :fractional_second, :timezone
223
224      def hour=(hour)
225        raise ArgumentError, "hour is not valid" if !ISO8601_TIME.valid_hour?(hour, @minute, @second)
226        @hour = hour
227      end
228
229      def minute_unknown?
230        @minute.nil?
231      end
232
233      def minute=(minute)
234        raise ArgumentError, "minute is not valid" if !minute.nil? and !ISO8601_TIME.valid_minute?(minute)
235        @minute = minute
236      end
237
238      def second_unknown?
239        @second.nil?
240      end
241
242      def second=(second)
243        raise ArgumentError, "minute not defined" if @minute.nil? and !second.nil?
244        raise ArgumentError, "second is not valid" if !second.nil? and !ISO8601_TIME.valid_second?(second)
245        @second = second
246      end
247
248      def fractional_second=(fractional_second)
249        raise ArgumentError, "minute not defined" if minute_unknown? and !fractional_second.nil?
250        raise ArgumentError, "second not defined" if second_unknown? and !fractional_second.nil?
251        raise ArgumentError, "fractional second should be lower than 1.0" if !fractional_second.nil? and fractional_second >= 1.0
252        @fractional_second = fractional_second
253      end
254
255      def has_fractional_second?
256        if @fractional_second.nil?
257          return false
258        else
259          return true
260        end
261      end
262
263      def timezone=(timezone)
264        unless timezone.nil? or timezone == 'Z'
265          if /[+-](\d{2}):?(\d{2})/ =~ timezone
266            @timezone = timezone
267          else
268            raise ArgumentError, "timezone invalid"
269          end
270        else
271          @timezone = nil
272        end
273      end
274
275      def is_decimal_sign_comma?
276        false
277      end
278
279      def is_extended?
280        true
281      end
282
283      def is_partial?
284        second_unknown? or minute_unknown?
285      end
286
287      def as_string
288        s = sprintf("%02d", @hour)
289        if !@minute.nil?
290          s += ":" + sprintf("%02d",@minute)
291          if !@second.nil?
292            s += ":" + sprintf("%02d", @second)
293            if !@fractional_second.nil?
294              s += "." + @fractional_second.to_s[2..-1]
295              if !@timezone.nil?
296                s += @timezone
297              end
298            end
299          end
300        end
301        return s
302      end
303    end
304
305    class ISO8601Time < TimeDefinitions
306      include ISO8601TimeModule
307      def initialize(string)
308        /(\d{2}):?(\d{2})?(:?)(\d{2})?((\.|,)(\d+))?(Z|([+-](\d{2}):?(\d{2})))?/ =~ string
309        if $2.nil?
310          self.minute = nil
311        else
312          self.minute = $2.to_i
313        end
314        if $4.nil?
315          self.second = nil
316        else
317          self.second = $4.to_i
318        end
319        if $1.nil?
320          raise ArgumentError, 'data invalid'
321        else
322          self.hour = $1.to_i
323        end
324        if $7.nil?
325          self.fractional_second = nil
326        else
327          self.fractional_second = ("0." + $7).to_f
328        end
329        if $8.nil?
330          self.timezone = nil
331        else
332          self.timezone = $8
333        end
334      end
335      def self.valid_iso8601_time?(s)
336        if /(\d{2}):?(\d{2})?(:?)(\d{2})?((\.|,)(\d+))?(Z|([+-](\d{2}):?(\d{2})))?/ =~ s
337# ISO 8601 regular expression by H. Yuki
338#  http://digit.que.ne.jp/work/wiki.cgi?Perl%E3%83%A1%E3%83%A2%2FW3C%E5%BD%A2%E5%BC%8F%E3%81%AE%E6%97%A5%E6%99%82%E3%81%AE%E8%A7%A3%E6%9E%90
339# (\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d))?)?(Z|([+-]\d{2}):(\d{2}))?)?)?)?
340          hh = $1; mm = $2; ss = $4; msec = $7; tz = $8
341          if hh.to_i == HOURS_IN_DAY and (mm.nil? or mm.to_i == 0) and (ss.nil? or ss.to_i == 0) and (msec.nil? or msec.to_i==0)
342            return true
343          end
344          if hh.nil? or (hh.to_i < 0 or hh.to_i >= HOURS_IN_DAY)
345            return false
346          end
347          if !mm.nil?
348            if !self.valid_minute?(mm.to_i)
349              return false
350            end
351          end
352          if !ss.nil?
353            if !self.valid_second?(ss.to_i)
354              return false
355            end
356          end
357          if !tz.nil? and tz != "Z"
358            if /[+-](\d{2}):?(\d{2})/ =~ tz
359              h = $1; m = $2
360              if h.to_i < 0 or h.to_i >= HOURS_IN_DAY
361                return false
362              end
363              if m.to_i < 0 or m.to_i >= MINUTES_IN_HOUR
364                return false
365              end
366            else
367              return false
368            end
369          end
370          return true
371        else
372          return false
373        end
374      end
375    end # end of ISO8601_TIME
376
377    module ISO8601DateTimeModule
378      include ISO8601DateModule, ISO8601TimeModule
379      def as_string
380        if (!@year.nil? and !@month.nil? and !@day.nil?)
381          s = Date.new(@year, @month, @day).to_s
382        elsif (!@year.nil? and !@month.nil? and @day.nil?)
383          return Date.new(@year, @month).to_s[0,7]
384        elsif (!@year.nil? and @month.nil? and @day.nil?)
385          return Date.new(@year).to_s[0,4]
386        end
387        unless hour.nil?
388          s += sprintf("T%02d", @hour)
389          unless @minute.nil?
390            s += ":" + sprintf("%02d",@minute)
391            unless @second.nil?
392              s += ":" + sprintf("%02d", @second)
393              unless @fractional_second.nil?
394                s += "." + @fractional_second.to_s[2..-1]
395                unless @timezone.nil?
396                  s += @timezone
397                end
398              end
399            end
400          end
401        end
402        return s
403      end
404    end
405
406    class ISO8601DateTime < ISO8601Date
407      include ISO8601DateTimeModule
408      def initialize(string)
409        /(\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|([+-]\d{2}):?(\d{2}))?)?)?)?/ =~ string
410        if $1.empty?
411          raise ArgumentError, 'format invalid'
412        else
413          self.year = $1.to_i
414        end
415        if $2.nil?
416          self.month = nil
417        else
418          self.month = $2.to_i
419        end
420        if $3.nil?
421          self.day = nil
422        else
423          self.day = $3.to_i
424        end
425        if $5.nil?
426          self.minute = nil
427        else
428          self.minute = $5.to_i
429        end
430        if $6.nil?
431          self.second = nil
432        else
433          self.second = $6.to_i
434        end
435        if $4.nil?
436          self.hour = nil
437        else
438          self.hour = $4.to_i
439        end
440        if $7.nil? or $7.empty?
441          self.fractional_second = nil
442        else
443          self.fractional_second = ("0."+$7).to_f
444        end
445        if $8.nil?
446          self.timezone = nil
447        else
448          self.timezone = $9+$10
449        end
450      end
451    end
452 
453    class ISO8601_TIMEZONE
454      attr_accessor :sign, :hour, :minute
455
456      def is_gmt?
457        @sign == "+1" and @hour == 0 and @minute == 0
458      end
459
460      def as_string
461        if @sign == "+1"
462          s = "+"
463        elsif @sign == "-1"
464          s = "-"
465        end
466        sprintf("Z%s%02d%02d", s, @hour, @minute)
467      end
468    end # end of ISO8601_TIMEZONE
469
470    module ISO8601_DURATION_MODULE
471      attr_reader :years, :months, :weeks, :days
472      attr_reader :hours, :minutes, :seconds, :fractional_second
473
474      def years=(years)
475        unless years.nil? || years >= 0
476          raise ArgumentError, 'years must be above zero'
477        end
478        @years = years
479      end
480
481      def months=(months)
482        unless months.nil? || months >= 0
483          raise ArgumentError, 'months must be above zero'
484        end
485        @months = months
486      end
487
488      def weeks=(weeks)
489        unless weeks.nil? || weeks >= 0
490          raise ArgumentError, 'weeks must be above zero'
491        end
492        @weeks = weeks
493      end
494
495      def days=(days)
496        unless days.nil? || days >= 0
497          raise ArgumentError, 'days must be above zero'
498        end
499        @days = days
500      end
501
502      def hours=(hours)
503        unless hours.nil? || hours >= 0
504          raise ArgumentError, 'hours must be above zero'
505        end
506        @hours = hours
507      end
508
509      def minutes=(minutes)
510        unless minutes.nil? || minutes >= 0
511          raise ArgumentError, 'minutes must be above zero'
512        end
513        @minutes = minutes
514      end
515
516      def seconds=(seconds)
517        unless seconds.nil? || seconds >= 0
518          raise ArgumentError, 'seconds must be above zero'
519        end
520        @seconds = seconds
521      end
522
523      def fractional_second=(fractional_second)
524        unless fractional_second.nil? || (fractional_second >= 0 && fractional_second < 1.0)
525          raise ArgumentError, 'fractional_second must be between 0.0 and 1.0'
526        end
527        @fractional_second = fractional_second
528      end
529
530      def as_string
531        str = 'P'
532        unless @years.nil?
533          str += @years.to_s + 'Y'
534        end
535        unless @months.nil?
536          str += @months.to_s + 'M'
537        end
538        unless @weeks.nil?
539          str += @weeks.to_s + 'W'
540        end
541        unless @days.nil?
542          str += @days.to_s + 'D'
543        end
544        unless @hours.nil?
545          str += 'T' + @hours.to_s + 'H'
546          unless @minutes.nil?
547            str += @minutes.to_s + 'M'
548            unless @seconds.nil?
549              str += @seconds.to_s
550              unless @fractional_second.nil?
551                str += @fractional_second.to_s[1 .. -1]
552              end
553              str += 'S'
554            end
555          end
556        end
557        return str
558      end
559    end
560
561    class ISO8601_DURATION < TimeDefinitions
562      include ISO8601_DURATION_MODULE
563      def initialize(str)
564        /^P((\d+)Y)?((\d+)M)?((\d+)W)?((\d)D)?(T((\d+)H)?((\d+)M)?((\d+)(\.\d+)?S)?)?$/ =~ str
565        self.years = $2.to_i
566        self.months = $4.to_i
567        self.weeks = $6.to_i
568        self.days = $8.to_i
569        self.hours = $11.to_i
570        self.minutes = $13.to_i
571        self.seconds = $15.to_i
572        self.fractional_second = $16.to_f
573      end
574    end # end of ISO8601_DURATION
575  end # end of Assumed_Types
576end # end of OpenEHR
Note: See TracBrowser for help on using the repository browser.