Class: Time

Inherits:
Object show all
Defined in:
lib/cosmos/core_ext/time.rb,
lib/cosmos/io/json_rpc.rb

Overview

This file contains the COSMOS specific additions to the Ruby Time class

Time is expressed in many different ways and with many different epochs. This file supports the following formats:

Julian Date (jd or julian)
Modified Julian Date (mjd)
yds (year, day, and seconds of day)
mdy (year, month, day, hour, minute, second, us of second)
ccsds (day, ms, us from Jan 1, 1958 midnight)
time (a ruby time object with unix epoch Jan 1, 1970 midnight)
sec (seconds since an arbitrary epoch)

Constant Summary collapse

JULIAN_DAYS_PER_CENTURY =

There are 365.25 days per year because of leap years. In the Gregorian calendar some centuries have 36524 days because of the divide by 400 rule while others have 36525 days. The Julian century is DEFINED as having 36525 days.

36525.0
JULIAN_DATE_OF_JULIAN_EPOCH =

-4713/01/01 Noon

0.0
JULIAN_DATE_OF_MJD_EPOCH =

1858/11/17 Midnight

2400000.5
JULIAN_DATE_OF_GPS_EPOCH =

1980/01/06 Midnight

2444244.5
JULIAN_DATE_OF_J2000_EPOCH =

2000/01/01 Noon

2451545.0
JULIAN_DATE_OF_CCSDS_EPOCH =

1958/01/01 Midnight

2436204.5
DATE_TIME_MJD_EPOCH =
DateTime.new(1858, 11, 17)
USEC_PER_MSEC =
1000
MSEC_PER_SECOND =
1000
SEC_PER_MINUTE =
60
MINUTES_PER_HOUR =
60
HOURS_PER_DAY =
24
USEC_PER_SECOND =
USEC_PER_MSEC * MSEC_PER_SECOND
MSEC_PER_MINUTE =
60 * MSEC_PER_SECOND
MSEC_PER_HOUR =
60 * MSEC_PER_MINUTE
MSEC_PER_DAY =
HOURS_PER_DAY * MSEC_PER_HOUR
SEC_PER_HOUR =
SEC_PER_MINUTE * MINUTES_PER_HOUR
SEC_PER_DAY =
HOURS_PER_DAY * SEC_PER_HOUR
USEC_PER_DAY =
USEC_PER_SECOND * SEC_PER_DAY
MINUTES_PER_DAY =
MINUTES_PER_HOUR * HOURS_PER_DAY
USEC_PER_MSEC_FLOAT =
USEC_PER_MSEC.to_f
MSEC_PER_SECOND_FLOAT =
MSEC_PER_SECOND.to_f
SEC_PER_MINUTE_FLOAT =
SEC_PER_MINUTE.to_f
MINUTES_PER_HOUR_FLOAT =
MINUTES_PER_HOUR.to_f
HOURS_PER_DAY_FLOAT =
HOURS_PER_DAY.to_f
USEC_PER_SECOND_FLOAT =
USEC_PER_SECOND.to_f
MSEC_PER_MINUTE_FLOAT =
MSEC_PER_MINUTE.to_f
MSEC_PER_HOUR_FLOAT =
MSEC_PER_HOUR.to_f
MSEC_PER_DAY_FLOAT =
MSEC_PER_DAY.to_f
SEC_PER_HOUR_FLOAT =
SEC_PER_HOUR.to_f
SEC_PER_DAY_FLOAT =
SEC_PER_DAY.to_f
USEC_PER_DAY_FLOAT =
USEC_PER_DAY.to_f
MINUTES_PER_DAY_FLOAT =
MINUTES_PER_DAY.to_f
LeapYearMonthDays =

The number of days in each month during a leap year

