Kurangi waktu menggunakan tanggal dan pesta

18

Semua pertanyaan lain pada jaringan SE berurusan dengan skenario di mana salah satu tanggal diasumsikan now( Q ) atau di mana hanya tanggal yang ditentukan ( Q ).

Yang ingin saya lakukan adalah memberikan tanggal dan waktu, lalu kurangi waktu dari itu.
Inilah yang saya coba pertama kali:

date -d "2018-12-10 00:00:00 - 5 hours - 20 minutes - 5 seconds"

Ini menghasilkan 2018-12-10 06:39:55- Ini menambahkan 7 jam. Kemudian kurangi 20:05 menit.

Setelah membaca mandan infohalaman date, saya pikir saya sudah memperbaikinya dengan ini:

date -d "2018-12-10T00:00:00 - 5 hours - 20 minutes - 5 seconds"

Tapi hasilnya sama. Dari mana bahkan mendapat 7 jam dari?

Saya mencoba kencan lain juga karena saya pikir mungkin kami memiliki 7200 detik kabisat pada hari itu, siapa tahu lol. Namun hasilnya sama.

Beberapa contoh lagi:

$ date -d "2018-12-16T00:00:00 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-17_02:00:00

$ date -d "2019-01-19T05:00:00 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-19_08:55:00

Tapi di sini menjadi menarik. Jika saya menghilangkan waktu pada input, itu berfungsi dengan baik:

$ date -d "2018-12-16 - 24 hours" +%Y-%m-%d_%H:%M:%S
2018-12-15_00:00:00

$ date -d "2019-01-19 - 2 hours - 5 minutes" +%Y-%m-%d_%H:%M:%S
2019-01-18_21:55:00

$ date --version
date (GNU coreutils) 8.30

Apa yang saya lewatkan?

Pembaruan: Saya telah menambahkan Zdi akhir, dan itu mengubah perilaku:

$ date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_04:00:00

Saya masih bingung. Tidak banyak tentang ini di halaman info GNU tentang tanggal.

Saya menduga ini adalah masalah zona waktu, tetapi mengutip The Calendar Wiki pada ISO 8601 :

Jika tidak ada informasi hubungan UTC diberikan dengan representasi waktu, waktu diasumsikan dalam waktu lokal.

Itulah yang saya inginkan. Waktu lokal saya diatur dengan benar juga. Saya tidak yakin mengapa kencan akan mengacaukan zona waktu sama sekali dalam kasus sederhana ini tentang saya menyediakan suatu waktu dan ingin mengurangi sesuatu darinya. Bukankah seharusnya mengurangi jam dari string tanggal terlebih dahulu? Bahkan jika itu mengubahnya menjadi kencan pertama dan kemudian melakukan pengurangan, jika saya meninggalkan pengurangan saya mendapatkan apa yang saya inginkan:

$ date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S
2019-01-19_05:00:00

Jadi JIKA ini benar-benar masalah zona waktu, dari mana datangnya kegilaan itu?

konfeti
sumber

Jawaban:

20

Contoh terakhir itu seharusnya menjelaskan hal-hal untuk Anda: zona waktu .

$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S
2019-01-19_03:00:00
$ TZ=Asia/Colombo date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S 
2019-01-19_08:30:00

Karena outputnya jelas bervariasi berdasarkan zona waktu, saya menduga beberapa default yang tidak jelas diambil untuk string waktu tanpa zona waktu yang ditentukan. Menguji beberapa nilai, tampaknya UTC-05: 00 , meskipun saya tidak yakin apa itu.

$ TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_08:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%S%Z
2019-01-19_03:00:00UTC
$ TZ=UTC date -d "2019-01-19T05:00:00" +%Y-%m-%d_%H:%M:%S%Z           
2019-01-19_05:00:00UTC

Ini hanya digunakan saat melakukan aritmatika tanggal.


Tampaknya masalah di sini adalah bahwa - 2 hoursini tidak diambil sebagai aritmatika, tetapi sebagai sebuah specifier zona waktu :

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547884800 epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547884800.000000000 (epoch-seconds)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC)
date: final: (Y-M-D) 2019-01-19 08:00:00 (UTC+00)
2019-01-19_08:00:00UTC

Jadi, tidak hanya tidak ada aritmatika yang dilakukan, tampaknya ada penghematan siang hari penyesuaian waktu 1 jam pada waktu itu, yang mengarah ke waktu yang agak tidak masuk akal bagi kita.

