Uzyskaj wczorajszą datę w bash w systemie Linux, bezpieczny dla czasu letniego

158

Mam skrypt powłoki, który działa w systemie Linux i używa tego wywołania, aby uzyskać wczorajszą datę w YYYY-MM-DDformacie:

date -d "1 day ago" '+%Y-%m-%d'

Działa przez większość czasu, ale kiedy skrypt został uruchomiony wczoraj rano 2013-03-11 0:35 CDT, "2013-03-09"zamiast tego powrócił "2013-03-10".

Prawdopodobnie winny jest czas letni (który rozpoczął się wczoraj). Domyślam się, że sposób "1 day ago"jest zaimplementowany to odjęto 24 godziny, a 24 godziny wcześniej 2013-03-11 0:35 CDTbyło 2013-03-09 23:35 CST, co doprowadziło do wyniku "2013-03-09".

Więc jaki jest dobry, bezpieczny dla czasu letniego sposób na uzyskanie wczorajszej daty w bash w systemie Linux?

Ike Walker
źródło
Czy zawsze uruchamiasz to w tym samym czasie, czy uruchamiasz to wielokrotnie?
tink
@tink jeździ codziennie o 00:35
Ike Walker

Odpowiedzi:

267

Myślę, że to powinno działać, niezależnie od tego, jak często i kiedy je uruchamiasz ...

date -d "yesterday 13:00" '+%Y-%m-%d'
tink
źródło
1
Jak przypisałbyś to do zmiennej do późniejszego wykorzystania?
null
6
@null - w ten sam sposób, w jaki przypisujesz dane wyjściowe dowolnego innego polecenia powłoki. zmienna = $ (data -d "wczoraj 13:00" '+% Y-% m-% d') ...
tink
Dla daty GNU:date -d yesterday 13:00 -I
gogstad
Polega to na tym, że przełączanie między czasem letnim a zimowym odbywa się zawsze w nocy, jeśli dobrze rozumiem?
Nicolas Raoul
2
@NicolasRaoul: To nie jest gwarantowane; ale powszechnie przestrzeganą konwencją jest zaplanowanie tego na wczesny poranek. Z drugiej strony, w IIRC nie ma miejsca, w którym zmiana nastąpiłaby w okolicach południa lokalnego. Zatem „1300J nigdy nie znajduje się na granicy czasu letniego” jest rozsądnym założeniem .
Piskvor opuścił budynek
56

data w systemie Mac OSX jest nieco inna.

Na wczoraj

date -v-1d +%F

W zeszłym tygodniu

date -v-1w +%F
Baran
źródło
8
Jeśli jesteś zainteresowany używaniem stylu GNU w OSX, możesz również użyć gdate, dostępnego w coreutilspakiecie homebrew .
user456584
11
Masz na myśli, że datejest inny.
augurar
12

To też powinno działać, ale może to za dużo:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"
perreal
źródło
Takie rzeczy są potrzebne, jeśli chcesz poradzić sobie z Mac OS X, datektóry nie obsługuje yesterdayskładni ...
Mikko Rantalainen
7

Jeśli masz pewność, że skrypt działa w pierwszych godzinach dnia, możesz to po prostu zrobić

  date -d "12 hours ago" '+%Y-%m-%d'

BTW, jeśli skrypt jest uruchamiany codziennie o 00:35 (przez crontab?), Powinieneś zadać sobie pytanie, co się stanie, jeśli zmiana czasu letniego nastąpi o tej godzinie; skrypt nie mógł się uruchomić lub w niektórych przypadkach uruchomić dwukrotnie. Nowoczesne implementacje cronsą jednak pod tym względem sprytne .

leonbloy
źródło
5

możesz użyć

date -d "30 days ago" +"%d/%m/%Y"

aby uzyskać datę sprzed 30 dni, analogicznie możesz zamienić 30 na x ilość dni

Dan Pickard
źródło
Powinieneś zmienić format na, %Y%m%daby pasował do pytania. I tak zagłosowałem za, ponieważ działało poprawnie.
isapir
4

Tutaj rozwiązanie, które będzie działało również z Solarisem i AIXem.

Manipulowanie strefą czasową jest możliwe w celu zmiany zegara o kilka godzin. Ze względu na czas letni, 24 godziny temu może być dzisiaj lub przedwczoraj.

Jesteś pewien, że wczoraj było 20 lub 30 godzin temu. Który? Cóż, najnowszy, którego nie ma dzisiaj.

echo -e "$(TZ=GMT+30 date +%Y-%m-%d)\n$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1

Parametr -e użyty w poleceniu echo jest potrzebny w bash, ale nie będzie działał z ksh. W ksh możesz użyć tego samego polecenia bez flagi -e.

Gdy twój skrypt będzie używany w różnych środowiskach, możesz uruchomić skrypt za pomocą #! / Bin / ksh lub #! / Bin / bash. Możesz także zastąpić \ n nową linią:

