Секунды
Чтобы измерить истекшее время (в секундах), нам нужно:
- целое число, представляющее количество прошедших секунд, и
- способ преобразованиятакое целое число в пригодном для использования формате.
Целочисленное значение прошедших секунд:
Преобразовать такое целое число в пригодный для использования формат
Внутренний bash printf
может сделать это напрямую:
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' 12345
03:25:45
аналогично
$ elapsedseconds=$((12*60+34))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
00:12:34
, но это не удастся в течение более 24 часов, так как мы на самом деле печатаемвремя настенного часа, а не длительность:
$ hours=30;mins=12;secs=24
$ elapsedseconds=$(( ((($hours*60)+$mins)*60)+$secs ))
$ TZ=UTC0 printf '%(%H:%M:%S)T\n' "$elapsedseconds"
06:12:24
Для любителей подробностей, с bash-hackers.org :
%(FORMAT)T
выходыстрока даты и времени, полученная в результате использования FORMAT в качестве строки формата для strftime(3)
.Связанный аргумент - это количество секунд с Epoch , или -1 (текущее время) или -2 (время запуска оболочки).Если соответствующий аргумент не указан, по умолчанию используется текущее время.
Так что вы можете просто вызвать textifyDuration $elpasedseconds
, где textifyDuration
- еще одна реализация печати продолжительности:
textifyDuration() {
local duration=$1
local shiff=$duration
local secs=$((shiff % 60)); shiff=$((shiff / 60));
local mins=$((shiff % 60)); shiff=$((shiff / 60));
local hours=$shiff
local splur; if [ $secs -eq 1 ]; then splur=''; else splur='s'; fi
local mplur; if [ $mins -eq 1 ]; then mplur=''; else mplur='s'; fi
local hplur; if [ $hours -eq 1 ]; then hplur=''; else hplur='s'; fi
if [[ $hours -gt 0 ]]; then
txt="$hours hour$hplur, $mins minute$mplur, $secs second$splur"
elif [[ $mins -gt 0 ]]; then
txt="$mins minute$mplur, $secs second$splur"
else
txt="$secs second$splur"
fi
echo "$txt (from $duration seconds)"
}
Дата GNU.
Чтобы получить форматированное время, мы должны использовать внешний инструмент (дата GNU) несколькими способами, чтобы получить почти годичный интервал, включая наносекунды.
Математика внутри даты.
Нет необходимости во внешней арифметике, сделайте все за один шаг внутри date
:
date -u -d "0 $FinalDate seconds - $StartDate seconds" +"%H:%M:%S"
Да, в нуле есть 0
командная строка.Это необходимо.
Предполагается, что вы можете изменить команду date +"%T"
на команду date +"%s"
, чтобы значения сохранялись (печатались) в считанные секунды.
Обратите внимание, что команда ограниченадо:
- Положительные значения
$StartDate
и $FinalDate
секунд. - Значение в
$FinalDate
больше (позже), чем $StartDate
. - Разница во времени меньше 24 часов.
- Вы принимаете формат вывода с часами, минутами и секундами.Очень легко изменить.
- Допустимо использовать время -u UTC.Чтобы избежать "DST" и коррекции местного времени.
Если вы должны использовать строку 10:33:56
, ну, просто конвертируйте ее в секунды,
также, слово секунд может быть сокращено как сек:
string1="10:33:56"
string2="10:36:10"
StartDate=$(date -u -d "$string1" +"%s")
FinalDate=$(date -u -d "$string2" +"%s")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S"
Обратите внимание, что преобразование времени в секундах (как показано выше) относительно начала «этого» дня (сегодня).
Концепция может быть расширена до наносекунд, например:
string1="10:33:56.5400022"
string2="10:36:10.8800056"
StartDate=$(date -u -d "$string1" +"%s.%N")
FinalDate=$(date -u -d "$string2" +"%s.%N")
date -u -d "0 $FinalDate sec - $StartDate sec" +"%H:%M:%S.%N"
Если требуется рассчитать более длительные (до 364 дней) временные разницы, мы должны использовать начало (некоторые)год в качестве ссылки и значение формата %j
(номер дня в году):
Аналогично:
string1="+10 days 10:33:56.5400022"
string2="+35 days 10:36:10.8800056"
StartDate=$(date -u -d "2000/1/1 $string1" +"%s.%N")
FinalDate=$(date -u -d "2000/1/1 $string2" +"%s.%N")
date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N"
Output:
026 days 00:02:14.340003400
К сожалению, в этом случае нам нужно вручную вычесть 1
ОДИН из числа дней.Команда date рассматривает первый день года как 1. Не так уж сложно ...
a=( $(date -u -d "2000/1/1 $FinalDate sec - $StartDate sec" +"%j days %H:%M:%S.%N") )
a[0]=$((10#${a[0]}-1)); echo "${a[@]}"
Использование большого количества секунд здесь допустимо и задокументировано:
https://www.gnu.org/software/coreutils/manual/html_node/Examples-of-date.html#Examples-of-date
Дата Busybox
Инструмент, используемый на небольших устройствах (очень маленький исполняемый файл для установки): Busybox.
Либо создайте ссылку на busybox с именемdate:
$ ln -s /bin/busybox date
Затем используйте его, позвонив по этому номеру date
(поместите его в каталог, включенный в PATH).
Или создайте псевдоним, например:
$ alias date='busybox date'
У даты занятой ячейки есть хорошая опция: -D, чтобы получить формат ввода времени.Это открывает много форматов, которые будут использоваться как время.Используя опцию -D, мы можем преобразовать время 10:33:56 напрямую:
date -D "%H:%M:%S" -d "10:33:56" +"%Y.%m.%d-%H:%M:%S"
И как вы можете видеть из вывода Команды выше, день считается «сегодняшним». Чтобы узнать время начала эпохи:
$ string1="10:33:56"
$ date -u -D "%Y.%m.%d-%H:%M:%S" -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56
Дата занятой ячейки может даже получать время (в формате выше) без -D:
$ date -u -d "1970.01.01-$string1" +"%Y.%m.%d-%H:%M:%S"
1970.01.01-10:33:56
А формат вывода может быть даже секундами с эпохи.
$ date -u -d "1970.01.01-$string1" +"%s"
52436
Для обоих раз и немного bash math (busybox пока не может сделать математику):
string1="10:33:56"
string2="10:36:10"
t1=$(date -u -d "1970.01.01-$string1" +"%s")
t2=$(date -u -d "1970.01.01-$string2" +"%s")
echo $(( t2 - t1 ))
Или в формате:
$ date -u -D "%s" -d "$(( t2 - t1 ))" +"%H:%M:%S"
00:02:14