I've been trying to get the following to work but I always seem get some sort of error..
get time from user input
use said time as variable
convert said time to JST
take away $duration (minutes) from time to give new time.
It would be something along the lines of code below.
#!/bin/sh
read -p "Please enter hour: " hour
read -p "Please enter minute: " minute
read -p "Please enter duration: " duration
jptime=$(TZ=JST date --date $hour$minute)
newtime=$(date -d "$jptime" "-$duration minutes")
echo "$newtime"
I'd use a language with a good datetime library. For example, perl
perl -MDateTime -E '
($hour, $minute, $duration) = #ARGV;
$fmt = "%F %T %Z";
$local = DateTime->now(time_zone=>"local")->set(hour=>$hour, minute=>$minute, second=>0);
say $local->strftime($fmt);
$jp = $local->clone->set_time_zone("Asia/Tokyo");
say $jp->strftime($fmt);
$jp2 = $jp->subtract(minutes => $duration);
say $jp2->strftime($fmt);
' 8 0 45
2015-01-17 08:00:00 EST
2015-01-17 22:00:00 JST
2015-01-17 21:15:00 JST
or Tcl
hour=8
minute=0
duration=45
export hour minute duration
tclsh <<'END'
set fmt "%Y-%m-%d %T %Z"
set t [clock scan "$env(hour):$env(minute) today"]
puts [clock format $t -format $fmt]
puts [clock format $t -format $fmt -timezone "Asia/Tokyo"]
puts [clock format [clock add $t -$env(duration) minutes] -format $fmt -timezone "Asia/Tokyo"]
END
2015-01-17 08:00:00 EST
2015-01-17 22:00:00 JST
2015-01-17 21:15:00 JST
Per your comment:
perl -MDateTime -E '
($hour, $minute, $duration) = #ARGV;
say uc DateTime->now(time_zone=>"local")
->set(hour=>$hour, minute=>$minute, second=>0)
->set_time_zone("Asia/Tokyo")
->subtract(minutes => $duration)
->strftime("%a %H:%M");
' 8 0 45 >| output.file
Related
Operating System: Red Hat Enterprise Linux Server 7.2 (Maipo)
I want to round the time to the nearest 5 minutes, only up, not down, for example:
08:09:15 should be 08:10:00
08:11:26 should be 08:15:00
08:17:58 should be 08:20:00
I have been trying with:
(date -d #$(( (($(date +%s) + 150) / 300) * 300)) "+%H:%M:%S")
This will round the time but also down (08:11:18 will result in 08:10:00 and not 08:15:00)
Any idea how i can achieve this?
You may use this utility function for your rounding up:
roundDt() {
local n=300
local str="$1"
date -d #$(( ($(date -d "$str" '+%s') + $n)/$n * $n)) '+%H:%M:%S'
}
Then invoke this function as:
roundDt '08:09:15'
08:10:00
roundDt '08:11:26'
08:15:00
roundDt '08:17:58'
08:20:00
To trace how this function is computing use -x (trace mode) after exporting:
export -f roundDt
bash -cx "roundDt '08:11:26'"
+ roundDt 08:11:26
+ typeset n=300
+ typeset str=08:11:26
++ date -d 08:11:26 +%s
+ date -d #1535631300 +%H:%M:%S
08:15:00
GNU date can calculate already. It is explained in the manual in the chapter "Relative items in date strings". So you need just one date call.
d=$(date +%T) # get the current time
IFS=: read h m s <<< "$d" # parse it in hours, minutes and seconds
inc=$(( 300 - (m * 60 + s) % 300 )) # calculate the seconds to increment
date -d "$d $inc sec" +%T # output the new time with the offset
Btw: +%T is the same as +%H:%M:%S.
I can have UTC current time with "date -u".
But i would like to print the current time of some common timezones at the same time. (UTC, EDT, CEST...)
I can create a script and add severals :
date -u -d 'x hour ago'
But sometimes, hours change. (Like in France)
Is there another solution to have the "real" current time, based on the country / cities maybe ?
EDIT :
Here what i have done with the answer :
function dateall(){
echo -n "US Pacific : " && TZ=US/Pacific date
echo -n "US Eastern : " && TZ=US/Eastern date
echo -n "UTC ~ GMT : " && date -u
echo -n "Europe Paris : " && TZ=Europe/Paris date
echo -n "Asia Bangkok : " && TZ=Asia/Bangkok date
}
You can point TZ to the timezone you want:
$ TZ=Europe/Paris date
Fri Jun 12 07:41:28 CEST 2015
In my dateutils there's datezone to do what you want in one simple command:
$ datezone now Europe/Paris US/Pacific US/Eastern UTC
2015-06-12T08:23:32+02:00 Europe/Paris
2015-06-11T23:23:32-07:00 US/Pacific
2015-06-12T02:23:32-04:00 US/Eastern
2015-06-12T06:23:32+00:00 UTC
2015-06-12T13:23:32+07:00 Asia/Bangkok
$
with the additional advantage that you also get the current UTC offsets and the times are guaranteed to coincide, i.e. the current time is determined only once and then used for all timezone conversions.
I have a backup script which is written in bash/shell scripting language. I calculate the total runtime/execution time by doing this:
#!/bin/bash
# start time
res1=$(date +%s.%N)
### do some work here
# end time & calculate
res2=$(date +%s.%N)
dt=$(echo "$res2 - $res1" | bc)
dd=$(echo "$dt/86400" | bc)
dt2=$(echo "$dt-86400*$dd" | bc)
dh=$(echo "$dt2/3600" | bc)
dt3=$(echo "$dt2-3600*$dh" | bc)
dm=$(echo "$dt3/60" | bc)
ds=$(echo "$dt3-60*$dm" | bc)
# finished
printf " >>> Process Completed - Total Runtime (d:h:m:s) : %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds
echo " "
exit 0
This outputs something like this:
How do you format the result, so it looks something like this:
0 Days, 0 Hours, 0 Minutes and 0.0968 Seconds
If it can intelligently show only values > 0, like these examples - it would be abonus:
7 Minutes and 5.126 Seconds
or
2 hours, 4 Minutes and 1.106 Seconds
or
7.215 Seconds etc...
You can use your last printf like this:
printf " >>> Process Completed - Total Runtime (d:h:m:s) : %d Days, %02d Hours, %02d Minutes, %02.4f Seconds\n" $dd $dh $dm $ds
However I would suggest you to use awk and do all calculations and formatting in awk itself so that you can avoid many invocations of bc.
Suggested awk script:
awk -v res1="$res1" -v res2="$res2" 'BEGIN {dt=res2-res1; dd=dt/86400; dt2=dt-86400*dd;
dh=dt2/3600; dt3=dt2-3600*dh; dm=dt3/60; ds=dt3-60*dm;
printf " >>> Process Completed - Total Runtime (d:h:m:s) : %d Days, %02d Hours, %02d Minutes, %02.4f Seconds\n",
dt/86400, dd, dh, dm, ds}'
In a Linux script: I have a file that has two time entries for each message within the file. A 'received time' and a 'source time'. there are hundreds of messages within the file.
I want to calculate the elapsed time between the two times.
2014-07-16T18:40:48Z (received time)
2014-07-16T18:38:27Z (source time)
The source time is 3 lines after the received time, not that it matters.
info on the input data:
The input has a lines are as follows:
TimeStamp: 2014-07-16T18:40:48Z
2 lines later: a bunch of messages in one line and within each line, multiple times is:
sourceTimeStamp="2014-07-16T18:38:27Z"
If you have GNU's date (not busybox's), you can give difference in seconds with:
#!/bin/bash
A=$(date -d '2014-07-16T18:40:48Z' '+%s')
B=$(date -d '2014-07-16T18:38:27Z' '+%s')
echo "$(( A - B )) seconds"
For busybox's date and ash (modern probably / BusyBox v1.21.0):
#!/bin/ash
A=$(busybox date -d '2014-07-16 18:40:48' '+%s')
B=$(busybox date -d '2014-07-16 18:38:27' '+%s')
echo "$(( A - B )) seconds"
you should be able to use date like this (e.g.)
date +%s --date="2014-07-16T18:40:48Z"
to convert both timestamps into a unix timestamp. Getting the time difference between them is then reduced to a simple subtraction.
Does this help?
I would use awk. The following script searches for the lines of interest, converts the time value into a UNIX timestamp and saves them in the start, end variables. At the end of the script the difference will get calculated and printed:
timediff.awk:
/received time/ {
"date -d "$1" +%s" | getline end
}
/source time/ {
"date -d "$1" +%s" | getline start
exit
}
END {
printf "%s seconds in between", end - start
}
Execute it like this:
awk -f timediff.awk log.file
Output:
141 seconds in between
This question already has answers here:
Filter log file entries based on date range
(5 answers)
Closed 6 years ago.
I have agent.log file. This file is updating as regular interval.
Entries are as follows 2014-01-07 03:43:35,223 INFO ...some data
I want to extract data of last 3 minutes, Is there any way so that I will get this data using bash script?
Try the solution below:
awk \
-v start="$(date +"%F %R" --date=#$(expr `date +%s` - 180))" \
-v end="$(date "+%F %R")" \
'$0 ~ start, $0 ~ end' \
agent.log
In the start variable there is the time stamp 3 minutes (180 seconds) before the current time.
In the end there is the current time.
$0 ~ start, $0 ~ end selects the lines between start and end
date +"%F %R" gives you the current time down to the minute.
grep '^'"$(date +"%F %R")" agent.log will select the last minute from the file
Now for the previous two minutes it's more tricky... I have developed some scripts that can do complete time manipulation in relative or absolute, and it may be simpler than fiddling with date...
2 minutes ago in the right format: date --date="#$(($(date +"%s") - 2*60))" +"%F %R"
Merge all 3:
NOW=$(date +"%F %R")
M1=$(date --date="#$(($(date +"%s") - 1*60))" +"%F %R")
M2=$(date --date="#$(($(date +"%s") - 2*60))" +"%F %R")
grep '^'"$NOW\|$M1\|$M2" agent.log
my answer considers the followings:
using bash and UNIX/Linux commands
the last log line is the start time not the actual server time
there is no expectation about the lines' date (minutes, days, years, etc.)
the whole script should be expandable to the inverse, or a specified from-to interval
#!/bin/bash
# this script expects descending dates in a log file (reverse as real life examples)!!!
FILE=$1
INTV=180 # sec
while read LINE
do
if [ -z $LAST_LOG_LINE ]
then
# interval stat line
LAST_LOG_LINE=$(date --date="$( echo "$LINE" | sed -e 's/INFO.*//')" +%s)
# mod
#continue
fi
ACT_LOG_LINE=$(date --date="$( echo "$LINE" | sed -e 's/INFO.*//')" +%s)
# print line if not greater than $INTV (180s)
# else break the reading and exit
if [ $(($LAST_LOG_LINE-$ACT_LOG_LINE)) -gt $INTV ]
then
break
fi
# actual print
echo "$LINE"
done < $FILE
Testing:
2014-01-07 03:43:35,223 INFO ...some data
2014-01-07 03:42:35,223 INFO ...some data
2014-01-07 03:41:35,223 INFO ...some data
2014-01-07 03:40:35,223 INFO ...some data
2014-01-07 02:43:35,223 INFO ...some data
2014-01-07 01:43:35,223 INFO ...some data
2014-01-06 03:43:35,223 INFO ...some data
$ /tmp/stack.sh /tmp/log
2014-01-07 03:42:35,223 INFO ...some data
2014-01-07 03:41:35,223 INFO ...some data
2014-01-07 03:40:35,223 INFO ...some data
$
I think you may be somewhat better off using Python in this case. Even if this script doesn't find a date exactly 3 minutes ago, it will still get any log entries in between the time the script was called and 3 minutes ago. This is both concise and more robust than some of the previous solutions offered.
#!/usr/bin/env python
from datetime import datetime, timedelta
with open('agent.log') as f:
for line in f:
logdate = datetime.strptime(line.split(',')[0], '%Y-%m-%d %H:%M:%S')
if logdate >= datetime.now() - timedelta(minutes=3):
print(line)
A Ruby solution (tested on ruby 1.9.3)
You can pass days, hours, minutes or seconds as a parameter and it will search for the expression and on the file specified (or directory, in which case it will append '/*' to the name):
In your case just call the script like so: $0 -m 3 "expression" log_file
Note: Also if you know the location of 'ruby' change the shebang (first line of the script),
for security reasons.
#! /usr/bin/env ruby
require 'date'
require 'pathname'
if ARGV.length != 4
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
exit 1
end
begin
total_amount = Integer ARGV[1]
rescue ArgumentError
$stderr.print "error: parameter 'time' must be an Integer\n"
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
end
if ARGV[0] == "-m"
gap = Rational(60, 86400)
time_str = "%Y-%m-%d %H:%M"
elsif ARGV[0] == "-s"
gap = Rational(1, 86400)
time_str = "%Y-%m-%d %H:%M:%S"
elsif ARGV[0] == "-h"
gap = Rational(3600, 86400)
time_str = "%Y-%m-%d %H"
elsif ARGV[0] == "-d"
time_str = "%Y-%m-%d"
gap = 1
else
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
exit 1
end
pn = Pathname.new(ARGV[3])
if pn.exist?
log = (pn.directory?) ? ARGV[3] + "/*" : ARGV[3]
else
$stderr.print "error: file '" << ARGV[3] << "' does not exist\n"
$stderr.print "usage: #{$0} -d|-h|-m|-s time expression log_file\n"
end
search_str = ARGV[2]
now = DateTime.now
total_amount.times do
now -= gap
system "cat " << log << " | grep '" << now.strftime(time_str) << ".*" << search_str << "'"
end