echo "$(TZ=GMT+30 date +%Y-%m-%d)
$(TZ=GMT+20 date +%Y-%m-%d)" | grep -v $(date +%Y-%m-%d) | tail -1
Walter A.
źródło
0

Po prostu użyj datei zaufane sekundy:

Jak słusznie zauważyłeś, wiele szczegółów dotyczących podstawowych obliczeń jest ukrytych, jeśli polegasz na angielskiej arytmetyce czasu. Np. -d yesterdayI -d 1 day agobędzie się zachowywał inaczej.

Zamiast tego możesz niezawodnie polegać na (dokładnie udokumentowanych) sekundach od czasu UTC w epoce unixa i na arytmetyce basha, aby uzyskać odpowiedni moment:

date -d @$(( $(date +"%s") - 24*3600)) +"%Y-%m-%d"

Wskazano na to w innej odpowiedzi . Ten formularz jest bardziej przenośny na różnych platformach z różnymi dateflagami wiersza poleceń, jest niezależny od języka (np. „Wczoraj” kontra „hier” w języku francuskim) i, szczerze mówiąc (w dłuższej perspektywie), będzie łatwiejszy do zapamiętania, ponieważ już to wiem. W przeciwnym razie możesz zadawać sobie pytanie: „Czy to było -d 2 hours agoczy -d 2 hour agoznowu?” lub „Czy to -d yesterdayalbo -d 1 day agoże chcę?”). Jedynym trudnym fragmentem jest tutaj @.

Uzbrojony w bash i nic więcej:

Bash wyłącznie na bash, możesz również uzyskać wczorajszy czas, za pomocą wbudowanego printf:

 %(datefmt)T
     causes printf to output the date-time string resulting from using
     datefmt as a format string for strftime(3).  The  corresponding  argu
     ment  is an integer representing the number of seconds since the
     epoch.  Two special argument values may be used: -1 represents the
     current time, and -2 represents the time the shell was invoked.
     If no argument is specified, conversion behaves as if -1 had
     been  given.
     This is an exception to the usual printf behavior.

Więc,

# inner printf gets you the current unix time in seconds
# outer printf spits it out according to the format
printf "%(%Y-%m-%d)T\n" $(( $(printf "%(%s)T" -1) - 24*3600 ))

lub równoważnie ze zmienną temp (zewnętrzna podpowłoka jest opcjonalna, ale utrzymuje zmienne środowiskowe w czystości).

(
  now=$(printf "%(%s)T" -1);
  printf "%(%Y-%m-%d)T\n" $((now - 24*3600));
)

Uwaga: pomimo strony podręcznika informującej, że żaden argument do programu %()Tformatującego nie przyjmie wartości domyślnej -1, zamiast tego wydaje mi się, że otrzymuję 0 (dziękuję, wersja podręcznika bash 4.3.48)

init_js
źródło
0
date -d "yesterday" '+%Y-%m-%d'

Aby użyć tego później:

date=$(date -d "yesterday" '+%Y-%m-%d')
suresh Palemoni
źródło
2
Chociaż ten kod może rozwiązać problem PO, lepiej jest dołączyć wyjaśnienie, w jaki sposób Twój kod rozwiązuje problem z PO. W ten sposób przyszli odwiedzający mogą uczyć się z Twojego posta i zastosować go do własnego kodu. SO nie jest usługą programistyczną, ale źródłem wiedzy. Wysokiej jakości, kompletne odpowiedzi wzmacniają ten pomysł i są bardziej skłonne do głosowania. Te funkcje, plus wymóg, aby wszystkie posty były niezależne, są mocnymi stronami SO jako platformy, która odróżnia nas od forów. Możesz edytować, aby dodać dodatkowe informacje i / lub uzupełnić wyjaśnienia o dokumentację źródłową.
SherylHohman
0

Możesz użyć:

date -d "yesterday 13:55" '+%Y-%m-%d'

Lub czas, który chcesz odzyskać, zostanie pobrany przez bash.

Za miesiąc:

date -d "30 days ago" '+%Y-%m-%d'
Daremitsu
źródło
0

Ponieważ to pytanie jest oznaczone „DST bezpieczny”

Używając widelca do datepolecenia implie delay, istnieje prosty i bardziej wydajny sposób użycia wbudowanego czystego bash :

printf -v tznow '%(%z %s)T' -1
TZ=${tznow% *} printf -v yesterday '%(%Y-%m-%d)T' $(( ${tznow#* } - 86400 ))
echo $yesterday

Jest to o wiele szybsze na bardziej przyjaznym dla systemu niż konieczność rozwidlania się date.

Z V> = 5,0 , jest nowa zmienna$EPOCHSECONDS

printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday
F. Hauri
źródło