Отримайте вчорашнє побачення в Linux на Linux, безпечному для DST


158

У мене є сценарій оболонки, який працює в Linux і використовує цей виклик, щоб отримати вчорашню дату у YYYY-MM-DDформаті:

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

Він працює більшу частину часу, але коли сценарій запускався вчора вранці, 2013-03-11 0:35 CDTвін повернувся "2013-03-09"замість "2013-03-10".

Імовірно, літній час (який розпочався вчора) винен. Я здогадуюсь, як "1 day ago"реалізований спосіб його відняли за 24 години, а за 24 години до цього 2013-03-11 0:35 CDTбуло 2013-03-09 23:35 CST, що призвело до результату "2013-03-09".

То який хороший DST-безпечний спосіб отримати вчорашнє побачення в Linux на Linux?


Ви завжди одночасно виконуєте це, чи повторюєте ви це повторно?
Тінь

@tink працює щодня о 00:35
Айк Уокер

Відповіді:


267

Я думаю, що це має спрацювати, незалежно від того, як часто і коли ви це запускаєте ...

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

1
Як би ви призначили цю змінну для подальшого використання?
недійсне

6
@null - так само, як ви призначили вихід будь-якої іншої команди оболонки. змінна = $ (дата -d "вчора 13:00" '+% Y-% m-% d') ...
тинк

Для GNU-дати:date -d yesterday 13:00 -I
gogstad

Це спирається на те, що перемикання між літнім і зимовим часом завжди робиться вночі, якщо я правильно розумію?
Nicolas Raoul

2
@NicolasRaoul: це не гарантується; але це загальноприйнята умова планувати це на ранковий ранок. З іншого боку, в IIRC немає місця, де перемикання відбудеться близько місцевого полудня. Отже, "1300J ніколи не знаходиться на кордоні DST" є розумним припущенням .
Пісквор вийшов з будівлі

56

дата в Mac OSX дещо відрізняється.

Бо вчора

date -v-1d +%F

За минулий тиждень

date -v-1w +%F

8
Якщо ви зацікавлені у використанні стилю GNU в OSX, ви також можете використовувати gdateдоступний в coreutilsпакеті homebrew .
користувач456584

11
Ти маєш на увазі dateрізне.
аугурар

12

Це також має працювати, але, можливо, це занадто:

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

Такі речі потрібні, якщо вам потрібно мати справу з Mac OS X, dateякий не підтримує yesterdayсинтаксис ...
Mikko Rantalainen

7

Якщо ви впевнені, що сценарій працює в перші години дня, ви можете просто зробити

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

До речі, якщо сценарій працює щодня о 00:35 (через crontab?), Ви повинні запитати себе, що станеться, якщо зміна DST впаде в цю годину; сценарій не вдалося запустити або запустити двічі в деяких випадках. Однак сучасні реалізації цього cronпроекту досить розумні .


5

ви можете використовувати

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

щоб отримати дату 30 днів тому, аналогічно ви можете замінити 30 на x кількість днів


Ви повинні змінити формат, %Y%m%dщоб відповідати питанню. Я все-таки виступав за те, щоб це було належним чином.
isapir

4

Ось рішення, яке також буде працювати з Solaris та AIX.

Маніпуляція часовим поясом можлива для зміни годин на кілька годин. Через літній час 24 години тому можуть бути сьогодні або позавчора.

Ви впевнені, що вчора 20 або 30 годин тому. Який? Ну, найновіший, який не є сьогодні.

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

Параметр -e, який використовується в команді echo, потрібен для bash, але не працюватиме з ksh. У ksh ви можете використовувати ту саму команду без прапора -e.

Коли ваш сценарій буде використовуватися в різних середовищах, ви можете запустити його з #! / Bin / ksh або #! / Bin / bash. Ви також можете замінити \ n новим рядком:

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

0

Просто скористайтеся dateнадійними секундами:

Як ви справедливо зазначаєте, багато деталей про основні обчислення приховано, якщо ви покладаєтесь на англійську арифметику часу. Наприклад -d yesterday, і -d 1 day agoматиме різну поведінку.

Натомість, ви можете надійно залежати від (точно задокументованих) секунд з моменту епохи unix UTC та арифметики bash, щоб отримати потрібний момент:

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

Це було зазначено в іншій відповіді . Ця форма є більш портативною на платформах з різними dateпрапорцями командного рядка, не залежить від мови (наприклад, "вчора" проти "hier" у французькій мові), і відверто (в довгостроковій перспективі) буде легше запам'ятовуватися, адже добре, ви знайте це вже. Ви інакше можете запитати себе: "Це було -d 2 hours agoчи -d 2 hour agoзнову?" або "Це -d yesterdayчи -d 1 day agoце я хочу?"). Єдиний складний біт тут @.

Озброєний башем і більше нічого:

Bash виключно на bash, ви також можете отримати вчорашній час через 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.

Так,

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

або, що еквівалентно, з тимчасовою змінною (зовнішня підзарядка необов'язкова, але підтримує чистоту середовища).

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

Примітка: незважаючи на тематичну сторінку, яка стверджує, що жоден аргумент для %()Tформатера не буде вважати за замовчуванням -1, я, здається, отримав 0 замість цього (дякую, інструкція bash версія 4.3.48)


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

Щоб скористатися цим пізніше:

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

2
Хоча цей код може вирішити проблему ОП, краще включити пояснення про те, як ваш код вирішує проблему ОП. Таким чином майбутні відвідувачі можуть дізнатисясь із вашої публікації та застосувати її до власного коду. ТАК - це не програма кодування, а ресурс для знань. Висока якість, повні відповіді підкріплюють цю ідею і, швидше за все, будуть схвалено. Ці функції, плюс вимога, щоб усі публікації були самостійними, є деякими сильними сторонами SO як платформи, що відрізняє нас від форумів. Ви можете редагувати, щоб додати додаткову інформацію та / або доповнити свої пояснення джерельною документацією.
ШерілХоман

0

Ви можете використовувати:

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

Або в будь-який час, який ви хочете отримати, буде отримано bash.

За місяць:

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

0

Оскільки це питання позначено тегами "DST safe"

І використовуючи fork для dateкоманди implie затримки, є простий і більш ефективний спосіб використання чистого вбудованого bash :

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

Це набагато швидше на більш дружній системі , ніж того , щоб розщедритися на date.

З V> = 5.0 , є нова змінна$EPOCHSECONDS

printf -v tz '%(%z)T' -1
TZ=$tz printf -v yesterday '%(%Y-%m-%d)T' $(( EPOCHSECONDS - 86400 ))
echo $yesterday
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.