Class: MarsDateTime

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/marsdate.rb

Constant Summary collapse

VERSION =
"1.0.6"
MSEC_PER_SOL =
88775244
SOLS_PER_MYEAR =
668.5921
MSEC_PER_DAY =
86400000
FAKE_MSEC_PER_MYEAR =
(668*MSEC_PER_SOL)
TimeStretch =
MSEC_PER_SOL/MSEC_PER_DAY.to_f
Months =
%w[ UNDEFINED
January   Gemini      February Cancer
March     Leo         April    Virgo 
May       Libra       June     Scorpio 
July      Sagittarius August   Capricorn 
September Aquarius    October  Pisces 
November  Aries       December Taurus ]
Week =

no month 0

%w[ Sunday Monday Tuesday Wednesday Thursday Friday Saturday ]
EpochMCE =
DateTime.new(1,1,21)
EpochCE =
DateTime.new(1,1,1)
FudgeOffset =
67784 + 44000 + 1099 + 87 - 56
JulianDay1 =

was …24

1721443

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*params) ⇒ MarsDateTime

Returns a new instance of MarsDateTime.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/marsdate.rb', line 100

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 DateTime
          init_datetime(params.first)
      else
        raise "Expected number or DateTime" 
      end
    else
      raise "Bad params: #{params.inspect}"
  end
  compute_stretched
end

Instance Attribute Details

#day_of_weekObject (readonly)

Returns the value of attribute day_of_week.



37
38
39
# File 'lib/marsdate.rb', line 37

def day_of_week
  @day_of_week
end

#dowObject (readonly)

Returns the value of attribute dow.



37
38
39
# File 'lib/marsdate.rb', line 37

def dow
  @dow
end

#epoch_solObject (readonly)

Returns the value of attribute epoch_sol.



33
34
35
# File 'lib/marsdate.rb', line 33

def epoch_sol
  @epoch_sol
end

#memsObject (readonly)

Returns the value of attribute mems.



35
36
37
# File 'lib/marsdate.rb', line 35

def mems
  @mems
end

#mhrsObject (readonly) Also known as: hr

Returns the value of attribute mhrs.



38
39
40
# File 'lib/marsdate.rb', line 38

def mhrs
  @mhrs
end

#mminObject (readonly) Also known as: min

Returns the value of attribute mmin.



38
39
40
# File 'lib/marsdate.rb', line 38

def mmin
  @mmin
end

#monthObject (readonly)

Returns the value of attribute month.



33
34
35
# File 'lib/marsdate.rb', line 33

def month
  @month
end

#msecObject (readonly) Also known as: sec

Returns the value of attribute msec.



38
39
40
# File 'lib/marsdate.rb', line 38

def msec
  @msec
end

#shrObject (readonly)

stretched time



34
35
36
# File 'lib/marsdate.rb', line 34

def shr
  @shr
end

#sminObject (readonly)

stretched time



34
35
36
# File 'lib/marsdate.rb', line 34

def smin
  @smin
end

#solObject (readonly)

Returns the value of attribute sol.



33
34
35
# File 'lib/marsdate.rb', line 33

def sol
  @sol
end

#ssecObject (readonly)

stretched time



34
35
36
# File 'lib/marsdate.rb', line 34

def ssec
  @ssec
end

#yearObject (readonly) Also known as: myear

Returns the value of attribute year.



33
34
35
# File 'lib/marsdate.rb', line 33

def year
  @year
end

#year_solObject (readonly)

Returns the value of attribute year_sol.



33
34
35
# File 'lib/marsdate.rb', line 33

def year_sol
  @year_sol
end

Class Method Details

.leap?(myear) ⇒ Boolean

class method for convenience

Returns:

  • (Boolean)


45
46
47
48
49
50
# File 'lib/marsdate.rb', line 45

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

.nowObject



58
59
60
61
# File 'lib/marsdate.rb', line 58

def self.now
  d = DateTime.now
  MarsDateTime.new(d)
end

.sols_in_month(m, year) ⇒ Object



52
53
54
55
56
# File 'lib/marsdate.rb', line 52

def self.sols_in_month(m, year)
  return 28 if m < 24
  return 25 if leap?(year)
  return 24
