class TimeOfDay
  yaml_as "tag:yaml.org,2002:timestamp#hms"
  yaml_as "tag:yaml.org,2002:time"
  yaml_as "tag:ruby.yaml.org,2002:time"
  
  attr_accessor :hour;   # 0 ‑ 23
  attr_accessor :minute; # 0 ‑ 59
  attr_accessor :second; # 0 ‑ 59
  
  def initialize(hour, minute, second = 0)
    raise "Invalid hour: #{hour}" unless hour >= 0 && hour <= 23
    raise "Invalid minute: #{minute}" unless minute >= 0 && hour <= 59
    raise "Invalid second: #{second}" unless second >= 0 && hour <= 59
    @hour, @minute, @second = hour, minute, second
  end
  
  def self.now
    Time.now.time_of_day
  end
  
  def self.parse(string)
    string.strip!
    return nil if string.empty?
    raise "Illegal time format: '#{string}'" unless string =~ /(\d{1,2}):?(\d{2})?(?::(\d{1,2}))?/
    self.new($1.to_i, $2.to_i, $3.to_i)
  end
  
  def on(date)
    Time.local(date.year, date.month, date.day, hour, minute, second)
  end
  
  def -(seconds)
    raise "Illegal argument: #{seconds.inspect}" unless seconds.is_a? Numeric
    t = Time.local(0,1,1, hour, minute, second)
    t -= seconds
    self.class.new(t.hour, t.min, t.sec)
  end
  
  def ==(other)
    return false unless other.is_a? TimeOfDay
     (self <=> other) == 0
  end
  
  def <=>(other)
    [@hour, @minute, @second] <=> [other.hour, other.minute, other.second]
  end
  
  def strftime(format)
    on(Date.today).strftime(format)
  end
  
  def to_s(with_seconds = true)
    if with_seconds
      "%02d:%02d:%02d" % [@hour, @minute, @second]
    else
      "%02d:%02d" % [@hour, @minute]
    end
  end
  
  def self.yaml_new( klass, tag, val )
    if String === val
      self.parse val
    else
      raise YAML::TypeError, "Invalid Time: " + val.inspect
    end
  end
  
  def to_yaml( opts = {} )
    YAML::quick_emit( nil, opts ) do |out|
      out.scalar( 'tag:yaml.org,2002:time', self.to_s, :plain )
    end
  end
  
end

class Time
  def time_of_day
    TimeOfDay.new(hour, min, sec)
  end
  
end

class Date
  def at(time_of_day)
    Time.local(year, month, day, time_of_day.hour, time_of_day.minute, time_of_day.second)
  end
  
end