別のタイムゾーンの公開日時をフォーマットするには、元の時点を保持し、フォーマッタに変換先のタイムゾーンを指定します。このMacのターミナルガイドでは、表示済みの文字列を解析したり、手作業で時間を加算したりせずに、読みやすい公開日時ラベルを作成します。

変換前に必要な情報は何ですか?

Macで文書を準備していて、合成した公開タイムスタンプ 2025-07-15T00:30:00Z を受け取ったとします。America/Los_Angeles で表示される公開日時が必要です。目的は、変換先の暦日と時刻を使って同じ瞬間を表すことです。

必要な情報がそろっているかどうかは、次の4つの用語で判断できます。

用語 合成した例 この作業における意味
暦日 2025-07-15 時刻やタイムゾーンを含まない暦上の日
時点 2025-07-15T00:30:00Z が表す瞬間 保持する時点
UTCオフセット -07:00 特定の時点における現地時刻とUTCとの差
名前付きタイムゾーン America/Los_Angeles 適用されるオフセットを決める規則

RFC 3339では、完全な日付、時刻、UTCオフセットを含むタイムスタンプが定義されています。RFC 9557では、さらに詳しい違いが説明されています。名前付きタイムゾーンは時点をオフセットに対応付ける規則を提供しますが、タイムスタンプの数値オフセットだけでは、別の時刻における場所のオフセットの振る舞いは分かりません。RFC 3339, Section 5.6、RFC 9557, Section 1.2。

変換を始める前に、次の入力判断を行ってください。

  • Foundationの Date がすでにある場合: 変換先のタイムゾーンを設定したフォーマッタに、そのまま渡します。
  • Z または明示的な数値オフセットを含むタイムスタンプがある場合: 元のタイムスタンプを一度解析し、得られた時点をフォーマットします。
  • 暦日しかない場合: 作業で時刻とタイムゾーンが明示されていない限り、日付のまま保持します。
  • オフセットのない現地の日付と時刻がある場合: 時点として扱う前に、元のタイムゾーンを確認し、あいまいさを解消します。

たとえば、2025-07-15 だけでは、公開が別のタイムゾーンで真夜中の前と後のどちらに行われたか分かりません。真夜中を割り当てると、推測を持ち込むことになります。同様に、オフセットが切り替わる前後では、現地の時刻が0個または複数の時点に対応することがあります。RFC 9557では、この境界が説明されています。すでに確定した時点を変換すれば、この逆引きの問題を避けられます。RFC 9557, Section 1.2。

なぜ表示文字列を再解析しないほうがよいのですか?

AppleはFoundationの Date を、暦やタイムゾーンとは独立した特定の時点として定義しています。別のタイムゾーンで表示するために、別の Date に変える必要はありません。テキストを生成するために必要な暦とタイムゾーンは、フォーマット時に指定します。Date。

表示用のラベルには情報が含まれていないことがあります。時刻を省いて時点を日付としてフォーマットし、そのラベルを解析しても、省略された時刻は復元できません。Appleのフォーマットに関するドキュメントでは、日付だけの値を往復変換して真夜中を再構築する例が示されています。真夜中が意図されている場合には便利ですが、任意の公開時点は保持できません。Date.FormatStyle。

次の順序で処理してください。

  1. 元のタイムスタンプを読み取るか、既存の Date を取得します。
  2. その時点を処理対象の値として保持します。
  3. 変換先のタイムゾーン、暦、表示ロケールを選びます。
  4. 保持した時点を最終ラベルにフォーマットします。

オフセットを手作業で加算すると、作業の内容が変わります。Appleは addingTimeInterval(_:) を経過時間を加えて新しい日付を作成するものとして説明しています。したがって7時間を加えると別の時点になりますが、フォーマッタのタイムゾーンを選ぶと、元の時点の表現が変わります。Date。

Macで公開時刻の例を最後まで実行する

この例には、Appleの Date.FormatStyle APIが利用できるmacOS 12以降と、動作するSwiftツールチェーンが必要です。追加パッケージは不要で、Foundationを使います。Date.FormatStyle。

ターミナルを開き、ツールチェーンを確認します。

swift --version

Appleのコマンドラインツールが必要な場合は、次のコマンドを実行してインストールダイアログを完了してください。Appleはこのインストール方法を案内しており、Xcodeのフルインストールにはコマンドラインツールが含まれると説明しています。その後、もう一度バージョンを確認します。コマンドラインツールのインストール。

xcode-select --install

TextEditで新規書類を作成し、フォーマット > 標準テキストにする を選びます。以下のSwiftプログラム全体を貼り付け、デスクトップに publication-time.swift として保存します。ファイル名の末尾が .swift になり、.txt 拡張子が追加されていないことを確認してください。AppleのTextEditガイドには、標準テキスト書類の作成、変換、保存方法が記載されています。MacのTextEditで書類を作成、開く、変換する。

このプログラムは、指定された秒単位のタイムスタンプを受け取り、一度解析して、同じ instant から3種類の表現を作成します。Appleは、ISO 8601形式の表現を解析、生成するための ISO8601DateFormatter と、RFC 3339形式に対応する withInternetDateTime オプションについて説明しています。ISO8601DateFormatter、withInternetDateTime。

import Foundation

// Synthetic publication data. Change these two values as needed.
let sourceTimestamp = "2025-07-15T00:30:00Z"
let destinationIdentifier = "America/Los_Angeles"

enum ConversionError: Error {
    case invalidTimestamp(String)
    case unknownTimeZone(String)
}

let inputFormatter = ISO8601DateFormatter()
inputFormatter.formatOptions = [.withInternetDateTime]
inputFormatter.timeZone = TimeZone(secondsFromGMT: 0)

