class MarsDateTime

Constants

EpochCE
EpochMCE
FAKE_MSEC_PER_MYEAR
JulianDay1
MSEC_PER_DAY
MSEC_PER_SOL
Months
SOLS_PER_MYEAR
TimeStretch
VERSION
Week

no month 0

Attributes

day_of_week[R]
dow[R]
epoch_sol[R]
hr[R]
mems[R]
mhrs[R]
min[R]
mmin[R]
month[R]
msec[R]
myear[R]
sec[R]
shr[R]
smin[R]
sol[R]
ssec[R]
year[R]
year_sol[R]

Public Class Methods

leap?(myear) click to toggle source
# File lib/marsdate.rb, line 44
def self.leap?(myear)    # class method for convenience
  return (myear % 2 == 1) if (myear % 10 != 0)
  return true if (myear % 1000 == 0)
  return false if (myear % 100 == 0)
  return true
end
long?(myear) click to toggle source
# File lib/marsdate.rb, line 55
def self.long?(myear)  # long year
  leap?(myear)
end
new(*params) click to toggle source
# File lib/marsdate.rb, line 115
def initialize(*params)
  n = params.size
  case n
    when 3..6
      init_yms(*params)
    when 0
      init_datetime(DateTime.now)
    when 1
      case params.first
        when Integer, Float
          init_mems(params.first)
        when Date
          init_date(params.first)
        when DateTime
          init_datetime(params.first)
      else
        raise "Expected number or DateTime" 
      end
    else
      raise "Bad params: #{params.inspect}"
  end
  compute_stretched
end
now() click to toggle source
# File lib/marsdate.rb, line 65
def self.now
  d = DateTime.now
  MarsDateTime.new(d)
end
short?(myear) click to toggle source
# File lib/marsdate.rb, line 51
def self.short?(myear)  # short year
  !leap?(myear)
end
sols_in_month(m, year) click to toggle source
# File lib/marsdate.rb, line 59
def self.sols_in_month(m, year)
  return 28 if m < 24
  return 25 if leap?(year)
  return 24
end
today() click to toggle source
# File lib/marsdate.rb, line 70
def self.today
  d = Date.today
  MarsDateTime.new(d)
end

Public Instance Methods

+(sols) click to toggle source
# File lib/marsdate.rb, line 241
def +(sols)  # FIXME? sols or secs?
  millisec = sols * MSEC_PER_SOL
  MarsDateTime.new(@mems + millisec)
end
-(other) click to toggle source
# File lib/marsdate.rb, line 225
def -(other)  # FIXME? sols or secs?
  case other
    when MarsDateTime
      diff = @mems - other.mems
      diff.to_f / MSEC_PER_SOL
    when DateTime
      other = MarsDateTime.new(other)
      diff = @mems - other.mems
      diff.to_f / MSEC_PER_SOL
    when Integer, Float
      self + (-other)
  else
    raise "Unexpected data type"
  end
end
<=>(other) click to toggle source
# File lib/marsdate.rb, line 246
def <=>(other)
  case other
    when MarsDateTime
      @mems <=> other.mems
    when DateTime
      @mems <=> MarsDateTime.new(other).mems
  else
    raise "Invalid comparison"
  end
end
check_ymshms(my, mm, msol, mhr=0, mmin=0, msec=0) click to toggle source
# File lib/marsdate.rb, line 139
def check_ymshms(my, mm, msol, mhr=0, mmin=0, msec=0)
  text = ""
  text << "year #{my} is not an integer\n" unless my.is_a? Integer
  text << "month #{mm} is out of range" unless (1..24).include? mm
  text << "sol #{msol} is out of range" unless (1..28).include? msol
  text << "hour #{mhr} is out of range" unless (0..24).include? mhr
  text << "minute #{mmin} is out of range" unless (0..59).include? mmin
  text << "second #{msec} is out of range" unless (0..59).include? msec
  if !MarsDateTime.leap?(my) && mm == 24 && msol > 24
    text << "sol #{msol} is invalid in a non-leap year" 
  end
  raise text unless text.empty?
end
compute_stretched() click to toggle source
# File lib/marsdate.rb, line 167
def compute_stretched
  # Handle stretched time...
  sec = @mhrs*3600 + @mmin*60 + @msec
  sec /= TimeStretch
  @shr,  sec = sec.divmod(3600)
  @smin, sec = sec.divmod(60)
  @ssec = sec.round
end
earth_date() click to toggle source
# File lib/marsdate.rb, line 257
def earth_date
  secs = @mems/1000
  days,secs = secs.divmod(86400)
  hrs, secs = secs.divmod(3600)
  min, secs = secs.divmod(60)
  jday = days + JulianDay1
  DateTime.jd(jday, hrs, min, secs)
end
init_date(date) click to toggle source
# File lib/marsdate.rb, line 208
def init_date(date)
  dt = date.to_datetime
  days = dt.jd - JulianDay1
  secs = days*86400 + dt.hour*3600 + dt.min*60 + dt.sec
  init_mems(secs*1000)
end
init_datetime(dt) click to toggle source
# File lib/marsdate.rb, line 215
def init_datetime(dt)
  days = dt.jd - JulianDay1
  secs = days*86400 + dt.hour*3600 + dt.min*60 + dt.sec
  init_mems(secs*1000)
