Moment: Menambah dan mengurangi tanggal tidak mempertahankan jam saat zona waktu memiliki aturan DST tengah malam

Dibuat pada 22 Agu 2018  ·  8Komentar  ·  Sumber: moment/moment

Deskripsi Masalah:

Saya mencoba membuat rentang tanggal dalam (larik tanggal momen) di mana setiap tanggal adalah 1 hari setelah sebelumnya. Ini berfungsi seperti yang diharapkan untuk semua pengguna, tetapi kami mendapatkan laporan bug untuk pengguna di zona waktu Amerika / Santiago yang melihat tanggal duplikat di UI. Yang saya perhatikan adalah menambahkan 1 hari ke tanggal tertentu sebenarnya hanya menambahkan 23 jam.

Dengan tanggal pada 8/11/2018 00:00:00 di zona waktu Amerika / Santiago, menambahkan 1 hari akan menambah waktu menjadi 8/11/2018 23:00:00 alih-alih 8/12/2018 00:00:00 .

Dokumen menjelaskan bahwa jam kerja harus dipertahankan saat berpindah ke DST _ saat menggunakan hari_, tetapi tampaknya tidak benar di sini.

Ada juga pertimbangan khusus yang perlu diingat saat menambahkan waktu yang melewati waktu musim panas. Jika Anda menambahkan tahun, bulan, minggu, atau hari, jam asli akan selalu cocok dengan jam yang ditambahkan.

// This code is from moment.js docs https://momentjs.com/docs/#/manipulating/add/
var m = moment(new Date(2011, 2, 12, 5, 0, 0)); // the day before DST in the US
m.hours(); // 5
m.add(1, 'days').hours(); // 5

Langkah-langkah untuk Mereproduksi

Dalam kode berikut, zona waktu yang digunakan memiliki aturan DST pada tengah malam. Saya memiliki tanggal pada tengah malam pada hari sebelum aturan ini berlaku. Saya menambahkan 1 hari untuk tanggal ini, dan berharap bahwa itu seharusnya adalah keesokan harinya pada tengah malam / 0 jam (jam harus dipertahankan), tetapi sebenarnya hanya menambahkan 23 jam.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/11/2018 00:00:00'), 'America/Santiago');
fmt(x);                       // "2018-08-11T00:00:00-04:00 America/Santiago"
fmt(x.clone().add(1, 'day')); // "2018-08-11T23:00:00-04:00 America/Santiago" - offset unchanged, added 23 hours not 1 day
fmt(x.clone().add(2, 'day')); // "2018-08-13T00:00:00-03:00 America/Santiago" - original hour preserved now

Di sini, Anda dapat melihat bahwa penambahan 24 jam menggesernya menjadi 25 jam dan offset zona waktu diubah.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/11/2018 00:00:00'), 'America/Santiago');
fmt(x);                          // "2018-08-11T00:00:00-04:00 America/Santiago"
fmt(x.clone().add(24, 'hours')); // "2018-08-12T01:00:00-03:00 America/Santiago" - offset changed, added 25 hours
fmt(x.clone().add(48, 'hours')); // "2018-08-13T01:00:00-03:00 America/Santiago"

Lingkungan Hidup:

  • Versi 68.0.3440.84 (Build Resmi) (64-bit)
  • Mac OSX El Capitan 10.11.6 (15G31)

Informasi lain yang mungkin berguna:

  • Zona Waktu Mesin: Zona Waktu Timur AS, Waktu Musim Panas
  • Kode Waktu & Tanggal dijalankan: 15:00 pada 22 Agustus 2018
  • Saya mereproduksi masalah ini di Alat Pengembang Chrome di halaman web Moment.js.

Output Tanggal JS

(Tanggal baru ()). toString ()

  • Rabu 22 Agustus 2018 15:13:40 GMT-0400 (Waktu Musim Panas Bagian Timur)

(Tanggal baru ()). toLocaleString ()

  • 22/8/2018, 15:13:40

(Tanggal baru ()). getTimezoneOffset ()

  • 240

navigator.userAgent

  • Mozilla / 5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit / 537.36 (KHTML, seperti Gecko) Chrome / 68.0.3440.84 Safari / 537.36

moment.version

  • 2.22.2
Bug DST Pending Next Release Up-For-Grabs

Komentar yang paling membantu

