Tarih ve bash kullanarak saati çıkarma


19

SE ağındaki diğer tüm sorular, tarihin now( Q ) olduğu veya yalnızca bir tarihin belirtildiği ( Q ) senaryolarla ilgilidir .

Ne yapmak istiyorum bir tarih ve saat sağlamak ve daha sonra bundan bir zaman çıkarmak.
İlk önce denediklerim:

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

Bu sonuç 2018-12-10 06:39:55- 7 saat ekledi. Sonra 20:05 dakika çıkarıldı.

manVe infosayfasını okuduktan sonra date, bununla düzelttiğimi düşündüm:

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

Ama aynı sonuç. 7 saati nereden alıyor?

Diğer günleri de denedim çünkü belki o gün 7200 artık saniye yaşadığımızı düşündüm. Ama aynı sonuçlar.

Birkaç örnek daha:

$ 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

Ama burada ilginç oluyor. Girişteki zamanı atlarsam iyi çalışır:

$ 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

Neyi kaçırıyorum?

Güncelleme:Z Sonuna bir ekledim ve davranışı değiştirdi:

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

Yine de kafam karıştı. GNU bilgi sayfasında tarihle ilgili çok fazla bir şey yok .

Bunun bir zaman dilimi konudur tahmin yürütüyorum ama alıntı yapıyorum Takvim Wiki üzerinde ISO 8601 :

Zaman temsili ile UTC ilişkisi bilgisi verilmezse, zamanın yerel saatte olduğu varsayılır.

İstediğim bu. Yerel saatim de doğru ayarlanmış. Bu basit durumda datetime sağlamak ve ondan bir şey çıkarmak isteyen tarihin neden saat dilimiyle uğraşacağından emin değilim. Saatleri önce tarih dizesinden çıkarmamalı mı? Önce bir tarihe dönüştürüp sonra çıkarma yaparsa bile, herhangi bir çıkarma çıkarırsam tam olarak istediğimi alırım:

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

Yani EĞER bu gerçekten zaman dilimi konudur, nerede çılgınlık nereden geliyor?


Yanıtlar:


21

Bu son örnekte sizin için bir şeyler açıklığa kavuşmuş olmalıydı: zaman dilimleri .

$ 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

Çıktı saat dilimine göre açıkça değiştiğinden, bir saat dilimi için belirtilen bir saat dizisi için alınan bazı belirsiz varsayılanlardan şüphelenirim. Birkaç değeri test etmek, UTC-05: 00 gibi görünüyor , ancak bunun ne olduğundan emin değilim.

$ 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

Yalnızca tarih aritmetiği yapılırken kullanılır.


Burada mesele olduğunu görünüyor - 2 hoursedilir değil aritmetik olarak alınan, ancak bir saat dilimi belirteci :

# 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

Yani, sadece aritmetik yapılmakla kalmıyor, aynı zamanda bizim için biraz saçma bir zamana yol açan gün ışığından yararlanma saati 1 saat ayarlaması var gibi görünüyor .

Bu ayrıca ekleme için de geçerlidir:

# 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

Biraz daha hata ayıklama, ayrıştırma şöyledir: 2019-01-19T05:00:00 - 2( -2saat dilimi olmak üzere) ve hours(= 1 saat), zımni bir ek ile. Bunun yerine dakika kullanıp kullanmadığınızı görmek kolaylaşır:

# 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

Yani, tarih aritmetiği yapılıyor, sadece istediğimiz değil. ¯ \ (ツ) / ¯


1
@confetti gerçekten yapar; Bence bu varsayılan saat dilimi yalnızca toplama / çıkarma (karşılaştırma 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) sırasında kullanılır
Olorin

2
dateISO 8601 standardına göre olası bir hata olabilir , eğer saat dilimi sağlanmazsa, yerel saat (bölge) varsayılmalıdır, bu da aritmetik bir işlem olması durumunda değildir. Ama bana çok garip bir konu.
konfeti

1
@confetti sorunu buldu: - 2 hourssaat dilimi belirteci olarak alınır.
Olorin