Ini juga berlaku untuk tambahan:

# TZ=UTC date -d "2019-01-19T05:00:00 + 5:30 hours" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC+05:30
date: parsed relative part: +1 hour(s)
date: input timezone: parsed date/time string (+05:30)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=+05:30' = 1547854200 epoch-seconds
date: after time adjustment (+1 hours, +0 minutes, +0 seconds, +0 ns),
date:     new time = 1547857800 epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547857800.000000000 (epoch-seconds)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC)
date: final: (Y-M-D) 2019-01-19 00:30:00 (UTC+00)
2019-01-19_00:30:00UTC

Debugging sedikit lebih, penguraian tampaknya menjadi: 2019-01-19T05:00:00 - 2( -2menjadi zona waktu), dan hours(= 1 jam), dengan tambahan tersirat. Menjadi lebih mudah untuk melihat jika Anda menggunakan menit sebagai gantinya:

# TZ=UTC date -d "2019-01-19T05:00:00 - 2 minutes" +%Y-%m-%d_%H:%M:%S%Z --debug
date: parsed datetime part: (Y-M-D) 2019-01-19 05:00:00 UTC-02
date: parsed relative part: +1 minutes
date: input timezone: parsed date/time string (-02)
date: using specified time as starting value: '05:00:00'
date: starting date/time: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02'
date: '(Y-M-D) 2019-01-19 05:00:00 TZ=-02' = 1547881200 epoch-seconds
date: after time adjustment (+0 hours, +1 minutes, +0 seconds, +0 ns),
date:     new time = 1547881260 epoch-seconds
date: timezone: TZ="UTC" environment value
date: final: 1547881260.000000000 (epoch-seconds)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC)
date: final: (Y-M-D) 2019-01-19 07:01:00 (UTC+00)
2019-01-19_07:01:00UTC

Jadi, well, tanggal aritmatika sedang dilakukan, hanya saja bukan yang kita minta. ¯ \ (ツ) / ¯

Olorin
sumber
1
@ confetti itu memang; Saya pikir zona waktu default ini hanya digunakan ketika menambah / mengurangi (membandingkan TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%Svs TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S)
Olorin
2
Bisa jadi bug yang mungkin terjadi date, sesuai standar ISO 8601, jika tidak ada zona waktu yang disediakan, waktu lokal (zona) harus diasumsikan, yang dalam kasus operasi aritmatika, bukan. Masalah yang sangat aneh bagi saya.
confetti
1
@ confetti menemukan masalahnya: - 2 hoursdiambil sebagai penentu zona waktu di sini.
Olorin
2
Wow. Sekarang entah bagaimana itu masuk akal. Yah, setidaknya itu menjelaskannya. Terima kasih banyak untuk memikirkannya. Bagi saya kedengarannya seperti parser mereka membutuhkan pembaruan, karena jelas dengan format dan spasi seperti ini Anda tidak ingin menentukan zona waktu dengan `- 2 jam` dan itu pasti menyebabkan kebingungan. Jika mereka tidak ingin memperbarui parser, setidaknya manual harus mendapat catatan tentang ini.
confetti
1
Hari ini saya belajar tentang --debugopsi kencan ! Penjelasan yang bagus.
Jeff Schaller
6

Ini berfungsi dengan benar ketika Anda mengonversi tanggal input ke ISO 8601 terlebih dahulu:

$ date -d "$(date -Iseconds -d "2018-12-10 00:00:00") - 5 hours - 20 minutes - 5 seconds"
So 9. Dez 18:39:55 CET 2018
pLumo
sumber
Terima kasih, itu memang berhasil, tetapi apakah ada penjelasan untuk ini? Saya telah menambahkan beberapa info lebih lanjut tentang ISO 8601 untuk pertanyaan saya, dan saya benar-benar tidak melihat di mana dateakan mengacaukan ini tanpa pengurangan yang diberikan, zona waktu dibiarkan tidak tersentuh dan semuanya seperti yang diharapkan juga tanpa harus memasok apa pun informasi zona waktu atau konversi.
confetti
Saya tidak tahu, maaf. Jika orang lain bisa menjawab ini, saya akan senang karena saya juga bertanya-tanya.
pLumo
Saya akan senang juga, tetapi saya akan menerima ini untuk saat ini karena itu memperbaiki masalah saya!
confetti
3
Ini berfungsi karena date -Ijuga menampilkan penentu zona waktu (mis. 2018-12-10T00:00:00+02:00) Yang memperbaiki masalah yang dijelaskan dalam jawaban
Olorin
6

