Moment: endOf( 'day')は、深夜に開始しない日に失敗します

作成日 2016年04月20日  ·  15コメント  ·  ソース: moment/moment

考えてみましょう:

moment("2016-10-16").endOf('day').format("YYYY-MM-DD HH:mm:ss")

ほとんどのタイムゾーンでは、これは"2016-10-16 23:59:59"を返します。 ただし、ブラジルでは、これは"2016-10-17 00:59:59"を返します。 2016-10-16の00:00から00:59までの時間のみが欠落しているため、これは正しくありません。 2016-10-16の終わりは、同じ日付の23:59:59のままです。

これは、moment-timezoneでも再現できます。

moment.tz("2016-10-16","America/Sao_Paulo").endOf('day').format("YYYY-MM-DD HH:mm:ss")

問題はendOfメソッドにあります。 1日の終わりを計算するには、その日の_start_を取得し、1日を加算してから、1ミリ秒を減算します。 日が深夜に始まらない場合、その論理には欠陥があります。 1日を追加する代わりに、特定の日の正確な期間の長さを追加する必要があります。

Bug DST

全てのコメント15件

これは元々、moment / moment-timezone#327として報告されていました。

2749は関連していますが、わずかに異なります。

1日の正確な時間の長さを計算するのとは対照的に、次のことを行うことはできません。

moment('2016-10-16').startOf('day').add(1, 'day').startOf('day').subtract(1, 'millisecond').format()
"2016-10-16T23:59:59-02:00"

16日にプッシュすると、15日に正常に動作します。

moment('2016-10-15').startOf('day').add(1, 'day').startOf('day').subtract(1, 'millisecond').format()
"2016-10-15T23:59:59-03:00"

私が考えていないエッジケースはありますか?

別の方法で行うとパフォーマンスが向上する可能性があると思いますが、読むことができます。

うーん、ここで何か他の厄介なもの:

moment.tz("2016-10-16","America/Sao_Paulo").startOf('day').add(1,'day').startOf('day').subtract(1,'ms').format()
// "2016-10-16T23:59:59-02:00"  (ok)

moment.tz("2016-10-15","America/Sao_Paulo").startOf('day').add(1,'day').startOf('day').subtract(1,'ms').format()
// "2016-10-14T23:59:59-03:00"  (wrong date)

moment.tz("2016-10-15","America/Sao_Paulo").startOf('day').add(1,'day').format()
// "2016-10-15T23:00:00-03:00"  should have skipped forward to "2016-10-16T01:00:00-02:00"

ここでは、あいまいなブラウザの動作全体を扱っているだけですか? それともそれ以上のものがありますか?

ブラジリアのウィンドウズタイムゾーンとクロームで:

moment("2016-10-15").startOf('day').format()
"2016-10-15T00:00:00-03:00"
moment("2016-10-15").startOf('day').add(1, 'day').format()
"2016-10-16T01:00:00-02:00"

モーメントタイムゾーンを使用して私のタイムゾーンでクロムで:

moment.tz("2016-10-15","America/Sao_Paulo").startOf('day').format()
"2016-10-15T00:00:00-03:00"
moment.tz("2016-10-15","America/Sao_Paulo").startOf('day').add(1, 'day').format()
"2016-10-15T23:00:00-03:00"

涼しい!

瞬間のタイムゾーンに何か問題がありますか? 私はそのコードを本当に知らないので、言うのは難しいです。 ただし、最初のブラウザが機能する場合は、ブラウザのようには見えません。

これは、その日付に「追加」されている場合、その瞬間のタイムゾーンが常に無効な日付を押し戻すという事実の結果だと思います。 設計によるものか偶然によるものかはわかりません。

//setting moves forward
moment.tz("2016-10-16T00:00:00","America/Sao_Paulo").format()
"2016-10-16T01:00:00-02:00"
//adding moves backward
moment.tz("2016-10-15T00:00:00","America/Sao_Paulo").add(1, 'day').format()
"2016-10-15T23:00:00-03:00"

//setting moves forward
moment.tz("2016-03-13T02:00:00","America/Chicago").format()
"2016-03-13T03:00:00-05:00"
//adding moves backward
moment.tz("2016-03-12T02:00:00","America/Chicago").add(1, 'day').format()
"2016-03-13T01:00:00-06:00"

//one more time for funzies
moment.tz("2016-03-27T01:00:00","Europe/London").format()
"2016-03-27T02:00:00+01:00"