Perilaku di sini sepertinya diubah antara zona waktu-momen v0.5.4 dan v0.5.26.
Di versi lama, menambahkan 1 hari ke '2018-08-11' memberi Anda jam 11 malam pada hari yang sama
Dalam versi baru, ini memberi Anda '2018-08-12' di tengah malam. Tetapi kali ini sebenarnya tidak ada, dan menambahkan 1 menit kembali ke satu jam - tetapi perubahan DST ini seharusnya menambahkan 1 jam.

Versi lama, berperilaku seperti yang dijelaskan dalam masalah ini:
https://jsfiddle.net/eqyvuxht/1/

Versi baru, perilaku berubah, tapi saya pikir masih salah?
https://jsfiddle.net/0h6atn4b/4/

Berikut ini solusinya:

function switchZone(m, zone) {
  let arr = [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second()];
  if(zone) {
    return moment.tz(arr, zone);
  }
  return moment(arr);
}

function safeAddDays(m, days) {
   let oldZone = m.tz();
   let utc = switchZone(m, 'UTC');
   utc.add(days, 'days');
   return switchZone(utc, oldZone);
}

Semua 8 komentar

Saya mencoba mereproduksi ini di zona waktu Eropa / Roma, dan tidak dapat:

Eropa / Roma - https://www.timeanddate.com/time/change/italy/rome

28 Okt 2018 - Waktu Musim Panas Berakhir
Saat waktu siang hari setempat akan segera tiba
Minggu, 28 Oktober 2018, jam 3:00:00 pagi diputar mundur 1 jam menjadi
Minggu, 28 Oktober 2018, pukul 02:00 waktu standar setempat sebagai gantinya.

Tidak ada bug dalam kode ini.

d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('10/27/2018 00:00:00'), 'Europe/Rome');
fmt(x)                       // "2018-10-27T06:00:00+02:00 Europe/Rome"
fmt(x.clone().add(1, 'day')) // "2018-10-28T06:00:00+01:00 Europe/Rome" - tz offset changed, hour preserved as expected
fmt(x.clone().add(2, 'day')) // "2018-10-29T06:00:00+01:00 Europe/Rome"

Anda dapat melihat bahwa TZ berubah, tetapi jamnya tetap konstan.

Berdasarkan langkah reproduksi, sepertinya ada ketidaksesuaian dalam kode saat penambahan tanggal mendarat dengan sempurna ke batas DST. Di zona waktu Santiago, aturan DST adalah bahwa pada tengah malam jam mundur satu jam.

Tampaknya kasus tepi ini mungkin khusus untuk zona waktu di mana batas waktu DST adalah tengah malam, karena saya tidak dapat mereproduksi dengan zona waktu di mana batas waktu adalah pada jam 3 pagi, dengan x tanggal pada jam 3 pagi.

Eropa / Roma - https://www.timeanddate.com/time/change/italy/rome

28 Okt 2018 - Waktu Musim Panas Berakhir
Saat waktu siang hari setempat akan segera tiba
Minggu, 28 Oktober 2018, jam 3:00:00 pagi diputar mundur 1 jam menjadi
Minggu, 28 Oktober 2018, pukul 02:00 waktu standar setempat sebagai gantinya.

Tidak ada bug dalam kode ini.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('October 27, 2018 03:00:00'), 'Europe/Rome');
fmt(x);                       // "2018-10-27T09:00:00+02:00 Europe/Rome"
fmt(x.clone().add(1, 'days')) // "2018-10-28T09:00:00+01:00 Europe/Rome" - tz offset changed, hour preserved as expected
fmt(x.clone().add(2, 'days')) // "2018-10-29T09:00:00+01:00 Europe/Rome"

Saya dapat mengonfirmasi bahwa bug ini hanya ada untuk zona waktu ketika batas waktu DST adalah tengah malam, dan Anda menambahkan waktu ke tanggal yang menyebabkan tanggal tersebut mendarat tepat di batas waktu.

Amerika / Punta_Arenas - https://www.timeanddate.com/time/zone/chile/punta-arenas
Pada 2016, zona waktu ini juga mengalami pemutusan pada tengah malam.

Kode ini tampaknya memiliki bug. Lihat komentar setelah setiap baris

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/13/2016 00:00:00'), 'America/Punta_Arenas')
fmt(x);                        // "2016-08-13T00:00:00-04:00 America/Punta_Arenas"
fmt(x.clone().add(1, 'days')); // "2016-08-13T23:00:00-04:00 America/Punta_Arenas" - 23 hours added, not 1 day, no tz offset change
fmt(x.clone().add(2, 'days')); // "2016-08-15T00:00:00-03:00 America/Punta_Arenas"

