Nhận ngày hôm qua trong bash trên Linux, DST-safe


158

Tôi có một tập lệnh shell chạy trên Linux và sử dụng cuộc gọi này để nhận ngày hôm qua ở YYYY-MM-DDđịnh dạng:

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

Nó hoạt động hầu hết thời gian, nhưng khi kịch bản chạy vào sáng hôm qua lúc 2013-03-11 0:35 CDTnó quay lại "2013-03-09"thay vì "2013-03-10".

Có lẽ thời gian tiết kiệm ánh sáng ban ngày (bắt đầu từ ngày hôm qua) là đáng trách. Tôi đoán đường đi "1 day ago"được thực hiện nó trừ 24 giờ và 24 giờ trước khi 2013-03-11 0:35 CDT2013-03-09 23:35 CST, dẫn đến kết quả của "2013-03-09".

Vì vậy, cách an toàn DST tốt để có được ngày hôm qua trong bash trên Linux là gì?


Bạn luôn luôn chạy cái này cùng một lúc, bạn có chạy nó nhiều lần không?
tink

@tink nó chạy hàng ngày lúc 00:35
Ike Walker

Câu trả lời:


267

Tôi nghĩ rằng nó sẽ hoạt động, bất kể tần suất và thời gian bạn chạy nó ...

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

1
Làm thế nào bạn sẽ gán cái này cho một biến để sử dụng sau này?
null

6
@null - giống như cách bạn gán đầu ra của bất kỳ lệnh shell nào khác. biến = $ (ngày -d "ngày hôm qua 13:00" '+% Y-% m-% d') ...
tink

Đối với ngày GNU:date -d yesterday 13:00 -I
gogstad

Điều này phụ thuộc vào thực tế là việc chuyển đổi giữa thời gian mùa hè và mùa đông luôn được thực hiện vào ban đêm, nếu tôi hiểu chính xác?
Nicolas Raoul

2
@NicolasRaoul: Nó không được bảo đảm; nhưng nó là một quy ước được theo dõi rộng rãi để sắp xếp việc này vào sáng sớm. Mặt khác, IIRC không có nơi nào chuyển đổi xảy ra gần với buổi trưa địa phương. Vì vậy, "1300J không bao giờ nằm ​​trong ranh giới DST" là một giả định hợp lý .
Piskvor rời khỏi tòa nhà

56

ngày theo Mac OSX hơi khác một chút.

Cho ngày hôm qua

date -v-1d +%F

Cho tuần trước

date -v-1w +%F

8
Nếu muốn sử dụng kiểu GNU trong OSX, bạn cũng có thể sử dụng gdate, có sẵn trong coreutilsgói của homebrew .
dùng456584

11
Ý bạn datelà khác
augurar

12

Điều này cũng sẽ làm việc, nhưng có lẽ nó là quá nhiều:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"

Những thứ như thế này là cần thiết nếu bạn cần đối phó với Mac OS X datekhông hỗ trợ yesterdaycú pháp ...
Mikko Rantalainen

7

Nếu bạn chắc chắn rằng tập lệnh chạy trong những giờ đầu tiên trong ngày, bạn có thể chỉ cần làm

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

BTW, nếu tập lệnh chạy hàng ngày lúc 00:35 (thông qua crontab?), Bạn nên tự hỏi điều gì sẽ xảy ra nếu thay đổi DST rơi vào giờ đó; tập lệnh không thể chạy hoặc chạy hai lần trong một số trường hợp. Triển khai hiện đại cronkhá thông minh trong vấn đề này, mặc dù.


5

bạn có thể dùng

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

để có được ngày từ 30 ngày trước, tương tự bạn có thể thay thế 30 bằng x số ngày


Bạn nên thay đổi định dạng của bạn %Y%m%dđể phù hợp với câu hỏi. Tôi đã nâng cấp dù sao nó hoạt động đúng.
isapir

4

Đây là một giải pháp sẽ hoạt động với Solaris và AIX.

Thao tác với múi giờ có thể thay đổi đồng hồ một vài giờ. Do thời gian tiết kiệm ánh sáng ban ngày, 24 giờ trước có thể là hôm nay hoặc ngày hôm kia.

