Moment.js ガイド

ガイドエリアは、開発者が日付と時刻の問題領域、および Moment.js ライブラリとのより良い対話を学ぶのを支援するために設計されています。ここでは、最も頻繁に寄せられるサポートリクエストに対処しているため、発生する可能性のある問題の解決策を確認するのに最適な場所です。

ガイドセクションは新しく、まだ建設中です。ここに表示したいガイドのリクエストがある場合、またはガイドを追加したい場合は、momentjs.com リポジトリで issue を作成するか、プルリクエストを送信してください。

始めたばかりの場合は、この scrimba moment ガイド をチェックしてください。

ミュータビリティ 1.0.0+

編集

Moment.js の moment オブジェクトはミュータブルです。これは、加算、減算、設定などの操作が元の moment オブジェクトを変更することを意味します。Moment.js を初めて使用する場合、多くの開発者は次のようなシナリオに混乱します

var a = moment('2016-01-01'); 
var b = a.add(1, 'week'); 
a.format();
"2016-01-08T00:00:00-06:00"

ご覧のとおり、1週間を追加すると `a` が変更されます。このような状況を回避するには、日付演算を実行する前に moment を複製します

var a = moment('2016-01-01'); 
var b = a.clone().add(1, 'week'); 
a.format();
"2016-01-01T00:00:00-06:00"

日付演算と時刻演算

編集

時刻演算と日付演算には論理的な違いがあります。

Moment.js では、時刻演算は線形タイムスケールを想定し、UTC ベースのタイムスタンプを指定された時間単位の量だけ増減します。

日付演算は線形タイムスケールを使用せず、カレンダーの日付を増減します。これは、1日、1か月、または1年の時間量が可変であるためです。たとえば、夏時間遷移のため、1日は23時間から25時間の長さになる場合があります。月の 日数はもちろん異なり、うるう年のため、年の長さも異なります。日付演算は興味深いシナリオを引き起こす可能性があります。

夏時間のため、1日は24時間に等しくない場合があります

//date math
moment('2016-03-12 13:00:00').add(1, 'day').format('LLL')
"March 13, 2016 1:00 PM"
//time math
moment('2016-03-12 13:00:00').add(24, 'hours').format('LLL')
"March 13, 2016 2:00 PM"

うるう年のため、1年は365日に等しくない場合があります

moment('2016-01-01').add(1, 'year').format('LL')
"January 1, 2017"
moment('2016-01-01').add(365, 'day').format('LL')
"December 31, 2016"

日演算の期間のばらつきのため、Moment の API は、日数以上の小数値の加算または減算を公式にサポートしていません。Moment.js は小数値を受け入れ、最も近い整数に丸めて処理しようとします。

**2.12.0** 以降、小数の日と月の値は絶対値/丸めを使用して整数に変換されます。これは、1.5は2に丸められ、-1.5は-2に丸められることを意味します。

moment().add(1.5, 'days') == moment().add(2, 'days')
moment().add(-1.5, 'days') == moment().add(-2, 'days') == moment().subtract(1.5, 'days') == moment().subtract(2, 'days')
moment().add(2.3, 'months') == moment().add(2, 'months')
moment().add(-2.3, 'months') == moment().add(-2, 'months') == moment().subtract(2.3, 'months') == moment().subtract(2, 'months')

四半期と年は月に変換され、絶対値/丸められます。

moment().add(1.5, 'years') == moment().add(18, 'months')
moment().add(.8, 'years') == moment().add(9.6, 'months') == moment().add(10, 'months')
moment().add(1.5, 'quarters') == moment().add(4.5, 'months') == moment().add(5, 'months')

タイムゾーンとオフセット

編集

タイムゾーンと UTC オフセットの違いについて混乱することがよくあります。

UTC オフセットは、特定の日付と時刻が UTC からどれだけ離れているかを表す値です。ほとんどの場合、HH:mm の形式で表されます。

