Берегись! Многие из решений bash здесь разбиты на диапазоны дат, которые охватывают дату, когда начинается летнее время (где применимо). Это связано с тем, что конструкция $ ((math)) выполняет операцию 'floor' / усечение над полученным значением, возвращая только целое число. Позвольте мне проиллюстрировать:
Летнее время началось 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 являются потерянным часом после переключения DST.
Так, как это должно быть сделано правильно?
Я бы предложил использовать это вместо:
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) ))
Это также должно быть безопасным с високосной секундой, если вы всегда вычитаете более раннюю дату из более поздней, поскольку високосные секунды будут только увеличивать разницу - усечение эффективно округляет до правильного результата.