Dapatkan tanggal kemarin di bash di Linux, aman DST

158

Saya memiliki skrip shell yang berjalan di Linux dan menggunakan panggilan ini untuk mendapatkan tanggal kemarin dalam YYYY-MM-DDformat:

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

Ini berfungsi sebagian besar waktu, tetapi ketika skrip berlari kemarin pagi 2013-03-11 0:35 CDTitu kembali "2013-03-09"bukan "2013-03-10".

Agaknya waktu musim panas (yang dimulai kemarin) harus disalahkan. Saya kira cara "1 day ago"ini diterapkan dikurangi 24 jam, dan 24 jam sebelumnya 2013-03-11 0:35 CDTadalah 2013-03-09 23:35 CST, yang menyebabkan hasil "2013-03-09".

Jadi apa cara aman-DST yang baik untuk mendapatkan kencan kemarin di bash di Linux?

Ike Walker
sumber
Apakah Anda selalu menjalankan ini pada saat yang sama, apakah Anda menjalankannya berulang kali?
tink
@tink itu berjalan setiap hari pukul 00:35
Ike Walker

Jawaban:

267

Saya pikir ini harus berhasil, terlepas dari seberapa sering dan kapan Anda menjalankannya ...

date -d "yesterday 13:00" '+%Y-%m-%d'
Tink
sumber
1
Bagaimana Anda menetapkan ini ke variabel untuk digunakan nanti?
null
6
@null - cara yang sama Anda akan menetapkan output dari perintah shell lainnya. variabel = $ (tanggal -d "kemarin 13:00" '+% Y-% m-% d') ...
tink
Untuk tanggal GNU:date -d yesterday 13:00 -I
gogstad
Ini bergantung pada fakta bahwa pergantian antara musim panas dan musim dingin selalu dilakukan pada malam hari, jika saya mengerti dengan benar?
Nicolas Raoul
2
@NicolasRaoul: Tidak dijamin; tetapi ini adalah konvensi yang diikuti secara luas untuk menjadwalkan ini hingga pagi hari. Di sisi lain, ada IIRC tidak ada tempat di mana saklar akan terjadi dekat dengan siang hari setempat. Jadi, "1300J tidak pernah pada batas DST" adalah asumsi yang masuk akal .
Piskvor meninggalkan gedung
56

tanggal di bawah Mac OSX sedikit berbeda.

Untuk kemarin

date -v-1d +%F

Untuk minggu lalu

date -v-1w +%F
Aries
sumber
8
Jika tertarik menggunakan gaya GNU dalam OSX, Anda juga dapat menggunakan gdate, tersedia dalam coreutilspaket homebrew .
user456584
11
Maksud Anda dateberbeda.
augurar
12

Ini juga bisa berhasil, tetapi mungkin terlalu banyak:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"
perreal
sumber
Hal-hal seperti ini diperlukan jika Anda perlu berurusan dengan Mac OS X dateyang tidak mendukung yesterdaysintaks ...
Mikko Rantalainen
7

Jika Anda yakin skrip berjalan pada jam-jam pertama hari itu, Anda bisa melakukannya

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

BTW, jika skrip berjalan setiap hari pada 00:35 (melalui crontab?) Anda harus bertanya pada diri sendiri apa yang akan terjadi jika perubahan DST jatuh pada jam itu; skrip tidak dapat berjalan, atau dijalankan dua kali dalam beberapa kasus. Implementasi modern cronyang cukup pintar dalam hal ini, meskipun.

leonbloy
sumber
5

kamu bisa memakai

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

untuk mendapatkan tanggal dari 30 hari yang lalu, Anda juga dapat mengganti 30 dengan jumlah x hari

Dan Pickard
sumber
Anda harus mengubah format %Y%m%dagar sesuai dengan pertanyaan. Saya tetap terpilih karena berfungsi dengan baik.
isapir
4

Di sini solusi yang akan bekerja dengan Solaris dan AIX juga.

Memanipulasi Zona Waktu dimungkinkan untuk mengubah jam beberapa jam. Karena waktu musim panas, 24 jam yang lalu bisa jadi hari ini atau sehari sebelum kemarin.