タイムゾーンは、すべての人が法的に義務付けられた標準時を観察する地理的領域です。

タイムゾーンは通常、夏時間のために UTC から複数のオフセットを持っています。年のある時点で、複数のタイムゾーンが同じオフセットを持つ場合があります。たとえば、タイムゾーン America/Chicago、America/Denver、および America/Belize はすべて、さまざまな時間に -06:00 のオフセットを持っています。このため、オフセット値だけからタイムゾーンを推測することは不可能です。

Moment.js コアライブラリは、オフセット値に基づいて時間を調整する機能を提供します。タイムゾーンデータに基づいて日付を調整するためのサポートは提供していません。これは Moment TimeZone ライブラリによって提供されています。

この問題の詳細な説明については、Stack Overflow タグを参照してください。

JavaScript Date

編集

Moment.js は、ネイティブの JavaScript date オブジェクトのラッパーを提供します。これにより、Moment.js は機能を拡張し、オブジェクトのいくつかの欠陥も考慮します。

パースは、ネイティブの日付では特に予測できません。たとえば、米国のコンピューターを使用していますが、日付が DD/MM/YYYY 形式であるとします。

var a = new Date('01/12/2016'); //December 1 2016 in DD/MM/YYYY format
//"Tue Jan 12 2016 00:00:00 GMT-0600 (Central Standard Time)"

ネイティブの Date オブジェクトでは、この動作に対する適切な回避策はありません。ただし、Moment のパーサーは問題なく処理します

moment('01/12/2016', 'DD/MM/YYYY', true).format()
"2016-12-01T00:00:00-06:00"

さらに、ECMA Script 5 仕様では、ISO 8601 日付のオフセットについて ungewöhnliche Behauptung を行っています

存在しないタイムゾーンオフセットの値は「Z」です

事実上、これは、オフセットのない ISO 8601 日付は UTC 値として扱われることを意味し、次の奇妙な点を生み出します

//US local format
var a = new Date('1/1/2016'); 
//"Fri Jan 01 2016 00:00:00 GMT-0600 (Central Standard Time)"

//ISO 8601
var a = new Date('2016-01-01');
//"Thu Dec 31 2015 18:00:00 GMT-0600 (Central Standard Time)"

ES2015 仕様ではこの間違いが修正され、オフセットがない場合は現地時間を指定する ISO8601 仕様と一致しています。これは、それ自体に多数の負の逆互換性の影響があるため、悪いことです。

Moment では、特に指定しない限り、日付は常に現地時間として解釈されます。これは、ES2015 の採用によって変わるものではありません。

moment('2016-01-01')
//"2016-01-01T00:00:00-06:00"

算術演算は、ネイティブの Date オブジェクトが不足しているもう1つの領域です。Date オブジェクトは実際にはこれに対する API を提供していません。代わりに、日付値のオーバーフローに依存します。2016年4月30日に1日を追加したいとします。date オブジェクトを使用すると、次のようになります

var a = new Date('4/30/2016'); 
a.setDate(a.getDate() + 1);

これはうまくいきますが、やや直観に反します。Moment は加算/減算するための API を提供します

moment('4/30/2016', 'MM/DD/YYYY').add(1, 'day')
//"2016-05-01T00:00:00-05:00"

内部プロパティ

編集

Moment オブジェクトには、`_` で始まるいくつかの内部プロパティがあります。

最も一般的に表示される内部プロパティは、Moment ラッパーである JavaScript Date を保持する `_d` プロパティです。多くの場合、開発者は `_d` の値のコンソール出力に混乱します。Moment はエポックシフトと呼ばれる手法を使用しており、このプロパティが Moment が反映する実際の日付値と異なる場合があります。特に Moment TimeZone を使用している場合、このプロパティは Moment がパブリック `format()` 関数から出力する実際の値と同じになることはほとんどありません。そのため、`_d` および `_` で始まる他のプロパティの値は、いかなる目的にも使用しないでください。

