ลบเวลาโดยใช้วันที่และทุบตี


18

คำถามอื่น ๆ ทั้งหมดเกี่ยวกับเครือข่าย SE จะจัดการกับสถานการณ์ที่สมมติว่าวันนั้นเป็นnow( Q ) หรือเมื่อมีการระบุเฉพาะวันที่ ( Q )

สิ่งที่ฉันต้องการทำคือระบุวันที่และเวลาจากนั้นลบเวลาออก
นี่คือสิ่งที่ฉันลองก่อน:

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

ผลลัพธ์นี้ใน2018-12-10 06:39:55- มันเพิ่ม 7 ชั่วโมง จากนั้นหัก 20:05 นาที

หลังจากอ่านmanและinfoหน้าของdateฉันคิดว่าฉันได้รับการแก้ไขด้วย:

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

แต่ผลลัพธ์เดียวกัน ใช้เวลา 7 ชั่วโมงจากที่ไหน?

ฉันลองวันอื่นด้วยเช่นกันเพราะฉันคิดว่าบางทีเราอาจมีวินาทีกระโดด 7200 วินาทีในวันนั้นใครจะรู้ฮ่า ๆ ๆ แต่ผลลัพธ์เดียวกัน

ตัวอย่างเพิ่มเติม:

$ 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

แต่ที่นี่มันน่าสนใจ หากฉันเว้นเวลาในการป้อนข้อมูลก็จะทำงานได้ดี:

$ 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

ฉันกำลังคิดถึงอะไร

ปรับปรุง:ฉันได้เพิ่มZในตอนท้ายและมันเปลี่ยนพฤติกรรม:

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

ฉันยังคงสับสนอยู่ มีไม่มากในหน้าข้อมูล GNUเกี่ยวกับวันที่

ฉันเดาว่านี่เป็นปัญหาเกี่ยวกับเขตเวลา แต่อ้างถึงThe Calendar WikiบนISO 8601 :

หากไม่มีการให้ข้อมูลความสัมพันธ์ UTC กับการแสดงเวลาเวลาจะถือว่าเป็นเวลาท้องถิ่น

ฉันต้องการอะไร เวลาท้องถิ่นของฉันตั้งค่าไว้ถูกต้องเช่นกัน ฉันไม่แน่ใจว่าทำไมวันที่จะยุ่งกับเขตเวลาในกรณีที่ง่าย ๆ นี้ของฉันที่ให้เวลาและต้องการลบบางสิ่งออกจากมัน ไม่ควรลบชั่วโมงจากสตริงวันที่ก่อนใช่หรือไม่ แม้ว่ามันจะแปลงเป็นวันที่ก่อนจากนั้นก็ทำการลบถ้าฉันลบการลบออกไปฉันก็จะได้สิ่งที่ต้องการ:

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

ดังนั้นหากนี่เป็นปัญหาเขตเวลาอย่างแท้จริงความบ้าคลั่งนั้นมาจากไหน?


1
นอกจากนี้: serverfault.com/questions/952279/…
B Layer

คำตอบ:


20

ตัวอย่างสุดท้ายที่ควรจะได้ชี้แจงสิ่งสำหรับคุณ: เขตเวลา

$ 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

เนื่องจากเอาต์พุตแตกต่างกันไปตามเขตเวลาอย่างชัดเจนฉันจึงสงสัยว่าจะใช้ค่าเริ่มต้นที่ไม่ชัดเจนสำหรับสตริงเวลาโดยไม่ระบุเขตเวลา การทดสอบค่าสองสามอย่างดูเหมือนว่าจะเป็นUTC-05: 00แต่ฉันไม่แน่ใจว่ามันคืออะไร

$ 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

มันจะใช้เฉพาะเมื่อดำเนินการทางคณิตศาสตร์วันที่


ดูเหมือนว่าปัญหาที่นี่คือการที่- 2 hoursจะไม่นำมาเป็นทางคณิตศาสตร์ แต่เป็นระบุเขตเวลา :

# 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

ดังนั้นไม่เพียง แต่จะไม่มีการคำนวณทางคณิตศาสตร์ แต่ดูเหมือนว่าจะมีการปรับเวลาตามฤดูกาลเป็นเวลา 1 ชั่วโมงซึ่งจะนำไปสู่เวลาที่ค่อนข้างไร้สาระสำหรับเรา

นอกจากนี้ยังถือสำหรับ:

# 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

การดีบักอีกเล็กน้อยการแยกวิเคราะห์ดูเหมือนว่า: 2019-01-19T05:00:00 - 2( -2เป็นเขตเวลา) และhours(= 1 ชั่วโมง) ด้วยการเพิ่มโดยนัย การดูว่าคุณใช้เวลากี่นาทีจะง่ายกว่าไหม

# 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

ดังนั้นเลขคณิตวันที่กำลังจะทำไม่ใช่แค่ที่เราขอ ¯ \ (ツ) / ¯


1
@ ตรวจสอบมันไม่แน่นอน; ฉันคิดว่าเขตเวลาเริ่มต้นนี้จะใช้เฉพาะเมื่อมีการเพิ่ม / ลบ (เปรียบเทียบTZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%Sกับ VS TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S)
Olorin

2
อาจเป็นข้อผิดพลาดที่เป็นไปได้dateตามมาตรฐาน ISO 8601 หากไม่มีการระบุเขตเวลาควรใช้เวลาท้องถิ่น (โซน) ซึ่งในกรณีของการดำเนินการทางคณิตศาสตร์จะไม่เกิดขึ้น ปัญหาที่แปลกมากสำหรับฉันว่า
ลูกปา

1
@confetti พบปัญหา: - 2 hoursถือเป็นตัวระบุเขตเวลาที่นี่
Olorin

2
ว้าว. ตอนนี้ที่เหมาะสม อย่างน้อยก็อธิบายได้ ขอบคุณมากที่คิดออก สำหรับฉันดูเหมือนว่า parser ของพวกเขาต้องการการปรับปรุง แต่อย่างชัดเจนด้วยรูปแบบและช่องว่างเช่นนี้คุณไม่ต้องการระบุเขตเวลาด้วย `- 2 ชั่วโมง 'และมันทำให้เกิดความสับสนอย่างแน่นอน หากพวกเขาไม่ต้องการอัปเดตตัวแยกวิเคราะห์อย่างน้อยคู่มือควรได้รับทราบเกี่ยวกับเรื่องนี้
confetti

1
วันนี้ฉันเรียนรู้เกี่ยวกับ--debugตัวเลือกของวันที่! คำอธิบายที่ดี
Jeff Schaller

6

ทำงานอย่างถูกต้องเมื่อคุณแปลงวันที่ป้อนเข้าเป็น ISO 8601 ก่อน:

$ 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

ขอบคุณที่ใช้งานได้จริง แต่มีคำอธิบายใด ๆ หรือไม่ ฉันได้เพิ่มข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ ISO 8601 ให้กับคำถามของฉันและฉันไม่เห็นว่าdateจะเกิดอะไรขึ้นถ้าไม่มีการลบออกเขตเวลาจะไม่ถูกแตะต้องและทุกอย่างเป็นไปตามที่คาดไว้โดยไม่ต้องป้อนอะไรเลย ข้อมูลหรือการแปลงเขตเวลา
ลูกปา

ฉันไม่สามารถบอกได้ขอโทษ หากคนอื่นสามารถตอบคำถามนี้ฉันก็มีความสุขเพราะฉันก็สงสัยเช่นกัน
pLumo

ฉันก็ดีใจเช่นกัน แต่ฉันจะยอมรับในตอนนี้เพราะมันช่วยแก้ไขปัญหาของฉันได้!
ลูกปา

3
วิธีนี้ใช้งานได้เนื่องจากdate -Iเอาต์พุตของตัวระบุเขตเวลา (เช่น2018-12-10T00:00:00+02:00) ซึ่งแก้ไขปัญหาที่อธิบายไว้ในคำตอบของ Olorin
ilkkachu

6

TLDR:นี่ไม่ใช่ข้อผิดพลาด คุณเคยพบเพียงแค่หนึ่งในพฤติกรรมที่บอบบาง dateแต่เอกสาร เมื่อดำเนินการทางคณิตศาสตร์เวลาที่มีการdateใช้รูปแบบเขตอิสระ (เช่นเวลา Unix) หรืออ่านมากอย่างรอบคอบเอกสารต้องรู้วิธีการอย่างถูกต้องใช้คำสั่งนี้


GNU dateใช้การตั้งค่าระบบของคุณ ( TZตัวแปรสภาพแวดล้อมหรือหากไม่มีการตั้งค่าเริ่มต้นระบบ) เพื่อกำหนดเขตเวลาของทั้งวันที่ป้อนด้วยตัวเลือก-d/ --dateและวันที่รายงานโดย+formatอาร์กิวเมนต์ ตัวเลือกนอกจากนี้ยังช่วยให้คุณสามารถแทนที่เขตเวลาสำหรับตัวเองตัวเลือกอาร์กิวเมนต์ แต่ก็ไม่ได้แทนที่เขตเวลาของ นั่นคือรากเหง้าของความสับสน IMHO--date+format

พิจารณาว่าเขตเวลาของฉันคือ UTC-6 เปรียบเทียบคำสั่งต่อไปนี้:

$ 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

คนแรกที่ใช้เขตเวลาของฉันสำหรับทั้งสองและ-d +formatคนที่สองใช้สำหรับ UTC แต่เขตเวลาของฉัน-d +formatอันที่สามใช้ UTC สำหรับทั้งคู่

ตอนนี้เปรียบเทียบการดำเนินการอย่างง่าย ๆ ต่อไปนี้:

$ 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

แม้ว่าเวลา Unix จะบอกฉันในสิ่งเดียวกันเวลา "ปกติ" จะแตกต่างกันเนื่องจากเขตเวลาของฉันเอง

ถ้าฉันต้องการที่จะดำเนินการเดียวกัน แต่ใช้เฉพาะเขตเวลาของฉัน:

$ 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

4
โหวตขึ้นสำหรับการกล่าวถึงกาลเวลา / Unix ซึ่ง imo เป็นวิธีที่มีเหตุผลอย่างเดียวในการคำนวณเวลา
Rui F Ribeiro

1
TL; DRหากคุณเพิ่งรู้ว่าเวลาท้องถิ่นของคุณชดเชยจากเวลาซูลู (เช่นฝั่งตะวันตกของสหรัฐอเมริกาคือ -8 ชั่วโมงหรือ-08:00) เพียงแค่ต่อท้ายคำสั่งนั้นเพื่อใส่วันที่และเวลา ... ไม่มีอะไรให้เล่นด้วย ตัวอย่าง: date -d '2019-02-28 14:05:36-08:00 +2 days 4 hours 3 seconds' +'local: %F %T'ให้ท้องถิ่น: 2019-03-02 18:05:36
B Layer

1
@ BLayer คุณพูดถูกมันทำงานได้ตามที่คาดไว้ในสถานการณ์นั้น แต่ลองจินตนาการถึงสถานการณ์ที่สคริปต์ที่กำหนดโดยใช้วิธีการนั้นดำเนินการในสถานที่ที่มีเขตเวลาที่แตกต่างกัน ผลลัพธ์ในขณะที่ถูกต้องทางเทคนิคอาจดูผิดปกติขึ้นอยู่กับข้อมูลที่ได้รับจาก+formatอาร์กิวเมนต์ ยกตัวอย่างเช่นในระบบของฉันที่ผลคำสั่งเดียวกันlocal: 2019-03-02 20:05:39(ถ้าฉันจะเพิ่ม%:zไป+formatมันจะกลายเป็นที่ชัดเจนว่าข้อมูลนั้นถูกต้องและความแตกต่างเป็นเพราะเขตเวลา)
nxnev

@ BLayer IMHO วิธีการทั่วไปที่ดีกว่าคือการใช้เขตเวลาเดียวกันสำหรับทั้ง-dและ+formatหรือ Unix ตามที่ฉันได้กล่าวไว้ในคำตอบเพื่อหลีกเลี่ยงผลลัพธ์ที่ไม่คาดคิด
nxnev

1
@nxnev เห็นด้วยถ้าคุณกำลังเขียนสคริปต์ที่จะเผยแพร่ / แชร์ คำเปิด "ถ้าคุณรู้เขตเวลาของคุณเอง" นอกเหนือจากที่มีความหมายตามตัวอักษรควรจะหมายถึงการใช้งานส่วนตัว / สบาย ๆ บางคนเผยแพร่สคริปต์ที่ใช้บางสิ่งบางอย่างบอบบางเมื่อรู้ว่าระบบของตัวเองอาจไม่ควรอยู่ในเกมแชร์สคริปต์ :) ในทางกลับกันที่ฉันทำ / จะใช้คำสั่งของตัวเองในทุกสภาพแวดล้อมส่วนตัวของฉัน
B Layer

4

GNU dateสนับสนุนการคำนวณวันที่อย่างง่ายแม้การคำนวณเวลาของยุคตามที่แสดงในคำตอบ @sudodus นั้นบางครั้งก็ชัดเจนขึ้น (และพกพาได้มากกว่า)

การใช้ +/- เมื่อไม่มีการระบุเขตเวลาในการประทับเวลาจะทำให้เกิดความพยายามในการจับคู่เขตเวลาถัดไปก่อนที่จะมีการแยกวิเคราะห์สิ่งอื่น

นี่เป็นวิธีหนึ่งในการทำใช้ "ที่ผ่านมา" แทน "-":

$ 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

หรือ

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

(แม้ว่าคุณจะไม่สามารถใช้ "Z" โดยพลการ แต่มันก็ใช้งานได้ในโซนของฉัน แต่มันทำให้มันเป็นเวลา UTC / GMT โซน - ใช้โซนของคุณเองหรือ% z /% Z โดยต่อท้าย${TZ:-$(date +%z)}ลงเวลาแทน)

การเพิ่มเงื่อนไขพิเศษของรูปแบบเหล่านี้ปรับเวลา:

  • "5 ชั่วโมงก่อน" ลบ 5 ชั่วโมง
  • เพิ่ม "4 ชั่วโมง" (โดยนัย) 4 ชั่วโมง
  • ดังนั้น "3 ชั่วโมงจึงเพิ่ม (ชัดแจ้ง) 3 ชั่วโมง (ไม่รองรับในรุ่นเก่ากว่า)

สามารถใช้การปรับเปลี่ยนที่ซับซ้อนหลายอย่างในลำดับใดก็ได้ (แม้ว่าคำที่เกี่ยวข้องและตัวแปรเช่น "14 สัปดาห์ดังนั้นเมื่อวันจันทร์ที่ผ่านมา" จะถามปัญหา ;-)

(มีอีก beartrap ขนาดเล็กที่นี่เช่นกันdateจะให้วันที่ที่ถูกต้องเสมอเพื่อdate -d "2019-01-31 1 month"ให้ 2019-03-03 เช่นเดียวกับ "เดือนถัดไป")

เมื่อกำหนดรูปแบบเวลาและวันที่ที่หลากหลายการแยกเขตเวลาจำเป็นต้องเป็นเรื่องยุ่งเหยิง: มันอาจเป็นคำต่อท้ายเดียวหรือหลายตัวอักษรหนึ่งชั่วโมงหรือหนึ่งชั่วโมง: ชดเชยเวลาหนึ่งนาทีชื่อ "America / Denver" (หรือแม้แต่ชื่อไฟล์ ในกรณีของTZตัวแปร)

2018-12-10T00:00:00รุ่นของคุณไม่ทำงานเนื่องจาก "T" เป็นเพียงตัวคั่นไม่ใช่เขตเวลาการเพิ่ม "Z" ในตอนท้ายทำให้การทำงานนั้น (ขึ้นอยู่กับความถูกต้องของโซนที่เลือก) ตามที่คาดไว้เช่นกัน

ดู: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html และในส่วนเฉพาะ 7.7


1
ตัวเลือกอื่น ๆ ได้แก่ : date -d "2018-12-10 00:00:00 now -5 hoursหรือtodayหรือ0 hourแทนnowสิ่งใดก็ตามที่บอกdateว่าโทเค็นหลังจากเวลาไม่ได้เป็นการชดเชยเขตเวลา
Stéphane Chazelas

1
หรือเพื่อความทั่วถึงอย่าทิ้งสิ่งใดหลังจากวันที่กำหนดเวลาโดยจัดเรียงลำดับใหม่: date -d "-5 ชั่วโมง 2018-12-10 00:00:00" `
B เลเยอร์

2

วิธีแก้ปัญหานี้ง่ายต่อการเข้าใจ แต่มีความซับซ้อนกว่าเล็กน้อยดังนั้นฉันจึงแสดงเป็น shellscript

  • แปลงเป็น 'วินาทีตั้งแต่ 1970-01-01 00:00:00 UTC'
  • เพิ่มหรือลบความแตกต่าง
  • แปลงกลับเป็นรูปแบบที่มนุษย์สามารถอ่านได้ด้วยdateบรรทัดคำสั่งสุดท้าย

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"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.