end

.todayObject



63
64
65
66
# File 'lib/marsdate.rb', line 63

def self.today
  d = DateTime.now
  MarsDateTime.new(d)
end

Instance Method Details

#+(sols) ⇒ Object



224
225
226
227
# File 'lib/marsdate.rb', line 224

def +(sols)
  millisec = sols * MSEC_PER_SOL
  MarsDateTime.new(@mems + millisec)
end

#-(other) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/marsdate.rb', line 208

def -(other)
  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 Fixnum, Float
      self + (-other)
  else
    raise "Unexpected data type"
  end
end

#<=>(other) ⇒ Object



229
230
231
232
233
234
235
236
237
238
# File 'lib/marsdate.rb', line 229

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) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/marsdate.rb', line 122

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? Fixnum
  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 !leap?(my) && mm == 24 && msol > 24
    text << "sol #{msol} is invalid in a non-leap year" 
  end
  raise text unless text.empty?
end

#compute_stretchedObject



150
151
152
153
154
155
156
157
# File 'lib/marsdate.rb', line 150

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_dateObject



240
241
242
243
244
245
246
247
# File 'lib/marsdate.rb', line 240

def earth_date
  secs = @mems/1000 + FudgeOffset
  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_datetime(dt) ⇒ Object



197
198
199
200
201
202
# File 'lib/marsdate.rb', line 197

def init_datetime(dt)
  days = dt.jd - JulianDay1
  secs = days*86400 + dt.hour*3600 + dt.min*60 + dt.sec
  secs -= FudgeOffset
  init_mems(secs*1000)
end

#init_mems(mems) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/marsdate.rb', line 159

def init_mems(mems)
  full_years = 0
  loop do
    millisec = FAKE_MSEC_PER_MYEAR
    millisec += MSEC_PER_SOL if 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

#    # check for leap year...
#    if mm == 24
#      max = leap?(my) ? 25 : 24
#      diff = ms - max
#      if diff > 0
#        my, mm, ms = my+1, 1, diff
#      end
#    end

  init_yms(my, mm, ms, mhr, mmin, msec)
end

#init_yms(my, mm, msol, mhr = 0, mmin = 0, msec = 0) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/marsdate.rb', line 136

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

#inspectObject



78
79
80
81
82
83
# File 'lib/marsdate.rb', line 78

def inspect
  time = ('%02d' % @mhrs) + ":" + ('%02d' % @mmin) + ":" + ('%02d' % @msec)
  "#@year/#{'%02d' % @month}/#{'%02d' % @sol} " + 
  "(#@year_sol, #@epoch_sol) #@day_of_week " +
  time
end

#leap?(myear) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/marsdate.rb', line 90

def leap?(myear)
  MarsDateTime.leap?(myear)    # DRY
end

#leaps(myr) ⇒ Object



68
69
70
71
72
# File 'lib/marsdate.rb', line 68

def leaps(myr)
  n = 0
  1.upto(myr) {|i| n+=1 if leap?(i) } 
  n
end

#month_nameObject



94
95
96
# File 'lib/marsdate.rb', line 94

def month_name
  Months[@month]
end

#strftime(fmt) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/marsdate.rb', line 249

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_name[0..2]
      when "%B"; final << month_name
      when "%d"; final << zsol
      when "%e"; final << ('%2d' % @sol)
      when "%F"; final << "#@year-#@month-#@sol"
      when "%H"; final << zhh
      when "%j"; final << @year_sol.to_s
      when "%m"; final << @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_sObject



85
86
87
88
# File 'lib/marsdate.rb', line 85

def to_s
  time = self.strftime('%H:%M:%S [%P:%Q:%R]')
  "#@day_of_week, #{Months[@month]} #@sol, #@year at #{time}"
end

#to_yaml_propertiesObject



74
75
76
# File 'lib/marsdate.rb', line 74

def to_yaml_properties
  %w[@myear @month @sol @epoch_sol @year_sol @dow @day_of_week @msme @mhrs @mmin @msec]
end

#ymshmsObject



204
205
206
# File 'lib/marsdate.rb', line 204

def ymshms
  [@year, @month, @sol, @mhrs, @mmin, @msec]
end