moment.tz("2016-03-26T01:00:00","Europe/London").add(1, 'day').format()
"2016-03-27T00:00:00Z"

これは、時間を維持できないときにkeepTime設定を使用しようとすることと関係があると思います。 このコードがなぜそのようになっているのかを説明するのに十分なほどこのコードを知りませんが、このコードのkeepTime呼び出しが1ではなく0の場合、すべてが期待どおりに進むことは確かです。 それが押し戻されるのは、キープタイムがオンになっているからです。 そして、時間を保つ必要があります、それはこのエッジケースでファンキーです。

    moment.updateOffset = function (mom, keepTime) {
        var zone = moment.defaultZone,
            offset;

        if (mom._z === undefined) {
            if (zone && needsOffset(mom) && !mom._isUTC) {
                mom._d = moment.utc(mom._a)._d;
                mom.utc().add(zone.parse(mom), 'minutes');
            }
            mom._z = zone;
        }
        if (mom._z) {
            offset = mom._z.offset(mom);
            if (Math.abs(offset) < 16) {
                offset = offset / 60;
            }
            if (mom.utcOffset !== undefined) {
                mom.utcOffset(-offset, keepTime);
            } else {
                mom.zone(offset, keepTime);
            }
        }
    };

ここで役立つmoment-timezoneについては十分にわかりませんが、 keepTimeが使用される理由に関するチケットは、調査する人に役立つでしょう: https

https://github.com/moment/moment/pull/1564

これは、処理ゾーンの現時点での最後の大きな変更です。 この問題に関連しているかどうかはわかりませんが、役立つはずです。

@maggiepint少し説明していただけますか

常に無効な日付をプッシュバックします

部。 つまり、moment-timezoneの問題を明らかにする例がありますか、それとも1日の始まり/終わりにDSTを検出して適切に処理するために、スマートコードが必要なだけですか。 2つ目は、中程度から簡単です。 モーメント-tzとモーメントの間を通過するゾーンを再度リファクタリングする必要がある場合は、その地獄です-3.0を待ち、新しいインターフェイスでこの問題が発生しないことを願っています。

@ichernev今日のコードの@ mj1856が最初に提起した問題です。これは、深夜に開始されない日が、ファンキーな1日の終わりの値で終わるということです。 これは瞬間的な問題です。

もう1つは、無効な日付が提示された瞬間のタイムゾーンは、その無効な日付がコンストラクターで使用されている場合は前方に移動しますが、その日付に「追加」されている場合は後方に移動します。 これは、現状では、瞬間ではなく瞬間のタイムゾーンの問題です。 タイムゾーンインターフェイスがそれをどのように変更するかはわかりません-少し考えなければなりません。

@maggiepint明確な説明をありがとうございます。

したがって、モーメント問題を修正するには、 endOf('day')が不正確です。1日の終わりを「狙う」だけです。00:00の場合はそこに行き、終了します。それ以外の場合は、2番目の狙いが00に達した場合はもう一度狙います。 00完了しました。それ以外の場合は、最初のショットを使用します。 2番目の目的は、DSTを飛び越えて、1時間「歪む」場合に役立ちます。 DSTが原因で一日の終わりが無効な時間である場合、2番目の目的は問題ありません。

このアルゴリズムをすべてのendOf 、場合によってはstartOfにも使用する必要がありますが、現在行っているゼロに設定すると、それを推論するのは困難です。

こんにちは皆さん、私も現在のバージョンのモーメント(2.14.1)でこの問題の影響を受けています。

これを修正するために#3716を送信しましたが、現在、トランスパイルの理由でTravisCIに問題が発生しています。 すぐに調査する予定です。

これも関連しています(デフォルトのブラジルのロケール)。

同じ日付の操作結果:

moment( "2017-10-16T00:59:59.999")。subtract(1、 'day')。endOf( 'day')
=>「2017-10-16T00:59:59.999」

ねえ-startOfとendOfでこれを修正する#4164を送信しました。 フィードバックをお待ちしておりますので、最終的にこれを前進させることができれば幸いです。

@ mj1856 、DSTエッジのendOfの問題が修正されたようであるため、PRを閉じました。 これも修正できますか?

#4164をテストしましたが、これは修正されているようです。 マージを待っています。

このページは役に立ちましたか?
0 / 5 - 0 評価