日付と時刻を扱うのは簡単だと思うかもしれません。 1 分は 60 秒、1 時間は 60 分、1 日は 24 時間、1 週間は 7 日、1 月は 28 ~ 31 日などとなります。
確かに、ここではロケット科学は必要ありません…
そうですね、これ以上真実からかけ離れたものはありません!
アプリケーションやバックエンドの開発中に遭遇する可能性のある日付と時刻に関連した罠や落とし穴を示します。
測定単位を最小単位から最大単位まで見ていきましょう。
日常的に使用される最小単位は秒です。これは Unix 時間の基準でもあります。
ただし、Java などの一部のプログラミング言語では、System.currentTimeMillis() メソッドなどのように、最も一般的な単位はミリ秒 (1/1000 秒) です。
この相違により多くのエラーが発生する可能性があります。
システムの外部から数値を受け取った場合、ドキュメントを読んでも、その数値がどのような測定単位を使用しているのかが一見しただけでは明確にならない可能性があります。
SMS および MMS コンテンツ プロバイダー (データベース) 列の DATE フィールドを確認します。どちらのドキュメントにも次のことしか書かれていません:
メッセージを受信した日付。
型: INTEGER (long)
ただし、SMS はミリ秒を使用しますが、MMS は秒を使用します。驚いた?まあ、特にそのような API が別の人によって設計されている場合には、それが起こる可能性があります。
このような場合にはどう対処すればよいでしょうか?そして、どうすればそれらを回避できるでしょうか?
幸いなことに、いくつかの一般的なルールを定式化できます。
受信データの形式を常に確認してください。
ドキュメントが間違っているか、古い可能性があるため、実際に確認してください。通常、開発中にエラーを探すと、5 万年先の日付などのエラーを見つけるのは非常に簡単です。この結果は、たとえばミリ秒が秒として扱われる場合に発生します。
値を送信する側に影響力がある場合 (例: システムが設計中であり、まだ誰も使用していない場合)、標準化されたテキスト形式を検討してください。
ここでは、一部のカスタム形式ではなく、標準化 (ISO 8601 など) に重点を置きます (このトピックは後で詳しく説明します)。数年も経てば、元のチームの誰もそのプロジェクトで働かなくなる可能性があり、テキスト形式は新しい開発者にとってドキュメントを見る必要がないため簡単に理解できます。
ただし、パフォーマンスが重要なアプライアンスでは数値形式の方が優れている可能性があります。
**日付/時刻/期間の値を扱うには、生の整数ではなく専用のクラスを使用します。
**Java の世界には、Instant や Duration などの便利なクラスが多数含まれた java.time パッケージがあります。たとえば、という整数がある場合。 eventDuration は、秒かミリ秒かを保存するか不明です。あるいは、もしかしたら数日も保存されるのでしょうか?
従来のコードなど、完全にリファクタリングできない生の値 (整数、long など) を扱う必要がある場合は、変数/フィールド名に測定単位を含めます .
たとえば、eventDurationSeconds は明確です。
おそらく閏年について聞いたことがあるでしょう。 「通常の」ものとは異なり、さらに 1 日長いです。うるう秒もあります!
うるう秒以外の秒よりも長いですか?
そうですね、それは状況によります!
まず、少し理論から始めましょう。地球は減速しています。実際には、これは哲学的な発言ではなく、科学的に証明された事実です。それはデルタ-Tと呼ばれます。
それで、これは実際に何を意味するのでしょうか?さて、私たちの時間の測定単位には標準化された長さがあります。基本単位は秒で、次のように定義されます。
セシウム 133 原子の基本的な摂動のない基底状態の 2 つの超微細準位間の遷移に対応する放射の 9 192 631 770 周期の継続時間。 (出典: bipm.org)
その期間は一定であり、秒から派生した他のすべての単位に通知されます。 1 分は 60 秒、1 時間は 60 分 (3,600 秒) などとなります。
しかし、地球の速度が遅くなる(そして日が長くなる)場合、私たちは何らかの方法でその速度の低下に対応して、私たちの単位で測定される時間が現実と一致するようにする必要があります。これは、余分な秒、つまりうるう秒を挿入することによって行われます。
うるう秒に利用できる枠は、年間で 6 月末と 12 月の 2 つだけです。最後の日とは、最終日 (それぞれ 30 日または 31 日) の 23:59:59 UTC 直後 (現地時間は異なります) を意味します。
通常最後の秒の後に、次の日に移る前に追加のうるう秒が挿入されます。したがって、23:59:60 (1 分は 0 から数えて 61 秒) になります。
速度の低下が一定ではないため、うるう秒は不規則に挿入されます。最新のもの(この記事を書いている2021年4月時点)は2016年12月に発生したもので、もう4年以上前になります。ただし、最後から 2 番目の日付は 2015 年 6 月であり、この 2 つの期間の差はわずか 1 年半です。
物理的な壁時計や一部のフレームワーク API など、時間を設定できる場所の一部では、うるう秒を観察したり設定したりする機能がない場合があります。
たとえば、昔ながらの Date Java クラスは、閏 2 秒もサポートしています。範囲は 0 から 61 までなので、62 秒も可能です。
ただし、java.time パッケージの最新の Instant クラスは、うるう秒をプログラマに公開しません。うるう秒は、その日の最後の 1,000 秒にわたって均等に延長されます (これらの秒はより長くなります)。
理論上、うるう秒は負の値になる可能性があることに注意してください。しかし、今のところそれは起こっていません。
これは、1 分が 59 秒で構成される可能性があることを意味し、大きな問題が発生する可能性があります。たとえば、何らかのアクションが 23:59:59.001 に実行されるようにスケジュールされているが、希望の時間が存在しないことが判明した場合…
幸いなことに、表示される秒数を拡大するのと同様に、プログラマに対して完全に透過的に縮小することもできます。
61 番目の秒が存在する可能性があることはわかっていますが、62 番目の秒はどうなるでしょうか?さて、ドキュメントには次のように書かれています:
同じ 1 分間に 2 つのうるう秒が発生する可能性は非常に低いですが、この仕様は ISO C の日付と時刻の規則に従っています。
確かに、C 仕様では [0, 61] の範囲があります。しかし、なぜ? 1992 年に tzdata (タイムゾーン データベース) の作成者である Paul Eggert も、同じ質問を悩ませました。アーカイブで読めるように:
*「なぜなら、実際には 1 年に 2 回のうるう秒 (別の日に) があり、ANSI C 委員会の誰かが、それらが同じ日に来たと考えていたからです。」 *— ソース groups.google.com.
数十年前に発生したこのわずかな解釈エラーは、新しい実装がこれらの標準との下位互換性を持っている必要があるため、今でも目に見えています。
アプリとバックエンド開発における他のエッジケースに移りましょう。
分と時間には予期せぬケースが含まれていないため、日の話に移りましょう。
一日とは何ですか?場合によります! 00:00:00 から 23:59:60 までの 24 時間 (うるう秒 ? を含む) と言えます。まあ、後者が一般的に正しいかどうかはわかりませんが、1 日は必ずしも 24 時間続くわけではなく、23 時、25 時、さらには 21 時や 27 時になる場合もあります。なぜそのような価値観があるのでしょうか?答えは…
いわゆる DST または「サマータイム」は、電力消費を削減することを目的として、暖かい季節に時計を進めます。暗闇が始まるのは「通常の」 (夏時間以外の) 時間よりも遅くなります。現在、DST には多くのデメリットがあるため、その必要性について議論が行われています。最近、DST の実施を中止した国もあります (例: 2014 年のロシア)。
DST にはどのような問題がありますか?見てみましょう!
柱時計の手動調整の忘れや公共交通機関の遅延などについては取り上げませんが、代わりにバックエンドとアプリ開発に関連する側面に焦点を当てます。
夏時間には時計を 1 時間進めると思うかもしれません。これは常に当てはまるわけではありません。世界には、その差が 3 時間 (ケイシーのように) または 30 分 (ロードハウ島のように) である場所があります。
サマータイムは、その名前が示すとおりではありませんが、春または秋の一部をカバーすることもありますが、一般的には暖かい季節に関連しています。つまり、それは半球によって異なります。
ヨーロッパには夏がありますが、オーストラリアには冬があり、その逆も同様です。つまり、オーストラリアの夏時間はヨーロッパの冬の間に発生します!
さらに、時間に関する夏の定義は管轄区域によって異なります。欧州連合のすべての国で同時に時刻が変更されるため、たとえば、夏でも冬でも、ベルリンとロンドンの時差は常に 1 時間です。
しかし、シドニーとロンドンの時差を考えてみましょう。この場合、それはその年の日によって異なります。これは、シドニーではロンドンとは異なる日付で夏時間の開始と終了が行われるためです。
インスピレーション: timeanddate.com
たとえば、1 月からは 10 時間の時差が生じます。シドニーではその時点で DST が適用されますが、ロンドンでは適用されません。 3 月末にロンドンでは DST が開始されますが、シドニーの州は変更されないため、その差は 9 時間に縮小します。その後、4 月にシドニーは夏時間の実施を中止するため、8 時間になります。 3種類のオフセットをご用意しております。つまり、シドニーとロンドンの時差に関する質問には 3 つの答えがあります!
米国、EU 加盟国、オーストラリアなどの国は、DST に関して安定した管轄権を持っています。遷移がいつ起こるかは、事前によくわかっています。ただし、一部の国では予期せず法律が変更される可能性があるため、常にそうとは限りません。たとえば、2020 年のフィジーでは、ルールが変更され、発表はわずか数か月前に到着しました。
ラマダンを正式に遵守している一部のイスラム諸国では、さらに複雑な状況が発生しています。夏時間も遵守する場合、後者は… (ラマダン期間中) 停止される可能性があります。
つまり、DST の移行は 1 年に複数回 (前後に)、場合によっては数回発生する可能性があります。さらに、ラマダンはイスラム暦(太陰暦)に従って計算されます。予測はラマダン境界日の計算に使用されますが、必ずしも正確であるとは限りません。たとえば、2020年のパレスチナでは、予測は約1週間短いことが判明した。変更はほんの数日前に適用されました。
ほとんどのシステムは、DST 移行時点のソースとして IANA タイム ゾーン データベースを使用します。通常、そのデータベースには毎年数回の更新があります。
DST の処理はアルゴリズムに影響を与える可能性があることに注意してください。いくつかの典型的なシナリオを考えてみましょう。
特定の壁時間 (例: 02:30) にアラームをスケジュールしている場合、そのような瞬間が存在しないことが判明する可能性があります (DST 移行中に時間が 02:00 から 03:00 に変更された場合)。時間を逆にすると2回存在します。たとえば、バックアップが行われなかったり、薬が投与されなかったり、二度投与されたりした場合、恐ろしい結果が生じる可能性があります。
もう 1 つの一般的な効果は、ユーザーが DST を観測しているタイムゾーンにいる場合に、周期的にトリガーされる時刻の変更です。たとえば、bitrise.io でビルドを 10:00 に起動するようにスケジュールした場合、突然 11:00 に起動し始める可能性があります。
これは、内部のロジックが DST を認識しておらず、ユーザーに表示される時間が UI のレンダリング時にのみ計算されるために発生します。絶対的な時間は常に同じですが、ユーザーに表示される時間は DST に応じて変化します。通常、それは顧客が期待するものではありません。
週の最初の日は何ですか?ヨーロッパに住んでいる人なら、おそらく月曜日と言うでしょう。米国では日曜日、アラビア諸国では土曜日になります。これらの事実は、カレンダーの構築および/またはレンダリングのアルゴリズムに影響を与える可能性があります。
年の何週目についてはどうでしょうか?すべては1月1日から始まると思っているかもしれません。
ご想像のとおり、これは常に当てはまるわけではありません!
ISO 標準によれば、年の第 1 週には木曜日が含まれなければなりません。たとえば、2021 年の 1 月 1 日は金曜日なので、前年の最終週に属します。 2021年の最初の週は1月4日から始まりました!ただし、この点に関して、すべてのロケールが ISO 標準と同じルールを使用しているわけではありません。
世界のほとんどの地域で使用されているグレゴリオ暦は、1 月から 12 月までの 12 か月です。ただし、他の種類のカレンダーでは、さらに多くの月が含まれる場合があります。たとえば、ヘブライ語のカレンダーには 13 か月がある場合があります。 13 番目のものは (IT の世界では) Undecmber と呼ばれます。
通常、1 年は 365 日続きます。ただし、4 で割り切れるそれぞれの年 (例: 2020、2024 など) は 366 日ある閏年です (2 月には通常の 28 日の代わりに 29 日が含まれます)。ただし、100 で割り切れる場合 (2100、2200 など)、それはうるう年ではありません。しかし ? 400 で割り切れる場合 (2000、2400 など)、それはうるう年です。
幸いなことに、そのような区別を自分で実装しようとする必要はありません (また、実装すべきではありません!)。特定のプログラミング言語の日付クラス/関数のよく知られたライブラリを使用する必要があります。
有名なサービスには、閏年の計算間違いに関連する重大なバグが多数ありました。 「うるう年問題」という用語もあります。
現地時間は経度によって異なります。特定の場所では正午ですが、地球の反対側では真夜中でもあります。
旅行中に時計を継続的に調整することは現実的ではないため、地球は、同じ現地公式時間を持つ地域をカバーするゾーンに分割されました。
小国の場合、または最大の国 (オーストラリア、米国、ロシアなど) の一部の地理的地域の場合、ゾーンの境界は通常、国の境界と同じです。
出典: timeanddate.com
場所によっては、政治的および経済的理由により、公式時間は「正当な」時間 (経度/太陽時間のみを考慮したもの) から大幅に異なります。たとえば、スペインでは、そのゾーンはイギリスではなく、経度的に近いイギリスではなく、中央ヨーロッパ大陸のほとんどの地域と同じです。
ほとんどのタイムゾーンには整数のオフセット (UTC との差、つまり標準時) があります。ベルリンでは +1 時間 (夏時間では +2)、ブエノスアイレスでは -3 時間 (アルゼンチン、夏時間なし)。ただし、オフセットには時間の半分が含まれる場合があります。ムンバイ (インド) では +5:30 時間です (夏時間なし)。
それだけでは不十分な場合は、カトマンズ (ネパール) では +5:45h なので、4 分単位の利用も可能です!
幸いなことに、この記事を書いている時点では、これ以上細かいオフセットはありません。ただし、オランダの +0:20 など、過去には存在していました。
時計には 24 時間があるという事実により、これも可能なオフセットの範囲であると考える人もいるかもしれません。 +12:00 と -12:00 を組み合わせると 24 になります。
ただし、タイムゾーン間の最も遠い時間距離は 26 時間です!
+14:00 のオフセットがあるため、これが可能です。オセアニアのいくつかの国は、オーストラリアとのつながりが強いため、この基準を採用しました。そのため、20 時間以上の時差があるよりも、数時間の時差があるほうが現実的であり、ほとんどの場合、別の日付になります。
飛行機の乗り継ぎ検索について考えてみましょう。往復航空券の場合、出発日と帰国日の両方を選択できます。当然のことですが、帰国時刻は出発後でなければなりません。
もちろん、これは真実からかけ離れたものではありません!
これらの時間は現地のタイムゾーンであることに注意してください。
次の例を見てください:
東京発00:30(オフセット+09:00)、サンフランシスコ(オフセット-07:00)行き
現地時間で前日の18:00にサンフランシスコに到着!
サンフランシスコから 19:50 に戻ります (最初の出発よりも前)
それでは、昨日どこかに行って戻ってくることができます。この例を参照してください:
アプリ/バックエンド開発で直面する可能性のあるもう 1 つの潜在的なエッジ ケースは、タイム ゾーンの命名に関連しています。
タイムゾーンをそのオフセットによって呼び出すことができることに気づくかもしれません。 +01:00 またはその識別子で指定します。ヨーロッパ/ベルリン。後者の表記法には複数の利点があることに注意してください。DST 移行に関する情報が含まれ (ベルリンには夏期 +02:00 のオフセットがあります)、履歴データも保持されます。
たとえば、現在では、ヨーロッパ/ワルシャワ ゾーンとヨーロッパ/ベルリン ゾーンはどちらも同一であるように見えます。どちらも年間を通じて同じオフセットがあり、DST の移行も常に同じ瞬間に発生します。
しかし、以前は必ずしもそうではありませんでした。たとえば、1977 年にはベルリンには夏時間がまったくありませんでしたが、ワルシャワでは 4 月 3 日から 9 月 25 日まで夏時間が実施されました (今日の夏時間とはまったく異なります)。
タイムゾーン識別子は都市名に基づいて構築されることに注意してください。国の境界は都市名よりもはるかに頻繁に変更される可能性があるため、これは意図的なものです。
たとえば、クリミアは実際にはウクライナからロシアに変わりましたが、都市名は変わりません。ヨーロッパ/シンフェロポリ ゾーンは、現在のデータが必要か過去のデータが必要かに関係なく使用できます。
何かが 1 日続くとしましょう。したがって、午前 09:15:00 に開始する場合は、24 時間を追加して終了時刻を取得できます (この場合は翌日の午前 9:15:00)。
そうですね、それは必ずしも簡単なことではありません。時計が人為的に前後に調整されると、DST が移行する可能性があることに注意してください。これは、通常 1 日は 24 時間であることを意味しますが、場合によっては 23 時間、25 時間、さらには 27 時間や 23 時間半などの奇妙な値になることもあります (ケイシーとロード・ハウを覚えていますか?)。
ビジネス ロジックを実装するときは、1 日を盲目的に 24 時間として扱わないことが重要です。そのためには、適切な日付/時刻 API を使用する必要があります。たとえば、Java には次のような個別のクラスがあります。
暦日を表す期間 — 日、月、年で測定されるサブスクリプションの有効期限などに最適です
継続時間。連続時間を表します。カレンダーの日付に関係なく、24 時間対応します。旅行やデバイス/プログラムの開始からの時間など、アクションの範囲を測定するのに最適です。
言語によっては、日照状態 (英語では day) と連続 24 時間 (英語では nychthemeron) を表す明確な単語がありますが、一般的には使用されません。
日付/時刻表現:
出典: xkcd.com
REST API などを介して他のシステムと通信する場合、データ形式について合意することが重要です。日付が 10/12/2021 と表示されている場合は、米国形式 (10 月 12 日) または英国形式 (12 月 10 日) のいずれかである可能性があります。
明確な表現を使用することをお勧めします!
日付と時刻については、ISO 8601 標準が適用されます。絶対的な日付/時刻だけでなく、期間も標準化されることに注意してください。また、ローカル (目覚まし時計のトリガー時間や生年月日などのデータを表す場合) またはゾーン (イベントの開始や写真の撮影などの瞬間を表す場合) など、適切なタイプを使用することも重要です。
標準化されていないカスタム形式は通常、混乱やバグを引き起こすため、避けてください。
UI に日付と時刻を表示するときは、ユーザーのロケールを考慮する必要があります。表示される形式は言語と地域によって異なります。すべて同じ日付を参照している次の例を見てください:
4/18/21 — 米国英語
2021/04/18 — 英国英語
2021.04.18 — ルーマニア語
١٨ /٤ /٢٠٢١ — サウジアラビア、東部アラビア数字を含むアラビア語
java.time の FormatStyle など、日付/時刻ライブラリによって提供される事前定義された形式タイプがあります。可能であればそれらを使用してください。それ以外の場合は、標準化されたパターン シンボルを使用して、よりカスタマイズされた形式を構築できます。詳細については、CLDR のドキュメントを参照してください。
月、四半期、曜日の場合、スタンドアロンのバリアントもあることに注意してください。これらは一部の言語でのみ意味を持ちます (英語では意味がありません)。たとえば、ポーランド語では 4 月は kwiecień と呼ばれます。これはスタンドアロン バージョンです。ただし、日付 (例: 4 月 18 日) に関連する場合、テキストは 18 kwietnia になります。
日付と時刻を扱うのは簡単ではありません。ただし、上記の基準に従い、適切な型を使用すれば、大きな間違いを避けることができます。
日付と時間のエッジケースに関する私の洞察があなたのプロジェクトに役立つことを願っています。頑張ってください!
元々は 2021 年 4 月 26 日に https://www.thedroidsonroids.com で公開されました。
以上がアプリおよびバックエンド開発におけるエッジケース — 日時の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。