ツイートIDからツイート時刻をミリ秒まで算出（ついでに時刻からツイートIDを作成）

#ツイートIDの算出
TwitterのID算出には **snowflake** というツールが用いられて _いました_ （2014/5 に今後更新しない旨が GitHub のレポジトリの README に記載されています）。ただし現状もこの算出方法に基づいて ID が生成されている _はず_ です。
[twitter/snowflake](https://github.com/twitter/snowflake/)
このツールの解説がありましたので、IDに付いての解説部分を引用します。

>snowflakeではlong値(64bit)としてIDを表現します。実際は、unsigned long / signed long いずれの環境でも特に細工なく使用できるようにするためか、63bitの範囲で表現しているようです。
bit layoutは以下。
>![timestamp: 41bit / machine id: 10bit / sequence: 12bit](http://developer.smartnews.com/blog/wp-content/uploads/2013/07/bit_layout2.png)

>* timestamp(41bit) : 現在のunixtime(milliseconds)から、ある時点のunixtimeを引いた値
>* machine id(10bit) : 生成器ごとに割り当てられたID
>* sequence(12bit) : 生成器ごとに採番するsequence番号

>ざっくり言うと、全体としての一意性はtimestampとmachine idで担保し、生成器内での一意性はsequenceで担保している、という設計になってます。
（中略）
snowflakeでは以下抜粋したコードで示すように現在日時から1288834974657を引いた値を扱っています。

>[軽量なTime-based ID生成器”shakeflake(仮称)”について | SmartNews開発者ブログ](http://developer.smartnews.be/blog/2013/07/31/shakeflake-is-a-tool-for-generating-unique-id-numbers/)

その他の解説

* [Twitterのsnowflakeについて](http://www.slideshare.net/moaikids/20130901-snowflake/17) (slideshare) <br>上の引用では図が消えちゃったので値の配置はこちらのスライドで確認してください。

#ツイートIDから時刻を算出する`tweet_id2time`メソッド
ツイートIDの算出を逆に演算すれば時刻を算出できます。
手順としては

1. IDからtimestamp部分のみを切り出す。<br>
下の22bitを落とします。すると、基準時刻からの現在時刻（ミリ秒）が求められます。
1. 差っ引かれていた値を加算。<br>
1288834974657(ミリ秒)を足してやります。
1. 秒に直すために1000.0で割ります。<br>
なお、1000で割ると整数演算になるのでミリ秒部分が落とされてしまいます。

```ruby
def tweet_id2time(id)
  Time.at(((id.to_i >> 22) + 1288834974657) / 1000.0)
end
```
##引数idのクラスをチェックするバージョン
```rb
def tweet_id2time(id)
  case id
  when Integer
    Time.at(((id >> 22) + 1288834974657) / 1000.0)
  else
    nil
  end
end
```

#実例
次のツイートの時刻を算出してみます。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/riocampos?ref_src=twsrc%5Etfw">@riocampos</a> 時刻表示テスト。6/23 22:22:22。</p>&mdash; りおかんぽす (@riocampos) <a href="https://twitter.com/riocampos/status/481064722297397249?ref_src=twsrc%5Etfw">June 23, 2014</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
このツイートのIDは481064722297397249です。

```ruby
tweet_id2time(481064722297397249).strftime("%Y-%m-%d %H:%M:%S.%L %Z")
#=> "2014-06-23 22:22:22.203 JST"
```
ミリ秒は`Time#strftime("%L")`で表示できます。

#時刻から疑似ツイートIDを作成する`time2tweet_id`メソッド
疑似ツイートIDであり、正確に言うと **ミリ秒単位にみた同一時刻におけるツイートIDの下限値** です。

```rb
def time2tweet_id(time)
  (time.to_f * 1000 - 1288834974657).to_i << 22
end
```
この疑似ツイートIDはTwitter gemのメソッドの引数に使う`:since_id`の値の初期値として使うことが出来ます。メソッドとしては例えば`Twitter::REST::Search#search`, `Twitter::REST::Timelines#home_timeline`, `Twitter::REST::Timelines#user_timeline`などが挙げられます。

#疑似ツイートIDと時刻との関係
ちなみに疑似ツイートID`time2tweet_id(time)`と時刻`time`との関係は
`time2tweet_id(time)` $≦$ `id` $<$ `time2tweet_id(time+0.001)` $=$ `time2tweet_id(time) + (1<<22)`
を満たします（なお`1<<22`は`2**22`であり4194304）。

確認してみましょう。先ほどのツイートのID 481064722297397249 を使ってみます。`tweet_id2time`メソッドを使うと、先ほどのツイートの時刻は`tweet_id2time(481064722297397249)`になります。

```rb
id = 481064722297397249
time2tweet_id(tweet_id2time(id)) <= id
#=> true
id < time2tweet_id(tweet_id2time(id) + 0.001)
#=> true
time2tweet_id(tweet_id2time(id) + 0.001) == (time2tweet_id(tweet_id2time(id)) + (1<<22))
#=> true
```
先ほどの関係式が確かに成り立っています。

#関連
[TwitterのステータスIDからつぶやかれた時刻を抽出する方法 - Qiita](http://qiita.com/eiichiroi/items/c3c330a8d9e1dc1bcca8)