0

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.

3
  • 1
    For your problem 1 did you try %S in caps instead of %s ? Commented Nov 8, 2014 at 10:31
  • Yes, I have tried switching to using the gnu date command but I can't seem to specify the input date format. I will just use string manipulation instead for now until I get a better solution Commented Nov 8, 2014 at 10:33
  • The OSX strftime man page says that %s returns seconds since the epoch. I wonder what strptime is doing with it. What does date -j -f "%H:%M:%s" "$processStartTime" show? Commented Nov 8, 2014 at 14:49

3 Answers 3

1

Two different way of string manipulation

Formating MaxOS's date dst format by using 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.

Using for parsing date value

# 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.

Sign up to request clarification or add additional context in comments.

Comments

0

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

3 Comments

Unfortunely MacOS's date command won't work like linux GNU's date command: ` -d dst Set the kernel's value for daylight saving time.... U have to use -j` switch...
I understand your point, but i don't understand why this would give me the error I'm experiencing: Why would outputting a timestamp in the same format produce a different timestamp?
0

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 ))"

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.