始まり
それは2024年12月29日日曜日の午前中に起きた。
それまで問題なかったGoogle Cloud Storage(Amazon S3互換のクラウドストレージ; 以下GCS)へのアクセスが出来なくなったのだ。
エラーはRequestTimeTooSkewed。これはリクエストを発行した時刻とGCS側のサーバの時刻が15分以上乖離しているときに生じるエラーだ。
なぜ?
調査
"RequestTimeTooSkewed"で検索してみると、大体が「あんたのマシンの時刻がズレてるだけだからNTP1を正しく設定しなさい」と宣っているのであった。
でも、当方のサーバはNTPについては正しく設定されており、時刻はなんならミリ秒単位で正確だ。
時刻は合っているのになぜ?
そこで、もう一度エラーメッセージを読み返してみると…
なんと、こちらのリクエストの日付が2025年12月29日(1年後の日付)になっているではあーりませんか!
そんな馬鹿な…。
判明した原因
実はGCSとのやりとりには自分で作成したプログラム2を使用していました。そのプログラムを書くために使った言語(Swift)の特性上、リクエスト日時のフォーマッティングにはUTS#35で定められているDate Formatを用いていました。
コードにすると次のような感じです:
let s3DateTimeFormatter = DateFormatter()
s3DateTimeFormatter.locale = Locale(identifier: "en_US")
s3DateTimeFormatter.timeZone = TimeZone(secondsFromGMT: 0)
s3DateTimeFormatter.dateFormat = "YYYYMMdd'T'HHmmss'Z'"
これ何が問題か分かりますか?
問題はYYYYの部分です。
結論から言うと、YYYY(大文字)ではなくyyyy(小文字)にしなくてはいけません。
YYYY(大文字)についてはUTS#35にこう書かれています:
Year in “Week of Year” based calendars in which the year transition occurs on a week boundary; may differ from calendar year ‘y’ near a year transition.
(YOCKOW拙訳)
週の境界によって年の遷移が起こるカレンダーに基づく「暦週」における年。年が変わる前後では暦年である'y'と異なる場合がある。
これは即ち、YYYY(大文字)はISO 8601が言うところの暦週における年を指しているということなのです。strftimeでの%Gと同じことです。
どういうことかというと、年末の最終週は翌年として扱われる場合があるということになります。
実際、2024年12月29日日曜日午前9時(JST)以降YYYY(大文字)は2025となり、結果として冒頭に書いたようなエラーが起きたわけでした。
修正
というわけで、YYYY(大文字)をyyyy(小文字)に直すだけで一件落着。
ちなみに、Appleが公開しているData Formatting GuideのDate Formattersにも次のように明記されていました:
A common mistake is to use
YYYY.yyyyspecifies the calendar year whereasYYYYspecifies the year (of “Week of Year”), used in the ISO year-week calendar. In most cases,yyyyandYYYYyield the same number, however they may be different. Typically you should use the calendar year.(YOCKOW拙訳)
(年部分を表すのに)よくある間違いとしてはYYYYを使うということがある。yyyyは暦年を示す一方でYYYYはISOの暦週を用いた年を示す。ほとんどの場合、yyyyとYYYYは同じ数値となるが、異なる場合もある。典型的には暦年を用いるべきである。
結論
YYYYではなくyyyyを使おう!
(というか、暦週の年って誰が使うの?)