はじめに
日付と時刻データは、データベースシステムによって一般的に管理され、非常に重要ですが、最初は思われるよりも正しく処理するのが難しい場合があります。データベースは、日付と時刻データを明確で曖昧さのない形式で保存し、そのデータをクライアントアプリケーションと対話するためのユーザーフレンドリーな形式に変換し、さまざまなタイムゾーンや夏時間変更のような複雑さを考慮して時間ベースの操作を実行できる必要があります。
このガイドでは、MongoDB が日付と時刻データを効果的に操作するために提供するツールの一部について説明します。関連するデータ型を探求し、演算子とメソッドを見て、これらのツールを最大限に活用して日付と時刻データを良好な状態に保つ方法について説明します。
Prisma を使用して MongoDB を使用している場合は、MongoDB コネクタを使用してデータベースに接続して管理できます。Prisma の date
型は、MongoDB の Date
型に直接マッピングされます。
MongoDB の Date
型と Timestamp
型
MongoDB の DATE
型は、日付と時刻の値を組み合わせてユニットとして保存できます。
ここで、左側の列はデータ型の BSON (バイナリ JSON) 名を表し、2 番目の列はその型に関連付けられた ID 番号を表します。最後の「エイリアス」列は、MongoDB が型を表すために使用する文字列を表します。
Type | Number | Alias |------------------ | ------ | ------------ |Date | 9 | "date" |
BSON Date 型は、Unix エポック (1970 年 1 月 1 日) からのミリ秒数を表す符号付き 64 ビット整数です。正の数はエポックからの経過時間を表し、負の数はエポックから遡る時間を表します。
日付と時刻データを大きな整数として保存することには利点があります。それは
- MongoDB が日付をミリ秒単位の精度で保存できるようにする
- 日付と時刻の表示方法に柔軟性を提供する
日付型はタイムゾーンのような追加情報を保存しないため、関連する場合はそのコンテキストを別途保存する必要があります。MongoDB は日付と時刻情報を内部的には UTC を使用して保存しますが、必要に応じて取得時に他のタイムゾーンに簡単に変換できます。
MongoDB は、主に内部的に使用される Timestamp
型も提供しています
Type | Number | Alias |------------------ | ------ | ------------ |Timestamp | 17 | "timestamp" |
これは主にレプリケーションやシャーディングのような内部プロセスを調整するために実装されているため、独自のアプリケーションロジックでは使用しない方がよいでしょう。日付型は、考えられる時間に関する要件を通常は満たすことができます。
Prisma で MongoDB データベースを管理する場合、MongoDB の Date
型は、Prisma 内の date
型に直接マッピングされます。
新しい日付の作成方法
Date
オブジェクトは、次の 2 つの異なる方法で作成できます
new Date()
: 日付と時刻をDate
オブジェクトとして返します。ISODate()
: 日付と時刻をDate
オブジェクトとして返します。
new Date()
メソッドと ISODate()
メソッドはどちらも、ISODate()
ヘルパー関数でラップされた Date
オブジェクトを生成します。
さらに、Date()
関数を new
コンストラクターなしで呼び出すと、日付と時刻は Date
オブジェクトではなく文字列として返されます
Date()
: 日付と時刻を文字列として返します。
利用可能な操作、情報の保存方法、および得られる柔軟性の量に影響するため、これら 2 つの型間の区別を念頭に置いておくことが重要です。一般に、日付情報は常に Date
型を使用して保存し、必要に応じて出力用にフォーマットするのが最善です。
MongoDB シェルセッションでこれがどのように機能するかを見てみましょう。
まず、新しい一時データベースに切り替えて、それぞれに date
フィールドを持つ 3 つのドキュメントを作成できます。オブジェクトごとに date
フィールドを設定するために異なる方法を使用します
use temp_dbdb.dates.insertMany([{name: "Created with `Date()`",date: Date(),},{name: "Created with `new Date()`",date: new Date(),},{name: "Created with `ISODate()`",date: ISODate(),},])
{"acknowledged" : true,"insertedIds" : [ObjectId("62726af5a3dc7398b97e6e93"),ObjectId("62726af5a3dc7398b97e6e94"),ObjectId("62726af5a3dc7398b97e6e95")]}
デフォルトでは、これらのメカニズムはそれぞれ現在の日付と時刻を保存します。 ISO 8601 形式の日付文字列を引数として追加することで、異なる日付と時刻を保存できます
db.dates.insertMany([{name: 'Future date',date: ISODate('2040-10-28T23:58:18Z'),},{name: 'Past date',date: new Date('1852-01-15T11:25'),},])
これらは、適切な日付と時刻に Date
オブジェクトを作成します。
注意すべき点の 1 つは、上記の最初の新しいドキュメントに末尾の Z
が含まれていることです。これは、日付と時刻が UTC として提供されていることを示します。Z
なしで日付を指定すると、MongoDB は入力を現在のローカルタイムに関連して解釈します (ただし、常に UTC 日付として内部的に変換および保存します)。
日付オブジェクトの型の検証
次に、結果のドキュメントを表示して、MongoDB が日付データをどのように保存したかを確認できます
db.dates.find().pretty()
{"_id" : ObjectId("62726af5a3dc7398b97e6e93"),"name" : "Created with `Date()`","date" : "Wed May 04 2022 12:00:53 GMT+0000 (UTC)"}{"_id" : ObjectId("62726af5a3dc7398b97e6e94"),"name" : "Created with `new Date()`","date" : ISODate("2022-05-04T12:00:53.307Z")}{"_id" : ObjectId("62726af5a3dc7398b97e6e95"),"name" : "Created with `ISODate()`","date" : ISODate("2022-05-04T12:00:53.307Z")}{"_id" : ObjectId("62728b57a3dc7398b97e6e96"),"name" : "Future date","date" : ISODate("2040-10-28T23:58:18Z")}{"_id" : ObjectId("62728c5ca3dc7398b97e6e97"),"name" : "Past date","date" : ISODate("1852-01-15T11:25:00Z")}
予想どおり、ISODate()
および new Date()
で設定された date
フィールドには、(ISODate
ヘルパーでラップされた) Date
オブジェクトが含まれています。対照的に、生の Date()
関数呼び出しによって設定されたフィールドは文字列として保存されます。
コレクション全体で map
関数を呼び出すことで、date
フィールドのどれに実際の Date
オブジェクトが含まれているかを確認できます。map は、各 date
フィールドをチェックして、そこに保存されているオブジェクトが Date
型のインスタンスであるかどうかを確認し、結果を is_a_Date_object
という新しいフィールドに表示します。さらに、valueOf()
メソッドを使用して、各 date
フィールドが MongoDB によって実際にどのように保存されているかを示します
db.dates.find().map(function (date_doc) {date_doc['is_a_Date_object'] = date_doc.date instanceof Datedate_doc['date_storage_value'] = date_doc.date.valueOf()return date_doc})
;[{_id: ObjectId('62726af5a3dc7398b97e6e93'),name: 'Created with `Date()`',date: 'Wed May 04 2022 12:00:53 GMT+0000 (UTC)',is_a_Date_object: false,date_storage_value: 'Wed May 04 2022 12:00:53 GMT+0000 (UTC)',},{_id: ObjectId('62726af5a3dc7398b97e6e94'),name: 'Created with `new Date()`',date: ISODate('2022-05-04T12:00:53.307Z'),is_a_Date_object: true,date_storage_value: 1651665653307,},{_id: ObjectId('62726af5a3dc7398b97e6e95'),name: 'Created with `ISODate()`',date: ISODate('2022-05-04T12:00:53.307Z'),is_a_Date_object: true,date_storage_value: 1651665653307,},{_id: ObjectId('62728b57a3dc7398b97e6e96'),name: 'Future date',date: ISODate('2040-10-28T23:58:18Z'),is_a_Date_object: true,date_storage_value: 2235081498000,},{_id: ObjectId('62728c5ca3dc7398b97e6e97'),name: 'Past date',date: ISODate('1852-01-15T11:25:00Z'),is_a_Date_object: true,date_storage_value: -3722502900000,},]
これにより、ISODATE(...)
として表示されるフィールドが Date
型のインスタンスであり、生の Date()
関数で作成された date
はそうではないことが確認されます。
さらに、上記の出力は、Date
型で保存されたオブジェクトが符号付き整数として記録されていることを示しています。予想どおり、1852 年の日付に関連付けられた日付オブジェクトは、1970 年 1 月から遡ってカウントしているため、負になります。
日付オブジェクトのクエリ
このように日付の表現が混在するコレクションがある場合は、$type
演算子を使用して、型が一致するフィールドをクエリできます。
たとえば、date
が Date
オブジェクトであるすべてのドキュメントをクエリするには、次のように入力します
db.dates.find({date: { $type: 'date' },}).pretty()
{"_id" : ObjectId("62726af5a3dc7398b97e6e94"),"name" : "Created with `new Date()`","date" : ISODate("2022-05-04T12:00:53.307Z")}{"_id" : ObjectId("62726af5a3dc7398b97e6e95"),"name" : "Created with `ISODate()`","date" : ISODate("2022-05-04T12:00:53.307Z")}{"_id" : ObjectId("62728b57a3dc7398b97e6e96"),"name" : "Future date","date" : ISODate("2040-10-28T23:58:18Z")}{"_id" : ObjectId("62728c5ca3dc7398b97e6e97"),"name" : "Past date","date" : ISODate("1852-01-15T11:25:00Z")}
date
フィールドが文字列として保存されているインスタンスを見つけるには、次のように入力します
db.dates.find({date: { $type: 'string' },}).pretty()
{"_id" : ObjectId("62726af5a3dc7398b97e6e93"),"name" : "Created with `Date()`","date" : "Wed May 04 2022 12:00:53 GMT+0000 (UTC)"}
Date
型を使用すると、時間単位間の関係を理解するクエリを実行できます。
たとえば、他の型と同様に、Date
オブジェクトを順序的に比較できます。将来の日付を確認するには、次のように入力します
db.dates.find({date: {$gt: new Date(),},}).pretty()
{"_id" : ObjectId("62728b57a3dc7398b97e6e96"),"name" : "Future date","date" : ISODate("2040-10-28T23:58:18Z")}
Date
型メソッドの使用方法
さまざまな組み込みメソッドと演算子を使用して、Date
オブジェクトを操作できます。たとえば、日付からさまざまな日付と時刻のコンポーネントを抽出し、さまざまな形式で出力できます。
デモンストレーションは、おそらくこの機能を最も早く紹介する方法です。
まず、日付オブジェクトを含むドキュメントから日付を選択しましょう
date_obj = db.dates.findOne({ name: 'Future date' }).date
次に、date
フィールドを選択し、オブジェクトでさまざまなメソッドを呼び出すことで、そこからさまざまなコンポーネントを抽出できます
date_obj.getUTCFullYear()date_obj.getUTCMonth()date_obj.getUTCDate()date_obj.getUTCHours()date_obj.getUTCMinutes()date_obj.getUTCSeconds()
2040 // year9 // month28 // date23 // hour58 // minutes18 // seconds
さまざまな時間および日付コンポーネントを提供することで時間を設定するために使用できるコンパニオンメソッドもあります。たとえば、.setUTCFullYear()
メソッドを呼び出すことで、年を変更できます
date_obj.toString()date_obj.setUTCFullYear(2028)date_obj.toString()date_obj.setUTCFullYear(2040)
Sun Oct 28 2040 23:58:18 GMT+0000 (UTC)1856390298000 // integer stored for the new date valueSat Oct 28 2028 23:58:18 GMT+0000 (UTC)2235081498000 // integer stored for the restored date value
日付をさまざまな表示形式にキャストすることもできます
date_obj.toDateString()date_obj.toUTCString()date_obj.toISOString()date_obj.toLocaleDateString()date_obj.toLocaleTimeString()date_obj.toString()date_obj.toTimeString()
Sun Oct 28 2040 // .toDateString()Sun, 28 Oct 2040 23:58:18 GMT // .toUTCString()2040-10-28T23:58:18.000Z // .toISOString()10/28/2040 // .toLocaleDateString()23:58:18 // .toLocaleTimeString()Sun Oct 28 2040 23:58:18 GMT+0000 (UTC) // .toString()23:58:18 GMT+0000 (UTC) // .toTimeString()
これらはすべて、主に JavaScript の Date
型に関連付けられたメソッドです。
MongoDB Date
集計関数の使用方法
MongoDB は、日付も操作できる他の関数も提供しています。この便利な例の 1 つは、$dateToString()
集計関数です。$dateToString()
を Date
オブジェクト、フォーマット文字列指定子、およびタイムゾーンインジケーターを渡して呼び出すことができます。MongoDB は、フォーマット文字列をテンプレートとして使用して、指定された Date
オブジェクトを出力する方法を把握し、タイムゾーンを使用して出力を UTC から正しくオフセットします。
ここでは、dates
コレクションの日付を任意の文字列を使用してフォーマットします。また、日付をニューヨークタイムゾーンにキャストします。
まず、date
フィールドを文字列として保存している可能性のある不要なドキュメントを削除する必要があります
db.dates.deleteMany({ date: { $type: 'string' } })
これで、$dateToString
関数を使用して集計を実行できます
db.dates.aggregate([{$project: {_id: 0,date: '$date',my_date: {$dateToString: {date: '$date',format:'Day %d of Month %m (Day %j of year %Y) at %H hours, %M minutes, and %S seconds (timezone offset: %z)',timezone: 'America/New_York',},},},},]).pretty()
{"date" : ISODate("2022-05-04T12:00:53.307Z"),"my_date" : "Day 04 of Month 05 (Day 124 of year 2022) at 08 hours, 00 minutes, and 53 seconds (timezone offset: -0400)"}{"date" : ISODate("2022-05-04T12:00:53.307Z"),"my_date" : "Day 04 of Month 05 (Day 124 of year 2022) at 08 hours, 00 minutes, and 53 seconds (timezone offset: -0400)"}{"date" : ISODate("2040-10-28T23:58:18Z"),"my_date" : "Day 28 of Month 10 (Day 302 of year 2040) at 19 hours, 58 minutes, and 18 seconds (timezone offset: -0400)"}{"date" : ISODate("1852-01-15T11:25:00Z"),"my_date" : "Day 15 of Month 01 (Day 015 of year 1852) at 06 hours, 28 minutes, and 58 seconds (timezone offset: -0456)"}
$dateToParts()
関数も同様に役立ちます。これは、Date
フィールドを構成要素に分解するために使用できます。
たとえば、次のように入力できます
db.dates.aggregate([{$project: {_id: 0,date: {$dateToParts: { date: '$date' },},},},])
{ "date" : { "year" : 2022, "month" : 5, "day" : 4, "hour" : 12, "minute" : 0, "second" : 53, "millisecond" : 307 } }{ "date" : { "year" : 2022, "month" : 5, "day" : 4, "hour" : 12, "minute" : 0, "second" : 53, "millisecond" : 307 } }{ "date" : { "year" : 2040, "month" : 10, "day" : 28, "hour" : 23, "minute" : 58, "second" : 18, "millisecond" : 0 } }{ "date" : { "year" : 1852, "month" : 1, "day" : 15, "hour" : 11, "minute" : 25, "second" : 0, "millisecond" : 0 } }
集計関数に関する MongoDB ドキュメントには、表示または比較のために Date
オブジェクトを操作するために使用できる追加の関数に関する情報があります。
結論
このガイドでは、MongoDB 内で日付と時刻データを操作できるさまざまな方法のいくつかについて説明しました。ほとんどの時間データは、MongoDB の Date
データ型に保存するのが適切でしょう。これは、データの操作や表示時に優れた柔軟性を提供するためです。
日付と時刻データが内部的にどのように保存されるか、出力時に望ましい形式に強制変換する方法、およびデータを比較、変更、分解して役立つチャンクにする方法を理解することで、さまざまな問題を解決できます。日付情報は扱うのが難しい場合がありますが、利用可能なメソッドと演算子を利用することで、面倒な作業の一部を軽減できます。
MongoDB を使用している場合は、Prisma の MongoDB コネクタをチェックしてください!Prisma Client を使用して、本番環境の MongoDB データベースを自信を持って管理できます。
MongoDB と Prisma の使用を開始するには、ゼロから始めるガイド、または既存のプロジェクトに追加する方法を確認してください。