Twitterでは、APIから簡単に取得できる投稿時間というのはcreated_atというキーで取得できるのですが、この値は「秒」までしか取得できないために、ミリ秒まで取得できる方法としてIDから取得する方法があります。このIDの形式はSnowflakeというのですが、今回はそれのDiscordバージョンのお話。(私も大雑把にしか知らないので間違ってたら申し訳ないです)

DiscordのID

Discordでも、ID管理はSnowflakeで行っているようです。実際にそれがわかるのはDiscordのDeveloperドキュメント

画像はチャンネルのデータですが、実際にTypeの欄を見るとidがSnowflakeで管理されていることがわかります。
また、API Referenceにも以下のように記載がありました。

Discord utilizes Twitter's snowflake format for uniquely identifiable descriptors (IDs). These IDs are guaranteed to be unique across all of Discord, except in some unique scenarios in which child objects share their parent's ID. Because Snowflake IDs are up to 64 bits in size (e.g. a uint64), they are always returned as strings in the HTTP API to prevent integer overflows in some languages. See Gateway ETF/JSON for more information regarding Gateway encoding.

実際にDiscordのIDをミリ秒レベルの時間に変えてみる

Snowflakeで管理されていることがわかったので、Twitterと同じ考えで適当に変換してみたら以下のようなデータになってしまいました。
2014/03/17 00:50:35.653
2018/05/13の23:10頃に動作させたので、Twitter方式のSnowflakeのデコードの仕方だとダメなのかとDiscordのAPI Referenceを読み漁ってるときちんと記載がありました。

この内容のとおりに、足す値を変更して「1420070400000」にしたところきちんと動作しました。

実際にプログラムとして動作させる

変換する際のプログラム(PHP)。メモとしてTwitterのも置いておきます。
わかりやすいように細かく分けて書いているので、短くしたい場合はコメント消したりして短くしてみてください。
ビット演算については、Qiitaのmpywさんの記事などがわかりやすいと思います。
※32bitのPHPなどでは正常に動作しません!PHP_INT_SIZEなどを確認して32bitか64bitかどうかを確認してから利用してください!

Discord

function SnowflakeToTimeDate_Discord($id, $format = "Y/m/d H:i:s.v") {
    $rightshift = $id >> 22; // 22ビット右にシフト
    $unixtime = ($rightshift + 1420070400000) / 1000.000; // 1420070400000足してミリ単位から秒に直すために1000で割る→UnixTime
    $datetime = DateTime::createFromFormat("U.u", $unixtime); //UnixTimeからDateTimeオブジェクトを作成
    $datetime->setTimeZone(new DateTimeZone("Asia/Tokyo")); // タイムゾーンをAsia/Tokyoにする

    return $datetime->format($format); // 返却
}
SnowflakeToTimeDate_Discord("221991565567066112"); // 2016/09/04 22:55:13.635 (わたしのDiscordアカウント作成時間)

Twitter

function SnowflakeToTimeDate_Twitter($id, $format = "Y/m/d H:i:s.v") {
    $rightshift = $id >> 22; // 22ビット右にシフト
    $unixtime = ($rightshift + 1288834974657) / 1000.000; // 1288834974657足してミリ単位から秒に直すために1000で割る→UnixTime
    $datetime = DateTime::createFromFormat("U.u", $unixtime); //UnixTimeからDateTimeオブジェクトを作成
    $datetime->setTimeZone(new DateTimeZone("Asia/Tokyo")); // タイムゾーンをAsia/Tokyoにする

    return $datetime->format($format); // 返却
}
SnowflakeToTimeDate_Twitter("995667705884696600"); // 2018/05/13 23:10:54.351 (testツイートのツイート時間)

最後に

DiscordのDeveloperReferenceのタイトルと一部文章が日本語化されているみたいです。私個人としては英語のままのほうが意味の変化とかがないからそのほうがいいしこういうデベロッパーページ系で日本語化って結構かっこ悪いな…と思っている人なのでうーんって感じですね…。

参考

Categories: 日記