Как объясняется ниже, но ваши даты неправильные: они должны быть гггг-мм-дд
Питер Флинн
Во многих ответах используется опция -d даты (GNU-). Я хочу добавить, что это НЕ является частью даты POSIX, поэтому менее переносимо. Пока OP работает не с такими дистрибутивами Unix, как Solaris, а только с «обычным Linux», он или она должны быть хороши. Соответствующий тег помог бы imho.
Кадоис,
Ответы:
34
Если у вас есть GNU date, он позволяет распечатать представление произвольной даты ( -dопция). В этом случае преобразуйте даты в секунды с момента EPOCH, вычтите и разделите на 24 * 3600.
@Tree: Я считаю, что нет портативного способа вычитания дат, кроме как реализовать его самостоятельно - это не так уж сложно, единственное, что интересно, - это високосные годы. Но в настоящее время все хотят вычитать даты, как кажется: взгляните на unix.stackexchange.com/questions/1825/… :)
15
Ради lazyweb, я думаю, что user332325 имел в виду:echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Пабло Мендес
2
Что в этом контексте означает переносимый?
htellez
@htellez то, что работает на нескольких платформах (Windows, Linux и т. д.)
Необязательно, чтобы даты были в %y%m%dформате. Например, gnu date примет %Y-%m-%dформат, используемый OP. В общем, ISO-8601 - хороший выбор (и формат OP является одним из таких форматов). Форматы с двузначным годом лучше избегать.
mc0e
2
Для отличия от сегодняшнего дня можно использовать days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) )). обратите внимание, что $days_diffэто будет целое число (т.е. без десятичных знаков)
Жюльен
1
Это зависит от варианта dateустановки. Это работает для GNU date. Он не работает с POSIX date. Вероятно, это не проблема для большинства читателей, но стоит отметить.
Просто боевой путаницы: формат OP является не%Y-%m-%d , пока мы не введем August 2.0и October 2.0формат OP является %Y-%d-%m. Соответствующий xkcd: xkcd.com/1179
Осторожно! Многие из решений bash здесь не работают для диапазонов дат, которые охватывают дату начала летнего времени (где применимо). Это связано с тем, что конструкция $ ((math)) выполняет операцию «пол» / усечения над результирующим значением, возвращая только целое число. Позвольте мне проиллюстрировать:
Летнее время в США началось 8 марта этого года, поэтому давайте использовать диапазон дат, охватывающий следующие области:
Это также должно быть безопасным для секунды координации, если вы всегда вычитаете более раннюю дату из более поздней, поскольку секунды координации будут только увеличивать разницу - усечение эффективно округляется до правильного результата.
Если вместо этого вы укажете часовой пояс как для начала, так и для конца (и убедитесь, что они совпадают), тогда у вас также больше не будет этой проблемы, например:, echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) ))которая возвращает 6 вместо 5.
Хэнк Шульц,
1
Ах, один из ответивших подумал о неточностях основного решения, повторенных другими в вариантах. Недурно! Как насчет дополнительных секунд? Это безопасно для секунды? Существуют прецеденты, когда программное обеспечение возвращало результат за один день при запуске в определенное время, ссылаясь на проблемы, связанные с дополнительной секундой .
Стефан Гуришон
Woops, "неправильное" вычисление, которое вы приводите в качестве примера, здесь правильно возвращает 6 (Ubuntu 15.04 AMD64, версия GNU date version 8.23). Возможно, ваш пример зависит от часового пояса? Мой часовой пояс - «Европа / Париж».
Стефан Гуришон
Тот же хороший результат для "неправильных" вычислений с GNU date 2.0 на MSYS, в том же часовом поясе.
Стефан Гуришон
1
@ StéphaneGourichon, изменение летнего времени в вашем часовом поясе, похоже, произошло 29 марта 2015 года, поэтому попробуйте диапазон дат, который включает это.
@mouviciel не совсем. Python присутствует не всегда.
mc0e
1
@ mc0e в этом контексте ничего не присутствует всегда
Sumudu
5
@Anub - это теги, указанные OP как bashи shell, поэтому я думаю, мы можем предположить, что мы говорим о системе * nix. Внутри таких систем echoи dateнадежно присутствуют, а вот питона нет.
mc0e 01
2
@ mc0e Да, в этом есть смысл. Несмотря на то, что python2 доступен в большинстве систем * nix, когда дело доходит до устройств низкого уровня (встроенных и т. Д.), Нам придется выжить только с основными инструментами.
Если опция -d работает в вашей системе, вот еще один способ сделать это. Есть предостережение, что он не будет учитывать високосные годы, поскольку я считал 365 дней в году.
@cavalcade - это потому, что вы еще не сделалиA="2002-20-10"; B="2003-22-11"
RAM237 05
Спасибо, несмотря на то, что это работает в этом конкретном случае, я бы предложил использовать echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400)), то есть заключить оба формата и переменные в двойные кавычки, иначе может произойти сбой в другом формате даты (например, %b %d, %Y/ Jul 5, 2017)
RAM237
@ RAM237 Вот что я получаю за то, что не обращаю внимания на хедсмастер Хорошо замечено, спасибо
cavalcade
5
Даже если у вас нет даты GNU, у вас, вероятно, установлен Perl:
use Time::Local;
sub to_epoch {
my ($t) = @_;
my ($y, $d, $m) = ($t =~ /(\d{4})-(\d{2})-(\d{2})/);
return timelocal(0, 0, 0, $d+0, $m-1, $y-1900);
}
sub diff_days {
my ($t1, $t2) = @_;
return (abs(to_epoch($t2) - to_epoch($t1))) / 86400;
}
print diff_days("2002-20-10", "2003-22-11"), "\n";
Это возвращается 398.041666666667- 398 дней и один час из-за летнего времени.
Вопрос снова появился в моей ленте. Вот более краткий метод с использованием модуля Perl.
Вот мой рабочий подход с использованием zsh. Проверено на OSX:
# Calculation dates## A random old date
START_DATE="2015-11-15"## Today's date
TODAY=$(date +%Y-%m-%d)
# Import zsh date modzmodload zsh/datetime
# Calculate number of days
DIFF=$(( ( $(strftime -r %Y-%m-%d $TODAY) - $(strftime -r %Y-%m-%d $START_DATE) ) / 86400 ))
echo"Your value: "$DIFF
Результат:
Your value: 1577
По сути, мы используем функцию strftimereverse ( -r) для преобразования нашей строки даты обратно в метку времени, а затем производим наши вычисления.
Нет переносимого способа сделать это в самой оболочке. Во всех альтернативах используются определенные внешние программы (например, GNU dateили какой-либо язык сценариев), и я искренне думаю, что Ruby - хороший вариант . Это очень короткое решение, в котором не используются нестандартные библиотеки или другие зависимости. На самом деле, я думаю, что вероятность того, что Ruby будет установлена, выше, чем дата GNU.
в unix у вас должны быть установлены даты GNU. вам не нужно отклоняться от bash. вот натянутое решение с учетом дней, просто чтобы показать шаги. его можно упростить и расширить до полных дат.
Предположим, мы вручную выполняем синхронизацию резервных копий Oracle DB на третичный диск. Затем мы хотим удалить старые резервные копии на этом диске. Итак, вот небольшой сценарий bash:
#!/bin/shfor backup_dir in {'/backup/cmsprd/local/backupset','/backup/cmsprd/local/autobackup','/backup/cfprd/backupset','/backup/cfprd/autobackup'}
dofor f in `find $backup_dir -type d -regex '.*_.*_.*' -printf"%f\n"`
do
f2=`echo$f | sed -e 's/_//g'`
days=$(((`date "+%s"` - `date -d "${f2}" "+%s"`)/86400))
if [ $days -gt 30 ]; then
rm -rf $backup_dir/$ffidonedone
Измените каталоги и срок хранения («30 дней») в соответствии с вашими потребностями.
Oracle помещает наборы резервных копий в Flash Recovery Area, используя такой формат, как 'YYYY_MM_DD', поэтому мы удаляем символы подчеркивания для передачи их на 'date -d'
Alex Cherkas
0
Для MacOS sierra (возможно, из Mac OS X yosemate),
Чтобы получить время эпохи (секунды с 1970 года) из файла и сохранить его в переменной:
old_dt=`date -j -r YOUR_FILE "+%s"`
Чтобы узнать время текущего времени
new_dt=`date -j "+%s"`
Чтобы вычислить разницу во времени двух эпох
(( diff = new_dt - old_dt ))
Чтобы проверить, не превышает ли разница 23 дней
(( new_dt - old_dt > (23*86400) )) && echo Is more than 23 days
Ответы:
Если у вас есть GNU
date
, он позволяет распечатать представление произвольной даты (-d
опция). В этом случае преобразуйте даты в секунды с момента EPOCH, вычтите и разделите на 24 * 3600.Или нужен переносной способ?
источник
echo "( `date -d $B +%s` - `date -d $A +%s`) / (24*3600)" | bc -l
Способ bash - конвертируйте даты в формат% y% m% d, а затем вы можете сделать это прямо из командной строки:
echo $(( ($(date --date="031122" +%s) - $(date --date="021020" +%s) )/(60*60*24) ))
источник
%y%m%d
формате. Например, gnu date примет%Y-%m-%d
формат, используемый OP. В общем, ISO-8601 - хороший выбор (и формат OP является одним из таких форматов). Форматы с двузначным годом лучше избегать.days_diff=$(( (`date -d $B +%s` - `date -d "00:00" +%s`) / (24*3600) ))
. обратите внимание, что$days_diff
это будет целое число (т.е. без десятичных знаков)date
установки. Это работает для GNUdate
. Он не работает с POSIXdate
. Вероятно, это не проблема для большинства читателей, но стоит отметить.%Y-%m-%d
, пока мы не введемAugust 2.0
иOctober 2.0
формат OP является%Y-%d-%m
. Соответствующий xkcd: xkcd.com/1179tl; dr
date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s)) / (60*60*24) ))
Осторожно! Многие из решений bash здесь не работают для диапазонов дат, которые охватывают дату начала летнего времени (где применимо). Это связано с тем, что конструкция $ ((math)) выполняет операцию «пол» / усечения над результирующим значением, возвращая только целое число. Позвольте мне проиллюстрировать:
Летнее время в США началось 8 марта этого года, поэтому давайте использовать диапазон дат, охватывающий следующие области:
start_ts=$(date -d "2015-03-05" '+%s') end_ts=$(date -d "2015-03-11" '+%s')
Посмотрим, что получится с двойными скобками:
echo $(( ( end_ts - start_ts )/(60*60*24) ))
Возвращает «5».
Если сделать это с большей точностью с помощью bc, то получится другой результат:
echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc
Возвращает «5.95» - недостающие 0,05 - это час, потерянный после перехода на летнее время.
Так как же это сделать правильно?
Я бы предложил использовать вместо этого:
printf "%.0f" $(echo "scale=2; ( $end_ts - $start_ts )/(60*60*24)" | bc)
Здесь printf округляет более точный результат, вычисленный параметром bc, давая нам правильный диапазон дат - 6.
Изменить: выделение ответа в комментарии от @ hank-schultz ниже, который я использовал в последнее время:
date_diff=$(( ($(date -d "2015-03-11 UTC" +%s) - $(date -d "2015-03-05 UTC" +%s) )/(60*60*24) ))
Это также должно быть безопасным для секунды координации, если вы всегда вычитаете более раннюю дату из более поздней, поскольку секунды координации будут только увеличивать разницу - усечение эффективно округляется до правильного результата.
источник
echo $(( ($(date --date="2015-03-11 UTC" +%s) - $(date --date="2015-03-05 UTC" +%s) )/(60*60*24) ))
которая возвращает 6 вместо 5.И в питоне
$python -c "from datetime import date; print (date(2003,11,22)-date(2002,10,20)).days" 398
источник
bash
иshell
, поэтому я думаю, мы можем предположить, что мы говорим о системе * nix. Внутри таких системecho
иdate
надежно присутствуют, а вот питона нет.Это работает для меня:
A="2002-10-20" B="2003-11-22" echo $(( ($(date -d $B +%s) - $(date -d $A +%s)) / 86400 )) days
Печать
Что происходит?
date -d
для обработки строк времениdate %s
для преобразования строк времени в секунды с 1970 года (unix epoche)источник
Если опция -d работает в вашей системе, вот еще один способ сделать это. Есть предостережение, что он не будет учитывать високосные годы, поскольку я считал 365 дней в году.
date1yrs=`date -d "20100209" +%Y` date1days=`date -d "20100209" +%j` date2yrs=`date +%Y` date2days=`date +%j` diffyr=`expr $date2yrs - $date1yrs` diffyr2days=`expr $diffyr \* 365` diffdays=`expr $date2days - $date1days` echo `expr $diffyr2days + $diffdays`
источник
Вот версия MAC OS X для вашего удобства.
$ A="2002-20-10"; B="2003-22-11"; $ echo $(((`date -jf %Y-%d-%m $B +%s` - `date -jf %Y-%d-%m $A +%s`)/86400))
nJoy!
источник
A="2002-20-10"; B="2003-22-11"
echo $(((`date -jf "%Y-%d-%m" "$B" +%s` - `date -jf "%Y-%d-%m" "$A" +%s`)/86400))
, то есть заключить оба формата и переменные в двойные кавычки, иначе может произойти сбой в другом формате даты (например,%b %d, %Y
/Jul 5, 2017
)Даже если у вас нет даты GNU, у вас, вероятно, установлен Perl:
use Time::Local; sub to_epoch { my ($t) = @_; my ($y, $d, $m) = ($t =~ /(\d{4})-(\d{2})-(\d{2})/); return timelocal(0, 0, 0, $d+0, $m-1, $y-1900); } sub diff_days { my ($t1, $t2) = @_; return (abs(to_epoch($t2) - to_epoch($t1))) / 86400; } print diff_days("2002-20-10", "2003-22-11"), "\n";
Это возвращается
398.041666666667
- 398 дней и один час из-за летнего времени.Вопрос снова появился в моей ленте. Вот более краткий метод с использованием модуля Perl.
days=$(perl -MDateTime -le ' sub parse_date { @f = split /-/, shift; return DateTime->new(year=>$f[0], month=>$f[2], day=>$f[1]); } print parse_date(shift)->delta_days(parse_date(shift))->in_units("days"); ' $A $B) echo $days # => 398
источник
Использование команды mysql
$ echo "select datediff('2013-06-20 18:12:54+08:00', '2013-05-30 18:12:54+08:00');" | mysql -N
Результат: 21
ПРИМЕЧАНИЕ. В расчетах используются только части значений даты.
Ссылка: http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_datediff
источник
Это самое простое, что мне удалось поработать над centos 7:
OLDDATE="2018-12-31" TODAY=$(date -d $(date +%Y-%m-%d) '+%s') LINUXDATE=$(date -d "$OLDDATE" '+%s') DIFFDAYS=$(( ($TODAY - $LINUXDATE) / (60*60*24) )) echo $DIFFDAYS
источник
Вот мой рабочий подход с использованием zsh. Проверено на OSX:
# Calculation dates ## A random old date START_DATE="2015-11-15" ## Today's date TODAY=$(date +%Y-%m-%d) # Import zsh date mod zmodload zsh/datetime # Calculate number of days DIFF=$(( ( $(strftime -r %Y-%m-%d $TODAY) - $(strftime -r %Y-%m-%d $START_DATE) ) / 86400 )) echo "Your value: " $DIFF
Результат:
По сути, мы используем функцию
strftime
reverse (-r
) для преобразования нашей строки даты обратно в метку времени, а затем производим наши вычисления.источник
Я бы предложил другое возможное решение на Ruby. Похоже, это самый маленький и чистый на данный момент:
A=2003-12-11 B=2002-10-10 DIFF=$(ruby -rdate -e "puts Date.parse('$A') - Date.parse('$B')") echo $DIFF
источник
date
или какой-либо язык сценариев), и я искренне думаю, что Ruby - хороший вариант . Это очень короткое решение, в котором не используются нестандартные библиотеки или другие зависимости. На самом деле, я думаю, что вероятность того, что Ruby будет установлена, выше, чем дата GNU.Другая версия Python:
python -c "from datetime import date; print date(2003, 11, 22).toordinal() - date(2002, 10, 20).toordinal()"
источник
Используйте функции оболочки из http://cfajohnson.com/shell/ssr/ssr-scripts.tar.gz ; они работают в любой стандартной оболочке Unix.
date1=2012-09-22 date2=2013-01-31 . date-funcs-sh _date2julian "$date1" jd1=$_DATE2JULIAN _date2julian "$date2" echo $(( _DATE2JULIAN - jd1 ))
См. Документацию по адресу http://cfajohnson.com/shell/ssr/08-The-Dating-Game.shtml
источник
в unix у вас должны быть установлены даты GNU. вам не нужно отклоняться от bash. вот натянутое решение с учетом дней, просто чтобы показать шаги. его можно упростить и расширить до полных дат.
DATE=$(echo `date`) DATENOW=$(echo `date -d "$DATE" +%j`) DATECOMING=$(echo `date -d "20131220" +%j`) THEDAY=$(echo `expr $DATECOMING - $DATENOW`) echo $THEDAY
источник
Предполагается, что месяц равен 1/12 года:
#!/usr/bin/awk -f function mktm(datespec) { split(datespec, q, "-") return q[1] * 365.25 + q[3] * 365.25 / 12 + q[2] } BEGIN { printf "%d\n", mktm(ARGV[2]) - mktm(ARGV[1]) }
источник
Попробуйте:
perl -e 'use Date::Calc qw(Delta_Days); printf "%d\n", Delta_Days(2002,10,20,2003,11,22);'
источник
Предположим, мы вручную выполняем синхронизацию резервных копий Oracle DB на третичный диск. Затем мы хотим удалить старые резервные копии на этом диске. Итак, вот небольшой сценарий bash:
#!/bin/sh for backup_dir in {'/backup/cmsprd/local/backupset','/backup/cmsprd/local/autobackup','/backup/cfprd/backupset','/backup/cfprd/autobackup'} do for f in `find $backup_dir -type d -regex '.*_.*_.*' -printf "%f\n"` do f2=`echo $f | sed -e 's/_//g'` days=$(((`date "+%s"` - `date -d "${f2}" "+%s"`)/86400)) if [ $days -gt 30 ]; then rm -rf $backup_dir/$f fi done done
Измените каталоги и срок хранения («30 дней») в соответствии с вашими потребностями.
источник
Для MacOS sierra (возможно, из Mac OS X yosemate),
Чтобы получить время эпохи (секунды с 1970 года) из файла и сохранить его в переменной:
old_dt=`date -j -r YOUR_FILE "+%s"`
Чтобы узнать время текущего времени
new_dt=`date -j "+%s"`
Чтобы вычислить разницу во времени двух эпох
(( diff = new_dt - old_dt ))
Чтобы проверить, не превышает ли разница 23 дней
(( new_dt - old_dt > (23*86400) )) && echo Is more than 23 days
источник
echo $(date +%d/%h/%y) date_today echo " The other date is 1970/01/01" TODAY_DATE="1970-01-01" BEFORE_DATE=$(date +%d%h%y) echo "THE DIFFERNS IS " $(( ($(date -d $BEFORE_DATE +%s) - $(date -d $TODAY_DATE +%s)) )) SECOUND
используйте его, чтобы получить дату сегодня и вашу секунду с нужной даты. если вы хотите, чтобы это было дни, просто разделите его на 86400
echo $(date +%d/%h/%y) date_today echo " The other date is 1970/01/01" TODAY_DATE="1970-01-01" BEFORE_DATE=$(date +%d%h%y) echo "THE DIFFERNS IS " $(( ($(date -d $BEFORE_DATE +%s) - $(date -d $TODAY_DATE +%s))/ 86400)) SECOUND
источник