Class: MarsDateTime

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

Constant Summary collapse

VERSION =
"1.1.7"
MSEC_PER_SOL =

Really + 0.09

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
M-January   Gemini      M-February Cancer
M-March     Leo         M-April    Virgo 
M-May       Libra       M-June     Scorpio 
M-July      Sagittarius M-August   Capricorn 
M-September Aquarius    M-October  Pisces 
M-November  Aries       M-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)
JulianDay1 =

Jan 1, 1 AD

1721445

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*params) ⇒ MarsDateTime

Returns a new instance of MarsDateTime.



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 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

Instance Attribute Details

#day_of_weekObject (readonly)

Returns the value of attribute day_of_week.



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

def day_of_week
  @day_of_week
end

#dowObject (readonly)

Returns the value of attribute dow.



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

def dow
  @dow
end

#epoch_solObject (readonly)

Returns the value of attribute epoch_sol.



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

def epoch_sol
  @epoch_sol
end

#memsObject (readonly)

Returns the value of attribute mems.



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

def mems
  @mems
end

#mhrsObject (readonly) Also known as: hr

Returns the value of attribute mhrs.



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

def mhrs
  @mhrs
end

#mminObject (readonly) Also known as: min

Returns the value of attribute mmin.



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

def mmin
  @mmin
end

#monthObject (readonly)

Returns the value of attribute month.



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

def month
  @month
end

#msecObject (readonly) Also known as: sec

Returns the value of attribute msec.



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

def msec
  @msec
end

#shrObject (readonly)

stretched time



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

def shr
  @shr
end

#sminObject (readonly)

stretched time



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

def smin
  @smin
end

#solObject (readonly)

Returns the value of attribute sol.



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

def sol
  @sol
end

#ssecObject (readonly)

stretched time



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

def ssec
  @ssec
end

#yearObject (readonly) Also known as: myear

Returns the value of attribute year.



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

def year
  @year
end

#year_solObject (readonly)

Returns the value of attribute year_sol.



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

def year_sol
  @year_sol
end

Class Method Details

.leap?(myear) ⇒ Boolean

class method for convenience

Returns:

  • (Boolean)


44
45
46
47
48
49
# 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) ⇒ Boolean

long year

Returns:

  • (Boolean)


55
56
57
# File 'lib/marsdate.rb', line 55

def self.long?(myear)  # long year
  leap?(myear)
end

.nowObject



65
66
67
68
# File 'lib/marsdate.rb', line 65

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

.short?(myear) ⇒ Boolean

short year

Returns:

  • (Boolean)


51
52
53
# File 'lib/marsdate.rb', line 51

def self.short?(myear)  # short year
  !leap?(myear)
end

.sols_in_month(m, year) ⇒ Object



59
60
61
62
63
# 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

.todayObject



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

def self.today
  d = Date.today
  MarsDateTime.new(d)
end

Instance Method Details

#+(sols) ⇒ Object

FIXME? sols or secs?



241
242
243
244
# File 'lib/marsdate.rb', line 241

def +(sols)  # FIXME? sols or secs?
  millisec = sols * MSEC_PER_SOL
  MarsDateTime.new(@mems + millisec)
end

#-(other) ⇒ Object

FIXME? sols or secs?



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# 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) ⇒ Object



246
247
248
249
250
251
252
253
254
255
# 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) ⇒ Object



139
140
141
142
143
144
145
146
147
148
149
150
151
# 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_stretchedObject



167
168
169
170
171
172
173
174
# 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_dateObject



257
258
259
260
261
262
263
264
# 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) ⇒ Object



208
209
210
211
212
213
# 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) ⇒ Object



215
216
217
218
219
# 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) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# 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) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
# 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

#inspectObject



85
86
87
88
89
90
# 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?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/marsdate.rb', line 97

def leap?
  MarsDateTime.leap?(@year)    # DRY
end

#leaps(myr) ⇒ Object



75
76
77
78
79
# File 'lib/marsdate.rb', line 75

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

#long?Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/marsdate.rb', line 105

def long?
  leap?
end

#month_nameObject



109
110
111
# File 'lib/marsdate.rb', line 109

def month_name
  Months[@month]
end

#short?Boolean

Returns:

  • (Boolean)


101
102
103
# File 'lib/marsdate.rb', line 101

def short?
  ! leap?
end

#strftime(fmt) ⇒ Object



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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# 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_sObject



92
93
94
95
# 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_propertiesObject



81
82
83
# 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

#ymshmsObject



221
222
223
# File 'lib/marsdate.rb', line 221

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