class Fugit::Duration

Constants

KEYS
NON_INFLA_KEYS
SECOND_ROUND

Round float seconds to 9 decimals when deflating

Attributes

h[R]
options[R]
original[R]

Public Class Methods

common_rewrite_dur(t) click to toggle source
# File lib/fugit/duration.rb, line 51
def common_rewrite_dur(t)

  t
    .subgather(nil)
    .inject({}) { |h, tt|
      v = tt.string; v = v.index('.') ? v.to_f : v.to_i
        # drops ending ("y", "m", ...) by itself
      h[tt.name] = (h[tt.name] || 0) + v
      h }
end
do_parse(s, opts={}) click to toggle source
# File lib/fugit/duration.rb, line 41
def do_parse(s, opts={})

  parse(s, opts) ||
  fail(ArgumentError.new("not a duration #{s.inspect}"))
end
new(s) click to toggle source
# File lib/fugit/duration.rb, line 11
def new(s)

  parse(s)
end
parse(s, opts={}) click to toggle source
# File lib/fugit/duration.rb, line 16
      def parse(s, opts={})

        return s if s.is_a?(self)

        original = s

        s = "#{s}s" if s.is_a?(Numeric)

        return nil unless s.is_a?(String)

        s = s.strip
#p [ original, s ]; Raabro.pp(Parser.parse(s, debug: 3), colours: true)

        h =
          if opts[:iso]
            IsoParser.parse(opts[:stricter] ? s : s.upcase)
          elsif opts[:plain]
            Parser.parse(s)
          else
            Parser.parse(s) || IsoParser.parse(opts[:stricter] ? s : s.upcase)
          end

        h ? self.allocate.send(:init, original, opts, h) : nil
      end
to_iso_s(o) click to toggle source
# File lib/fugit/duration.rb, line 48
def to_iso_s(o); do_parse(o).deflate.to_iso_s; end
to_long_s(o, opts={}) click to toggle source
# File lib/fugit/duration.rb, line 49
def to_long_s(o, opts={}); do_parse(o).deflate.to_long_s(opts); end
to_plain_s(o) click to toggle source
# File lib/fugit/duration.rb, line 47
def to_plain_s(o); do_parse(o).deflate.to_plain_s; end

Public Instance Methods

+(a)
Alias for: add
-(a)
Alias for: subtract
-@()
Alias for: opposite
==(o) click to toggle source
# File lib/fugit/duration.rb, line 279
def ==(o)

  o.is_a?(Fugit::Duration) && o.h == @h
end
Also aliased as: eql?
add(a) click to toggle source
# File lib/fugit/duration.rb, line 253
def add(a)

  case a
  when Numeric then add_numeric(a)
  when Fugit::Duration then add_duration(a)
  when String then add_duration(self.class.parse(a))
  when ::Time, EtOrbi::EoTime then add_to_time(a)
  else fail ArgumentError.new(
    "cannot add #{a.class} instance to a Fugit::Duration")
  end
end
Also aliased as: +
add_duration(d) click to toggle source
# File lib/fugit/duration.rb, line 214
def add_duration(d)

  params = d.h.inject(@h.dup) { |h, (k, v)| h[k] = (h[k] || 0) + v; h }

  self.class.allocate.init(nil, {}, params)
end
add_numeric(n) click to toggle source
# File lib/fugit/duration.rb, line 206
def add_numeric(n)

  h = @h.dup
  h[:sec] = (h[:sec] || 0) + n.to_i

  self.class.allocate.init(nil,{}, h)
end
add_to_time(t) click to toggle source
# File lib/fugit/duration.rb, line 221
def add_to_time(t)

  t = ::EtOrbi.make_time(t)

  INFLA_KEYS.each do |k, a|

    v = @h[k]; next unless v

    t = t + v * a[:s]
  end

  NON_INFLA_KEYS.each do |k, a|

    v = @h[k]; next unless v
    at = [ t.year, t.month, t.day, t.hour, t.min, t.sec ]

    at[a[:x]] += v

    if at[1] > 12
      n, m = at[1] / 12, at[1] % 12
      at[0], at[1] = at[0] + n, m
    elsif at[1] < 1
      n, m = -at[1] / 12, -at[1] % 12
      at[0], at[1] = at[0] - n, m
    end

    t = ::EtOrbi.make_time(at, t.zone)
  end

  t
end
deflate(options={}) click to toggle source
# File lib/fugit/duration.rb, line 161
def deflate(options={})

  id = inflate
  h = id.h.dup
  s = h.delete(:sec) || 0

  keys = INFLA_KEYS

  mon = options[:month]
  yea = options[:year]
  keys = keys.dup if mon || yea

  if mon
    mon = 30 if mon == true
    mon = "#{mon}d" if mon.is_a?(Integer)
    keys.unshift([ :mon, { s: Fugit::Duration.parse(mon).to_sec } ])
  end
  if yea
    yea = 365 if yea == true
    yea = "#{yea}d" if yea.is_a?(Integer)
    keys.unshift([ :yea, { s: Fugit::Duration.parse(yea).to_sec } ])
  end

  keys[0..-2].each do |k, v|

    vs = v[:s]; next if s < vs

    h[k] = (h[k] || 0) + s.to_i / vs
    s = s % vs
  end

  h[:sec] = s.is_a?(Integer) ? s : s.round(SECOND_ROUND)

  self.class.allocate.init(@original, {}, h)