[31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
CommonYearMonthDays =

The number of days in each month during a year (not a leap year)

[31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.ccsds2julian(day, ms, us) ⇒ Float

Returns The CCSDS date converted to a julian date.

Parameters:

  • day (Float)

    CCSDS day

  • ms (Integer)

    CCSDS milliseconds

  • us (Integer)

    CCSDS microseconds

Returns:

  • (Float)

    The CCSDS date converted to a julian date



334
335
336
# File 'lib/cosmos/core_ext/time.rb', line 334

def self.ccsds2julian(day, ms, us)
  (day + JULIAN_DATE_OF_CCSDS_EPOCH) + ((ms.to_f + (us / 1000.0)) / MSEC_PER_DAY_FLOAT)
end

.ccsds2mdy(day, ms, us) ⇒ Array<Year, Month, Day, Hour, Minute, Second, Microsecond>

Convert a CCSDS Date to mdy format Note that an array is returned rather than a Time object because Time objects cannot represent all possible CCSDS dates

Parameters:

  • day (Float)

    CCSDS day

  • ms (Integer)

    CCSDS milliseconds

  • us (Integer)

    CCSDS microseconds

Returns:

  • (Array<Year, Month, Day, Hour, Minute, Second, Microsecond>)

    CCSDS date converted to an array of values



297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/cosmos/core_ext/time.rb', line 297

def self.ccsds2mdy(day, ms, us)
  jdate = day + JULIAN_DATE_OF_CCSDS_EPOCH
  year, month, day, hour, minute, second, _ = julian2mdy(jdate)
  hour = (ms / MSEC_PER_HOUR).to_i
  temp = ms - (hour * MSEC_PER_HOUR)
  minute = (temp / MSEC_PER_MINUTE).to_i
  temp -= minute * MSEC_PER_MINUTE
  second = temp / MSEC_PER_SECOND
  temp -= second * MSEC_PER_SECOND
  us = us + (temp * USEC_PER_MSEC)
  return [year, month, day, hour, minute, second, us]
end

.ccsds2sec(day, ms, us, sec_epoch_jd = JULIAN_DATE_OF_CCSDS_EPOCH) ⇒ Float

Returns The number of seconds from the given epoch to the given CCSDS day, milliseconds, and microseconds.

Parameters:

  • day (Float)

    CCSDS day

  • ms (Integer)

    CCSDS milliseconds

  • us (Integer)

    CCSDS microseconds

  • sec_epoch_jd (Float) (defaults to: JULIAN_DATE_OF_CCSDS_EPOCH)

    Epoch to convert seconds from as a julian date

Returns:

  • (Float)

    The number of seconds from the given epoch to the given CCSDS day, milliseconds, and microseconds.



358
359
360
# File 'lib/cosmos/core_ext/time.rb', line 358

def self.ccsds2sec(day, ms, us, sec_epoch_jd = JULIAN_DATE_OF_CCSDS_EPOCH)
  (self.ccsds2julian(day, ms, us) - sec_epoch_jd) * SEC_PER_DAY_FLOAT
end

.days_from_j2000(time) ⇒ Float

Returns Number of julian days since Jan 1, 2000 at noon.

Parameters:

Returns:

  • (Float)

    Number of julian days since Jan 1, 2000 at noon



223
224
225
# File 'lib/cosmos/core_ext/time.rb', line 223

def self.days_from_j2000(time)
  time.to_julian - JULIAN_DATE_OF_J2000_EPOCH
end

.format_seconds(seconds) ⇒ String

Returns Seconds formatted as a human readable string with days, hours, minutes, and seconds.

Parameters:

  • seconds (Numeric)

    Total number of seconds

Returns:

  • (String)

    Seconds formatted as a human readable string with days, hours, minutes, and seconds.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/cosmos/core_ext/time.rb', line 74

def self.format_seconds(seconds)
  result = ""
  mm, ss = seconds.divmod(60)
  hh, mm = mm.divmod(60)
  dd, hh = hh.divmod(24)
  if dd != 0
    if dd == 1
      result << "%d day, " % dd
    else
      result << "%d days, " % dd
    end
  end
  if hh != 0
    if hh == 1
      result << "%d hour, " % hh
    else
      result << "%d hours, " % hh
    end
  end
  if mm != 0
    if mm == 1
      result << "%d minute, " % mm
    else
      result << "%d minutes, " % mm
    end
  end
  if ss > 0
    result << "%.2f seconds" % ss
  else
    result = result[0..-3]
  end
  result
end

.init_epoch_delta(epoch) ⇒ Float

Ruby time objects cannot handle times before the Unix Epoch. Calculate a delta (in seconds) to be used when real epochs are before the Unix Epoch. Each received timestamp will be adjusted by this delta so a ruby time object can be used to parse the time.

Parameters:

  • epoch (String)

    epoch is a string in the following format: "yyyy/mm/dd hh:mm:ss"

Returns:

  • (Float)

    unix_epohc_delta



423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
# File 'lib/cosmos/core_ext/time.rb', line 423

def self.init_epoch_delta (epoch)
  # UnixEpoch - Jan 1, 1970 00:00:00
  unix_epoch = DateTime.new(1970, 1, 1, 0, 0, 0)

  split_epoch = epoch.split
  epoch_date = split_epoch[0].split("/")
  epoch_time = split_epoch[1].split(":")

  if epoch_date[0].to_i < 1970 then
    # Calculate delta between epoch and unix epoch
    real_epoch = DateTime.new(epoch_date[0].to_i, epoch_date[1].to_i, epoch_date[2].to_i, epoch_time[0].to_i, epoch_time[1].to_i, epoch_time[2].to_i)
    day_delta = (unix_epoch - real_epoch).to_i
    unix_epoch_delta = day_delta * 86400
    if real_epoch.hour != 0 or real_epoch.min != 0 or real_epoch.sec != 0
      hour_delta = 23 - real_epoch.hour
      min_delta = 59 - real_epoch.min
      sec_delta = 60 - real_epoch.sec
      unix_epoch_delta += ((hour_delta * 3600) + (min_delta * 60) + sec_delta)
    end
  else
    unix_epoch_delta = 0
  end

  unix_epoch_delta
end

.julian2ccsds(jdate) ⇒ Array<day, ms, us>

Returns Julian converted to CCSDS.

Parameters:

  • julian (Float)

    date

Returns:

  • (Array<day, ms, us>)

    Julian converted to CCSDS



340
341
342
343
344
345
346
347
348
349
350
# File 'lib/cosmos/core_ext/time.rb', line 340

def self.julian2ccsds(jdate)
  day = jdate - JULIAN_DATE_OF_CCSDS_EPOCH
  fraction = day % 1.0
  day = day.to_i
  ms  = fraction * MSEC_PER_DAY_FLOAT
  fraction = ms % 1.0
  ms = ms.to_i
  us = fraction * USEC_PER_MSEC
  us = us.to_i
  return [day, ms, us]
end

.julian2mdy(jdate) ⇒ Array<Year, Month, Day, Hour, Minute, Second, Microsecond>

Convert a Julian Date to mdy format Note that an array is returned rather than a Time object because Time objects cannot represent all possible Julian dates

Parameters:

  • jdate (Float)

    Julian date

Returns:

  • (Array<Year, Month, Day, Hour, Minute, Second, Microsecond>)

    Julian date converted to an array of values



239
240
241
242
243
244
245
246
247
248
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
# File 'lib/cosmos/core_ext/time.rb', line 239

def self.julian2mdy(jdate)
  z = (jdate + 0.5).to_i
  w = ((z - 1867216.25) / 36524.25).to_i
  x = w / 4
  a = z + 1 + w - x
  b = a + 1524
  c = ((b - 122.1) / 365.25).to_i
  d = (365.25 * c).to_i
  e = ((b - d) / 30.6001).to_i
  f = (30.6001 * e).to_i

  day = b - d - f
  if e > 13
    month = e - 13
  else
    month = e - 1
  end
  if month > 2
    year = c - 4716
  else
    year = c - 4715
  end

  fraction = jdate - jdate.to_i

  if fraction >= 0.5
    hour = (fraction - 0.5) * 24.0
  else
    hour = (fraction * 24.0) + 12.0
  end

  fraction = hour - hour.to_i
  hour = hour.to_i

  minute = fraction * 60.0

  fraction = minute - minute.to_i
  minute = minute.to_i

  second = fraction * 60.0

  fraction = second - second.to_i
  second = second.to_i

  us = fraction * 1000000.0
  us = us.to_i

  return [year, month, day, hour, minute, second, us]
end

.julian_centuries_since_j2000(time) ⇒ Float

Returns Number of julian centuries since Jan 1, 2000 at noon.

Parameters:

Returns:

  • (Float)

    Number of julian centuries since Jan 1, 2000 at noon



229
230
231
# File 'lib/cosmos/core_ext/time.rb', line 229

def self.julian_centuries_since_j2000(time)
  self.days_from_j2000(time) / JULIAN_DAYS_PER_CENTURY
end

.leap_year?(year) ⇒ Boolean

Returns Whether the year is a leap year.

Parameters:

  • year (Integer)

Returns:

  • (Boolean)

    Whether the year is a leap year



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/cosmos/core_ext/time.rb', line 174

def self.leap_year? (year)
  return_value = false

  if (year % 4) == 0
    return_value = true

    if (year % 100) == 0
      return_value = false

      if (year % 400) == 0
        return_value = true
      end
    end
  end

  return return_value
end

.mdy2ccsds(year, month, day, hour, minute, second, us) ⇒ Array<day, ms, us>

Convert from mdy format to CCSDS Date Note that an array is used rather than a Time object because Time objects cannot represent all possible CCSDS dates

Parameters:

  • year (Integer)
  • month (Integer)
  • day (Integer)
  • hour (Integer)
  • minute (Integer)
  • second (Integer)
  • us (Integer)

Returns:

  • (Array<day, ms, us>)

    MDY converted to CCSDS



322
323
324
325
326
327
328
# File 'lib/cosmos/core_ext/time.rb', line 322

def self.mdy2ccsds(year, month, day, hour, minute, second, us)
  ms  = (hour * MSEC_PER_HOUR) + (minute * MSEC_PER_MINUTE) + (second * MSEC_PER_SECOND) + (us / USEC_PER_MSEC)
  us  = us % USEC_PER_MSEC
  jd  = Time.mdy2julian(year, month, day, 0, 0, 0, 0)
  day = (jd - JULIAN_DATE_OF_CCSDS_EPOCH).round
  return [day, ms, us]
end

.mdy2julian(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, us = 0) ⇒ Float

Convert the given year, month, day, hour, minute, second, and us into a Julian date. Julian dates are the number of days (plus fractional days) since Jan 1, 4713 BC at noon.

Parameters:

  • year (Integer)
  • month (Integer) (defaults to: 1)
  • day (Integer) (defaults to: 1)
  • hour (Integer) (defaults to: 0)
  • minute (Integer) (defaults to: 0)
  • second (Integer) (defaults to: 0)
  • us (Integer) (defaults to: 0)

Returns:

  • (Float)

    The given time as a Julian date



130
131
132
133
134
# File 'lib/cosmos/core_ext/time.rb', line 130

def self.mdy2julian(year, month=1, day=1, hour=0, minute=0, second=0, us=0)
  # Note DateTime does not support fractions of seconds
  date_time = DateTime.new(year, month, day, hour, minute, second)
  (date_time - DATE_TIME_MJD_EPOCH).to_f + JULIAN_DATE_OF_MJD_EPOCH + us / USEC_PER_DAY_FLOAT
end

.mdy2mjd(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, us = 0) ⇒ Time

Convert the given year, month, day, hour, minute, second, and us into a Modified Julian date. Modified Julian dates have an Epoch of Nov 17, 1858 at midnight.

Parameters:

  • year (Integer)
  • month (Integer) (defaults to: 1)
  • day (Integer) (defaults to: 1)
  • hour (Integer) (defaults to: 0)
  • minute (Integer) (defaults to: 0)
  • second (Integer) (defaults to: 0)
  • us (Integer) (defaults to: 0)

Returns:

  • (Time)

    The given time as a Julian date



154
155
156
# File 'lib/cosmos/core_ext/time.rb', line 154

def self.mdy2mjd(year, month=1, day=1, hour=0, minute=0, second=0, us=0)
  return Time.mdy2julian(year, month, day, hour, minute, second, us) - JULIAN_DATE_OF_MJD_EPOCH
end

.sec2ccsds(sec, sec_epoch_jd = JULIAN_DATE_OF_CCSDS_EPOCH) ⇒ Array<day, ms, us>

Returns CCSDS date.

Parameters:

  • seconds (Float)
  • sec_epoch_jd (Float) (defaults to: JULIAN_DATE_OF_CCSDS_EPOCH)

    Epoch to of seconds value

Returns:

  • (Array<day, ms, us>)

    CCSDS date



365
366
367
# File 'lib/cosmos/core_ext/time.rb', line 365

def self.sec2ccsds(sec, sec_epoch_jd = JULIAN_DATE_OF_CCSDS_EPOCH)
  self.julian2ccsds((sec / SEC_PER_DAY_FLOAT) + sec_epoch_jd)
end

.total_seconds(hour, minute, second, us) ⇒ Float

Returns The number of seconds represented by the hours, minutes, seconds and microseconds.

Parameters:

  • hour (Integer)
  • minute (Integer)
  • second (Integer)
  • us (Integer)

Returns:

  • (Float)

    The number of seconds represented by the hours, minutes, seconds and microseconds



203
204
205
# File 'lib/cosmos/core_ext/time.rb', line 203

def self.total_seconds(hour, minute, second, us)
  (hour * SEC_PER_HOUR_FLOAT) + (minute * SEC_PER_MINUTE_FLOAT) + second + (us / USEC_PER_SECOND_FLOAT)
end

.yds(year, day_of_year, sec_of_day) ⇒ Object

Create a new time object given year, day of year (1-366), and seconds of day

Parameters:

  • year (Integer)
  • day_of_year (Integer)

    (1-366)

  • sec_of_day (Integer)


168
169
170
# File 'lib/cosmos/core_ext/time.rb', line 168

def self.yds(year, day_of_year, sec_of_day)
  return Time.utc(*yds2mdy(year, day_of_year, sec_of_day))
end

.yds2julian(year, day, sec) ⇒ Float

Returns Year, day, seconds converted to the Julian date.

Parameters:

  • year (Integer)

    Year

  • day (Integer)

    Day of the year

  • sec (Integer)

    Seconds in the day

Returns:

  • (Float)

    Year, day, seconds converted to the Julian date



413
414
415
416
# File 'lib/cosmos/core_ext/time.rb', line 413

def self.yds2julian(year, day, sec)
  year, month, day, hour, min, seconds, usec = self.yds2mdy(year, day, sec)
  Time.mdy2julian(year, month, day, hour, min, seconds, usec)
end

.yds2mdy(year, day, sec) ⇒ Array

Returns [year, month, day, hour, minute, second, usec].

Parameters:

  • year (Integer)

    Year

  • day (Integer)

    Day of the year

  • sec (Float)

    Seconds in the day

Returns:

  • (Array)
    year, month, day, hour, minute, second, usec


373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/cosmos/core_ext/time.rb', line 373

def self.yds2mdy(year, day, sec)
  # Convert day of year (1-366) to day of month (1-31)
  if self.leap_year?(year)
    array = Time::LeapYearMonthDays
  else
    array = Time::CommonYearMonthDays
  end

  month = 1
  array.each do |days|
    if (day - days) >= 1
      day   -= days
      month += 1
    else
      break
    end
  end

  # Calculate hour of day (0-23)
  hour = (sec / SEC_PER_HOUR).to_i
  sec -= (hour * SEC_PER_HOUR).to_f

  # Calculate minute of hour (0-59)
  min  = (sec / SEC_PER_MINUTE).to_i
  sec -= (min * SEC_PER_MINUTE).to_f

  # Calculate second of minute (0-60)
  seconds = sec.to_i
  sec -= seconds.to_f

  # Calculate useconds of second (0-999999)
  usec = (sec * 1000000.0).to_i

  return [year, month, day, hour, min, seconds, usec]
end

Instance Method Details

#as_json(options = nil) ⇒ Object

:nodoc:



88
89
90
# File 'lib/cosmos/io/json_rpc.rb', line 88

def as_json(options = nil) #:nodoc:
  to_json(options).remove_quotes
end

#formatted(include_year = true, fractional_digits = 3) ⇒ String

Returns Date formatted as YYYY/MM/DD HH:MM:SS.US.

Returns:

  • (String)

    Date formatted as YYYY/MM/DD HH:MM:SS.US



213
214
215
216
217
218
219
# File 'lib/cosmos/core_ext/time.rb', line 213

def formatted(include_year = true, fractional_digits = 3)
  if include_year
    self.strftime("%Y/%m/%d %H:%M:%S.%#{fractional_digits}N")
  else
    self.strftime("%H:%M:%S.%#{fractional_digits}N")
  end
end

#leap_year?Boolean

Returns Whether the year is a leap year.

Returns:

  • (Boolean)

    Whether the year is a leap year



193
194
195
# File 'lib/cosmos/core_ext/time.rb', line 193

def leap_year?
  Time.leap_year?(self.year)
end

#seconds_of_dayFloat

Returns The number of seconds in the day (0-86399.99).

Returns:

  • (Float)

    The number of seconds in the day (0-86399.99)



208
209
210
# File 'lib/cosmos/core_ext/time.rb', line 208

def seconds_of_day
  Time.total_seconds(self.hour, self.min, self.sec, self.usec)
end

#to_julianFloat Also known as: to_jd

Returns The Time converted to a julian date.

Returns:

  • (Float)

    The Time converted to a julian date



137
138
139
# File 'lib/cosmos/core_ext/time.rb', line 137

def to_julian
  return Time.mdy2julian(self.year, self.month, self.day, self.hour, self.min, self.sec, self.usec)
end

#to_mjdObject

Convert a time object to the modified julian date



159
160
161
# File 'lib/cosmos/core_ext/time.rb', line 159

def to_mjd
  return Time.mdy2mjd(self.year, self.month, self.day, self.hour, self.min, self.sec, self.usec)
end