cmd/coordinator: make durations more human-friendly

Previously we printed duration to seven decimal points after the
second, which isn't helpful or necessary to determine how long a
build took. Instead, round the duration to the nearest tenth of a
second (if the build took more than 10 seconds), the nearest hundredth
of a second (if it took 1-10 seconds), or the nearest tenth of a
millisecond (if it took less than a second), which should be more than
enough precision and is much easier to read.

Change-Id: I1c29d4a81335bf9ee3cddda0a341d3f321e82f6b
Reviewed-on: https://go-review.googlesource.com/40855
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/cmd/coordinator/coordinator.go b/cmd/coordinator/coordinator.go
index 3d489f2..6ec0bb0 100644
--- a/cmd/coordinator/coordinator.go
+++ b/cmd/coordinator/coordinator.go
@@ -2888,7 +2888,7 @@
 	s.end = t1
 	td := t1.Sub(s.start)
 	var text bytes.Buffer
-	fmt.Fprintf(&text, "after %v", td)
+	fmt.Fprintf(&text, "after %s", friendlyDuration(td))
 	if err != nil {
 		fmt.Fprintf(&text, "; err=%v", err)
 	}
diff --git a/cmd/coordinator/gce.go b/cmd/coordinator/gce.go
index 7e6eeee..aec1f9a 100644
--- a/cmd/coordinator/gce.go
+++ b/cmd/coordinator/gce.go
@@ -349,7 +349,7 @@
 		fmt.Fprintf(w, "<ul>")
 		for i, inst := range active {
 			if i < show/2 || i >= len(active)-(show/2) {
-				fmt.Fprintf(w, "<li>%v, %v</li>\n", inst.name, time.Since(inst.creation))
+				fmt.Fprintf(w, "<li>%v, %s</li>\n", inst.name, friendlyDuration(time.Since(inst.creation)))
 			} else if i == show/2 {
 				fmt.Fprintf(w, "<li>... %d of %d total omitted ...</li>\n", len(active)-show, len(active))
 			}
diff --git a/cmd/coordinator/reverse.go b/cmd/coordinator/reverse.go
index 520c882..1987149 100644
--- a/cmd/coordinator/reverse.go
+++ b/cmd/coordinator/reverse.go
@@ -328,14 +328,14 @@
 		if b.inUse {
 			machStatus = "working"
 		}
-		fmt.Fprintf(&buf, "<li>%s (%s) version %s, %s: connected %v, %s for %v</li>\n",
+		fmt.Fprintf(&buf, "<li>%s (%s) version %s, %s: connected %s, %s for %s</li>\n",
 			b.hostname,
 			b.conn.RemoteAddr(),
 			b.version,
 			b.hostType,
-			time.Since(b.regTime),
+			friendlyDuration(time.Since(b.regTime)),
 			machStatus,
-			time.Since(b.inUseTime))
+			friendlyDuration(time.Since(b.inUseTime)))
 		total[b.hostType]++
 		if b.inUse && !b.inHealthCheck {
 			inUse[b.hostType]++
diff --git a/cmd/coordinator/status.go b/cmd/coordinator/status.go
index 062faa1..b4164e0 100644
--- a/cmd/coordinator/status.go
+++ b/cmd/coordinator/status.go
@@ -116,6 +116,19 @@
 	}
 }
 
+func friendlyDuration(d time.Duration) string {
+	if d > 10*time.Second {
+		d2 := ((d + 50*time.Millisecond) / (100 * time.Millisecond)) * (100 * time.Millisecond)
+		return d2.String()
+	}
+	if d > time.Second {
+		d2 := ((d + 5*time.Millisecond) / (10 * time.Millisecond)) * (10 * time.Millisecond)
+		return d2.String()
+	}
+	d2 := ((d + 50*time.Microsecond) / (100 * time.Microsecond)) * (100 * time.Microsecond)
+	return d2.String()
+}
+
 func diskFree() string {
 	out, _ := exec.Command("df", "-h").Output()
 	return string(out)
diff --git a/cmd/coordinator/status_test.go b/cmd/coordinator/status_test.go
new file mode 100644
index 0000000..b8b4c21
--- /dev/null
+++ b/cmd/coordinator/status_test.go
@@ -0,0 +1,35 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"testing"
+	"time"
+)
+
+var durationTests = []struct {
+	in   time.Duration
+	want string
+}{
+	{10*time.Second + 555*time.Millisecond, "10.6s"},
+	{10*time.Second + 500*time.Millisecond, "10.5s"},
+	{10*time.Second + 499*time.Millisecond, "10.5s"},
+	{10*time.Second + 401*time.Millisecond, "10.4s"},
+	{9*time.Second + 401*time.Millisecond, "9.4s"},
+	{9*time.Second + 456*time.Millisecond, "9.46s"},
+	{9*time.Second + 445*time.Millisecond, "9.45s"},
+	{1 * time.Second, "1s"},
+	{859*time.Millisecond + 445*time.Microsecond, "859.4ms"},
+	{859*time.Millisecond + 460*time.Microsecond, "859.5ms"},
+}
+
+func TestFriendlyDuration(t *testing.T) {
+	for _, tt := range durationTests {
+		got := friendlyDuration(tt.in)
+		if got != tt.want {
+			t.Errorf("friendlyDuration(%v): got %s, want %s", tt.in, got, tt.want)
+		}
+	}
+}