Moment の値を出力するには、`.format()`、`.toString()`、または `.toISOString()` を使用します。

MomentからネイティブのDateオブジェクトを取得するには、.toDate()を使用します。この関数は、サードパーティAPIとの連携のために適切にシフトされた日付を返します。

Moment.jsは、非常に柔軟で高度なパーサーを備えており、幅広い機能を実現します。
パーサーの柔軟性ゆえに、Moment.jsで最も頻繁に誤用されるツールの1つでもあります。

このセクションでは、状況に応じてパーサーを正しく使用する方法に関するガイドラインをいくつか示します。

ローカル vs UTC vs オフセット

編集

Momentは、日付を解析するための3つの関数、基本的なmoment関数、moment.utc、およびmoment.parseZoneを提供します。

ユーザーのローカル時間のコンテキストで日付を操作する場合は、moment関数を使用します。

moment('2016-01-01T23:35:01');

これにより、ローカルコンピューターと同じUTCオフセットを持つ日付が生成されます。

"2016-01-01T23:35:01-06:00"

日付をUTC日付として操作する場合は、moment.utcを使用します。

moment.utc('2016-01-01T23:35:01');

これにより、UTCオフセットが+0:00の日付が生成されます。

"2016-01-01T23:35:01+00:00"

日付形式に固定のタイムゾーンオフセットがある場合は、moment.parseZoneを使用します。

moment.parseZone("2013-01-01T00:00:00-13:00");

これにより、固定オフセットを持つ日付が生成されます。

"2013-01-01T00:00:00-13:00"

moment()またはmoment.utc()を使用してオフセットが指定された日付を解析すると、日付はそのオフセットからローカルまたはUTCに変換されることに注意してください。

この日付は8時間シフトされ、+2から-6(ローカルマシンのオフセット)に移動します。

moment('2016-01-01T00:00:00+02:00').format()
"2015-12-31T16:00:00-06:00"

この日付は2時間シフトされ、+2からUTCに移動します。

moment.utc('2016-01-01T00:00:00+02:00').format()
"2015-12-31T22:00:00+00:00"

既知の日付形式

編集

解析する日付文字列の形式がわかっている場合は、常にその形式を明示的に指定するのが最良の選択です。

moment('01/01/2016', 'MM/DD/YYYY')
moment('2016-01-01 11:31:23 PM', 'YYYY-MM-DD hh:mm:ss a')

日付がISO 8601形式の場合は、momentに組み込まれている定数を使用してそれを示すことができます。

moment('2016-01-01 12:25:32', moment.ISO_8601)

ISO 8601形式には、以下が含まれますが、これらに限定されません。

2013-02-08               # A calendar date part
2013-W06-5               # A week date part
2013-02-08T09            # An hour time part separated by a T
2013-02-08 09            # An hour time part separated by a space
2013-02-08 09:30:26      # An hour, minute, and second time part
2013-02-08 09+07:00      # +-HH:mm

完全なリストについては、文字列の解析に関するAPIドキュメントを参照してください。

厳密モード

編集

厳密モードは、日付を解析するための推奨モードです。コードベースで許可される場合は、常に厳密モードを使用する必要があります。 GitHubおよびStack Overflowで見られるパーサーの問題の半分以上は、厳密モードで修正できます。

今後のリリースでは、パーサーはデフォルトで厳密モードを使用するようになります。

厳密モードでは、momentへの入力が、区切り文字を含め、指定された形式と完全に一致する必要があります。厳密モードは、moment関数の3番目のパラメーターとしてtrueを渡すことによって設定されます。

moment('01/01/2016', 'MM/DD/YYYY', true).format()
"2016-01-01T00:00:00-06:00"
moment('01/01/2016 some text', 'MM/DD/YYYY', true).format()
"Invalid date"

区切り文字のマッチング