TLDR: Ini bukan bug. Anda baru saja menemukan salah satu perilaku yang halus namun terdokumentasi date. Saat melakukan aritmatika waktu date, gunakan format mandiri zona waktu (seperti waktu Unix) atau baca dengan cermat dokumentasi untuk mengetahui cara menggunakan perintah ini dengan benar.


GNU datemenggunakan pengaturan sistem Anda ( TZvariabel lingkungan atau, jika tidak disetel, default sistem) untuk menentukan zona waktu dari tanggal yang diumpankan dengan opsi -d/ --datedan tanggal yang dilaporkan oleh +formatargumen. The --datepilihan juga memungkinkan Anda mengabaikan zona waktu untuk opsi-argumen sendiri tetapi tidak mengesampingkan zona waktu +format.Itulah akar kebingungan, IMHO.

Mengingat zona waktu saya adalah UTC-6, bandingkan perintah berikut:

$ date -d '1970-01-01 00:00:00' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 -06:00
Unix: 21600
$ date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1969-12-31 18:00:00 -06:00
Unix: 0
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 00:00:00 +00:00
Unix: 0

Yang pertama menggunakan zona waktu saya untuk keduanya -ddan +format. Yang kedua menggunakan UTC untuk -dtetapi zona waktu saya untuk +format. Yang ketiga menggunakan UTC untuk keduanya.

Sekarang, bandingkan operasi sederhana berikut:

$ date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-01 18:00:00 -06:00
Unix: 86400
$ TZ='UTC0' date -d '1970-01-01 00:00:00 UTC +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 +00:00
Unix: 86400

Bahkan jika waktu Unix mengatakan hal yang sama kepada saya, waktu "Normal" berbeda karena zona waktu saya sendiri.

Jika saya ingin melakukan operasi yang sama tetapi hanya menggunakan zona waktu saya:

$ TZ='CST+6' date -d '1970-01-01 00:00:00 -06:00 +1 day' '+Normal: %F %T %:z%nUnix: %s'
Normal: 1970-01-02 00:00:00 -06:00
Unix: 108000
nxnev
sumber
4
Terpilih untuk menyebutkan zaman / waktu Unix, yang imo adalah satu-satunya cara yang waras untuk melakukan aritmatika waktu
Rui F Ribeiro
1
TL; DR Jika Anda hanya tahu waktu lokal Anda diimbangi dari waktu Zulu (mis. Pantai barat USA adalah -8 jam atau -08:00), cukup tambahkan itu untuk memasukkan waktu-tanggal ... tidak ada hal lain yang perlu diutak-atik. Contoh: date -d '2019-02-28 14:05:36-08:00 +2 days 4 hours 3 seconds' +'local: %F %T'beri lokal: 2019-03-02 18:05:36
B Layer
1
@BLayer Anda benar, ini berfungsi seperti yang diharapkan dalam skenario itu. Tapi bayangkan situasi di mana skrip yang diberikan menggunakan pendekatan itu dijalankan di tempat dengan zona waktu yang berbeda. Output, sementara secara teknis benar, mungkin terlihat salah tergantung pada informasi yang diberikan oleh +formatargumen. Misalnya, pada sistem saya, perintah yang sama output: local: 2019-03-02 20:05:39(jika saya menambahkan %:zuntuk +format, menjadi jelas bahwa info yang benar dan perbedaan tersebut adalah karena zona waktu).
nxnev
@Layer IMHO, pendekatan umum yang lebih baik adalah dengan menggunakan zona waktu yang sama untuk keduanya -ddan +formatatau waktu Unix, seperti yang saya katakan dalam jawaban, untuk menghindari hasil yang tidak terduga.
nxnev
1
@nxnev Setuju, jika Anda menulis skrip untuk dipublikasikan / dibagikan. Kata-kata pembuka "jika Anda tahu zona waktu Anda sendiri", selain memiliki arti harfiah, seharusnya menyiratkan penggunaan biasa / pribadi. Seseorang yang menerbitkan skrip mengandalkan sesuatu yang tipis seperti hanya mengetahui sistem mereka sendiri, mungkin seharusnya tidak ada dalam permainan berbagi skrip. :) Di sisi lain saya lakukan / akan menggunakan perintah saya sendiri di semua lingkungan pribadi saya.
B Layer
4

