Class: RDF::Literal::Duration

Inherits:
RDF::Literal show all
Defined in:
lib/rdf/xsd/duration.rb

Overview

A duration literal.

duration is a datatype that represents durations of time. The concept of duration being captured is drawn from those of ISO 8601, specifically durations without fixed endpoints.

Direct Known Subclasses

DayTimeDuration, YearMonthDuration

Constant Summary collapse

DATATYPE =
RDF::XSD.duration
GRAMMAR =
%r(\A
  (?<si>-)?
  P(?:(?:(?:(?:(?<yr>\d+)Y)(?:(?<mo>\d+)M)?(?:(?<da>\d+)D)?)
      |  (?:(?:(?<mo>\d+)M)(?:(?<da>\d+)D)?)
      |  (?:(?<da>\d+)D)
      )
      (?:T(?:(?:(?:(?<hr>\d+)H)(?:(?<mi>\d+)M)?(?:(?<se>\d+(?:\.\d+)?)S)?)
          |  (?:(?:(?<mi>\d+)M)(?:(?<se>\d+(?:\.\d+)?)S)?)
          |  (?:(?<se>\d+(?:\.\d+)?)S)
          )
      )?
   |(?:T(?:(?:(?:(?<hr>\d+)H)(?:(?<mi>\d+)M)?(?:(?<se>\d+(?:\.\d+)?)S)?)
        |  (?:(?:(?<mi>\d+)M)(?:(?<se>\d+(?:\.\d+)?)S)?)
        |   (?:(?<se>\d+(?:\.\d+)?)S)
        )
    )
   )
\z)x.freeze

Instance Method Summary collapse

Constructor Details

#initialize(value, datatype: nil, lexical: nil, **options) ⇒ Duration

