class XDate

    def initialize(year, month, day)
	@year, @month, @day = year.to_i, month.to_i, day.to_i
    end

    attr_reader :year, :month, :day

    def to_s
	format('%04d-%02d-%02d', @year, @month, @day)
    end

    alias :inspect :to_s

    def to_time
	Time.gm(@year, @month, @day)
    end

    def to_date
	Date.new(@year, @month, @day)
    end

    def -(other)
	case other
	when XDate
	    (to_date - other.to_date)
	when Time
	    to_time - other
	when Date
            (to_date - other.to_date)
	else
	    to_date - other
	end
    end

    def +(other)
	t = to_date + other
	self.class.new(t.year, t.month, t.mday)
    end

end

class TimeNode < TerminalNode

    def initialize(date, time, zone)
	@date, @time, @zone = date, time, zone
	if :now === @date then
	    now = Time.now.utc
	    @date = XDate.new(now.year, now.month, now.day)
	    @time = ((now.hour * 60 + now.min) * 60 + Float(now.sec))
	else
	    qdays = (@time / 86400).floor
	    if not qdays.zero?
	        @date += qdays
	        @time -= (qdays * 86400)
	    end
	end
	raise TypeError unless XDate === @date
	@time = 0.0 unless @time
	raise TypeError unless Float === @time
	@zone = 0 unless @zone
	raise TypeError unless Integer === @zone
    end

    attr_reader :date, :time, :zone

    def to_s
	hr = @time.floor / 3600
	mi = (@time.floor / 60) % 60
	sc = @time % 60
	tzm = @zone.abs
	tzh = tzm / 60
	tzm %= 60
	tzh = -tzh if @zone < 0
	format("%sT%02d:%02d:%05.2f %+03d:%02d", \
	    @date.to_s, hr, mi, sc, tzh, tzm)
    end

    def self::pentad(d)
	(d > 25) ? 5 : ((d - 1) / 5)
    end

    def add_time(increment)
	inc = increment.reduce5
	case inc.name
	when 's'
	    t2 = @time + inc.factor
	    d2 = @date + (t2 / 86400)
	    t2 = t2 % 86400
	    self.class.new(d2, t2, @zone)
	when 'pentad'
	    ifac = Integer(inc.factor)
	    ipen = ifac % 6
	    imon = ifac / 6
	    spen = self.class.pentad(@date.day)
	    smon = @date.month + imon + spen / 6
	    spen = spen % 6
	    sday = spen * 5 + (@date.day - 1) % 5 + 1
	    syear = @date.year + (smon - 1) / 12
	    smon = (smon - 1) % 12 + 1
	    sdate = XDate.new(syear, smon, sday)
	    self.class.new(sdate, @time, @zone)
	else
	    raise "bad time unit '#{inc.name}'"
	end
    end

    def utcsod
	@time - @zone * 60
    end

    def div_time(units)
	base = units.ref
	inc = units.deref.reduce5
	begin
	    incname = inc.name
	rescue Exception
	    incname = "(undefined)"
	end
	case incname
	when 's'
	    dif = (@date - base.date) * 86400 + (utcsod - base.utcsod)
	    dif / inc.factor
	when 'pentad'
	    dif = (@date.year - base.date.year) * 72
	    dif += (@date.month - base.date.month) * 6
	    dif += self.class.pentad(@date.day)
	    dif -= self.class.pentad(base.date.day)
	    dif = Float(dif) if dif % inc.factor != 0
	    dif / inc.factor
	else
	    raise "bad time unit '#{incname}'"
	end
    end

end