//forgiving mode
moment('01-01-2016', 'MM/DD/YYYY', false).format()
"2016-01-01T00:00:00-06:00"
//strict mode
moment('01-01-2016', 'MM/DD/YYYY', true).format()
"Invalid date"

厳密モードで修正されるシナリオ

//UUID matches YYYYDDD because it starts with 7 digits
moment('5917238b-33ff-f849-cd63-80f4c9b37d0c', moment.ISO_8601).format()
"5917-08-26T00:00:00-05:00"
//strict mode fails because trailing data exists
moment('5917238b-33ff-f849-cd63-80f4c9b37d0c', moment.ISO_8601, true).format()
"Invalid date"
//date has out of range value but is parsed anyways
moment('100110/09/2015', 'MM/DD/YYYY').format()
"2015-10-09T00:00:00-05:00"
//strict mode catches out of range issue
moment('100110/09/2015', 'MM/DD/YYYY', true).format()
"Invalid date"
//wrong date is parsed because non-strict mode ignores data after format
moment('2016-12-31 11:32 PM').format('LT')
"11:32 AM"
//trailing data is noticed
moment('2016-12-31 11:32 PM', moment.ISO_8601, true).format('LT')
"Invalid date"

寛容モード

編集

ほとんどの場合、厳密モードの方がうまく機能しますが、momentに渡される文字列の形式が異なる場合がある場合は、寛容モードが非常に役立ちます。

寛容モードが役立つ一般的なシナリオは、サードパーティAPIが日付を提供しており、そのAPIの日付形式が変更される可能性がある場合です。 APIが「YYYY-MM-DD」形式で日付を送信し始め、後で「MM/DD/YYYY」形式に変更されたとします。

厳密モードでは、次のコードを実行すると「無効な日付」と表示されます。

moment('01/12/2016', 'YYYY-MM-DD', true).format()
"Invalid date"

形式文字列を使用した寛容モードでは、間違った日付が取得されます。

moment('01/12/2016', 'YYYY-MM-DD').format()
"2001-12-20T00:00:00-06:00"

寛容モードでの間違った日付のシナリオは、ユーザーにとって確かにそれほど明白ではありませんが、そのため、長い間気付かれない可能性があります。

厳密モードと寛容モードのどちらを選択するかは、日付が正確であることと、日付が「無効な日付」と表示されないことのどちらが重要かを考慮することが重要です。

複数の形式

編集

Momentのパーサーは、日付文字列に複数の可能な形式を指定することをサポートしています。これは、日付が複数のデータソースから来る可能性がある場合に非常に役立ちます。形式を配列として渡すだけです。

moment('12 March, 2016', ['DDMMMMY', 'MMMMDDY']).format()
"2016-03-12T00:00:00-06:00"
moment('March 12, 2016', ['DDMMMMY', 'MMMMDDY']).format()
"2016-03-12T00:00:00-06:00"

この機能が正しく動作するためには、momentは提供されたすべての形式を解析する必要があります。このため、使用する形式が多いほど、解析に時間がかかります。どの形式を使用するかを決定するためのMomentのヒューリスティックは次のとおりです。

  • 無効な日付よりも有効な日付になる形式を優先します。
  • 文字列のより多くの部分を解析し、形式のより多くの部分を使用する形式、つまり、より厳密な解析を優先します。
  • 配列の後の形式よりも前の形式を優先します。

Moment.jsが、将来削除される機能に関する非推奨の警告を表示する場所がいくつかあります。回避策はここに概説されています。

JS日付の構築

編集
Moment construction falls back to js Date. 
This is discouraged and will be removed in an upcoming major release.

この非推奨の警告は、文字列コンストラクターに渡された日付に既知の形式が見つからない場合にスローされます。この問題を回避するには、moment()に渡される文字列の形式を指定します。

詳細については、解析ドキュメントを参照してください。

元のGitHubの問題を表示

ロケールのオーバーライドの定義