2
Vay. Şimdi bir şekilde mantıklı. En azından açıklıyor. Bunu anladığınız için çok teşekkür ederim. Bana göre ayrıştırıcı bir güncellemeye ihtiyaç duyuyor gibi geliyor, açıkça bir format ve beyaz boşluk ile `` 2 saat '' ile saat dilimini belirtmek istemiyorsunuz ve kesinlikle karışıklığa neden oluyor. Ayrıştırıcıyı güncellemek istemiyorlarsa, en azından kılavuz bu konuda bir not almalıdır.
konfeti

1
Bugün tarihin --debugseçeneğini öğrendim ! Harika bir açıklama.
Jeff Schaller

6

Önce giriş tarihini ISO 8601'e dönüştürdüğünüzde doğru çalışır:

$ 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

Teşekkürler, bu gerçekten işe yarıyor, ama bunun için bir açıklama var mı? Soruma ISO 8601 şey hakkında biraz daha bilgi ekledim ve gerçekten dateherhangi bir çıkarma olmadan bunu nereye karıştıracağını görmüyorum , saat dilimi dokunulmadan bırakıldı ve her şey de tedarik etmek zorunda kalmadan beklendiği gibi saat dilimi bilgisi veya dönüşüm.
konfeti

Söyleyemem, üzgünüm. Başka biri buna cevap verebilirse ben de mutlu olurum çünkü ben de merak ediyordum.
pLumo

Ben de memnun olurum ama şimdilik bunu kabul edeceğim çünkü sorunumu düzeltir!
konfeti

3
Bu da çalışır çünkü date -Iaynı zamanda Olorin'in cevabında2018-12-10T00:00:00+02:00 açıklanan sorunu gideren zaman dilimi belirleyicisini (örn. ) Verir
ilkkachu

6

TLDR: Bu bir hata değil. Adlı kullanıcının ince ama belgelenmiş davranışlarından birini buldunuz date. İle zaman aritmetiği yaparken date, saat diliminden bağımsız bir format kullanın (Unix zamanı gibi) veya okuyun çok düzgün bu komutu nasıl kullanılacağını bilmek dikkatle belgeleri.


GNU , hem / seçeneğiyle beslenen tarihin hem de bağımsız değişken tarafından bildirilen tarihin saat dilimini belirlemek dateiçin sistem ayarlarınızı ( TZortam değişkeni veya ayarlanmamışsa sistem varsayılanları) kullanır . Bu seçenek aynı zamanda kendi seçenek bağımsız değişkeni için saat dilimini geçersiz kılmanıza izin verir, ancak saat dilimini-d--date+format--date+format . Bu karışıklığın kökü IMHO.

Saat dilimimin UTC-6 olduğu düşünüldüğünde, aşağıdaki komutları karşılaştırın:

$ 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

İlki saat dilimimi hem -dve için kullanır +format. İkincisi için UTC kullanılır-d ancak saat dilimim için +format. Üçüncüsü her ikisi için UTC kullanır.

Şimdi, aşağıdaki basit işlemleri karşılaştırın:

$ 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 zamanı bana aynı şeyi söylüyor olsa bile, "Normal" zaman kendi saat dilimimden dolayı değişir.

Aynı işlemi yapmak, ancak yalnızca saat dilimimi kullanmak istersem:

$ 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

5
Dönem aritmetiği yapmanın tek aklı yolu olan epoch / Unix zamanından bahsettiği için seçildi
Rui F Ribeiro

1
TL; DR Zulu saatinden yerel saat uzaklığınızı biliyorsanız (ör. Batı kıyısı ABD -8 saat ya da -08:00), bunu tarih-saat girmek için ekleyin ... Örnek: yereldate -d '2019-02-28 14:05:36-08:00 +2 days 4 hours 3 seconds' +'local: %F %T' verir : 2019-03-02 18:05:36
B Katmanı

1
@BLayer Haklısınız, bu senaryoda beklendiği gibi çalışıyor. Ancak, bu yaklaşımı kullanan belirli bir komut dosyasının farklı bir saat dilimine sahip bir yerde yürütüldüğünü düşünün. Çıktı, teknik olarak doğru olsa da, +formatargüman tarafından sağlanan bilgilere bağlı olarak yanlış görünebilir . Örneğin, benim sistemde, aynı komut çıkışları: local: 2019-03-02 20:05:39(ı eklerseniz %:ziçin +format, bu bilgilerin doğru olup olmadığını ve tutarsızlık saat dilimleri nedeniyle olduğu belli olur).
nxnev

