
Все найденные мной до сих пор тонны статей, похоже, были сосредоточены на получении итоговой даты, что все еще полезно, но не то, что мне нужно в данном случае.
Пример ссылки:Unix и Linux SE — быстрый расчет разницы дат.
Что касается datediff
других вопросов и ответов, то это больше похоже на то, что я хотел сказать относительно результата длительности даты/времени, и по сути он использует математику временных меток Unix с этими предыдущими преобразованиями, но мне нужна детализация до секунды, поэтому я не делю на 86400.
Имея доступную длительность в секундах, я теперь хочу отформатировать ее во что-то вроде использования date
команды GNU следующим образом:date -d "@69600" "+ %Y years %m months %e days %H:%M:%S"
Я понимаю, что по сути все представляет собой длительность даты/времени из эпохи Unix, но теперь у меня возникла проблема с числами дня, месяца и года, которые не начинаются с 0, что не будет правильно представлять нужное мне значение длительности.
Я мог бы заняться сокращением этого формата и применением большего expr
количества команд, чтобы по сути вычитать 1 или 1970 из каждого значения, но мне интересно, есть ли какой-либо другой более простой и легкий способ справиться с вычислением даты/времени, чтобы получить результат продолжительности, помимо объединения математических операций и шагов форматирования вместе. Может быть, есть какая-то другая опция в GNU, date
которой я мог бы воспользоваться, или другой инструмент, который принимал бы 2 аргумента даты/времени и немедленно выдавал бы мне результат, который я ищу.
Было бы плюсом, если бы он был доступен в Homebrew для Mac :).
Простая ссылка на математику дат:Новости Уокера - Арифметика дат в скриптах оболочки Linux
Ссылки для форматирования случайной даты:
решение1
Сdateutils'sdatediff
(не GNU, извините), (ранее ddiff
, dateutils.ddiff
в Debian):
$ dateutils.ddiff -f '%Y years, %m months, %d days, %H:%0M:%0S' \
'2012-01-23 15:23:01' '2017-06-01 09:24:00'
5 years, 4 months, 8 days, 18:00:59
(даты берутся в формате UTC, добавьте что-то вроде , --from-zone=Europe/London
чтобы даты принимались в формате местного времени в соответствующем часовом поясе. --from-zone=localtime
может работать для часового пояса по умолчанию в системе; --from-zone="${TZ#:}"
будет работать, если $TZ
указан путь к файлу часового пояса IANA (например TZ=:Europe/London
, , а не TZ=GMT0BST,M3.5.0/1:00:00,M10.5.0/2:00:00
спецификация TZ в стиле POSIX)).
Сast-открытыйdate
, поэтому извините, но все еще не с инструментами GNU (хотя если вы используете ksh93
оболочку, она date
может быть доступна как встроенная) вы можете использовать ее date -E
для получения разницы между двумя датами в формате, который выдает 2 числа с единицами измерения:
$ date -E 1970-01-01 2017-06-01
47Y04M
$ date -E 2017-01-01 2017-06-01
4M29d
$ date -E 12:12:01 23:01:43
10h49m
Обратите внимание, что все, что вышечаснеоднозначно, поскольку в регионах с летним временем дни имеют разное количество часов (23, 24 или 25), а месяцы и годы имеют разное количество дней, поэтому может не иметь особого смысла иметь большую точность, чем эти две единицы выше.
Например, между 2015-01-01 00:00:00 и 2016-01-01 00:00:00 или между 2016-01-01 00:00:00 и 2017-01-01 00:00:00 находится ровно один год, но это не та же самая продолжительность (365*24 часов в одном случае, 366*24 часов в другом).
Между 2017-03-24 12:00:00 и 2017-03-25 12:00:00 или между 2017-03-25 12:00:00 и 2017-03-26 12:00:00 есть ровно один день, но если это местное время в европейских странах (которые перешли на летнее время 2017-03-26), тоодин деньв одном случае это 24 часа, а в другом — 23 часа.
Другими словами, длительность, которая упоминает годы, месяцы, недели или дни, имеет смысл только тогда, когда связана с одной из границ, к которым она должна применяться (и часовым поясом). Поэтому вы не можете преобразовать количество секунд в такую длительность, не зная, к какому времени (от или до) она должна применяться (если только вы не хотите использовать приблизительные определения дня (24 часа), месяца (30*24 часа) или года (365*24 часа)).
В какой-то степени даже длительность в секундах неоднозначна. Для упрощения секунды в эпохе Unix определяются как 86400-я часть заданных земных суток 1 . Эти секунды становятся все длиннее и длиннее по мере замедления вращения Земли.
Итак, что-то вроде (с GNU date
):
d1='2016-01-01 00:00:00' d2='2017-01-01 00:00:00'
eval "$(date -d "@$(($(date -d "$d2" +%s) - $(date -d "$d1" +%s)))" +'
delta="$((%-Y - 1970)) years, $((%-m - 1)) months, $((%-d - 1)) days, %T"')"
echo "$delta"
Или в fish
:
date -d@(expr (date -d $d2 +%s) - (date -d $d1 +%s)) +'%Y %-m %-d %T' | \
awk '{printf "%s years, %s months, %s days, %s\n", $1-1970, $2-1, $3-1, $4, $5}'
В общем случае не даст вам верных данных, поскольку временная разница применяется к 1970 году вместо фактической даты начала 2016 года.
Например, выше это дает мне (в часовом поясе Европа/Лондон):
1 years, 0 months, 1 days, 01:00:00
вместо
1 years, 0 months, 0 days, 00:00:00
(возможно, это все еще будет для вас достаточно хорошим приближением).
1 Технически, хотя день длится 86400 секунд Unix, сетевые системы обычно синхронизируют свои часы с атомными часами, используя секунды SI. Когда атомные часы показывают 12:00:00.00, эти системы Unix также показывают 12:00:00.00. Исключение составляет только введение дополнительных секунд, когда либо есть одна секунда Unix, которая длится 2 секунды, либо несколько секунд, которые длятся немного дольше, чтобы размазать эту дополнительную секунду по более длинному периоду.
Таким образом, можно узнать точную продолжительность (в атомных секундах) между двумя временными метками Unix (выданными системами, синхронизированными с атомными часами): получить разницу времени эпохи Unix между двумя временными метками и прибавить количество дополнительных секунд, добавленных между ними.
datediff
также имеет, -f %rS
чтобы получить числонастоящийсекунд между двумя датами:
$ dateutils.ddiff -f %S '1990-01-01 00:00:00' '2010-01-01 00:00:00' 631152000 $ dateutils.ddiff -f %rS '1990-01-01 00:00:00' '2010-01-01 00:00:00' 631152009
Разница заключается в 9 дополнительных секундах, добавленных в период с 1990 по 2010 год.
Теперь, это числонастоящийсекунды не помогут вам подсчитать количество дней, так как дни не имеют постоянного числа.настоящийсекунды. Между этими двумя датами ровно 20 лет, и эти 9 дополнительных секунд скорее мешают получить это значение. Также обратите внимание, что ddiff
' %rS
работает только тогда, когда не объединен с другими спецификаторами формата. Кроме того, будущие дополнительные секунды не известны заранее, но также информация о последних дополнительных секундах может быть недоступна. Например, моя система не знает о секундах после 2012-07-01.
решение2
Хорошо, предположим, у вас есть две date
-совместимые строки, определяющие два момента времени, длительность которых вы хотите вычислить между ними:
echo $(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'
Но предположим, что вы хотите, чтобы текст был более читабельным:
convertsecs() {
hours=$(($1/3600))
minutes=$(($1%3600/60))
seconds=$(($1%60))
}
totalduration="$(($(date +%s -d "3 days ago")-$(date +%s -d "4 weeks 2 hours 1 minutes 33 seconds ago"))) | sed 's/-//'"
convertsecs "$totalduration"
echo "Duration was ${hours} hours, ${minutes} minutes, and ${seconds} seconds."
решение3
Время — сложная штука, и важно управлять своими ожиданиями при его расчете. Я не верю, что технически возможно сделать то, о чем вы просите, и точно, и аккуратно в общем случае, поскольку определенное количество секунд может соответствовать другому количеству минут, если учесть такие вещи, как високосные секунды, которые сами по себе не происходят на полностью предсказуемой основе.
Другими словами, хотя мы и говорим, что день состоит из 24 часов,каждыйДень состоит ровно из 24 часов, поэтому, если вы хотите узнать, сколько дней, минут и секунд составляла данная продолжительность, вам нужно закрепить эту продолжительность где-то в реальном времени, чтобы узнать, была ли в ней дополнительная секунда или даже существовало ли 29 апреля где-то в этой продолжительности.
Еще более экзотично то, что не в каждом месяце одинаковое количество дней.
Если вас это не беспокоит, просто проведите подсчет секунд через базовую арифметику; используйте date
для преобразования в секунды с начала эпохи, разделите на 86400, чтобы получить дни, остаток на 3600, чтобы получить часы, остаток на 3600, чтобы получить минуты и т. д.