GNU datemendukung aritmatika tanggal sederhana, meskipun perhitungan waktu seperti yang ditunjukkan dalam jawaban @sudodus terkadang lebih jelas (dan lebih portabel).

Penggunaan +/- ketika tidak ada zona waktu yang ditentukan dalam stempel waktu memicu upaya untuk mencocokkan zona waktu berikutnya, sebelum yang lainnya diuraikan.

Ini salah satu cara untuk melakukannya, gunakan "lalu" daripada "-":

$ date -d "2018-12-10 00:00:00 5 hours ago 20 minutes ago 5 seconds ago"
Sun Dec  9 18:39:55 GMT 2018

atau

$ date -d "2018-12-10 00:00:00Z -5 hours -20 minutes -5 seconds"
Sun Dec  9 18:39:55 GMT 2018

(Meskipun Anda tidak dapat secara sewenang-wenang menggunakan "Z", itu berfungsi di zona saya, tetapi itu menjadikannya cap waktu zona UTC / GMT - gunakan zona Anda sendiri, atau% z /% Z dengan menambahkan ${TZ:-$(date +%z)}ke stempel waktu sebagai gantinya.)

Menambahkan syarat waktu tambahan dari formulir ini sesuaikan waktu:

  • "5 jam yang lalu" kurangi 5 jam
  • "4 jam" tambahkan (implisit) 4 jam
  • "3 jam karenanya" tambahkan (eksplisit) 3 jam (tidak didukung dalam versi yang lebih lama)

Banyak penyesuaian kompleks, dalam urutan apa pun, dapat digunakan (meskipun istilah relatif dan variabel seperti "14 minggu maka hari Senin terakhir" meminta masalah ;-)

(Ada beartrap kecil lain di sini juga, dateakan selalu memberikan tanggal yang valid, jadi date -d "2019-01-31 1 month"berikan 2019-03-03, seperti halnya "bulan depan")

Mengingat beragamnya format waktu dan tanggal yang didukung, penguraian zona waktu menjadi ceroboh: itu bisa berupa sufiks tunggal atau multi-huruf, satu jam atau satu jam: offset menit, nama "America / Denver" (atau bahkan nama file) dalam kasus TZvariabel).

Anda 2018-12-10T00:00:00versi tidak bekerja karena "T" hanya pembatas, tidak zona waktu, menambahkan "Z" di akhir membuat pekerjaan (tunduk kebenaran zona dipilih) seperti yang diharapkan juga.

Lihat: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html dan di bagian tertentu 7.7.

mr.spuratic
sumber
1
Pilihan lain termasuk:, date -d "2018-12-10 00:00:00 now -5 hoursatau todayatau 0 hourbukannya now, apa pun yang memberi tahu datebahwa token setelah waktu tersebut bukan offset zona waktu.
Stéphane Chazelas
1
Atau, demi ketelitian, tidak meninggalkan apa pun setelah tanggal-waktu dengan menata ulang argumen: date -d "-5 jam 2018-12-10 00:00:00" `
B Layer
2

Solusi ini mudah dimengerti, tetapi sedikit lebih rumit, jadi saya menunjukkannya sebagai shellscript.

  • konversi ke 'detik sejak 1970-01-01 00:00:00 UTC'
  • tambahkan atau kurangi perbedaannya
  • mengonversi kembali ke format yang dapat dibaca manusia dengan datebaris perintah terakhir

Shellscript:

#!/bin/bash

startdate="2018-12-10 00:00:00"

ddif="0"          # days
diff="-5:-20:-5"  # hours:minutes:seconds

#-----------------------------------------------------------------------------

ss1970in=$(date -d "$startdate" "+%s")  # seconds since 1970-01-01 00:00:00 UTC
printf "%11s\n" "$ss1970in"

h=${diff%%:*}
m=${diff#*:}
m=${m%:*}
s=${diff##*:}
difs=$(( (((ddif*24+h)*60)+m)*60+s ))
printf "%11s\n" "$difs"

ss1970ut=$((ss1970in + difs))  # add/subtract the time difference
printf "%11s\n" "$ss1970ut"

date -d "@$ss1970ut" "+%Y-%m-%d %H:%M:%S"
sudodus
sumber