Saya menemukan apa yang tampaknya menjadi masalah serupa saat mengurangi jenis zona waktu yang sama (DST di tengah malam).

Saat mengurangi tanggal tepat ke shift DST, jam tidak dipertahankan hanya pada hari itu, tetapi tanggalnya berubah.

fmt = d => d.format() + ' ' + d.tz()

x = moment.tz(new Date('08/13/2018 23:00:00'), 'America/Santiago');
fmt(x);                             // "2018-08-14T00:00:00-03:00 America/Santiago"
fmt(x.clone().subtract(1, 'days')); // "2018-08-13T00:00:00-03:00 America/Santiago"
fmt(x.clone().subtract(2, 'days')); // "2018-08-12T01:00:00-03:00 America/Santiago" - hour not preserved, but date changed
fmt(x.clone().subtract(3, 'days')); // "2018-08-11T00:00:00-04:00 America/Santiago" - original hour preserved now

Kami menerima laporan lain tentang masalah ini, dari pengguna dengan America/Asuncion sebagai zona waktunya.

Fungsi yang menghasilkan bug dalam aplikasi kita terlihat seperti ini:

function generateDayRange(start, end) {
    const days = [];
    let current = start.clone();

    while (current <= end) {
        days.push(current.clone());
        current = current.add(1, 'days');
    }

    return days;
}

Saat kami menggunakan fungsi ini, kami meneruskan tanggal mulai di awal hari, dan tanggal akhir di mana waktunya tidak terlalu signifikan:

generateDayRange(startDate.clone().startOf('week'), startDate.clone().endOf('week'));
generateDayRange(getAppDate(startDate), getAppDate(endDate));
generateDayRange(startRange, startRange.clone().add(27, 'days'));

Masalahnya adalah deretan tanggal momen yang dihasilkan berisi hari duplikat, yang tercermin dalam UI aplikasi kita (kalender memiliki hari duplikat).

Kekhawatiran saya yang lebih besar adalah karena perpustakaan tidak berperilaku seperti yang diharapkan, mungkin ada masalah yang lebih halus yang tidak diketahui, seperti hilangnya data, permintaan buruk, dll.

Saya akan bekerja untuk mendapatkan serangkaian tes dalam codepen atau sesuatu, sehingga pembaca yang penasaran dapat mencoba memecahkan masalah ini.

Masalah terkait # 4785 memiliki tautan ke kode:
contoh momen https://runkit.com/embed/1r62d83amq7x
namun luxon menangani ini dengan benar
https://runkit.com/embed/t49achvensqf

Saya tahu ada perubahan DST baru-baru ini, jadi kami harus memeriksa kode yang saat ini ada di develop (tetapi belum dirilis). Atau, kita bisa menunggu hingga rilis berikutnya untuk melihat apakah semuanya masih sama.

Perilaku di sini sepertinya diubah antara zona waktu-momen v0.5.4 dan v0.5.26.
Di versi lama, menambahkan 1 hari ke '2018-08-11' memberi Anda jam 11 malam pada hari yang sama
Dalam versi baru, ini memberi Anda '2018-08-12' di tengah malam. Tetapi kali ini sebenarnya tidak ada, dan menambahkan 1 menit kembali ke satu jam - tetapi perubahan DST ini seharusnya menambahkan 1 jam.

Versi lama, berperilaku seperti yang dijelaskan dalam masalah ini:
https://jsfiddle.net/eqyvuxht/1/

Versi baru, perilaku berubah, tapi saya pikir masih salah?
https://jsfiddle.net/0h6atn4b/4/

Berikut ini solusinya:

function switchZone(m, zone) {
  let arr = [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second()];
  if(zone) {
    return moment.tz(arr, zone);
  }
  return moment(arr);
}

function safeAddDays(m, days) {
   let oldZone = m.tz();
   let utc = switchZone(m, 'UTC');
   utc.add(days, 'days');
   return switchZone(utc, oldZone);
}

Kami mengalami masalah yang sama bulan ini (Sept 2019) di mana pengguna Santiago kami menggunakan alat pilih tanggal kami melihat kalender dengan tanggal 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, untuk bulan September ... Di balik terpal, kami juga menggunakan .add(1, 'days') . Ini akan terjadi lagi tahun depan pada September 2020 jika kita belum bisa memperbaikinya saat itu.

Solusi @mblandfo tampaknya juga berhasil bagi kami. Terima kasih!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat