Trừ thời gian sử dụng ngày và bash


18

Tất cả các câu hỏi khác trên mạng SE xử lý các tình huống trong đó ngày được giả sử là now( Q ) hoặc trong đó chỉ một ngày được chỉ định ( Q ).

Những gì tôi muốn làm là cung cấp một ngày và thời gian, và sau đó trừ đi một thời gian từ đó.
Đây là những gì tôi đã thử đầu tiên:

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

Kết quả này trong 2018-12-10 06:39:55- Nó thêm 7 giờ. Sau đó trừ đi 20:05 phút.

Sau khi đọc maninfotrang date, tôi nghĩ rằng tôi có nó cố định với điều này:

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

Nhưng, kết quả tương tự. Nó thậm chí còn nhận được 7 giờ từ đâu?

Tôi cũng đã thử những ngày khác vì tôi nghĩ có lẽ chúng tôi đã có 7200 giây nhuận vào ngày hôm đó, ai biết lol. Nhưng kết quả tương tự.

Một vài ví dụ nữa:

$ 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

Nhưng ở đây nó trở nên thú vị. Nếu tôi bỏ qua thời gian trên đầu vào, nó hoạt động tốt:

$ 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

Tôi đang thiếu gì?

Cập nhật: Tôi đã thêm một Zở cuối và nó đã thay đổi hành vi:

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

Tôi vẫn còn bối rối. Không có nhiều về điều này trong trang thông tin GNU về ngày.

Tôi đoán đây là sự cố múi giờ, nhưng trích dẫn Lịch Wiki trên ISO 8601 :

Nếu không có thông tin quan hệ UTC nào được đưa ra với biểu diễn thời gian, thì thời gian được coi là theo giờ địa phương.

Đó là những gì tôi muốn. Giờ địa phương của tôi được đặt chính xác quá. Tôi không chắc tại sao ngày tháng lại gây rối với múi giờ trong trường hợp đơn giản này là tôi cung cấp một mốc thời gian và muốn trừ đi thứ gì đó từ nó. Không nên trừ số giờ từ chuỗi ngày đầu tiên? Ngay cả khi nó chuyển đổi nó thành một ngày đầu tiên và sau đó thực hiện phép trừ, nếu tôi bỏ qua bất kỳ phép trừ nào, tôi sẽ nhận được chính xác những gì tôi muốn:

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

Vậy NẾU đây thực sự là một vấn đề múi giờ, sự điên rồ đó đến từ đâu?


Câu trả lời:


20

Đó là ví dụ cuối cùng nên làm rõ mọi thứ cho bạn: múi giờ .

$ 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

Vì đầu ra thay đổi rõ ràng theo múi giờ, tôi nghi ngờ một số mặc định không rõ ràng được thực hiện cho chuỗi thời gian mà không có múi giờ được chỉ định. Thử nghiệm một vài giá trị, nó dường như là UTC-05: 00 , mặc dù tôi không chắc đó là gì.

$ 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

Nó chỉ được sử dụng khi thực hiện số học ngày.


Dường như vấn đề ở đây là - 2 hoursđược không lấy làm số học, nhưng như một specifier múi giờ :

# 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

Vì vậy, không chỉ không có số học được thực hiện, dường như có một tiết kiệm ánh sáng ban ngày 1 giờ điều chỉnh về thời gian, dẫn đến một thời gian hơi vô nghĩa đối với chúng tôi.

Điều này cũng giữ cho bổ sung:

# 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

Gỡ lỗi thêm một chút, phân tích cú pháp dường như là: 2019-01-19T05:00:00 - 2( -2là múi giờ) và hours(= 1 giờ), với một bổ sung ngụ ý. Nó trở nên dễ dàng hơn để xem nếu bạn sử dụng phút thay thế:

# 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

Vì vậy, tốt, số học ngày đang được thực hiện, không phải là số mà chúng tôi yêu cầu. ¯ \ (ツ) /


1
@confetti nó thực sự; Tôi nghĩ múi giờ mặc định này chỉ được sử dụng khi thêm / bớt (so sánh TZ=UTC date -d "2019-01-19T05:00:00Z - 2 hours" +%Y-%m-%d_%H:%M:%Svới TZ=UTC date -d "2019-01-19T05:00:00 - 2 hours" +%Y-%m-%d_%H:%M:%S)
Olorin

2
Có thể là một lỗi có thể xảy ra date, theo tiêu chuẩn ISO 8601, nếu không có múi giờ nào được cung cấp, nên sử dụng giờ địa phương (vùng), trong trường hợp hoạt động số học thì không. Vấn đề rất lạ với tôi mặc dù.
confetti

1
@confetti đã tìm thấy vấn đề: - 2 hoursđược coi là công cụ xác định múi giờ ở đây.
Olorin

2
Ồ Bây giờ bằng cách nào đó có ý nghĩa. Vâng, ít nhất nó giải thích nó. Cảm ơn bạn rất nhiều vì đã tìm ra điều đó. Đối với tôi có vẻ như trình phân tích cú pháp của họ cần một bản cập nhật, vì rõ ràng với định dạng và khoảng trắng như thế này, bạn không muốn chỉ định múi giờ theo `- 2 giờ` và nó chắc chắn gây nhầm lẫn. Nếu họ không muốn cập nhật trình phân tích cú pháp, ít nhất hướng dẫn sẽ nhận được ghi chú về điều này.
confetti

1
Hôm nay tôi đã biết về --debuglựa chọn của ngày ! Giải thích tuyệt vời.
Jeff Schaller

6

Nó hoạt động chính xác khi bạn chuyển đổi ngày đầu vào thành ISO 8601 trước:

$ 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

Cảm ơn bạn, điều đó thực sự làm việc, nhưng có bất kỳ lời giải thích cho điều này? Tôi đã thêm một số thông tin về điều ISO 8601 vào câu hỏi của mình và tôi thực sự không thấy nơi datenào sẽ làm rối tung điều này vì không có bất kỳ phép trừ nào được cung cấp, múi giờ không bị ảnh hưởng và mọi thứ cũng như mong đợi mà không phải cung cấp bất kỳ thông tin múi giờ hoặc chuyển đổi.
confetti

Tôi không thể nói, xin lỗi. Nếu ai đó có thể trả lời điều này, tôi sẽ rất vui vì tôi cũng đang tự hỏi.
pLumo

Tôi cũng rất vui nhưng tôi sẽ chấp nhận điều này ngay bây giờ vì nó đã khắc phục được vấn đề của tôi!
confetti

3
Điều này hoạt động vì date -Icũng xuất ra trình xác định múi giờ (ví dụ 2018-12-10T00:00:00+02:00) khắc phục sự cố được mô tả trong câu trả lời của
Olorin

6

TLDR: Đây không phải là một lỗi. Bạn vừa tìm thấy một trong những hành vi tinh vi nhưng được ghi lại date. Khi thực hiện thời gian số học với date, sử dụng một định dạng múi giờ độc lập (như thời gian Unix) hoặc đọc rất cẩn thận các tài liệu hướng dẫn để biết cách sử dụng đúng lệnh này.


GNU datesử dụng các cài đặt hệ thống của bạn ( TZbiến môi trường hoặc, nếu không đặt, mặc định hệ thống) để xác định múi giờ của cả ngày được cung cấp với tùy chọn -d/ --datevà ngày được báo cáo bởi +formatđối số. Các --datetùy chọn cũng cho phép bạn ghi đè lên các múi giờ cho riêng tùy chọn đối số của nó nhưng nó không ghi đè lên các múi giờ của +format. Đó là gốc rễ của sự nhầm lẫn, IMHO.

Xem xét rằng múi giờ của tôi là UTC-6, hãy so sánh các lệnh sau:

$ 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

Cái đầu tiên sử dụng múi giờ của tôi cho cả hai -d+format. Cái thứ hai sử dụng UTC cho -dnhưng múi giờ của tôi cho +format. Cái thứ ba sử dụng UTC cho cả hai.

Bây giờ, so sánh các hoạt động đơn giản sau đây:

$ 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

Ngay cả khi thời gian Unix đang nói với tôi điều tương tự, thời gian "Bình thường" vẫn khác biệt vì múi giờ của riêng tôi.

Nếu tôi muốn thực hiện thao tác tương tự nhưng chỉ sử dụng múi giờ của mình:

$ 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
Được khuyến khích để đề cập đến thời gian epoch / Unix, mà imo là cách duy nhất để làm số học thời gian
Rui F Ribeiro

1
TL; DR Nếu bạn chỉ biết thời gian bù giờ địa phương của mình từ giờ Zulu (ví dụ: bờ tây Hoa Kỳ là -8 giờ hoặc -08:00), chỉ cần thêm nó vào đầu vào ngày giờ ... không có gì khác để sử dụng. Ví dụ: date -d '2019-02-28 14:05:36-08:00 +2 days 4 hours 3 seconds' +'local: %F %T'cung cấp cho địa phương: 2019/03/02 18:05:36
Lớp B

1
@BLayer Bạn nói đúng, nó hoạt động như mong đợi trong kịch bản đó. Nhưng hãy tưởng tượng tình huống trong đó một tập lệnh đã cho sử dụng phương pháp đó được thực thi ở một nơi có múi giờ khác. Đầu ra, trong khi đúng về mặt kỹ thuật, có thể trông sai tùy thuộc vào thông tin được cung cấp bởi +formatđối số. Chẳng hạn, trên hệ thống của tôi, cùng một lệnh xuất ra: local: 2019-03-02 20:05:39(nếu tôi thêm %:zvào +format, rõ ràng thông tin là chính xác và sự khác biệt là do các múi giờ).
nxnev

@BLayer IMHO, một cách tiếp cận chung tốt hơn sẽ là sử dụng cùng một múi giờ cho cả thời gian -d+formathoặc Unix, như tôi đã nói trong câu trả lời, để tránh kết quả không mong muốn.
nxnev

1
@nxnev Đồng ý, nếu bạn đang viết một kịch bản sẽ được xuất bản / chia sẻ. Các từ mở đầu "nếu bạn biết múi giờ của riêng bạn", bên cạnh việc có nghĩa đen, được cho là ngụ ý sử dụng thông thường / cá nhân. Ai đó xuất bản một kịch bản dựa trên một cái gì đó mỏng manh như chỉ biết hệ thống của riêng họ có lẽ không nên có trong trò chơi chia sẻ kịch bản. :) Mặt khác, tôi làm / sẽ sử dụng lệnh riêng của mình trên tất cả các môi trường cá nhân.
Lớp B

4

GNU datekhông hỗ trợ số học ngày đơn giản, mặc dù các phép tính thời gian kỷ nguyên như trong câu trả lời của @sudodus đôi khi rõ ràng hơn (và dễ mang theo hơn).

Việc sử dụng +/- khi không có múi giờ được chỉ định trong dấu thời gian sẽ kích hoạt nỗ lực khớp với múi giờ tiếp theo, trước khi mọi thứ khác được phân tích cú pháp.

Đây là một cách để làm điều đó, sử dụng "trước" thay vì "-":

$ 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

hoặc là

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

(Mặc dù bạn không thể tùy tiện sử dụng "Z", nó hoạt động trong khu vực của tôi, nhưng mà làm cho nó một khu vực timestamp UTC / GMT - sử dụng khu vực của riêng bạn, hoặc% z /% Z bằng cách phụ thêm ${TZ:-$(date +%z)}. Để dấu thời gian thay vì)

Thêm điều khoản thời gian thêm của các hình thức này điều chỉnh thời gian:

  • "5 giờ trước" trừ 5 giờ
  • "4 giờ" thêm (ngầm) 4 giờ
  • "3 giờ do đó" thêm (rõ ràng) 3 giờ (không được hỗ trợ trong các phiên bản cũ hơn)

Nhiều điều chỉnh phức tạp, theo bất kỳ thứ tự nào, có thể được sử dụng (mặc dù các thuật ngữ tương đối và biến đổi như "14 tuần kể từ thứ hai tuần trước" đang yêu cầu sự cố ;-)

(Cũng có một con gấu nhỏ khác ở đây, datesẽ luôn đưa ra một ngày hợp lệ, do đó, date -d "2019-01-31 1 month"đưa ra 2019-03-03, cũng như "tháng tới")

Với nhiều định dạng ngày và giờ được hỗ trợ, phân tích múi giờ nhất thiết phải cẩu thả: nó có thể là hậu tố đơn hoặc nhiều chữ cái, bù giờ hoặc giờ: phút, tên "America / Denver" (hoặc thậm chí là tên tệp trong trường hợp của TZbiến).

2018-12-10T00:00:00Phiên bản của bạn không hoạt động vì "T" chỉ là một dấu phân cách, không phải là múi giờ, việc thêm "Z" vào cuối sẽ khiến công việc đó (tùy thuộc vào tính chính xác của vùng được chọn) như mong đợi.

Xem: https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html và đặc biệt là phần 7.7.


1
Các tùy chọn khác bao gồm : date -d "2018-12-10 00:00:00 now -5 hours, hoặc todayhoặc 0 hourthay vì now, bất cứ điều gì cho biết datemã thông báo sau thời gian không phải là phần bù múi giờ.
Stéphane Chazelas

1
Hoặc, vì mục đích kỹ lưỡng, không để lại gì sau thời gian hẹn hò bằng cách sắp xếp lại các đối số: ngày -d "-5 giờ 2018-12-10 00:00:00" `
Lớp B

2

Giải pháp này rất dễ hiểu, nhưng phức tạp hơn một chút, vì vậy tôi hiển thị nó dưới dạng shellscript.

  • chuyển đổi thành 'giây kể từ 1970-01-01 00:00:00 UTC'
  • cộng hoặc trừ chênh lệch
  • chuyển đổi trở lại định dạng có thể đọc được bằng datedòng lệnh cuối cùng

Vỏ sò:

#!/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"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.