Получите вчерашнюю дату в bash для Linux, DST-safe

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 часа, а раньше 2013-03-11 0:35 CDTбыл 24 часа 2013-03-09 23:35 CST, что привело к результату "2013-03-09".

Так что же это хороший DST-безопасный способ получить вчерашнюю дату в bash для Linux?

Айк Уокер
источник
Вы всегда запускаете это в одно и то же время?
Тинк
@ tink, это бежит ежедневно в 00:35
Айк Уокер
Возможный дубликат Get date (за день до текущего времени) в Bash
shgnInc

Ответы:

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
Это зависит от того, что переключение между летним и зимним временем всегда происходит ночью, если я правильно понимаю?
Николас Рауль,
2
@NicolasRaoul: это не гарантировано; но это широко распространенное соглашение, чтобы запланировать это на раннее утро. С другой стороны, в IIRC нет места, где переключение произошло бы ближе к полудню. Итак, «1300J никогда не бывает на границе летнего времени» - разумное предположение .
Писквор покинул здание
56

дата под Mac OSX немного отличается.

Вчера

date -v-1d +%F

За последнюю неделю

date -v-1w +%F
Овен
источник
8
Если вы заинтересованы в использовании стиля GNU в OSX, вы также можете использовать его gdateв coreutilsпакете homebrew .
user456584
11
Вы имеете в виду dateдругое.
Авгурар
12

Это также должно работать, но, возможно, это слишком много:

date -d @$(( $(date +"%s") - 86400)) +"%Y-%m-%d"
perreal
источник
Такие вещи нужны, если вам нужно иметь дело с Mac OS X, dateкоторая не поддерживает yesterdayсинтаксис ...
Микко Ранталайнен,
7

Если вы уверены, что скрипт запускается в первые часы дня, вы можете просто сделать

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

Кстати, если скрипт запускается ежедневно в 00:35 (через crontab?), Вы должны спросить себя, что произойдет, если изменение DST произойдет в этот час; Сценарий не может быть запущен или запущен дважды в некоторых случаях. Современные реализации cronявляются довольно умны в этом отношении, хотя.

leonbloy
источник
5

ты можешь использовать

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

чтобы получить дату от 30 дней назад, аналогично вы можете заменить 30 на x количество дней

Дэн Пикард
источник
Вы должны изменить свой формат, %Y%m%dчтобы соответствовать вопросу. Я все равно проголосовал, поскольку это работало должным образом.
Исапир
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будет иметь другое поведение.

Вместо этого вы можете надежно зависеть от (точно задокументированных) секунд, прошедших с UTC эпохи Unix, и арифметики bash, чтобы получить нужный момент:

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

Это было указано в другом ответе . Эта форма более переносима на платформах с разными dateфлагами командной строки, не зависит от языка (например, «вчера» или «выше» во французском языке), и, честно говоря (в долгосрочной перспективе) будет легче запомнить, потому что вы знаю это уже. В противном случае вы могли бы продолжать спрашивать себя: "Это было -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 ))

или, что эквивалентно с переменной temp (внешняя подоболочка необязательна, но сохраняет переменные среды чистыми).

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

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

init_js
источник
0
date -d "yesterday" '+%Y-%m-%d'

Чтобы использовать это позже:

date=$(date -d "yesterday" '+%Y-%m-%d')
Суреш Палемони
источник
2
Хотя этот код может решить проблему OP, лучше включить объяснение того, как ваш код решает проблему OP. Таким образом, будущие посетители могут учиться на вашем посте и применять его к собственному коду. SO - это не сервис кодирования, а ресурс для знаний. Высокое качество, полные ответы усиливают эту идею, и, скорее всего, будут отклонены. Эти функции, а также требование, чтобы все сообщения были автономными, являются сильными сторонами SO как платформы, которая отличает нас от форумов. Вы можете редактировать, чтобы добавить дополнительную информацию и / или дополнить ваши пояснения исходной документацией.
Шерил Хохман
0

Ты можешь использовать:

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

Или в любое время, которое вы хотите получить, будет восстановлено с помощью bash.

На месяц:

date -d "30 days ago" '+%Y-%m-%d'
Daremitsu
источник
0

Как этот вопрос помечен "DST сейф"

А использование fork для dateзадания задержки подразумевает простой и более эффективный способ использования встроенного чистого 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
Ф. Хаури
источник