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

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

iso8601 date time completed

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 h.nil?
112          return false
113        end
114        if !m.nil? and !valid_minute?(m)
115          return false
116        end
117        if !s.nil? and (!m.nil? and !valid_second?(s))
118          return false
119        end
120        (h >= 0 and h < HOURS_IN_DAY) or (h == HOURS_IN_DAY and m == 0 and s == 0)
121      end
122
123      def self.valid_minute?(mi)
124        mi >= 0 and mi < MINUTES_IN_HOUR
125      end
126
127      def self.valid_second?(s)
128        s >= 0 and s < SECONDS_IN_MINUTE
129      end
130
131      def self.valid_month?(mo)
132        mo >= 1 and mo <= MONTH_IN_YEAR
133      end
134    end # end of TimeDefinitions
135
136    module ISO8601DateModule
137      attr_reader :year, :month, :day
138
139      def year=(year)
140        unless ISO8601Date.valid_year?(year)
141          raise ArgumentError, "Year is not valid"
142        end
143        @year = year
144      end
145
146      def month=(month)
147        raise ArgumentError, "Month is not valid" unless month.nil? or ISO8601Date.valid_month?(month)
148        @month = month
149      end
150
151      def day=(day)
152       
153        raise ArgumentError, "Day is not valid" unless day.nil? or ISO8601Date.valid_day?(@year, @month, day)
154        @day = day
155      end
156
157      def as_string
158        if (!@year.nil? and !@month.nil? and !@day.nil?)
159          Date.new(@year, @month, @day).to_s
160        elsif (!@year.nil? and !@month.nil? and @day.nil?)
161          Date.new(@year, @month).to_s[0,7]
162        elsif (!@year.nil? and @month.nil? and @day.nil?)
163          Date.new(@year).to_s[0,4]
164        end         
165      end
166
167      def month_unknown?
168        @month.nil?
169      end
170
171      def day_unknown?
172        @day.nil?
173      end
174
175      def is_extended?
176        true
177      end
178
179      def is_partial?
180        month_unknown? or day_unknown?
181      end
182
183      protected
184      def leapyear?(year)
185        case
186        when year % 400 == 0: true
187        when year % 100 == 0: false
188        else year % 4 == 0
189        end
190      end
191    end
192
193    class ISO8601Date < TimeDefinitions
194      include ISO8601DateModule
195      def initialize(string)
196        /(\d{4})(?:-(\d{2})(?:-(\d{2})?)?)?/ =~ string
197        if $1.nil?
198          raise ArgumentError, 'data invalid'
199        else
200          self.year = $1.to_i
201        end
202        if $2.nil?
203          self.month = nil
204        else
205          self.month = $2.to_i
206        end
207        if $3.nil?
208          self.day = nil
209        else
210          self.day = $3.to_i
211        end
212      end
213
214      def self.valid_iso8601_date?(string)
215        begin
216          Date.parse(string)
217        rescue
218          return false
219        end
220        true
221      end
222    end # end of ISO8601_DATE
223
224    module ISO8601TimeModule
225      attr_reader :hour, :minute, :second, :fractional_second, :timezone
226
227      def hour=(hour)
228        unless ISO8601Time.valid_hour?(hour, @minute, @second)
229          raise ArgumentError, "hour is not valid"
230        end
231        @hour = hour
232      end
233
234      def minute_unknown?
235        @minute.nil?
236      end
237
238      def minute=(minute)
239        raise ArgumentError, "minute is not valid" if !minute.nil? and !ISO8601Time.valid_minute?(minute)
240        @minute = minute
241      end
242
243      def second_unknown?
244        @second.nil?
245      end
246
247      def second=(second)
248        raise ArgumentError, "minute not defined" if @minute.nil? and !second.nil?
249        raise ArgumentError, "second is not valid" if !second.nil? and !ISO8601Time.valid_second?(second)
250        @second = second
251      end
252
253      def fractional_second=(fractional_second)
254        raise ArgumentError, "minute not defined" if minute_unknown? and !fractional_second.nil?
255        raise ArgumentError, "second not defined" if second_unknown? and !fractional_second.nil?
256        if !fractional_second.nil? &&
257            (fractional_second < 0.0 || fractional_second >= 1.0)
258          raise ArgumentError, 'fractional second should be between 0.0 - 1.0'
259        end
260        @fractional_second = fractional_second
261      end
262
263      def has_fractional_second?
264        return !@fractional_second.nil?
265      end
266
267      def timezone=(timezone)
268        unless timezone.nil? or timezone == 'Z'
269          if /[+-](\d{2}):?(\d{2})/ =~ timezone
270            @timezone = timezone
271          else
272            raise ArgumentError, "timezone invalid"
273          end
274        else
275          @timezone = nil
276        end
277      end
278
279      def is_decimal_sign_comma?
280        false
281      end
282
283      def is_extended?
284        true
285      end
286
287      def is_partial?
288        second_unknown? or minute_unknown?
289      end
290
291      def as_string
292        s = sprintf("%02d", @hour)
293        if !@minute.nil?
294          s += ":" + sprintf("%02d",@minute)
295          if !@second.nil?
296            s += ":" + sprintf("%02d", @second)
297            if !@fractional_second.nil?
298              s += "." + @fractional_second.to_s[2..-1]
299              if !@timezone.nil?
300                s += @timezone
301              end
302            end
303          end
304        end
305        return s
306      end
307    end
308
309    class ISO8601Time < TimeDefinitions
310      include ISO8601TimeModule
311      def initialize(string)
312        /(\d{2}):?(\d{2})?(:?)(\d{2})?((\.|,)(\d+))?(Z|([+-](\d{2}):?(\d{2})))?/ =~ string
313        if $2.nil?
314          self.minute = nil
315        else
316          self.minute = $2.to_i
317        end
318        if $4.nil?
319          self.second = nil
320        else
321          self.second = $4.to_i
322        end
323        if $1.nil?
324          raise ArgumentError, 'data invalid'
325        else
326          self.hour = $1.to_i
327        end
328        if $7.nil?
329          self.fractional_second = nil
330        else
331          self.fractional_second = ("0." + $7).to_f
332        end
333        if $8.nil?
334          self.timezone = nil
335        else
336          self.timezone = $8
337        end
338      end
339
340      def self.valid_iso8601_time?(s)
341        if /^(\d{2}):?(\d{2})?(:?)(\d{2})?((\.|,)(\d+))?(Z|([+-](\d{2}):?(\d{2})))?$/ =~ s
342# ISO 8601 regular expression by H. Yuki
343#  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
344# (\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d))?)?(Z|([+-]\d{2}):(\d{2}))?)?)?)?
345          hh = $1; mm = $2; ss = $4; msec = $7; tz = $8
346          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)
347            return true
348          end
349          if hh.nil? or (hh.to_i < 0 or hh.to_i >= HOURS_IN_DAY)
350            return false
351          end
352          if !mm.nil?
353            if !self.valid_minute?(mm.to_i)
354              return false
355            end
356          end
357          if !ss.nil?
358            if !self.valid_second?(ss.to_i)
359              return false
360            end
361          end
362          if !tz.nil? and tz != "Z"
363            if /[+-](\d{2}):?(\d{2})/ =~ tz
364              h = $1; m = $2
365              if h.to_i < 0 or h.to_i >= HOURS_IN_DAY
366                return false
367              end
368              if m.to_i < 0 or m.to_i >= MINUTES_IN_HOUR
369                return false
370              end
371            end
372          end
373          return true
374        else
375          return false
376        end
377      end
378    end # end of ISO8601_TIME
379
380    module ISO8601DateTimeModule
381      include ISO8601DateModule, ISO8601TimeModule
382      def as_string
383        if (!@year.nil? and !@month.nil? and !@day.nil?)
384          s = Date.new(@year, @month, @day).to_s
385        elsif (!@year.nil? and !@month.nil? and @day.nil?)
386          return Date.new(@year, @month).to_s[0,7]
387        elsif (!@year.nil? and @month.nil? and @day.nil?)
388          return Date.new(@year).to_s[0,4]
389        end
390        unless hour.nil?
391          s += sprintf("T%02d", @hour)
392          unless @minute.nil?
393            s += ":" + sprintf("%02d",@minute)
394            unless @second.nil?
395              s += ":" + sprintf("%02d", @second)
396              unless @fractional_second.nil?
397                s += "." + @fractional_second.to_s[2..-1]
398                unless @timezone.nil?
399                  s += @timezone
400                end
401              end
402            end
403          end
404        end
405        return s
406      end
407    end
408
409    class ISO8601DateTime < ISO8601Date
410      include ISO8601DateTimeModule
411      def initialize(string)
412        unless /(\d{4})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?(Z|([+-]\d{2}):?(\d{2}))?)?)?)?/ =~ string
413          raise ArgumentError, 'format invalid'
414        else
415          self.year = $1.to_i
416        end
417        if $2.nil?
418          self.month = nil
419        else
420          self.month = $2.to_i
421        end
422        if $3.nil?
423          self.day = nil
424        else
425          self.day = $3.to_i
426        end
427        if $5.nil?
428          self.minute = nil
429        else
430          self.minute = $5.to_i
431        end
432        if $6.nil?
433          self.second = nil
434        else
435          self.second = $6.to_i
436        end
437        if $4.nil?
438          self.hour = nil
439        else
440          self.hour = $4.to_i
441        end
442        if $7.nil? or $7.empty?
443          self.fractional_second = nil
444        else
445          self.fractional_second = ("0."+$7).to_f
446        end
447        if $8.nil?
448          self.timezone = nil
449        else
450          self.timezone = $9+$10
451        end
452      end
453    end
454 
455    class ISO8601_TIMEZONE
456      attr_accessor :sign, :hour, :minute
457
458      def is_gmt?
459        @sign == "+1" and @hour == 0 and @minute == 0
460      end
461
462      def as_string
463        if @sign == "+1"
464          s = "+"
465        elsif @sign == "-1"
466          s = "-"
467        end
468        sprintf("Z%s%02d%02d", s, @hour, @minute)
469      end
470    end # end of ISO8601_TIMEZONE
471
472    module ISO8601_DURATION_MODULE
473      attr_reader :years, :months, :weeks, :days
474      attr_reader :hours, :minutes, :seconds, :fractional_second
475
476      def years=(years)
477        unless years.nil? || years >= 0
478          raise ArgumentError, 'years must be above zero'
479        end
480        @years = years
481      end
482
483      def months=(months)
484        unless months.nil? || months >= 0
485          raise ArgumentError, 'months must be above zero'
486        end
487        @months = months
488      end
489
490      def weeks=(weeks)
491        unless weeks.nil? || weeks >= 0
492          raise ArgumentError, 'weeks must be above zero'
493        end
494        @weeks = weeks
495      end
496
497      def days=(days)
498        unless days.nil? || days >= 0
499          raise ArgumentError, 'days must be above zero'
500        end
501        @days = days
502      end
503
504      def hours=(hours)
505        unless hours.nil? || hours >= 0
506          raise ArgumentError, 'hours must be above zero'
507        end
508        @hours = hours
509      end
510
511      def minutes=(minutes)
512        unless minutes.nil? || minutes >= 0
513          raise ArgumentError, 'minutes must be above zero'
514        end
515        @minutes = minutes
516      end
517
518      def seconds=(seconds)
519        unless seconds.nil? || seconds >= 0
520          raise ArgumentError, 'seconds must be above zero'
521        end
522        @seconds = seconds
523      end
524
525      def fractional_second=(fractional_second)
526        unless fractional_second.nil? || (fractional_second >= 0 && fractional_second < 1.0)
527          raise ArgumentError, 'fractional_second must be between 0.0 and 1.0'
528        end
529        @fractional_second = fractional_second
530      end
531
532      def as_string
533        str = 'P'
534        unless @years.nil?
535          str += @years.to_s + 'Y'
536        end
537        unless @months.nil?
538          str += @months.to_s + 'M'
539        end
540        unless @weeks.nil?
541          str += @weeks.to_s + 'W'
542        end
543        unless @days.nil?
544          str += @days.to_s + 'D'
545        end
546        unless @hours.nil?
547          str += 'T' + @hours.to_s + 'H'
548          unless @minutes.nil?
549            str += @minutes.to_s + 'M'
550            unless @seconds.nil?
551              str += @seconds.to_s
552              unless @fractional_second.nil?
553                str += @fractional_second.to_s[1 .. -1]
554              end
555              str += 'S'
556            end
557          end
558        end
559        return str
560      end
561    end
562
563    class ISO8601_DURATION < TimeDefinitions
564      include ISO8601_DURATION_MODULE
565      def initialize(str)
566        /^P((\d+)Y)?((\d+)M)?((\d+)W)?((\d)D)?(T((\d+)H)?((\d+)M)?((\d+)(\.\d+)?S)?)?$/ =~ str
567        self.years = $2.to_i
568        self.months = $4.to_i
569        self.weeks = $6.to_i
570        self.days = $8.to_i
571        self.hours = $11.to_i
572        self.minutes = $13.to_i
573        self.seconds = $15.to_i
574        self.fractional_second = $16.to_f
575      end
576    end # end of ISO8601_DURATION
577  end # end of Assumed_Types
578end # end of OpenEHR
Note: See TracBrowser for help on using the repository browser.