Reputation: 645
I am currently experiencing 2 problems with the mac date command.
I am running a shell script which first executes the command (ps -p $p_id -o etime=) to give me the elapsed time of a process with p_id, which returns a date of the form [[dd-]hh:]mm:ss
Problem 1: The following command to convert a date to another format seems to be causing weird problems: (date -j -f "%H:%M:%s" "$processStartTime" "+%H:%M:%s"). Say the prcoessStartTime=12:30:33. The output date this command returns is 01:00:33 even though I'm converting the date to the same format. It only gets the seconds right and the hours and minutes are wrong. If i use different quotation marks like '' surrounding the date format, i get even stranger results! The goal is to convert the time into minutes so i can see how many minutes the process has been running for.
Problem 2: Since the process date can be in the following format [[dd-]hh:]mm:ss how should i handle different date formats in my code? Should i have 3 conditions to check the format? or is there a way to handle this? i.e. some dates might be of the form hh:mm:ss and others mm:ss, and i need to have the input date format correct in the command otherwise it throws an error.
Upvotes: 0
Views: 1713
Reputation: 37
To solve problem no. 1 you have to replace %s
by %S
(as already suggested by RBH):
processStartTime='12:30:33'
- date -j -f "%H:%M:%s" "$processStartTime" "+%H:%M:%s"
+ date -j -f "%H:%M:%S" "$processStartTime" "+%H:%M:%S"
Combining the ps ho lstart= $pid
approach introduced by F. Hauri with setting LANG='C'
it is possible to avoid problem no. 2 as well:
{
pid='1'
ps_date="$(LANG='C' ps ho lstart= $pid)"
# convert ps_date to Unix epoch time
processStartTimeEpochTime="$(LANG='C' date -j -f '%a %b %d %T %Y' "$ps_date" '+%s' 2>/dev/null)"
elapsed_secs="$(( $(date '+%s') - ${processStartTimeEpochTime} ))"
printf '%s\n' "ps_date: $ps_date" "processStartTimeEpochTime: $processStartTimeEpochTime"
printf '%s\n' "elapsed seconds: $elapsed_secs" "elapsed minutes: $(( $elapsed_secs / 60 ))" "elapsed hours: $(( $elapsed_secs / 3600 ))"
printf '%s' 'processStartTimeEpochTime reversed: '
date -r "$processStartTimeEpochTime" '+%F--%T'
}
If you insist on using ps -p $pid -o etime=
it will get a bit clumsy because of parsing the [[dd-]hh:]mm:ss
format.
{
pid='1'
# days, hours, minutes, seconds
# format: [[dd-]hh:]mm:ss
dhms="$(ps -p $pid -o etime=)"
days=""
h=""
m=""
s=""
get_hms() {
hms="$1"
h=${hms%%:*}
hms=${hms#*:};
m=${hms%%:*}
hms=${hms#*:};
s=${hms}
return 0
}
case "$dhms" in
??-??:??:??) days=${dhms%%-*}; hms=${dhms#*-}; get_hms "$hms";;
?-??:??:??) days=${dhms%%-*}; hms=${dhms#*-}; get_hms "$hms";;
??:??:??) days="0"; hms="$dhms"; get_hms "$hms";;
?:??:??) days="0"; hms="0${dhms}"; get_hms "$hms";;
??:??) days="0"; hms="00:${dhms}"; get_hms "$hms";;
?:??) days="0"; hms="00:0${dhms}"; get_hms "$hms";;
??) days="0"; hms="00:00:${dhms}"; get_hms "$hms";;
?) days="0"; hms="00:00:0${dhms}"; get_hms "$hms";;
*) days=""; hms="";;
esac
# strip a leading 0 if any
days="${days#0}"
h="${h#0}"
m="${m#0}"
s="${s#0}"
[ X"$days" = "X" ] && days='0'
[ X"$h" = "X" ] && h='0'
[ X"$m" = "X" ] && m='0'
[ X"$s" = "X" ] && s='0'
printf '%s\n' "d: $days" "h: $h" "m: $m" "s: $s"
days_in_secs=$(( $days * 86400 ))
hours_in_secs=$(( $h * 3600 ))
minutes_in_secs=$(( $m * 60 ))
time_span_in_secs=$(( $days_in_secs + $hours_in_secs + $minutes_in_secs + $s ))
processStartEpochTime="$(( $(date '+%s') - ${time_span_in_secs} ))"
printf '%s\n' "dhms: $dhms"
printf '%s' 'processStartEpochTime reversed: '
date -r "$processStartEpochTime" '+%F--%T'
printf '%s\n' "elapsed seconds: $time_span_in_secs" "elapsed minutes: $(( $time_span_in_secs / 60 ))" "elapsed hours: $(( $time_span_in_secs / 3600 ))"
}
Upvotes: 0
Reputation: 70742
sed
.There is a strong sed
command to re-place values for MacOs's date command (not very sexy, but it work):
date -j $(
ps ho lstart= $p_id |
sed '
s/^....//;
s/Jan/01/;s/Feb/02/;s/Mar/03/;s/Apr/04/;s/May/05/;s/Jun/06/;
s/Jul/07/;s/Aug/08/;s/Sep/09/;s/Oct/10/;s/Nov/11/;s/Dec/12/;
s/^\([0-9]\{2\}\) \([0-9]\{2\}\) \([0-9]\{2\}\):\([0-9]\{2\}\):\([0-9]\{2\}\) [0-9]\{2\}\([0-9]\{2\}\)/\1\2\3\4\6.\5/;
') +%s
This will render starting time of process $p_id
in unixtimestamp.
# Building translation variables for months strings
for ((i=1;i<13;i++));do
val=$(printf %02d $i)
mnt=$(date -j ${val}010101 +%b)
eval mn$mnt=$val
done
IFS=': ' read -a psdate < <(ps ho lstart= $p_id)
var=mn${psdate[1]}
printf -v fdte "%02d%02d%02d%02d%02d.%02d" ${!var} ${psdate[2]} \
${psdate[3]} ${psdate[4]} ${psdate[6]#??} ${psdate[5]}
date -j $fdte +%s
Both command was tested on MacOS. I find the second one more sexy.
Upvotes: 1
Reputation: 189327
You seem to be mixing apples and oranges.
The date
command works with timestamps -- these are simple, atomic representations of a single discrete point in time, and as such, they refer to absolute time. In terms of implementation, a time_t
is traditionally the number of seconds since midnight, January 1, 1970 UTC (on Unix).
By contrast, the timing information you are processing is relative and represents a time span.
If A is "now" and B is "one hour and thirty-three seconds", does it refer to a time starting at A-B and ending at A, or does it refer to a time starting at A and ending at A+B? Or is it relative to some other point in time? The date
command conventionally interprets times without a date as relative to the preceding midnight; so you get (implicitly) a range starting at midnight and ending at the specified time.
The date
command also has a parser for various human expressions of relative time, but it's necessarily inexact and unpredictable, because that's how human language is.
vbvntv$ date -d 'tomorrow 07:00'
Tue Nov 11 07:00:00 EET 2014
I don't have access to a Mac where I could test this, but generally, it seems to be less capable and featureful when it comes to parsing human dates.
In your situation, it's not clear what you really want to do. Perhaps you want to calculate when A-B, i.e. given a workload which finished now (A) and took B amount of time, when did it start?
A common way of doing that is to convert these to absolute seconds.
vbvntv$ date -d "00:00"
Mon Nov 10 00:00:00 EET 2014
vbvntv$ date -d "00:00" +%s
1415570400
vbvntv$ date -d "01:00:33"
Mon Nov 10 01:00:33 EET 2014
vbvntv$ date -d "01:00:33" +%s
1415574033
vbvntv$ echo $((1415574033-1415570400))
3633
vbvntv$ date -d "3633 seconds ago"
Mon Nov 10 09:53:05 EET 2014
vbvntv$ # or alternatively
vbvntv$ date +%s
1415609708
vnvntv$ date -d @$((1415609708-3633))
Mon Nov 10 09:53:15 EET 2014
Upvotes: 0