guard let instant = inputFormatter.date(from: sourceTimestamp) else {
    throw ConversionError.invalidTimestamp(sourceTimestamp)
}

guard let destination = TimeZone(identifier: destinationIdentifier) else {
    throw ConversionError.unknownTimeZone(destinationIdentifier)
}

let destinationFormatter = ISO8601DateFormatter()
destinationFormatter.formatOptions = [.withInternetDateTime]
destinationFormatter.timeZone = destination

let publicationLabelStyle = Date.FormatStyle(
    date: .long,
    time: .complete,
    locale: Locale(identifier: "en_GB"),
    calendar: Calendar(identifier: .gregorian),
    timeZone: destination
)

let publicationDateStyle = Date.FormatStyle(
    date: .long,
    time: .omitted,
    locale: Locale(identifier: "en_GB"),
    calendar: Calendar(identifier: .gregorian),
    timeZone: destination
)

print("Source instant: \(inputFormatter.string(from: instant))")
print("Destination zone: \(destination.identifier)")
print("Destination timestamp: \(destinationFormatter.string(from: instant))")
print("Publication label: \(instant.formatted(publicationLabelStyle))")
print("Publication date: \(instant.formatted(publicationDateStyle))")

保存したプログラムを実行します。

swift "$HOME/Desktop/publication-time.swift"

指定した値の場合、変換先のタイムスタンプは 2025-07-14T17:30:00-07:00 となり、公開日は2025年7月14日を表します。読みやすいラベルには時刻とタイムゾーンの情報も含まれます。具体的な文言や句読点はフォーマットシステムによって決まります。

Swiftは研究環境で利用できなかったため、プログラムは取得したAPIドキュメントと照合しましたが、実行していません。数値による変換例は、Pythonの日付、時刻、タイムゾーンAPIを使って別途確認しました。

結果を決める設定はどれですか?

フォーマッタの初期化子では、ロケール、暦、タイムゾーンを個別に設定できます。ここでは、en_GB でイギリス英語の書式規則を指定し、グレゴリオ暦で日付体系を決め、destination で現地の日付と時刻を決めます。別の変換先が必要な場合はタイムゾーン識別子を、別の表示規則が必要な場合はロケールを変更します。init(date:time:locale:calendar:timeZone:capitalizationContext:)。

日付のみの結果も、引き続き instant から直接作成されます。time: .omitted を設定すると、出力ラベルから時刻が除かれます。時点を真夜中に設定し直す必要はありません。

日付が真夜中をまたぐ場合と季節によるオフセットを確認する

意図した処理が維持されているか、次の合成例で確認してください。説明用の公開タイムスタンプと、計算した変換先の表現です。

元の時点 変換先のタイムゾーン 変換先での表現
2025-01-15T00:30:00Z America/Los_Angeles 2025-01-14T16:30:00-08:00
2025-07-15T00:30:00Z America/Los_Angeles 2025-07-14T17:30:00-07:00
2025-07-15T00:30:00Z Asia/Tokyo 2025-07-15T09:30:00+09:00
2025-07-15T23:30:00Z Asia/Tokyo 2025-07-16T08:30:00+09:00

最初の2行は、名前付きの変換先に固定の時間調整では不十分な理由を示しています。適用されるオフセットは時点によって異なる場合があります。最後の2行は、時刻だけでなく、変換先の暦日全体も導き出す必要がある理由を示しています。公開日時は前日、同日、または翌日に表示されることがあります。

名前付きタイムゾーンは、タイムゾーンデータベースの規則を使います。IANAによると、macOSは同データベースを使用し、その更新は通常、OSのアップデートを通じて利用者に届きます。マシンによって結果が異なる場合は、元のタイムスタンプを変更する前に、タイムゾーン識別子と利用可能なアップデートを確認してください。タイムゾーンと夏時間のデータ。

入力や出力が想定と異なる場合はどうしますか?

次の順で確認してください。

  1. 元の値が時点を特定していることを確認します。 Z または数値オフセットを保持してください。日付だけの値や現地時刻には、追加情報が必要です。
  2. 変換先の識別子を確認します。 America/Los_Angeles のような識別子を使います。Appleの初期化子は不明な識別子に対して nil を返します。このプログラムは別のタイムゾーンに置き換えず、エラーで停止します。
  3. タイムスタンプが対応する形式か確認します。 この例は、秒単位のインターネットタイムスタンプ用に設定されています。小数秒や別の入力形式を使うには、適切なパーサー設定が必要です。変更時も元の入力は保持してください。
  4. 変換先の数値タイムスタンプを比較します。 表示上の句読点を調べる前に、日付、時刻、オフセットを使って変換を評価します。
  5. すべての出力が元の instant を使っていることを確認します。 ラベルをフォーマットし、そのラベルを次の変換のために解析する中間処理があれば取り除いてください。init(identifier:)、ISO8601DateFormatter。

RFC 9557では、名前付きタイムゾーンとオフセットを拡張タイムスタンプに併記することもできます。ただし、その規格が存在するからといって、この例のパーサーが角括弧付きのタイムゾーン注釈に対応しているとは限りません。拡張形式の入力経路を意図的に実装し、検証するのでない限り、上記で使う別個の destinationIdentifier 値に変換先の識別子を保持してください。RFC 9557。

公開日時ラベルでは、元の時点を再利用可能な値として保持し、各変換先のテキストをそこから生成します。これにより、変換先のタイムゾーン、表示言語、表示する詳細を変更しても、すべてのラベルを同じ公開時点に結び付けられます。

出典