end
init_mems(mems) click to toggle source
# File lib/marsdate.rb, line 176
def init_mems(mems)
  # Note: The sol length is off by 0.09 msec -- to properly fix this
  # will require measuring in microseconds so as to avoid floating-point math.
  # The "round" calls below were experimental and were "mostly" successful.
  full_years = 0
  loop do
    millisec = FAKE_MSEC_PER_MYEAR
    millisec += MSEC_PER_SOL if MarsDateTime.leap?(full_years+1)
    break if mems < millisec
    mems -= millisec
    # puts "Subtracting #{millisec} -- one full year => #{mems}"
    full_years += 1
  end

  mspm = MSEC_PER_SOL*28
  full_months,mems = mems.divmod(mspm)
  full_days, mems  = mems.divmod(MSEC_PER_SOL)
  full_hrs, mems   = mems.divmod(3_600_000)
  full_min, mems   = mems.divmod(60_000)

  sec = mems/1000.0
  my = full_years  + 1  # 1-based
  mm = full_months + 1
  ms = full_days   + 1
  mhr = full_hrs        # 0-based
  mmin = full_min
  msec = sec.to_i
  frac = sec - msec     # fraction of a sec

  init_yms(my, mm, ms, mhr, mmin, msec)
end
init_yms(my, mm, msol, mhr=0, mmin=0, msec=0) click to toggle source
# File lib/marsdate.rb, line 153
  def init_yms(my, mm, msol, mhr=0, mmin=0, msec=0)
    check_ymshms(my, mm, msol, mhr, mmin, msec)
    zsol = msol - 1  # z means zero-based
    zmy  = my - 1    # my means Martian year
    zesol = zmy*668 + leaps(my-1) + (mm-1)*28 + zsol
#   @mems is "Martian (time since) epoch in milliseconds"
    @mems = zesol*MSEC_PER_SOL + (mhr*3600 + mmin*60 + msec)*1000
    @year, @month, @sol, @mhrs, @mmin, @msec = my, mm, msol, mhr, mmin, msec
    @epoch_sol = zesol + 1
    @dow = (@epoch_sol-1) % 7
    @day_of_week = Week[@dow]
    @year_sol  = (mm-1)*28 + msol
  end
inspect() click to toggle source
# File lib/marsdate.rb, line 85
def inspect
  time = ('%02d' % @mhrs) + ":" + ('%02d' % @mmin) + ":" + ('%02d' % @msec)
  "#@year/#{'%02d' % @month}/#{'%02d' % @sol} " + 
  "(#@year_sol, #@epoch_sol) #@day_of_week " +
  time
end
leap?() click to toggle source
# File lib/marsdate.rb, line 97
def leap?
  MarsDateTime.leap?(@year)    # DRY
end
leaps(myr) click to toggle source
# File lib/marsdate.rb, line 75
def leaps(myr)
  n = 0
  1.upto(myr) {|i| n+=1 if MarsDateTime.leap?(i) } 
  n
end
long?() click to toggle source
# File lib/marsdate.rb, line 105
def long?
  leap?
end
month_name() click to toggle source
# File lib/marsdate.rb, line 109
def month_name
  Months[@month]
end
short?() click to toggle source
# File lib/marsdate.rb, line 101
def short?
  ! leap?
end
strftime(fmt) click to toggle source
# File lib/marsdate.rb, line 266
def strftime(fmt)
  str = fmt.dup
  pieces = str.scan(/(%.|[^%]+)/).flatten
  final = ""
  zmonth = '%02d' % @month
  zsol = '%02d' % @sol
  zhh = '%02d' % @mhrs  # stretched
  zmm = '%02d' % @mmin
  zss = '%02d' % @msec
  zhc = '%02d' % @shr   # canonical
  zmc = '%02d' % @smin
  zsc = '%02d' % @ssec
 
  pieces.each do |piece|
    case piece
      when "%a"; final << @day_of_week[0..2]
      when "%A"; final << @day_of_week
      when "%b"; final << (@month.odd? ? month_name[2..4] : month_name[0..2])
      when "%B"; final << month_name
      when "%d"; final << zsol
      when "%e"; final << ('%2d' % @sol)
      when "%F"; final << "#@year-#{zmonth}-#{zsol}"
      when "%H"; final << zhh
      when "%j"; final << @year_sol.to_s
      when "%m"; final << zmonth  # @month.to_s
      when "%M"; final << zmm
      when "%s"; final << @msec.to_s  # was: (@mems*1000).to_i.to_s
      when "%S"; final << zss
      when "%u"; final << (@dow + 1).to_s
      when "%U"; final << (@year_sol/7 + 1).to_s
      when "%w"; final << @dow.to_s
      when "%x"; final << "#@year/#{zmonth}/#{zsol}"
      when "%X"; final << "#{zhh}:#{zmm}:#{zss}"
      when "%Y"; final << @year.to_s
      when "%n"; final << "\n"
      when "%t"; final << "\t"
      when "%%"; final << "%"
      when "%P"; final << ("%02d" % @shr)
      when "%Q"; final << ("%02d" % @smin)
      when "%R"; final << ("%02d" % @ssec)
      else
        final << piece
    end
  end
  final
end
to_s() click to toggle source
# File lib/marsdate.rb, line 92
def to_s
  time = self.strftime('%H:%M:%S [%P:%Q:%R]')
  "#@day_of_week, #{Months[@month]} #@sol, #@year at #{time}"
end
to_yaml_properties() click to toggle source
# File lib/marsdate.rb, line 81
def to_yaml_properties
  %w[@myear @month @sol @epoch_sol @year_sol @dow @day_of_week @msme @mhrs @mmin @msec]
end
ymshms() click to toggle source
# File lib/marsdate.rb, line 221
def ymshms
  [@year, @month, @sol, @mhrs, @mmin, @msec]
end