@BLayer IMHO, daha iyi bir genel yaklaşım ikisi için de aynı zaman dilimini kullanmak olacaktır -dve +formatben beklenmeyen sonuçlar önlemek için, cevaben olarak, veya Unix zaman.
nxnev

1
@nxnev Yayınlanacak / paylaşılacak bir komut dosyası yazıyorsanız kabul ediyorum. "Kendi saat diliminizi biliyorsanız" açılış kelimelerinin, kelimenin tam anlamıyla bir anlamının yanı sıra, gündelik / kişisel kullanım anlamına gelmesi gerekir. Sadece kendi sistemlerinin tz'sini bilmek kadar dayanıksız bir şeye dayanan bir senaryo yayınlayan birisi muhtemelen senaryo paylaşım oyununda olmamalıdır. :) Öte yandan tüm kişisel ortamlarımda kendi komutumu kullanıyorum / kullanacağım.
B Katmanı

4

GNU date, basit tarih aritmetiğini desteklese de, sudodus'un cevabında gösterildiği gibi dönem zamanı hesaplamaları bazen daha net (ve daha taşınabilir) olabilir.

Zaman damgasında herhangi bir saat dilimi belirtilmediğinde +/- kullanılması, başka bir şey ayrıştırılmadan önce bir saat dilimiyle eşleşme girişimini tetikler.

Bunu yapmanın bir yolu var, "-" yerine "önce" kullanın:

$ 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

veya

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

(Keyfi olarak "Z" kullanamasanız da, bölgemde çalışır, ancak bu bir UTC / GMT bölgesi zaman damgası yapar - kendi zaman diliminize ekleyerek kendi bölgenizi veya% z /% Z ${TZ:-$(date +%z)}kullanın.)

Bu formların fazladan zaman terimlerinin eklenmesi zamanı ayarlar:

  • "5 saat önce" 5 saat çıkar
  • "4 saat" ekleme (kapalı) 4 saat
  • "3 saat dolayısıyla" 3 saat ekleyin (açık) (eski sürümlerde desteklenmez)

Herhangi bir sırayla birçok karmaşık ayarlama kullanılabilir ("14 hafta dolayısıyla geçen Pazartesi" gibi göreli ve değişken terimler sorun ister ;-)

(Burada da başka bir küçük beartrap var, dateher zaman geçerli bir tarih verecek, bu yüzden date -d "2019-01-31 1 month""gelecek ay" gibi 2019-03-03 veriyor)

Desteklenen çok çeşitli saat ve tarih biçimleri göz önüne alındığında, saat dilimi ayrıştırma mutlaka özensizdir: tek veya çok harfli bir sonek, bir saat veya saat olabilir: dakika ofseti, bir ad "Amerika / Denver" (hatta bir dosya adı) durumunda TZ değişken ).

Sizin 2018-12-10T00:00:00 "T" sonunda "Z" ekleyerek, sadece bir ayırıcı değil, bir zaman dilimi olduğu için sürümü çok beklendiği gibi bu işi (seçilen bölgenin doğruluğu bağlı olarak) yapar işi yapmaz.

Bkz. Https://www.gnu.org/software/tar/manual/html_node/Date-input-formats.html ve özellikle bölüm 7.7.


1
Diğer seçenekler şunlardır: date -d "2018-12-10 00:00:00 now -5 hoursya todayya 0 houryerine nowanlatır şey, datebir süre sonra belirteç ofset bir zaman dilimi olmadığını.
Stéphane Chazelas

1
Veya, iyilik uğruna, argümanları yeniden sıralayarak tarih-saatinden sonra hiçbir şey bırakmayın: tarih -d "-5 saat 2018-12-10 00:00:00" `
B Katman

2

Bu çözümü anlamak kolaydır, ancak biraz daha karmaşıktır, bu yüzden bunu bir kabuk komut dosyası olarak gösterdim.

  • '1970-01-01 00:00:00 UTC'den beri saniye' birimine dönüştür
  • farkı ekle veya çıkar
  • son datekomut satırı ile okunabilir bir biçime dönme

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"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.