アプリケーションが大規模に開始されると、ユーザーの増加はさらに増加します。発生する可能性が非常に高いのは、ユーザーの位置が同じエリア内だけでなく、タイムゾーンが異なる別のエリアにある可能性があるということです。したがって、バックエンド開発者としては、タイムゾーンの違いの処理に関連することを考慮することが非常に重要です。
最近、タイムゾーンに関する問題に遭遇しました。正直に言うと、日付と時刻の処理は、人間が対処しなければならない最も複雑な領域の 1 つです。そして、これは私にとって、サーバー側で日付と時刻を適切に処理する方法を学ぶ機会になりました。
この記事では、バックエンド開発者としてサーバー側でタイムゾーンの違いをどのように処理するかについての私の経験を共有します。おそらく誰かが喜んで修正し、私にとって価値のある追加の意見を提供してくれれば。
タイムゾーンは、時間の測定を整理および標準化するために世界中で使用されている標準的な時間分割システムです。この概念は、特に通信および輸送技術の発展に伴う世界的な時間調整の必要性への対応として登場しました。
タイムゾーンの基本原理は、地球を 24 のゾーンに分割することです。通常、各タイム ゾーンは、その隣のタイム ゾーンと 1 時間異なります。タイム ゾーンの主な基準は、グリニッジ標準時 (GMT) または協定世界時 (UTC) です。これは、イギリスのグリニッジを通過する経度 0 度の線にあります。
ウィキメディア・コモンズのヘレリックによるイラスト
小さな例としては、ジャカルタでは時計が正午を示しているのに、ニューヨークでは時刻が 00:00 または真夜中を示していることが挙げられます。これは、ジャカルタ人がランチを楽しんでいる間、ニューヨーカーは新しい一日を始めたばかりであることを意味します。ここから、アプリケーションを構築する際にタイムゾーンを正しく処理することの重要性が確かに想像できるでしょう。
上記の説明を見た後、サーバー アプリケーションが API にアクセスするクライアントからタイム ゾーンを処理するリクエストを受信したときに実行できるポイントを説明します。
この記事では、サーバー側でタイムゾーンを処理するためのいくつかのアプローチについて説明します。ここでは、Golang (Go) 言語のコード例を使用します。 Golang には、時間関連のデータを操作するための time パッケージがあり、非常に完成されていると考えられています。ここで説明するいくつかのポイントがあります:
最初に議論するのは、どの時間をデータベースに保存するかということです。たとえば、フラッシュ セールを行う e コマース アプリケーションがあり、このアプリケーションはすでに国際規模になっています。
ユーザーがアメリカで購入取引を処理するとき、またはユーザーがインドネシアにいる場合、ユーザーは別の現地時間をサーバーに送信します。問題は、データベースがユーザーの現地時間に従って時刻データを保存するかどうかです。答えが「はい」の場合、データを取得したい場合や、たとえば管理者がデータ処理を行いたい場合、どのユーザーが最も早いトランザクションを行うかは、複雑な問題である可能性があります。
これを克服するためのベスト プラクティスは、クロックと時刻設定の主要な時間標準である UTC (協定世界時) タイム ゾーンでトランザクション時間を保存することです。ここでは時刻を UTC に適用します。
package main import ( "fmt" "time" ) func main() { now := time.Now() fmt.Printf("Local time: %s\n", now) nowInUTC := now.UTC() fmt.Printf("UTC time: %s\n", nowInUTC) }
上のコードの意味を見てみましょう。
まず、now := time.Now() コード行では、time パッケージの Now() 関数を使用して、システムのローカル タイム ゾーンに基づいて現在時刻を取得します。結果は現在の変数に保存されます。
次に、nowInUTC := now.UTC() 行で、UTC() メソッドを使用して現地時間 (現在) を UTC 時間に変換します。結果は新しい変数 nowInUTC に保存され、今回はサーバーに保存できます。これにより、開発者はタイムゾーンの異なるさまざまな地域のサーバーとユーザーの間のタイムゾーンの違いによって発生する可能性のあるあいまいさやエラーを回避できることが期待されます。
上記のコードを実行した場合の結果は次のとおりです:
しかし、これが常に使用すべき最善の解決策であるとは限りません。特定のユースケースでは留意すべき点がいくつかあるかもしれません。その 1 つは、ユーザーが異なるタイムゾーンから来ているというのは本当ですか?それが不可能な場合は、時刻を UTC で保存するとコードが複雑になる可能性があります。
上記の議論の中で、ユーザーの時間データを 1 つの場所、つまり UTC に保存することに同意しました。次に、ユーザーが自分の位置に応じて正確な時間を確認できるようにします。上記の議論の例は、私たちが所有する電子商取引アプリケーションでのフラッシュ セールです。この場合、ユーザーはどのユーザーが最初の取引を行ったかに関する情報も知りたいと考えています。したがって、現時点では、データベースに保存されている時間をユーザーの現地時間に変換することは、無視すべきではないもう 1 つの重要なことです。
私が採用するアプローチは、サーバー側が常にクライアントにユーザー側のタイムゾーンを送信するように要求することです。これは、クライアントがキータイムゾーンを含むヘッダーを送信し、ユーザーのタイムゾーン値を含むリクエスト側で実行できます。たとえば、インドネシアには、WIT( 9)、WITA( 8)、WIB( 7) という 3 つのタイム ゾーン区分があります。各ゾーンには 1 時間の差があります。以前サーバーに UTC 時間を 00.00 で保存していた場合、WIT 側では 09.00、WITA 側では 08.00、WIB は 07.00 でした。
上記のケースを処理するサンプルコードは次のとおりです:
package main import ( "fmt" "time" ) func ParseTimezoneFromString(tz string) *time.Location { if len(tz) > 0 { t, err := time.Parse("2006 -07:00", fmt.Sprintf("2007 %s", tz)) if err != nil { panic(err) } else { return t.Location() } } return time.Now().Location() } func main() { timeServerInUTC := "2024-08-04 00:00:00" nowInUTC, err := time.Parse("2006-01-02 15:04:05", timeServerInUTC) if err != nil { panic(err) } fmt.Printf("UTC time: %s\n", nowInUTC) witLocation := ParseTimezoneFromString(" 09:00") nowInWIT := nowInUTC.In(witLocation) fmt.Printf("WIT time: %s\n", nowInWIT) witaLocation := ParseTimezoneFromString(" 08:00") nowInWITA := nowInUTC.In(witaLocation) fmt.Printf("WITA time: %s\n", nowInWITA) wibLocation := ParseTimezoneFromString(" 07:00") nowInWIB := nowInUTC.In(wibLocation) fmt.Printf("WIB time: %s\n", nowInWIB) }
この関数を作成した dikac のクレジット ParseTimezoneFromString
上記のコードの意味を理解しましょう:
まず、関数 ParseTimezoneFromString を作成します。この関数は、引数 tz または指定されたユーザーの場所のタイムゾーンに基づいて時間の場所を見つけるために使用されます。文字列値 tz が有効な場合、 time.Parse 関数を使用して文字列のタイムゾーンを変換し、文字列を時刻オブジェクトに変換し、そのオブジェクトから位置 (タイムゾーン) を抽出します。また、文字列が空であるか解析が失敗した場合も処理し、関数はシステムのローカル タイム ゾーンを返します。
func ParseTimezoneFromString(tz string) *time.Location { if len(tz) > 0 { t, err := time.Parse("2006 -07:00", fmt.Sprintf("2007 %s", tz)) if err != nil { panic(err) } else { return t.Location() } } return time.Now().Location() }
次に、時間データも次の文字列形式で定義します:
timeServerInUTC := "2024-08-04 00:00:00" nowInUTC, err := time.Parse("2006-01-02 15:04:05", timeServerInUTC) if err != nil { panic(err) }
これはサーバーから取得するタイミング データと考えることができます。そしてそれを時間オブジェクトに解析します。
次に、定義した文字列引数に基づいて以前に作成した ParseTimezoneFromString 関数に基づいて、ユーザーの正確な位置を見つけようとします。注意が必要なのは、この文字列引数が、受信リクエストを介してクライアントによって送信されるタイムゾーン ヘッダーの値を意味することです。
ParseTimezoneFromString 関数から取得した位置を使用して、サーバーから取得した時間をユーザーの現地時間に変換またはシフトできます。これは、次のコード スニペットに示すように、時間パッケージにも含まれている In 関数を使用して行うことができます:
nowInWIT := nowInUTC.In(witLocation) nowInWITA := nowInUTC.In(witaLocation) nowInWIB := nowInUTC.In(wibLocation)
これを実行すると、定義したタイムゾーンの場所に対応する時刻が取得されます。
最後のポイント、つまりテストと検証も同様に重要です。開発プロセスで開発者が予期せぬ間違いを犯すことがよくある場合、テストと検証は常に重要です。
上記のポイント 2 の説明では、タイムゾーンを処理する際に ParseTimezoneFromString 関数が重要でした。アプリケーションが期待に応える結果を得るには、テストと検証を繰り返すことが重要です。
テストには、追加可能ないくつかのシナリオを備えた最小単位でテストが実行される単体テストを使用することをお勧めします。シナリオが増えるほど、これらの時差を処理できる可能性は低くなります。
バックエンド開発者にとってタイムゾーンの処理は確かに難しい場合があります。ただし、私たちが乗り越えるすべての困難な課題は、私たちの成長とスキルの向上に貢献するということを覚えておくことが重要です。タイムゾーンを適切に管理することは、技術的に必要なだけではなく、スケジュールの正確性を確保し、さまざまな地域のアプリケーション ユーザーにスムーズなエクスペリエンスを提供します。
この記事で共有されている、UTC での時刻の保存、ユーザーの現地時間への変換、堅牢な変換関数の実装に関するポイントは、この複雑な問題に取り組むための出発点となります。ただし、ここで説明したアプローチには欠点や改善の余地がある可能性があることを認めます。このため、開発者コミュニティからの追加の入力や提案が非常に貴重です。
この記事で提供される洞察とコード例が、将来プロジェクトでタイムゾーン関連の課題に遭遇したときに役立つことを心から願っています。目標は、地理的な場所に関係なく、ユーザーにとってシームレスに動作するアプリケーションを作成することであることを忘れないでください。
以下のコメントセクションでこの議論を続けましょう。タイムゾーンの処理に関するご経験、直面した課題、効果的だと感じた別のアプローチについてぜひお聞かせください。あなたの洞察は、私や同様の問題に直面している他の読者にとって非常に貴重なものになる可能性があります。
読んでいただきありがとうございます。この記事があなたの開発の旅に役立つことを願っています。これからも一緒に学び、向上していきましょう! ?
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3