Bạn chắc chắn rằng ngày hôm qua là 20 hoặc 30 giờ trước. Cái nào? Chà, cái gần đây nhất không phải hôm nay.

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

Tham số -e được sử dụng trong lệnh echo là cần thiết với bash, nhưng sẽ không hoạt động với ksh. Trong ksh bạn có thể sử dụng cùng một lệnh mà không cần cờ -e.

Khi tập lệnh của bạn sẽ được sử dụng trong các môi trường khác nhau, bạn có thể bắt đầu tập lệnh với #! / Bin / ksh hoặc #! / Bin / bash. Bạn cũng có thể thay thế \ n bằng một dòng mới:

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

0

Chỉ cần sử dụng datevà tin tưởng giây:

Như bạn chỉ ra một cách đúng đắn, rất nhiều chi tiết về tính toán cơ bản bị ẩn nếu bạn dựa vào số học thời gian tiếng Anh. Ví dụ -d yesterday, và -d 1 day agosẽ có hành vi khác nhau.

Thay vào đó, bạn có thể tin cậy tùy thuộc vào giây (được ghi lại chính xác) kể từ UTC epix unix và bash số học để có được khoảnh khắc bạn muốn:

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

Điều này đã được chỉ ra trong một câu trả lời khác . Biểu mẫu này dễ di chuyển hơn trên các nền tảng với các datecờ dòng lệnh khác nhau , không phụ thuộc vào ngôn ngữ (ví dụ: "ngày hôm qua" so với "hier" trong ngôn ngữ Pháp) và thẳng thắn (về lâu dài) sẽ dễ nhớ hơn, bởi vì, tốt, bạn biết rồi Bạn có thể tiếp tục tự hỏi: "Là nó -d 2 hours agohay -d 2 hour agomột lần nữa?" hoặc "Là nó -d yesterdayhay -d 1 day agotôi muốn?"). Một chút khó khăn duy nhất ở đây là @.

Được trang bị bash và không có gì khác:

Bash chỉ trên bash, bạn cũng có thể có được thời gian của ngày hôm qua, thông qua bản dựng sẵn 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.

Vì thế,

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

hoặc, tương đương với một biến tạm thời (tùy chọn bên ngoài lớp vỏ bên ngoài, nhưng giữ cho các vars môi trường sạch sẽ).

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

Lưu ý: mặc dù trang man nói rằng không có đối số nào cho trình %()Tđịnh dạng sẽ mặc định -1, tôi dường như nhận được 0 thay vào đó (cảm ơn bạn, bash phiên bản thủ công 4.3.48)


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

Để sử dụng sau này:

date=$(date -d "yesterday" '+%Y-%m-%d')

2
Mặc dù mã này có thể giải quyết vấn đề của OP, tốt hơn hết là bao gồm một lời giải thích về cách mã của bạn giải quyết vấn đề của OP. Bằng cách này, khách truy cập trong tương lai có thể học hỏi từ bài đăng của bạn và áp dụng nó vào mã của riêng họ. SO không phải là một dịch vụ mã hóa, mà là một nguồn tài nguyên cho kiến ​​thức. Chất lượng cao, câu trả lời đầy đủ củng cố ý tưởng này, và có nhiều khả năng được nâng cao hơn. Các tính năng này, cộng với yêu cầu tất cả các bài đăng phải khép kín, là một số điểm mạnh của SO như một nền tảng phân biệt chúng tôi với các diễn đàn. Bạn có thể chỉnh sửa để thêm thông tin bổ sung & / hoặc để bổ sung giải thích của bạn với tài liệu nguồn.
SherylHohman

0

Bạn có thể dùng:

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

Hoặc bất cứ lúc nào bạn muốn lấy sẽ được lấy bằng bash.

Cho tháng:

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

0

Vì câu hỏi này được gắn thẻ "DST an toàn"

Và sử dụng fork để ra datelệnh trì hoãn, có một cách đơn giản và hiệu quả hơn bằng cách sử dụng bash thuần túy tích hợp:

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

Đây là nhanh hơn rất nhiều vào hệ thống thân thiện hơn vì phải ngã ba đến date.

Từ V> = 5.0 , có một biến mới$EPOCHSECONDS

printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday
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.