0

Assuming the following time-formats:

MM:DD:YYYY hh:mm:ss:nn

How can I compute the difference between two times? I have tried the following, but it seems to fail.

% Value1='08:27:2018 23:53:50:08'
% Value2='08:28:2018 00:00:08:89' 
% echo "$(($(date -d "$Value2" '+%s') - $(date -d "$Value1" '+%s')))"
user2316896
  • 29
  • 1
  • 6
  • 2
    Welcome to SO. Stack Overflow is a question and answer site for professional and enthusiast programmers. The goal is that you add some code of your own to your question to show at least the research effort you made to solve this yourself. – Cyrus Sep 12 '18 at 06:40
  • A question like this is the best way to get downvoted. Please have a look at the other comments. – Carlo Sep 12 '18 at 06:42
  • @user2316896 : Where in your code are the milliseconds? It's unlikely to be the, i.e., `89`, because this is a number with only 2 digits. – user1934428 Sep 12 '18 at 07:08
  • How in my logs I am getting above values – user2316896 Sep 12 '18 at 07:18

2 Answers2

2

Update: as the OP changed his format

$ Value1='08:27:2018 23:53:50:08'
$ Value2='08:28:2018 00:00:08:89'
$ Value1=${Value1/://}; Value1=${Value1/://}; Value1=${Value1%:*}.${Value1##*:}
$ Value2=${Value2/://}; Value2=${Value2/://}; Value2=${Value2%:*}.${Value2##*:}
$ echo $(date -d "$Value2" '+%s.%N') - $(date -d "$Value1" '+%s.%N') | bc -l
378.810000000

So all you need to do is convert it to a format that date knows, this is MM/DD/YYYY


Original answer below:

The problem is that your date-time format is not really recognized.

The date format:

$ date -d "08272018"
date: invalid date ‘08272018’

The date command knows many formats, but it is hard for it to distinguish between YYYYMMDD, DDMMYYYY and MMDDYYYY. To be clear, what does 10021002 represent as a date?

In this simple format—without delimiters—date will recognize YYYYMMDD and YYMMDD

$ date -d "20180708"
Sun  8 Jul 00:00:00 UTC 2018

The time format:

The notation HH:MM:SS:ss is by far from standard. What does ss represent, ss 60th of a second? The normal notation would be more HH:MM:SS.sss This will be recognized.

$ date -d "23:53:50:08"
date: invalid date ‘23:53:50:08’
$ date -d "23:53:50.08" "+%a %d %b %T.%N %Z %Y"
Wed 12 Sep 23:53:50.080000000 UTC 2018

So if you get your date format correct, you already get a long way:

% Value1='20180827 23:53:50.08'
% Value2='20180828 00:00:08.89' 
% echo "$(($(date -d "$Value2" '+%s') - $(date -d "$Value1" '+%s')))"
378

The sad thing is that we are missing our milliseconds for this you need floating point arithmetic and bash does not support it. But there are ways around that (How do I use floating-point division in bash?)

$ echo $(date -d "$Value2" '+%s.%N') - $(date -d "$Value1" '+%s.%N') | bc -l
378.810000000
kvantour
  • 25,269
  • 4
  • 47
  • 72
  • Thanks , but I am getting data in following formate Value1='08:27:2018 23:53:50.08' Value2='08:28:2018 00:00:08.89' – user2316896 Sep 12 '18 at 07:24
  • Updated. But please bear in mind, for the next time you ask a question, to type your question accurately. This way we do not need to iterate. As you notice now, half of the answer does not make any sense anymore wrt to the current state of the question. Don't take this in any form negative, merely a recommendation for next time. – kvantour Sep 12 '18 at 07:40
  • @user2316896 I have updated to fix the mistake `:` vs `.` – kvantour Sep 12 '18 at 13:30
  • @user2316896 If you like this answer, feel free to upvote/and or accept the answer. – kvantour Sep 13 '18 at 07:11
1

I shortened variable names:

v1='08:27:2018 23:53:50:08'
v2='08:28:2018 00:00:08:89'

With GNU date, just stick to one safe input format, you can convert YYYY-MM-DD HH:MM:SS.NN to... anything another. (side note: I love freebsd date, where you can just specify -f option for strptime. I wish I could do that with GNU date). So we can:

v1_epoch=$(date -d "${v1:6:4}-${v1:0:2}-${v1:3:2} ${v1:11:2}:${v1:14:2}:${v1:17:2}.${v1:20}" +%s.%N)
v2_epoch=$(date -d "${v2:6:4}-${v2:0:2}-${v2:3:2} ${v2:11:2}:${v2:14:2}:${v2:17:2}.${v2:20}" +%s.%N)

It will get us values of seconds with nanosecond resolution since epoch time. Now we need to calc a difference, we need to use a tool like bc, cause bash does not support floating point calculations.

diff=$(printf "scale=9; %s - %s\n" "$v2_epoch" "$v1_epoch" | bc)

Now this represents the difference of time we need to represent in hours, minutes, seconds and miliseconds.

printf "%s.%.3s" $(date -d@"$diff" -u +'%H:%M:%S %N')

That's simple, but it will wrap around at 23 hours, so we can do better with bc. The rounding in bc is sometimes unexpected... you need to just get used to unexpected scale=0 lines:

printf "%02d:%02d:%02d.%03d\n" $(printf 'scale=11; a=%s; scale=0; h=a/3600; m=a%%3600/60; s=a%%60/1; ms=a*1000%%1000/1; h \n m \n s \n ms \n' '$diff' | bc -l)

A "neat" oneliner:

$ v1='08:27:2018 23:53:50:08'
$ v2='08:28:2018 00:00:08:89'
$ printf "%02d:%02d:%02d.%03d\n" $(printf 'scale=11; a=%s; scale=0; h=a/3600; m=a%%3600/60; s=a%%60/1; ms=a*1000%%1000/1; h \n m \n s \n ms \n' "$(printf "scale=9; %s - %s\n" "$(date -d "${v2:6:4}-${v2:0:2}-${v2:3:2} ${v2:11:2}:${v2:14:2}:${v2:17:2}.${v2:20}" +%s.%N)" "$(date -d "${v1:6:4}-${v1:0:2}-${v1:3:2} ${v1:11:2}:${v1:14:2}:${v1:17:2}.${v1:20}" +%s.%N)" | bc)" | bc -l)

I guess this could be even shortened with some here strings, but that just harms readability:

printf "%02d:%02d:%02d.%03d\n" $(<<<"scale=11; a=$(<<< "scale=9; $(date -d "${v2:6:4}-${v2:0:2}-${v2:3:2} ${v2:11:2}:${v2:14:2}:${v2:17:2}.${v2:20}" +%s.%N) - $(date -d "${v1:6:4}-${v1:0:2}-${v1:3:2} ${v1:11:2}:${v1:14:2}:${v1:17:2}.${v1:20}" +%s.%N)"$'\n' bc); scale=0; h=a/3600; m=a%3600/60; s=a%60/1; ms=a*1000%1000/1; h"$'\n'"m"$'\n'"s"$'\n'"ms"$'\n' bc -l)

Or you can create a function for conversion:

mydate_read() { date -d "${1:6:4}-${1:0:2}-${1:3:2} ${1:11:2}:${1:14:2}:${1:17:2}.${1:20}" +%s.%N; };
printf "%02d:%02d:%02d.%03d\n" $(<<<"scale=11; a=$(<<< "scale=9; $(mydate_read "$v2") - $(mydate_read "$v1")"$'\n' bc); scale=0; h=a/3600; m=a%3600/60; s=a%60/1; ms=a*1000%1000/1; h"$'\n'"m"$'\n'"s"$'\n'"ms"$'\n' bc -l)

I forgot, we can merge the two bc calls into one:

mydate_read() { date -d "${1:6:4}-${1:0:2}-${1:3:2} ${1:11:2}:${1:14:2}:${1:17:2}.${1:20}" +%s.%N; };
printf "%02d:%02d:%02d.%03d\n" $(printf 'scale=11; a=%s - %s; scale=0; h=a/3600; m=a%%3600/60; s=a%%60/1; ms=a*1000%%1000/1; h \n m \n s \n ms \n' "$(mydate_read "$v2")" "$(mydate_read "$v1")" | bc -l)
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • If I removed single coat from V1 and V2 v1=08:27:2018 23:53:50:08 v2=08:28:2018 00:00:08:89 It is showing value 00 – user2316896 Sep 12 '18 at 10:35
  • You removed a 'coat'? It was warn anyway. You can test the code online: http://tpcg.io/DoghKO – KamilCuk Sep 12 '18 at 11:06
  • I am getting data in v1=08:27:2018 23:53:50:08 , v2=08:28:2018 00:00:08:89 formate. so it getting error date: invalid date `2018-08-28 3::6::7:.3' when pass via argument – user2316896 Sep 12 '18 at 11:40
  • How is `v1='08:27:2018 23:53:50:08'` different from what you have written? I fail to see the difference. Where does it differ? What do you mean? Can you show the whole code? The code is: `v1='08:27:2018 23:53:50:08'; echo "${v1:6:4}-${v1:0:2}-${v1:3:2} ${v1:11:2}:${v1:14:2}:${v1:17:2}.${v1:20}";` and it works. Maybe you have two spaces in between parts? Maybe more? Maybe less? Maybe a tab? Maybe there are some invisible characters? – KamilCuk Sep 12 '18 at 12:49