Creates a new Duration instance.

  • Given a String, parse as xsd:duration into months and seconds

  • Given a Hash containing any of :yr, :mo, :da,:hr,:miand:si`, it is transformed into months and seconds

  • Given a Rational, the result is interpreted as days, hours, minutes, and seconds.

  • Given an Integer, the result is interpreted as years and months.

  • Object representation is the Array(months, seconds)

Parameters:

  • value (Literal::Duration, Hash, Array, Literal::Numeric, #to_s)

    If provided an Array, it is the same as the object form of this literal, an array of two integers, the first of which may be negative.

  • lexical (String) (defaults to: nil)

    (nil) Supplied lexical representation of this literal, otherwise it comes from transforming value to a string form..

  • datatype (URI) (defaults to: nil)

    (nil)

  • options (Hash{Symbol => Object})

    other options passed to RDF::Literal#initialize.

Options Hash (**options):

  • :validate (Boolean) — default: false
  • :canonicalize (Boolean) — default: false


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rdf/xsd/duration.rb', line 50

def initialize(value, datatype: nil, lexical: nil, **options)
  super
  @object   = case value
  when Hash
    months = value[:yr].to_i * 12 + value[:mo].to_i
    seconds = value[:da].to_i * 3600 * 24 +
              value[:hr].to_i * 3600 +
              value[:mi].to_i * 60 +
              value[:se].to_f

    if value[:si]
      if months != 0
        months = -months
      else
        seconds = -seconds
      end
    end
    [months, seconds]
  when Rational
    [0, value * 24 * 3600]
  when Integer, ::Integer
    [value.to_i, 0]
  when Literal::Duration then value.object
  when Array then    value
  else               parse(value.to_s)
  end
end

Instance Method Details

#==(other) ⇒ Object

Returns true if self and other are durations of the same length.

From the XQuery function op:duration-equal.



174
175
176
177
178
179
# File 'lib/rdf/xsd/duration.rb', line 174

def ==(other)
  # If lexically invalid, use regular literal testing
  return super unless self.valid?

  other.is_a?(Literal::Duration) && other.valid? ? @object == other.object : super
end

#canonicalize!Literal

Converts this literal into its canonical lexical representation.



83
84
85
86
87
# File 'lib/rdf/xsd/duration.rb', line 83

def canonicalize!
  @string = @humanize = @hash = nil
  self.to_s  # side-effect
  self
end

#daysInteger

Days

From the XQuery function fn:days-from-duration.



203
# File 'lib/rdf/xsd/duration.rb', line 203

def days; Integer.new(to_h[:da] * (to_h[:si] ? -1 : 1)); end

#hoursInteger

Hours

From the XQuery function fn:hours-from-duration.



211
# File 'lib/rdf/xsd/duration.rb', line 211

def hours; Integer.new(to_h[:hr] * (to_h[:si] ? -1 : 1)); end

#humanize(lang = :en) ⇒ Object

Returns a human-readable value for the interval



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/rdf/xsd/duration.rb', line 147

def humanize(lang = :en)
  @humanize ||= {}
  @humanize[lang] ||= begin
    # Just english, for now
    return "Invalid duration #{value.to_s.inspect}" unless valid?

    md = value.match(GRAMMAR)
    ar = []
    ar << plural(md[:yr], "year") if md[:yr]
    ar << plural(md[:mo], "month") if md[:mo]
    ar << plural(md[:da], "day") if md[:da]
    ar << plural(md[:hr], "hour") if md[:hr]
    ar << plural(md[:mi], "minute") if md[:mi]
    ar << plural(md[:se], "second") if md[:se]
    last = ar.pop
    first = ar.join(" ")
    res = first.empty? ? last : "#{first} and #{last}"
    md[:si] == '-' ? "#{res} ago" : res
  end
end

#minutesInteger

Minutes

From the XQuery function fn:minutes-from-duration.



219
# File 'lib/rdf/xsd/duration.rb', line 219

def minutes; Integer.new(to_h[:mi] * (to_h[:si] ? -1 : 1)); end

#monthsInteger

Months

From the XQuery function fn:months-from-duration.



195
# File 'lib/rdf/xsd/duration.rb', line 195

def months; Integer.new(to_h[:mo] * (to_h[:si] ? -1 : 1)); end

#plural(v, str) ⇒ Object



141
142
143
# File 'lib/rdf/xsd/duration.rb', line 141

def plural(v, str)
  "#{v} #{str}#{v.to_i == 1 ? '' : 's'}" if v
end

#secondsDecimal

Seconds

From the XQuery function fn:seconds-from-duration.



227
# File 'lib/rdf/xsd/duration.rb', line 227

def seconds; Decimal.new(to_h[:se] * (to_h[:si] ? -1 : 1)); end

#to_hHash

Returns a hash representation.

Returns:

  • (Hash)


104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rdf/xsd/duration.rb', line 104

def to_h
  @hash ||= {
    si: ('-' if (@object.first == 0 ? @object.last : @object.first) < 0),
    yr: (@object.first.abs / 12),
    mo: (@object.first.abs % 12),
    da: (@object.last.abs.to_i / (3600 * 24)),
    hr: ((@object.last.abs.to_i / 3600) % 24),
    mi: ((@object.last.abs.to_i / 60) % 60),
    se: sec_str.to_f
  }
end

#to_sString

Returns the value as a string.

Returns:

  • (String)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rdf/xsd/duration.rb', line 120

def to_s
  @string ||= begin
    hash = to_h
    str = (@object.first == 0 ? @object.last : @object.first) < 0 ? '-P' : 'P'
    hash = to_h
    str << "%dY" % hash[:yr] if hash[:yr] > 0
    str << "%dM" % hash[:mo] if hash[:mo] > 0
    str << "%dD" % hash[:da] if hash[:da] > 0
    str << "T" if (hash[:hr] + hash[:mi] + hash[:se]) > 0
    str << "%dH" % hash[:hr] if hash[:hr] > 0
    str << "%dM" % hash[:mi] if hash[:mi] > 0
    str << sec_str + 'S' if hash[:se] > 0
    # Ensure some legal representation
    if str.end_with?('P')
      is_a?(Literal::YearMonthDuration) ? 'P0M' : 'PT0S'
    else
      str
    end
  end
end

#valid?Boolean

Returns true if the value adheres to the defined grammar of the datatype.

Special case for date and dateTime, for which ‘0000’ is not a valid year

Returns:

  • (Boolean)


96
97
98
# File 'lib/rdf/xsd/duration.rb', line 96

def valid?
  !!value.match?(self.class.const_get(:GRAMMAR))
end

#yearsInteger

Years

From the XQuery function fn:years-from-duration.



187
# File 'lib/rdf/xsd/duration.rb', line 187

def years; Integer.new(to_h[:yr] * (to_h[:si] ? -1 : 1)); end