Рассчитать накопленное время из JSON с помощью сценария Bash - PullRequest
0 голосов
/ 09 января 2019

У меня есть данные в формате json, в которые записываются метки времени (чч: мм в 24-часовом формате) с событием (вход / выход). Моя цель - сложить все различия во времени между событием «IN» и следующим событием «OUT».

Для упрощения я предполагаю, что несоответствий нет (первым элементом всегда является «IN», а за каждым «IN» следует «OUT»). Исключение: если последним элементом является «IN», вычисление должно выполняться между текущим временем и отметкой времени с последнего события «IN».

Пока это мой скрипт, который вычисляет все временные промежутки, также между OUT и IN. Но мне нужны только те, которые находятся между событиями IN и OUT.

Любые советы, которые могут быть более полезными, приветствуются!

#!/bin/bash

JSON='{ "times": [ [ "7:43", "IN" ], [ "8:26", "OUT" ], [ "8:27", "IN" ], [ "9:12", "OUT" ], [ "9:14", "IN" ], [ "9:22", "OUT" ], [ "9:23", "IN " ], [ "12:12", "OUT" ], [ "13:12", "IN" ] ]}'
IN_TIMES=$(jq '.times | to_entries | .[] | select(.value[1]| tostring | contains("IN")) | .value[0]' <<< "$JSON")
OUT_TIMES=$(jq '.times | to_entries | .[] | select(.value[1]| tostring | contains("OUT")) | .value[0]' <<< "$JSON")
ALL_TIMES=$(jq -r '.times| to_entries | .[] | .value[0]' <<< "$JSON")

prevtime=0
count=0
for i in $(echo $ALL_TIMES | sed "s/ / /g")
do
    if [[ "$count" -eq 0 ]]; then
     (( count++ ))
     prevtime=$i
     continue
    else
     (( count++ ))
    fi

    time1=`date +%s -d ${prevtime}`
    time2=`date +%s -d ${i}`
    diffsec=`expr ${time2} - ${time1}`

    echo From $prevtime to $i: `date +%H:%M -ud @${diffsec}`
    prevtime=$i

done

Ответы [ 3 ]

0 голосов
/ 09 января 2019

Поскольку вы измеряете время с точностью до минуты, достаточно вычислить минуты, не путаясь с командой date. У меня есть решение awk:

awk -F: -vIRS=" " -vfmt="From %5s to %5s: %4u minutes\n" \
  '{this=$1*60+$2}a{printf(fmt,at,$0,this-a);a=0;next}{a=this;at=$0}\
  END{if(a){$0=strftime("%H:%M");printf(fmt,at,$0,$1*60+$2-a)}}' <<<"$ALL_TIMES"

, который работает путем определения двоеточия в качестве разделителя полей и пробела в качестве разделителя записей. Таким образом, мы получаем отдельную запись с двумя полями для каждого времени. Тогда

  • {this=$1*60+$2}: мы вычисляем, сколько минут в текущей записи, и помещаем их в переменную this.
  • a{printf(fmt,at,$0,this-a);a=0;next}: Если (изначально пустая) переменная a не равна нулю и не равна нулю, мы читаем запись OUT, поэтому мы печатаем то, что хотим, устанавливаем a в ноль, потому что следующее поле будет быть записью IN, и мы переходим к следующей записи.
  • {a=this;at=$0}: В противном случае мы читаем запись IN и устанавливаем a в ее минуты и at в ее строковое представление (необходимо, мы распечатаем его, как в предыдущем случае).
  • END{if(a){$0=strftime("%H:%M");printf(fmt,at,$0,$1*60+$2-a)}}: в конце, если у нас все еще есть какие-то свисающие данные IN, мы устанавливаем $0 как правильно отформатированное текущее время и печатаем то, что хотим.

Все сделано.

0 голосов
/ 13 января 2019

С Xidel и небольшим количеством магии XQuery это довольно просто:

#!/bin/bash

JSON='{"times": [["7:43", "IN"], ["8:26", "OUT"], ["8:27", "IN"], ["9:12", "OUT"], ["9:14", "IN"], ["9:22", "OUT"], ["9:23", "IN "], ["12:12", "OUT"], ["13:12", "IN"]]}'

xidel -s - --xquery '
  let $in:=$json/(times)()[contains(.,"IN")](1) ! time(
        substring(
          "00:00:00",
          1,
          8-string-length(.)
        )||.
      ),
      $out:=$json/(times)()[contains(.,"OUT")](1) ! time(
        substring(
          "00:00:00",
          1,
          8-string-length(.)
        )||.
      )
  for $x at $i in $out return
  concat(
    "From ",
    $in[$i],
    " to ",
    $x,
    ": ",
    $x - $in[$i] + time("00:00:00")
  )
' <<< "$JSON"

$in

00:07:43
00:08:27
00:09:14
00:09:23
00:13:12

$out:

00:08:26
00:09:12
00:09:22
00:12:12

Выход:

From 00:07:43 to 00:08:26: 00:00:43
From 00:08:27 to 00:09:12: 00:00:45
From 00:09:14 to 00:09:22: 00:00:08
From 00:09:23 to 00:12:12: 00:02:49
0 голосов
/ 09 января 2019

Вот решение only-jq, которое вызывает jq только один раз. Тем не менее, обратите внимание, что может потребоваться настройка, учитывающая особенности часового пояса, обработку ошибок и, возможно, другие сложности:

def mins: split(":") | map(tonumber) | .[0] * 60 + .[1];

def diff: (.[1] - .[0]) | if . >= 0 then . else 24*60 + . end;

def now_mins: now | gmtime | .[3] * 60 + .[4];

def pairs:
  range(0; length; 2) as $i | [.[$i], .[$i+1] ];

def sigma(s): reduce s as $s (0; . + $s);

.times
| map( .[0] |= mins )
| if .[-1][1] == "IN" then . + [ [now_mins, "OUT"] ] else . end
| sigma(pairs | map(.[0]) | diff)
...