はじめに
「3分前」「2時間前」のような相対時刻表示、便利だけれど実装が地味に面倒だと感じたことはないでしょうか。サーバー側で「3分前」という文字列を生成してHTMLに埋め込んでしまうと、そのHTMLはアクセスした瞬間にしか正しくありません。CDNやページキャッシュを効かせた途端、1時間前にキャッシュされたページを開いたユーザーには「たった今」と表示されてしまいます。かといって、表示のたびにJavaScriptで日時を計算し直すのも、タイムゾーンやロケールを考えると意外と骨が折れる処理です。
relative-time-elementは、この悩みをHTMLの構造そのもので解決するライブラリです。GitHubが自社のUI(Issueのタイムスタンプなど)のために開発したCustom Elementで、datetime属性にISO 8601形式の日時を持たせておくだけで、ブラウザ側のJavaScriptが「3分前」「2時間前」といった表示に自動で書き換えてくれます。サーバーはキャッシュ可能な静的HTMLを返すだけでよく、相対時刻への変換はすべてクライアント側の責任になります。
とはいえ、説明より実際に動かしたほうが早いと思います。<relative-time>要素のformat属性を切り替えると表示がどう変わるか、先に触っておきたい方はこちらからどうぞ。
relative-time-elementとは
relative-time-elementは、GitHub社が開発・公開しているCustom Elements(Web Components)ライブラリです。<relative-time>というカスタムHTML要素を提供し、datetime属性で指定した日時を、Intl.DateTimeFormatとIntl.RelativeTimeFormatというブラウザ標準APIを使ってローカライズされた文字列に変換します。GitHub.com自体のIssueやコミット一覧に表示されているタイムスタンプも、このライブラリで実装されています。
主な特徴
- サーバーキャッシュと相性がよい -
datetime属性とフォールバックのテキストさえHTMLに含めておけば、あとはブラウザ側で表示を更新するため、サーバーは同じHTMLフラグメントをキャッシュしたまま配信できる - JavaScript未実行でも表示が壊れない - JSが読み込まれる前や無効な環境では、要素のテキストコンテンツ(例:
April 1, 2014)がそのまま表示される - フレームワーク非依存のCustom Elements - React・Vue・素のHTMLのどれからでも
<relative-time>タグとして扱える - relative・datetime・durationの3形式 - 同じ要素で「3日前」のような相対表示、ローカライズされた絶対日付、残り時間のカウントダウンを切り替えられる
インストール
npmから導入する場合は次のコマンドを実行します。
npm install @github/relative-time-element
導入後、エントリポイントを1回importするだけで<relative-time>要素がカスタム要素として登録されます。
import '@github/relative-time-element'
CDN経由で読み込む場合は、ESモジュール対応の配信元からimportできます。
<script type="module">
import '@github/relative-time-element'
</script>
relative-time-elementのサンプルを動かす
まずは<relative-time>要素の基本を触って確かめてみましょう。下のサンプルは、日時を選ぶ入力欄と、format属性をrelative・duration・microに切り替えるボタンを用意しています。入力欄の日時を変えたりボタンを押したりすると、同じ<relative-time>要素の表示がその場で変わります。
要点だけを抜き出すと次のようになります。
<relative-time id="rt" datetime="2024-01-01T00:00:00Z" format="relative">
January 1, 2024
</relative-time>
<script type="module">
import '@github/relative-time-element'
const rt = document.getElementById('rt')
rt.date = new Date() // datetime属性をDateオブジェクトで更新
rt.format = 'duration' // 表示形式を切り替え
</script>
実際に動かせるものが下です。日時入力を書き換えると相対時刻がその場で再計算され、relative / duration / microボタンで見た目がどう変わるかも比較できます。
rt.date = new Date(...)のようにDateオブジェクトを代入すると、内部でdatetime属性のISO文字列に変換されます。formatプロパティを書き換えるだけで、同じ日時データから「3時間前」(relative)、「2時間30分」(duration)、「2h」(micro)という異なる表示に切り替わる点が確認できるはずです。日時入力を未来の日付にすると「〜後」という表示になることも試してみてください。
基本的な使い方
<relative-time>要素の最小構成は、datetime属性とフォールバックテキストを持つHTMLだけです。
<relative-time datetime="2014-04-01T16:30:00-08:00">
April 1, 2014 4:30pm
</relative-time>
<script type="module">
import '@github/relative-time-element'
</script>
datetimeにはISO 8601形式の日時を指定します。ライブラリを読み込むと、要素のテキストが自動的に「6年前」のような相対表示に書き換わります。デフォルトのformatはrelative(内部的にはautoのエイリアス)なので、format属性を省略した場合は相対表示になります。表示は自動で更新され続けるため、ページを開いたままにしていても「たった今」から「1分前」へと表示が古びていきます。
実践的なユースケース
締切までの残り時間をカウントダウン表示する
イベントの申込締切や公開予定日など、「あとどれくらいで到達するか」を見せたい場面ではformat="duration"が向いています。tense="future"と組み合わせると、過去の日時を渡しても0秒として扱われ、マイナス表示になりません。
import '@github/relative-time-element'
const countdown = document.getElementById('countdown')
countdown.date = deadlineDate // 締切日時をDateで渡す
下のサンプルでは、締切の日時を入力欄で変更すると、<relative-time format="duration" tense="future">が「◯日, ◯時間, ◯分」という残り時間に変換して表示します。締切を過去の日時に変更すると「0分」に張り付く挙動も確認できます。
precision="minute"を指定しているため、秒単位の細かい増減は表示されず「◯時間, ◯分」までに丸められます。precisionをhourにすると分の桁も消え、大まかな残り時間だけを見せる表示に変えられます。
コメント一覧のタイムスタンプをコンパクトに表示する
コメントや通知の一覧では、1件ごとに「3時間前」とフルで書くとスペースを取りすぎることがあります。format="micro"は「3h」「2d」のような省略形で表示するモードで、GitHubの通知バッジのような密度の高い一覧に向いています。
import '@github/relative-time-element'
// 各アイテムに datetime と format="micro" を設定するだけ
el.setAttribute('datetime', isoString)
el.setAttribute('format', 'micro')
el.setAttribute('tense', 'past')
下のサンプルは、投稿時刻が異なる3件のコメントをformat="micro" tense="past"で表示しています。それぞれ「2m」「1h」「3d」のように、経過時間に応じて単位が自動で選ばれる様子が確認できます。
tense="past"を付けると、micro形式でも「2m ago」のように過去を表す短い言い回しが付きます(tenseを省略したautoだと単位だけの「2m」表示になります)。一覧のような場所ではラベルを省いた方が見やすいことが多いため、用途に応じてtenseの有無を選ぶとよいでしょう。
一定期間を過ぎたら絶対日付に切り替える
「3週間前」「2ヶ月前」のような表示は、時間が経つほど直感的に分かりにくくなります。relative-time-elementにはthresholdとprefixという属性があり、指定した期間を超えると自動的に絶対日付表示へ切り替えられます。デフォルトのthresholdはP30D(30日)ですが、任意のISO 8601 Durationで調整可能です。
import '@github/relative-time-element'
// 7日を超えたら「投稿日: 2026年8月1日」のような絶対表示に切り替わる
el.setAttribute('threshold', 'P7D')
el.setAttribute('prefix', '投稿日:')
下のサンプルはスライダーで「何日前の投稿か」を調整できます。threshold="P7D"を指定しているため、7日以内は「3日前」のような相対表示、7日を超えるとprefixで指定した「投稿日:」に続けて絶対日付が表示されるように切り替わります。
スライダーを7日目のあたりで動かすと、「7日前」という相対表示から「投稿日: ◯月◯日」という絶対表示に切り替わる境界がはっきり分かります。SNSのタイムラインやコメント欄のように、直近の投稿は相対表示で目立たせつつ、古い投稿は日付そのものを見せたい場合に有効な設定です。
まとめ
relative-time-elementは、「3分前」のような相対時刻表示を、サーバーのキャッシュ戦略を崩さずに実現できるCustom Elementライブラリです。datetime属性とフォールバックテキストだけをHTMLに埋め込んでおけば、あとはブラウザ側のIntl.RelativeTimeFormatが表示を引き受けてくれるため、JavaScriptが読み込まれる前でも壊れた見た目になりません。format属性でrelative・duration・microを切り替え、thresholdとprefixで絶対日付への切り替えタイミングを制御できる柔軟さも備えています。
日時表示のたびに自前でロケールやタイムゾーンの計算ロジックを書いていた方は、<relative-time>タグに置き換えるだけで実装がぐっとシンプルになるはずです。ぜひ手元のプロジェクトで試してみてください。
