Stardates

in   Code   , ,

Stardates are common usage in the Star Trek universe. You’d easily hear Captain Picard talking in the intro scene, “Captain’s log, stardate 41325.9”. These combinations of numbers are meant to give the viewer a sense of the future, without explicitly stating the exact timeframe in which the show occurs. Of course, we know that Star Trek: The Next Generation is set in the 24th century, but that’s beside the point.

Here, I’m going to talk about the stardates shown in the header of the posts. These are meant to give me some semblance of futuristic activity, however, they are tied to very real and very current dates. In the field of astronomy, observations are recorded using a number format similar to stardates, however, these are called Julian days . In short, like Unix time is the number of seconds since the Unix epoch on January 1st, 1970, midnight GMT, Julian days are the number of days since the epoch at noon GMT on January 1st, 4713 BC in the Proleptic Gregorian Calendar . The current Julian day, as of this writing is 2456333. Most observations record the time with an additional degree of precision by adding a fractional portion of the day. So, for example, it’s now 2456333.5.

Obviously, one could take this and extend it ad infinitum, but we’re not going to do that. For the purpose of this blog, it’s quite sufficient to maintain time precision of about two and a half hours. In addition, it’s not necessary to display the full Julian day, but truncate the most significant two digits. That, is, I’d display the current time as 56333.5 (Feb 9th, 2013, 17:59 PST).

This is easily done in Ruby with the following code:

Stardate
1
2
t = Time.now
s = format('%5.9f', t.strftime('%s').to_f / 86400.0 + 40587.5)[0..6]

The '%5.9f' syntax should be straightforward to anybody using Python or Ruby, as this is the recommended way to use printf style format specifiers to format strings. The t.strftime{'%s'}.to_f returns the current Unix timestamp as a floating point number, and divinding by 86400 gives us the number of days since the Unix epoch. The last portion, + 40587.5, gives us the number to add to get the current Julian day, or fraction thereof. Finally, the [0..6] portion tells Ruby to only return the first 7 characters of the string. This way, I gain the added precision of using multiple digits after the decimal point but truncating instead of rounding to prevent overflow. You see, 2440587.5 is the Julian day timestamp at midnight GMT on January 1st, 1970. So, by adding the number of days since the epoch to 40587.5, I get exactly what I want, the current Julian day (minus 2400000, of course).

The astute observer would see that this is not quite perfect, I’d get a rollover when the counter reaches 99999.9. Sure, but I’m not too worried about that. The rollover will happen on August 31st, 2132 at noon GMT, which is well beyond my lifetime, and by which point, we may even be using some other form of timekeeping other than the Julian calendar.

The even more astute observer would notice that this calculation doesn’t account for leap seconds. Again, I don’t care about them. The rounding off means that a second here or there doesn’t matter; each 10th of a day is 8640 seconds, or 2 hours and 24 minutes.

Now, how do we get this to work in Octopress? The syntax is straightforward, we can tweak the date plugin as shown below.

diff --git a/plugins/date.rb b/plugins/date.rb
index 49fb79a..af660df 100644
--- a/plugins/date.rb
+++ b/plugins/date.rb
@@ -29,15 +29,27 @@ module Octopress
       end
     end
 
+    def stardate(date)
+      date = datetime(date).strftime('%s').to_f
+      format("%5.9f", date / 86400.0 + 40587.5)[0..6]
+    end
+
     # Formats date either as ordinal or by given date format
     # Adds %o as ordinal representation of the day
     def format_date(date, format)
       date = datetime(date)
       if format.nil? || format.empty? || format == "ordinal"
         date_formatted = ordinalize(date)
       else
         date_formatted = date.strftime(format)
         date_formatted.gsub!(/%o/, ordinal(date.strftime('%e').to_i))
+        date_formatted.gsub!(/%J/, stardate(date)
       end
       date_formatted
     end

Like the original date plugin uses the %o syntax to generate ordinal representations of days, the stardate representation uses the %J syntax. Since neither of these are in use by strftime, we can repurpose them for our own use.