Anda yakin kemarin 20 atau 30 jam yang lalu. Yang mana? Nah, yang terbaru bukan hari ini.

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

Parameter -e yang digunakan dalam perintah echo diperlukan dengan bash, tetapi tidak akan bekerja dengan ksh. Di ksh Anda dapat menggunakan perintah yang sama tanpa tanda -e.

Saat skrip Anda akan digunakan di lingkungan yang berbeda, Anda dapat memulai skrip dengan #! / Bin / ksh atau #! / Bin / bash. Anda juga dapat mengganti \ n dengan baris baru:

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
sumber
0

Cukup gunakan datedan percayai detik:

Seperti yang Anda tunjukkan dengan benar, banyak detail tentang perhitungan yang mendasarinya disembunyikan jika Anda mengandalkan aritmatika waktu bahasa Inggris. Misalnya -d yesterday, dan -d 1 day agoakan memiliki perilaku yang berbeda.

Sebagai gantinya, Anda dapat bergantung pada detik (didokumentasikan dengan tepat) sejak unix epoch UTC, dan bash aritmatika untuk mendapatkan momen yang Anda inginkan:

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

Ini ditunjukkan dalam jawaban lain . Formulir ini lebih portabel di semua platform dengan datebendera baris perintah yang berbeda , tidak tergantung pada bahasa (misalnya "kemarin" vs "hier" di lokal Prancis), dan terus terang (dalam jangka panjang) akan lebih mudah diingat, karena Anda juga sudah tahu itu. Anda mungkin terus bertanya pada diri sendiri: "Apakah itu -d 2 hours agoatau -d 2 hour agolagi?" atau "Apakah itu -d yesterdayatau -d 1 day agoyang saya inginkan?"). Satu-satunya yang sulit di sini adalah @.

Berbekal bash dan tidak ada yang lain:

Bash hanya dengan bash, Anda juga bisa mendapatkan waktu kemarin, melalui printf builtin:

 %(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.

Begitu,

# 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 ))

atau, ekuivalen dengan variabel temp (subshell luar opsional, tetapi menjaga kebersihan lingkungan).

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

Catatan: meskipun halaman manual menyatakan bahwa tidak ada argumen ke %()Tformatter akan menganggap default -1, saya tampaknya mendapatkan 0 sebagai gantinya (terima kasih, bash manual versi 4.3.48)

init_js
sumber
0
date -d "yesterday" '+%Y-%m-%d'

Untuk menggunakan ini nanti:

date=$(date -d "yesterday" '+%Y-%m-%d')
suresh Palemoni
sumber
2
Meskipun kode ini dapat menyelesaikan masalah OP, lebih baik untuk menyertakan penjelasan tentang bagaimana kode Anda mengatasi masalah OP. Dengan cara ini, pengunjung masa depan dapat belajar dari pos Anda, & menerapkannya pada kode mereka sendiri. SO bukan layanan pengkodean, tetapi sumber daya untuk pengetahuan. Kualitas tinggi, jawaban lengkap memperkuat gagasan ini, dan lebih cenderung terangkat naik. Fitur-fitur ini, ditambah persyaratan bahwa semua posting mandiri, adalah beberapa kekuatan SO sebagai platform yang membedakan kami dari forum. Anda dapat mengedit untuk menambahkan info tambahan & / atau untuk melengkapi penjelasan Anda dengan dokumentasi sumber.
SherylHohman
0

Kamu bisa memakai:

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

Atau kapan pun Anda ingin mengambil akan diambil dengan bash.

Untuk bulan:

date -d "30 days ago" '+%Y-%m-%d'
Daremitsu
sumber
0

Saat pertanyaan ini ditandai "DST aman"

Dan menggunakan garpu untuk datememerintahkan penundaan implikasi, ada cara sederhana dan lebih efisien menggunakan bash murni bawaan :

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

Ini adalah jauh lebih cepat pada sistem lebih ramah daripada harus garpu untuk date.

Dari V> = 5.0 , ada variabel baru$EPOCHSECONDS

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