end
drop_seconds() click to toggle source

Returns a copy of this duration, omitting its seconds.

# File lib/fugit/duration.rb, line 297
def drop_seconds

  h = @h.dup
  h.delete(:sec)
  h[:min] = 0 if h.empty?

  self.class.allocate.init(nil, { literal: true }, h)
end
eql?(o)
Alias for: ==
hash() click to toggle source
# File lib/fugit/duration.rb, line 285
def hash

  @h.hash
end
inflate() click to toggle source
# File lib/fugit/duration.rb, line 141
def inflate

  params =
    @h.inject({ sec: 0 }) { |h, (k, v)|
      a = KEYS[k]
      if a[:I]
        h[:sec] += (v * a[:s])
      else
        h[k] = v
      end
      h
    }

  self.class.allocate.init(@original, {}, params)
end
next_time(from=::EtOrbi::EoTime.now) click to toggle source
# File lib/fugit/duration.rb, line 290
def next_time(from=::EtOrbi::EoTime.now)

  add(from)
end
opposite() click to toggle source
# File lib/fugit/duration.rb, line 197
def opposite

  params = @h.inject({}) { |h, (k, v)| h[k] = -v; h }

  self.class.allocate.init(nil, {}, params)
end
Also aliased as: -@
subtract(a) click to toggle source
# File lib/fugit/duration.rb, line 266
def subtract(a)

  case a
  when Numeric then add_numeric(-a)
  when Fugit::Duration then add_duration(-a)
  when String then add_duration(-self.class.parse(a))
  when ::Time, ::EtOrbi::EoTime then add_to_time(a)
  else fail ArgumentError.new(
    "cannot subtract #{a.class} instance to a Fugit::Duration")
  end
end
Also aliased as: -
to_h() click to toggle source

For now, let's alias to h

# File lib/fugit/duration.rb, line 126
def to_h; h; end
to_iso_s() click to toggle source
# File lib/fugit/duration.rb, line 88
def to_iso_s

  t = false

  s = StringIO.new
  s << 'P'

  KEYS.each_with_index do |(k, a), i|
    v = @h[k]; next unless v
    if i > 3 && t == false
      t = true
      s << 'T'
    end
    s << v.to_s; s << a[:i]
  end

  s.string
end
to_long_s(opts={}) click to toggle source
# File lib/fugit/duration.rb, line 107
def to_long_s(opts={})

  s = StringIO.new
  adn = [ false, 'no' ].include?(opts[:oxford]) ? ' and ' : ', and '

  a = @h.to_a
  while kv = a.shift
    k, v = kv
    aa = KEYS[k]
    s << v.to_i
    s << ' '; s << aa[:l]; s << 's' if v > 1
    s << (a.size == 1 ? adn : ', ') if a.size > 0
  end

  s.string
end
to_plain_s() click to toggle source
# File lib/fugit/duration.rb, line 85
def to_plain_s; _to_s(:a); end
to_rufus_h() click to toggle source
# File lib/fugit/duration.rb, line 128
def to_rufus_h

  KEYS.inject({}) { |h, (ks, kh)| v = @h[ks]; h[kh[:r].to_sym] = v if v; h }
end
to_rufus_s() click to toggle source
# File lib/fugit/duration.rb, line 86
def to_rufus_s; _to_s(:r); end
to_sec() click to toggle source

Warning: this is an “approximation”, months are 30 days and years are 365 days, …

# File lib/fugit/duration.rb, line 136
def to_sec

  KEYS.inject(0) { |s, (k, a)| v = @h[k]; next s unless v; s += v * a[:s] }
end

Protected Instance Methods

_to_s(key) click to toggle source
# File lib/fugit/duration.rb, line 74
def _to_s(key)

  KEYS.inject([ StringIO.new, '+' ]) { |(s, sign), (k, a)|
    v = @h[k]
    next [ s, sign ] unless v
    sign1 = v < 0 ? '-' : '+'
    s << (sign1 != sign ? sign1 : '') << v.abs.to_s << a[key]
    [ s, sign1 ]
  }[0].string
end
init(original, options, h) click to toggle source
# File lib/fugit/duration.rb, line 308
def init(original, options, h)

  @original = original
  @options = options

  if options[:literal]
    @h = h
  else
    @h = h.reject { |k, v| v == 0 }
    @h[:sec] = 0 if @h.empty?
  end

  self
end