編集
Use moment.updateLocale(localeName, config) to change an existing locale. 
moment.defineLocale(localeName, config) should only be used for creating a new locale

この非推奨の警告は、defineLocale関数を使用して既存のロケールを変更しようとするとスローされます。これを行うと、プロパティの継承に関連する予期しない動作が発生します。 moment.updateLocaleは、既存のロケールのプロパティを適切に置き換えます。

元のプルリクエストを表示

親ロケールが未定義

編集

2.16.0以降、警告は削除されました。

ロケールは、親自体が定義またはロードされる前に、親を使用して定義できます。 momentの作成時に親が存在しないか、遅延ロードできない場合、親はグローバルロケールにデフォルト設定されます。

ロケールが見つかりません

編集
Locale <key> not found. Did you forget to load it?

この警告は、グローバルロケールが設定されているが、Momentが見つけることができない場合に表示されます。おそらく、このロケールはコピーにバンドルされていません。

加算/減算

編集
moment().add(period, number) is deprecated. Please use moment().add(number, period)
moment().subtract(period, number) is deprecated. Please use moment().subtract(number, period)

Momentは、addおよびsubtractのパラメーターの順序を(期間、数値)として非推奨にしました。パラメーターを反転してください。

悪い例

moment().add('hours', 3);

良い例

moment().add(3, 'hours');

最小/最大

編集
moment().min is deprecated, use moment.max
moment().max is deprecated, use moment.min

この警告はタイプミスではありませんが、混乱を招きます。

バージョン2.7.0より前は、momentはmoment()。minおよびmoment()。max関数をサポートしていました。これらの関数は直感的ではありませんでした。

minは問題の2つのモーメントの大きい方を返し、maxは小さい方を返します。

この反転動作のため、非推奨の警告で提供される提案は正しいです。

moment('2016-01-01').min('2016-02-01').format()
"2016-02-01T00:00:00-06:00"
//is equivalent to
moment.max(moment('2016-01-01'), moment('2016-02-01')).format()
"2016-02-01T00:00:00-06:00"
moment('2016-01-01').max('2016-02-01').format()
"2016-01-01T00:00:00-06:00"
//is equivalent to
moment.min(moment('2016-01-01'), moment('2016-02-01')).format()
"2016-01-01T00:00:00-06:00"

元のGitHubの問題を参照してください。

ゾーン

編集
moment().zone is deprecated, 
use moment().utcOffset instead.

この非推奨は、明確にするために行われました。

moment().zone()の結果は、特定の瞬間がUTCからオフセットされている分数を示す整数であり、符号が反転されています(米国の瞬間は正の値になります)。

moment().zone(number)を使用してオフセットを設定すると、日付のオフセットも反転符号を使用して設定されます。

タイムゾーンはオフセットと同じではないため、名前はutcOffsetに変更されました。その時点で、符号はUTCオフセットの実際の方向を反映するように修正されました。

moment().zone()
360
//is replaced by
moment().utcOffset()
-360

moment().zone(420)
//is replaced by 
moment().utcOffset(-420)

タイムゾーンとオフセットの詳細については、タイムゾーンとオフセットのガイドを参照してください。

元のGitHubの問題を表示

これらのリソースは、日付/時刻/タイムゾーンコミュニティのメンバーによって作成されています。

ブログ

編集

Matt Johnsonのブログ

  • Moment.jsコアコントリビューター。プログラミングにおける日付と時刻の概念。

Maggie Pintのブログ

  • Moment.jsコアコントリビューター。 Moment.jsの開発の進捗状況とロードマップ。 Moment.jsライブラリのヘルプ。

Lau Taarnskovのブログ

  • 著者-Elixir Calendar。日付と時刻のプログラミングの概念。 Elixirの日付と時刻。

Jon Skeetのブログ

  • 著者-NodaTime。 Stack Overflow#1ユーザー。日付と時刻のプログラミングの概念。