Browse code

Move timeutils functions to the only places where they are used.

- Move time json marshaling to the jsonlog package: this is a docker
internal hack that we should not promote as a library.
- Move Timestamp encoding/decoding functions to the API types: This is
only used there. It could be a standalone library but I don't this
it's worth having a separated repo for this. It could introduce more
complexity than it solves.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/12/16 04:49:41
Showing 19 changed files
... ...
@@ -6,8 +6,8 @@ import (
6 6
 	"time"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
+	timetypes "github.com/docker/docker/api/types/time"
9 10
 	"github.com/docker/docker/pkg/parsers/filters"
10
-	"github.com/docker/docker/pkg/timeutils"
11 11
 )
12 12
 
13 13
 // Events returns a stream of events in the daemon in a ReadCloser.
... ...
@@ -17,14 +17,14 @@ func (cli *Client) Events(options types.EventsOptions) (io.ReadCloser, error) {
17 17
 	ref := time.Now()
18 18
 
19 19
 	if options.Since != "" {
20
-		ts, err := timeutils.GetTimestamp(options.Since, ref)
20
+		ts, err := timetypes.GetTimestamp(options.Since, ref)
21 21
 		if err != nil {
22 22
 			return nil, err
23 23
 		}
24 24
 		query.Set("since", ts)
25 25
 	}
26 26
 	if options.Until != "" {
27
-		ts, err := timeutils.GetTimestamp(options.Until, ref)
27
+		ts, err := timetypes.GetTimestamp(options.Until, ref)
28 28
 		if err != nil {
29 29
 			return nil, err
30 30
 		}
... ...
@@ -6,7 +6,7 @@ import (
6 6
 	"time"
7 7
 
8 8
 	"github.com/docker/docker/api/types"
9
-	"github.com/docker/docker/pkg/timeutils"
9
+	timetypes "github.com/docker/docker/api/types/time"
10 10
 )
11 11
 
12 12
 // ContainerLogs returns the logs generated by a container in an io.ReadCloser.
... ...
@@ -22,7 +22,7 @@ func (cli *Client) ContainerLogs(options types.ContainerLogsOptions) (io.ReadClo
22 22
 	}
23 23
 
24 24
 	if options.Since != "" {
25
-		ts, err := timeutils.GetTimestamp(options.Since, time.Now())
25
+		ts, err := timetypes.GetTimestamp(options.Since, time.Now())
26 26
 		if err != nil {
27 27
 			return nil, err
28 28
 		}
... ...
@@ -13,11 +13,11 @@ import (
13 13
 	"github.com/docker/distribution/registry/api/errcode"
14 14
 	"github.com/docker/docker/api/server/httputils"
15 15
 	"github.com/docker/docker/api/types"
16
+	timetypes "github.com/docker/docker/api/types/time"
16 17
 	"github.com/docker/docker/daemon"
17 18
 	derr "github.com/docker/docker/errors"
18 19
 	"github.com/docker/docker/pkg/ioutils"
19 20
 	"github.com/docker/docker/pkg/signal"
20
-	"github.com/docker/docker/pkg/timeutils"
21 21
 	"github.com/docker/docker/runconfig"
22 22
 	"github.com/docker/docker/utils"
23 23
 	"golang.org/x/net/context"
... ...
@@ -101,7 +101,7 @@ func (s *containerRouter) getContainersLogs(ctx context.Context, w http.Response
101 101
 
102 102
 	var since time.Time
103 103
 	if r.Form.Get("since") != "" {
104
-		s, n, err := timeutils.ParseTimestamps(r.Form.Get("since"), 0)
104
+		s, n, err := timetypes.ParseTimestamps(r.Form.Get("since"), 0)
105 105
 		if err != nil {
106 106
 			return err
107 107
 		}
... ...
@@ -9,10 +9,10 @@ import (
9 9
 	"github.com/docker/docker/api"
10 10
 	"github.com/docker/docker/api/server/httputils"
11 11
 	"github.com/docker/docker/api/types"
12
+	timetypes "github.com/docker/docker/api/types/time"
12 13
 	"github.com/docker/docker/pkg/ioutils"
13 14
 	"github.com/docker/docker/pkg/jsonmessage"
14 15
 	"github.com/docker/docker/pkg/parsers/filters"
15
-	"github.com/docker/docker/pkg/timeutils"
16 16
 	"golang.org/x/net/context"
17 17
 )
18 18
 
... ...
@@ -46,11 +46,11 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
46 46
 	if err := httputils.ParseForm(r); err != nil {
47 47
 		return err
48 48
 	}
49
-	since, sinceNano, err := timeutils.ParseTimestamps(r.Form.Get("since"), -1)
49
+	since, sinceNano, err := timetypes.ParseTimestamps(r.Form.Get("since"), -1)
50 50
 	if err != nil {
51 51
 		return err
52 52
 	}
53
-	until, untilNano, err := timeutils.ParseTimestamps(r.Form.Get("until"), -1)
53
+	until, untilNano, err := timetypes.ParseTimestamps(r.Form.Get("until"), -1)
54 54
 	if err != nil {
55 55
 		return err
56 56
 	}
57 57
new file mode 100644
... ...
@@ -0,0 +1,124 @@
0
+package time
1
+
2
+import (
3
+	"fmt"
4
+	"math"
5
+	"strconv"
6
+	"strings"
7
+	"time"
8
+)
9
+
10
+// These are additional predefined layouts for use in Time.Format and Time.Parse
11
+// with --since and --until parameters for `docker logs` and `docker events`
12
+const (
13
+	rFC3339Local     = "2006-01-02T15:04:05"           // RFC3339 with local timezone
14
+	rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
15
+	dateWithZone     = "2006-01-02Z07:00"              // RFC3339 with time at 00:00:00
16
+	dateLocal        = "2006-01-02"                    // RFC3339 with local timezone and time at 00:00:00
17
+)
18
+
19
+// GetTimestamp tries to parse given string as golang duration,
20
+// then RFC3339 time and finally as a Unix timestamp. If
21
+// any of these were successful, it returns a Unix timestamp
22
+// as string otherwise returns the given value back.
23
+// In case of duration input, the returned timestamp is computed
24
+// as the given reference time minus the amount of the duration.
25
+func GetTimestamp(value string, reference time.Time) (string, error) {
26
+	if d, err := time.ParseDuration(value); value != "0" && err == nil {
27
+		return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
28
+	}
29
+
30
+	var format string
31
+	var parseInLocation bool
32
+
33
+	// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
34
+	parseInLocation = !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
35
+
36
+	if strings.Contains(value, ".") {
37
+		if parseInLocation {
38
+			format = rFC3339NanoLocal
39
+		} else {
40
+			format = time.RFC3339Nano
41
+		}
42
+	} else if strings.Contains(value, "T") {
43
+		// we want the number of colons in the T portion of the timestamp
44
+		tcolons := strings.Count(value, ":")
45
+		// if parseInLocation is off and we have a +/- zone offset (not Z) then
46
+		// there will be an extra colon in the input for the tz offset subtract that
47
+		// colon from the tcolons count
48
+		if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
49
+			tcolons--
50
+		}
51
+		if parseInLocation {
52
+			switch tcolons {
53
+			case 0:
54
+				format = "2006-01-02T15"
55
+			case 1:
56
+				format = "2006-01-02T15:04"
57
+			default:
58
+				format = rFC3339Local
59
+			}
60
+		} else {
61
+			switch tcolons {
62
+			case 0:
63
+				format = "2006-01-02T15Z07:00"
64
+			case 1:
65
+				format = "2006-01-02T15:04Z07:00"
66
+			default:
67
+				format = time.RFC3339
68
+			}
69
+		}
70
+	} else if parseInLocation {
71
+		format = dateLocal
72
+	} else {
73
+		format = dateWithZone
74
+	}
75
+
76
+	var t time.Time
77
+	var err error
78
+
79
+	if parseInLocation {
80
+		t, err = time.ParseInLocation(format, value, time.FixedZone(time.Now().Zone()))
81
+	} else {
82
+		t, err = time.Parse(format, value)
83
+	}
84
+
85
+	if err != nil {
86
+		// if there is a `-` then its an RFC3339 like timestamp otherwise assume unixtimestamp
87
+		if strings.Contains(value, "-") {
88
+			return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
89
+		}
90
+		return value, nil // unixtimestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
91
+	}
92
+
93
+	return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
94
+}
95
+
96
+// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
97
+// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
98
+// if the incoming nanosecond portion is longer or shorter than 9 digits it is
99
+// converted to nanoseconds.  The expectation is that the seconds and
100
+// seconds will be used to create a time variable.  For example:
101
+//     seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
102
+//     if err == nil since := time.Unix(seconds, nanoseconds)
103
+// returns seconds as def(aultSeconds) if value == ""
104
+func ParseTimestamps(value string, def int64) (int64, int64, error) {
105
+	if value == "" {
106
+		return def, 0, nil
107
+	}
108
+	sa := strings.SplitN(value, ".", 2)
109
+	s, err := strconv.ParseInt(sa[0], 10, 64)
110
+	if err != nil {
111
+		return s, 0, err
112
+	}
113
+	if len(sa) != 2 {
114
+		return s, 0, nil
115
+	}
116
+	n, err := strconv.ParseInt(sa[1], 10, 64)
117
+	if err != nil {
118
+		return s, n, err
119
+	}
120
+	// should already be in nanoseconds but just in case convert n to nanoseonds
121
+	n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
122
+	return s, n, nil
123
+}
0 124
new file mode 100644
... ...
@@ -0,0 +1,93 @@
0
+package time
1
+
2
+import (
3
+	"fmt"
4
+	"testing"
5
+	"time"
6
+)
7
+
8
+func TestGetTimestamp(t *testing.T) {
9
+	now := time.Now()
10
+	cases := []struct {
11
+		in, expected string
12
+		expectedErr  bool
13
+	}{
14
+		// Partial RFC3339 strings get parsed with second precision
15
+		{"2006-01-02T15:04:05.999999999+07:00", "1136189045.999999999", false},
16
+		{"2006-01-02T15:04:05.999999999Z", "1136214245.999999999", false},
17
+		{"2006-01-02T15:04:05.999999999", "1136214245.999999999", false},
18
+		{"2006-01-02T15:04:05Z", "1136214245.000000000", false},
19
+		{"2006-01-02T15:04:05", "1136214245.000000000", false},
20
+		{"2006-01-02T15:04:0Z", "", true},
21
+		{"2006-01-02T15:04:0", "", true},
22
+		{"2006-01-02T15:04Z", "1136214240.000000000", false},
23
+		{"2006-01-02T15:04+00:00", "1136214240.000000000", false},
24
+		{"2006-01-02T15:04-00:00", "1136214240.000000000", false},
25
+		{"2006-01-02T15:04", "1136214240.000000000", false},
26
+		{"2006-01-02T15:0Z", "", true},
27
+		{"2006-01-02T15:0", "", true},
28
+		{"2006-01-02T15Z", "1136214000.000000000", false},
29
+		{"2006-01-02T15+00:00", "1136214000.000000000", false},
30
+		{"2006-01-02T15-00:00", "1136214000.000000000", false},
31
+		{"2006-01-02T15", "1136214000.000000000", false},
32
+		{"2006-01-02T1Z", "1136163600.000000000", false},
33
+		{"2006-01-02T1", "1136163600.000000000", false},
34
+		{"2006-01-02TZ", "", true},
35
+		{"2006-01-02T", "", true},
36
+		{"2006-01-02+00:00", "1136160000.000000000", false},
37
+		{"2006-01-02-00:00", "1136160000.000000000", false},
38
+		{"2006-01-02-00:01", "1136160060.000000000", false},
39
+		{"2006-01-02Z", "1136160000.000000000", false},
40
+		{"2006-01-02", "1136160000.000000000", false},
41
+		{"2015-05-13T20:39:09Z", "1431549549.000000000", false},
42
+
43
+		// unix timestamps returned as is
44
+		{"1136073600", "1136073600", false},
45
+		{"1136073600.000000001", "1136073600.000000001", false},
46
+		// Durations
47
+		{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix()), false},
48
+		{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
49
+		{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
50
+
51
+		// String fallback
52
+		{"invalid", "invalid", false},
53
+	}
54
+
55
+	for _, c := range cases {
56
+		o, err := GetTimestamp(c.in, now)
57
+		if o != c.expected ||
58
+			(err == nil && c.expectedErr) ||
59
+			(err != nil && !c.expectedErr) {
60
+			t.Errorf("wrong value for '%s'. expected:'%s' got:'%s' with error: `%s`", c.in, c.expected, o, err)
61
+			t.Fail()
62
+		}
63
+	}
64
+}
65
+
66
+func TestParseTimestamps(t *testing.T) {
67
+	cases := []struct {
68
+		in                        string
69
+		def, expectedS, expectedN int64
70
+		expectedErr               bool
71
+	}{
72
+		// unix timestamps
73
+		{"1136073600", 0, 1136073600, 0, false},
74
+		{"1136073600.000000001", 0, 1136073600, 1, false},
75
+		{"1136073600.0000000010", 0, 1136073600, 1, false},
76
+		{"1136073600.00000001", 0, 1136073600, 10, false},
77
+		{"foo.bar", 0, 0, 0, true},
78
+		{"1136073600.bar", 0, 1136073600, 0, true},
79
+		{"", -1, -1, 0, false},
80
+	}
81
+
82
+	for _, c := range cases {
83
+		s, n, err := ParseTimestamps(c.in, c.def)
84
+		if s != c.expectedS ||
85
+			n != c.expectedN ||
86
+			(err == nil && c.expectedErr) ||
87
+			(err != nil && !c.expectedErr) {
88
+			t.Errorf("wrong values for input `%s` with default `%d` expected:'%d'seconds and `%d`nanosecond got:'%d'seconds and `%d`nanoseconds with error: `%s`", c.in, c.def, c.expectedS, c.expectedN, s, n, err)
89
+			t.Fail()
90
+		}
91
+	}
92
+}
... ...
@@ -14,7 +14,6 @@ import (
14 14
 	"github.com/docker/docker/daemon/logger"
15 15
 	"github.com/docker/docker/daemon/logger/loggerutils"
16 16
 	"github.com/docker/docker/pkg/jsonlog"
17
-	"github.com/docker/docker/pkg/timeutils"
18 17
 	"github.com/docker/docker/pkg/units"
19 18
 )
20 19
 
... ...
@@ -87,8 +86,7 @@ func New(ctx logger.Context) (logger.Logger, error) {
87 87
 
88 88
 // Log converts logger.Message to jsonlog.JSONLog and serializes it to file.
89 89
 func (l *JSONFileLogger) Log(msg *logger.Message) error {
90
-
91
-	timestamp, err := timeutils.FastMarshalJSON(msg.Timestamp)
90
+	timestamp, err := jsonlog.FastTimeMarshalJSON(msg.Timestamp)
92 91
 	if err != nil {
93 92
 		return err
94 93
 	}
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	"errors"
12 12
 	"time"
13 13
 
14
-	"github.com/docker/docker/pkg/timeutils"
14
+	"github.com/docker/docker/pkg/jsonlog"
15 15
 )
16 16
 
17 17
 // ErrReadLogsNotSupported is returned when the logger does not support reading logs.
... ...
@@ -19,7 +19,7 @@ var ErrReadLogsNotSupported = errors.New("configured logging reader does not sup
19 19
 
20 20
 const (
21 21
 	// TimeFormat is the time format used for timestamps sent to log readers.
22
-	TimeFormat           = timeutils.RFC3339NanoFixed
22
+	TimeFormat           = jsonlog.RFC3339NanoFixed
23 23
 	logWatcherBufferSize = 4096
24 24
 )
25 25
 
... ...
@@ -20,11 +20,11 @@ import (
20 20
 	"github.com/docker/docker/daemon/logger"
21 21
 	"github.com/docker/docker/dockerversion"
22 22
 	"github.com/docker/docker/opts"
23
+	"github.com/docker/docker/pkg/jsonlog"
23 24
 	flag "github.com/docker/docker/pkg/mflag"
24 25
 	"github.com/docker/docker/pkg/pidfile"
25 26
 	"github.com/docker/docker/pkg/signal"
26 27
 	"github.com/docker/docker/pkg/system"
27
-	"github.com/docker/docker/pkg/timeutils"
28 28
 	"github.com/docker/docker/pkg/tlsconfig"
29 29
 	"github.com/docker/docker/registry"
30 30
 	"github.com/docker/docker/utils"
... ...
@@ -150,7 +150,7 @@ func (cli *DaemonCli) CmdDaemon(args ...string) error {
150 150
 		logrus.Warn("Running experimental build")
151 151
 	}
152 152
 
153
-	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
153
+	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: jsonlog.RFC3339NanoFixed})
154 154
 
155 155
 	if err := setDefaultUmask(); err != nil {
156 156
 		logrus.Fatalf("Failed to set umask: %v", err)
... ...
@@ -11,7 +11,7 @@ import (
11 11
 	"time"
12 12
 
13 13
 	"github.com/docker/docker/pkg/integration/checker"
14
-	"github.com/docker/docker/pkg/timeutils"
14
+	"github.com/docker/docker/pkg/jsonlog"
15 15
 	"github.com/go-check/check"
16 16
 )
17 17
 
... ...
@@ -75,7 +75,7 @@ func (s *DockerSuite) TestLogsTimestamps(c *check.C) {
75 75
 
76 76
 	for _, l := range lines {
77 77
 		if l != "" {
78
-			_, err := time.Parse(timeutils.RFC3339NanoFixed+" ", ts.FindString(l))
78
+			_, err := time.Parse(jsonlog.RFC3339NanoFixed+" ", ts.FindString(l))
79 79
 			c.Assert(err, checker.IsNil, check.Commentf("Failed to parse timestamp from %v", l))
80 80
 			// ensure we have padded 0's
81 81
 			c.Assert(l[29], checker.Equals, uint8('Z'))
... ...
@@ -13,8 +13,6 @@
13 13
 //        "bytes"
14 14
 //-
15 15
 //        "unicode/utf8"
16
-//+
17
-//+       "github.com/docker/docker/pkg/timeutils"
18 16
 // )
19 17
 //
20 18
 // func (mj *JSONLog) MarshalJSON() ([]byte, error) {
... ...
@@ -43,7 +41,7 @@
43 43
 //        }
44 44
 //        buf.WriteString(`"time":`)
45 45
 //-       obj, err = mj.Created.MarshalJSON()
46
-//+       timestamp, err = timeutils.FastMarshalJSON(mj.Created)
46
+//+       timestamp, err = FastTimeMarshalJSON(mj.Created)
47 47
 //        if err != nil {
48 48
 //                return err
49 49
 //        }
... ...
@@ -69,8 +67,6 @@ package jsonlog
69 69
 import (
70 70
 	"bytes"
71 71
 	"unicode/utf8"
72
-
73
-	"github.com/docker/docker/pkg/timeutils"
74 72
 )
75 73
 
76 74
 // MarshalJSON marshals the JSONLog.
... ...
@@ -111,7 +107,7 @@ func (mj *JSONLog) MarshalJSONBuf(buf *bytes.Buffer) error {
111 111
 		buf.WriteString(`,`)
112 112
 	}
113 113
 	buf.WriteString(`"time":`)
114
-	timestamp, err = timeutils.FastMarshalJSON(mj.Created)
114
+	timestamp, err = FastTimeMarshalJSON(mj.Created)
115 115
 	if err != nil {
116 116
 		return err
117 117
 	}
118 118
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+// Package jsonlog provides helper functions to parse and print time (time.Time) as JSON.
1
+package jsonlog
2
+
3
+import (
4
+	"errors"
5
+	"time"
6
+)
7
+
8
+const (
9
+	// RFC3339NanoFixed is our own version of RFC339Nano because we want one
10
+	// that pads the nano seconds part with zeros to ensure
11
+	// the timestamps are aligned in the logs.
12
+	RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
13
+	// JSONFormat is the format used by FastMarshalJSON
14
+	JSONFormat = `"` + time.RFC3339Nano + `"`
15
+)
16
+
17
+// FastTimeMarshalJSON avoids one of the extra allocations that
18
+// time.MarshalJSON is making.
19
+func FastTimeMarshalJSON(t time.Time) (string, error) {
20
+	if y := t.Year(); y < 0 || y >= 10000 {
21
+		// RFC 3339 is clear that years are 4 digits exactly.
22
+		// See golang.org/issue/4556#c15 for more discussion.
23
+		return "", errors.New("time.MarshalJSON: year outside of range [0,9999]")
24
+	}
25
+	return t.Format(JSONFormat), nil
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,47 @@
0
+package jsonlog
1
+
2
+import (
3
+	"testing"
4
+	"time"
5
+)
6
+
7
+// Testing to ensure 'year' fields is between 0 and 9999
8
+func TestFastTimeMarshalJSONWithInvalidDate(t *testing.T) {
9
+	aTime := time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local)
10
+	json, err := FastTimeMarshalJSON(aTime)
11
+	if err == nil {
12
+		t.Fatalf("FastTimeMarshalJSON should throw an error, but was '%v'", json)
13
+	}
14
+	anotherTime := time.Date(10000, 1, 1, 0, 0, 0, 0, time.Local)
15
+	json, err = FastTimeMarshalJSON(anotherTime)
16
+	if err == nil {
17
+		t.Fatalf("FastTimeMarshalJSON should throw an error, but was '%v'", json)
18
+	}
19
+
20
+}
21
+
22
+func TestFastTimeMarshalJSON(t *testing.T) {
23
+	aTime := time.Date(2015, 5, 29, 11, 1, 2, 3, time.UTC)
24
+	json, err := FastTimeMarshalJSON(aTime)
25
+	if err != nil {
26
+		t.Fatal(err)
27
+	}
28
+	expected := "\"2015-05-29T11:01:02.000000003Z\""
29
+	if json != expected {
30
+		t.Fatalf("Expected %v, got %v", expected, json)
31
+	}
32
+
33
+	location, err := time.LoadLocation("Europe/Paris")
34
+	if err != nil {
35
+		t.Fatal(err)
36
+	}
37
+	aTime = time.Date(2015, 5, 29, 11, 1, 2, 3, location)
38
+	json, err = FastTimeMarshalJSON(aTime)
39
+	if err != nil {
40
+		t.Fatal(err)
41
+	}
42
+	expected = "\"2015-05-29T11:01:02.000000003+02:00\""
43
+	if json != expected {
44
+		t.Fatalf("Expected %v, got %v", expected, json)
45
+	}
46
+}
... ...
@@ -7,8 +7,8 @@ import (
7 7
 	"strings"
8 8
 	"time"
9 9
 
10
+	"github.com/docker/docker/pkg/jsonlog"
10 11
 	"github.com/docker/docker/pkg/term"
11
-	"github.com/docker/docker/pkg/timeutils"
12 12
 	"github.com/docker/docker/pkg/units"
13 13
 )
14 14
 
... ...
@@ -123,9 +123,9 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
123 123
 		return nil
124 124
 	}
125 125
 	if jm.TimeNano != 0 {
126
-		fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(timeutils.RFC3339NanoFixed))
126
+		fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(jsonlog.RFC3339NanoFixed))
127 127
 	} else if jm.Time != 0 {
128
-		fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(timeutils.RFC3339NanoFixed))
128
+		fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(jsonlog.RFC3339NanoFixed))
129 129
 	}
130 130
 	if jm.ID != "" {
131 131
 		fmt.Fprintf(out, "%s: ", jm.ID)
... ...
@@ -7,8 +7,8 @@ import (
7 7
 	"testing"
8 8
 	"time"
9 9
 
10
+	"github.com/docker/docker/pkg/jsonlog"
10 11
 	"github.com/docker/docker/pkg/term"
11
-	"github.com/docker/docker/pkg/timeutils"
12 12
 )
13 13
 
14 14
 func TestError(t *testing.T) {
... ...
@@ -71,8 +71,8 @@ func TestJSONMessageDisplay(t *testing.T) {
71 71
 			From:   "From",
72 72
 			Status: "status",
73 73
 		}: {
74
-			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(timeutils.RFC3339NanoFixed)),
75
-			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(timeutils.RFC3339NanoFixed)),
74
+			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
75
+			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(now.Unix(), 0).Format(jsonlog.RFC3339NanoFixed)),
76 76
 		},
77 77
 		// General, with nano precision time
78 78
 		JSONMessage{
... ...
@@ -81,8 +81,8 @@ func TestJSONMessageDisplay(t *testing.T) {
81 81
 			From:     "From",
82 82
 			Status:   "status",
83 83
 		}: {
84
-			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(timeutils.RFC3339NanoFixed)),
85
-			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(timeutils.RFC3339NanoFixed)),
84
+			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
85
+			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
86 86
 		},
87 87
 		// General, with both times Nano is preferred
88 88
 		JSONMessage{
... ...
@@ -92,8 +92,8 @@ func TestJSONMessageDisplay(t *testing.T) {
92 92
 			From:     "From",
93 93
 			Status:   "status",
94 94
 		}: {
95
-			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(timeutils.RFC3339NanoFixed)),
96
-			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(timeutils.RFC3339NanoFixed)),
95
+			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
96
+			fmt.Sprintf("%v ID: (from From) status\n", time.Unix(0, now.UnixNano()).Format(jsonlog.RFC3339NanoFixed)),
97 97
 		},
98 98
 		// Stream over status
99 99
 		JSONMessage{
100 100
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-// Package timeutils provides helper functions to parse and print time (time.Time).
2
-package timeutils
3
-
4
-import (
5
-	"errors"
6
-	"time"
7
-)
8
-
9
-const (
10
-	// RFC3339NanoFixed is our own version of RFC339Nano because we want one
11
-	// that pads the nano seconds part with zeros to ensure
12
-	// the timestamps are aligned in the logs.
13
-	RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
14
-	// JSONFormat is the format used by FastMarshalJSON
15
-	JSONFormat = `"` + time.RFC3339Nano + `"`
16
-)
17
-
18
-// FastMarshalJSON avoids one of the extra allocations that
19
-// time.MarshalJSON is making.
20
-func FastMarshalJSON(t time.Time) (string, error) {
21
-	if y := t.Year(); y < 0 || y >= 10000 {
22
-		// RFC 3339 is clear that years are 4 digits exactly.
23
-		// See golang.org/issue/4556#c15 for more discussion.
24
-		return "", errors.New("time.MarshalJSON: year outside of range [0,9999]")
25
-	}
26
-	return t.Format(JSONFormat), nil
27
-}
28 1
deleted file mode 100644
... ...
@@ -1,47 +0,0 @@
1
-package timeutils
2
-
3
-import (
4
-	"testing"
5
-	"time"
6
-)
7
-
8
-// Testing to ensure 'year' fields is between 0 and 9999
9
-func TestFastMarshalJSONWithInvalidDate(t *testing.T) {
10
-	aTime := time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local)
11
-	json, err := FastMarshalJSON(aTime)
12
-	if err == nil {
13
-		t.Fatalf("FastMarshalJSON should throw an error, but was '%v'", json)
14
-	}
15
-	anotherTime := time.Date(10000, 1, 1, 0, 0, 0, 0, time.Local)
16
-	json, err = FastMarshalJSON(anotherTime)
17
-	if err == nil {
18
-		t.Fatalf("FastMarshalJSON should throw an error, but was '%v'", json)
19
-	}
20
-
21
-}
22
-
23
-func TestFastMarshalJSON(t *testing.T) {
24
-	aTime := time.Date(2015, 5, 29, 11, 1, 2, 3, time.UTC)
25
-	json, err := FastMarshalJSON(aTime)
26
-	if err != nil {
27
-		t.Fatal(err)
28
-	}
29
-	expected := "\"2015-05-29T11:01:02.000000003Z\""
30
-	if json != expected {
31
-		t.Fatalf("Expected %v, got %v", expected, json)
32
-	}
33
-
34
-	location, err := time.LoadLocation("Europe/Paris")
35
-	if err != nil {
36
-		t.Fatal(err)
37
-	}
38
-	aTime = time.Date(2015, 5, 29, 11, 1, 2, 3, location)
39
-	json, err = FastMarshalJSON(aTime)
40
-	if err != nil {
41
-		t.Fatal(err)
42
-	}
43
-	expected = "\"2015-05-29T11:01:02.000000003+02:00\""
44
-	if json != expected {
45
-		t.Fatalf("Expected %v, got %v", expected, json)
46
-	}
47
-}
48 1
deleted file mode 100644
... ...
@@ -1,124 +0,0 @@
1
-package timeutils
2
-
3
-import (
4
-	"fmt"
5
-	"math"
6
-	"strconv"
7
-	"strings"
8
-	"time"
9
-)
10
-
11
-// These are additional predefined layouts for use in Time.Format and Time.Parse
12
-// with --since and --until parameters for `docker logs` and `docker events`
13
-const (
14
-	rFC3339Local     = "2006-01-02T15:04:05"           // RFC3339 with local timezone
15
-	rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
16
-	dateWithZone     = "2006-01-02Z07:00"              // RFC3339 with time at 00:00:00
17
-	dateLocal        = "2006-01-02"                    // RFC3339 with local timezone and time at 00:00:00
18
-)
19
-
20
-// GetTimestamp tries to parse given string as golang duration,
21
-// then RFC3339 time and finally as a Unix timestamp. If
22
-// any of these were successful, it returns a Unix timestamp
23
-// as string otherwise returns the given value back.
24
-// In case of duration input, the returned timestamp is computed
25
-// as the given reference time minus the amount of the duration.
26
-func GetTimestamp(value string, reference time.Time) (string, error) {
27
-	if d, err := time.ParseDuration(value); value != "0" && err == nil {
28
-		return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
29
-	}
30
-
31
-	var format string
32
-	var parseInLocation bool
33
-
34
-	// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
35
-	parseInLocation = !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
36
-
37
-	if strings.Contains(value, ".") {
38
-		if parseInLocation {
39
-			format = rFC3339NanoLocal
40
-		} else {
41
-			format = time.RFC3339Nano
42
-		}
43
-	} else if strings.Contains(value, "T") {
44
-		// we want the number of colons in the T portion of the timestamp
45
-		tcolons := strings.Count(value, ":")
46
-		// if parseInLocation is off and we have a +/- zone offset (not Z) then
47
-		// there will be an extra colon in the input for the tz offset subtract that
48
-		// colon from the tcolons count
49
-		if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
50
-			tcolons--
51
-		}
52
-		if parseInLocation {
53
-			switch tcolons {
54
-			case 0:
55
-				format = "2006-01-02T15"
56
-			case 1:
57
-				format = "2006-01-02T15:04"
58
-			default:
59
-				format = rFC3339Local
60
-			}
61
-		} else {
62
-			switch tcolons {
63
-			case 0:
64
-				format = "2006-01-02T15Z07:00"
65
-			case 1:
66
-				format = "2006-01-02T15:04Z07:00"
67
-			default:
68
-				format = time.RFC3339
69
-			}
70
-		}
71
-	} else if parseInLocation {
72
-		format = dateLocal
73
-	} else {
74
-		format = dateWithZone
75
-	}
76
-
77
-	var t time.Time
78
-	var err error
79
-
80
-	if parseInLocation {
81
-		t, err = time.ParseInLocation(format, value, time.FixedZone(time.Now().Zone()))
82
-	} else {
83
-		t, err = time.Parse(format, value)
84
-	}
85
-
86
-	if err != nil {
87
-		// if there is a `-` then its an RFC3339 like timestamp otherwise assume unixtimestamp
88
-		if strings.Contains(value, "-") {
89
-			return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
90
-		}
91
-		return value, nil // unixtimestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
92
-	}
93
-
94
-	return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
95
-}
96
-
97
-// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
98
-// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
99
-// if the incoming nanosecond portion is longer or shorter than 9 digits it is
100
-// converted to nanoseconds.  The expectation is that the seconds and
101
-// seconds will be used to create a time variable.  For example:
102
-//     seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
103
-//     if err == nil since := time.Unix(seconds, nanoseconds)
104
-// returns seconds as def(aultSeconds) if value == ""
105
-func ParseTimestamps(value string, def int64) (int64, int64, error) {
106
-	if value == "" {
107
-		return def, 0, nil
108
-	}
109
-	sa := strings.SplitN(value, ".", 2)
110
-	s, err := strconv.ParseInt(sa[0], 10, 64)
111
-	if err != nil {
112
-		return s, 0, err
113
-	}
114
-	if len(sa) != 2 {
115
-		return s, 0, nil
116
-	}
117
-	n, err := strconv.ParseInt(sa[1], 10, 64)
118
-	if err != nil {
119
-		return s, n, err
120
-	}
121
-	// should already be in nanoseconds but just in case convert n to nanoseonds
122
-	n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
123
-	return s, n, nil
124
-}
125 1
deleted file mode 100644
... ...
@@ -1,93 +0,0 @@
1
-package timeutils
2
-
3
-import (
4
-	"fmt"
5
-	"testing"
6
-	"time"
7
-)
8
-
9
-func TestGetTimestamp(t *testing.T) {
10
-	now := time.Now()
11
-	cases := []struct {
12
-		in, expected string
13
-		expectedErr  bool
14
-	}{
15
-		// Partial RFC3339 strings get parsed with second precision
16
-		{"2006-01-02T15:04:05.999999999+07:00", "1136189045.999999999", false},
17
-		{"2006-01-02T15:04:05.999999999Z", "1136214245.999999999", false},
18
-		{"2006-01-02T15:04:05.999999999", "1136214245.999999999", false},
19
-		{"2006-01-02T15:04:05Z", "1136214245.000000000", false},
20
-		{"2006-01-02T15:04:05", "1136214245.000000000", false},
21
-		{"2006-01-02T15:04:0Z", "", true},
22
-		{"2006-01-02T15:04:0", "", true},
23
-		{"2006-01-02T15:04Z", "1136214240.000000000", false},
24
-		{"2006-01-02T15:04+00:00", "1136214240.000000000", false},
25
-		{"2006-01-02T15:04-00:00", "1136214240.000000000", false},
26
-		{"2006-01-02T15:04", "1136214240.000000000", false},
27
-		{"2006-01-02T15:0Z", "", true},
28
-		{"2006-01-02T15:0", "", true},
29
-		{"2006-01-02T15Z", "1136214000.000000000", false},
30
-		{"2006-01-02T15+00:00", "1136214000.000000000", false},
31
-		{"2006-01-02T15-00:00", "1136214000.000000000", false},
32
-		{"2006-01-02T15", "1136214000.000000000", false},
33
-		{"2006-01-02T1Z", "1136163600.000000000", false},
34
-		{"2006-01-02T1", "1136163600.000000000", false},
35
-		{"2006-01-02TZ", "", true},
36
-		{"2006-01-02T", "", true},
37
-		{"2006-01-02+00:00", "1136160000.000000000", false},
38
-		{"2006-01-02-00:00", "1136160000.000000000", false},
39
-		{"2006-01-02-00:01", "1136160060.000000000", false},
40
-		{"2006-01-02Z", "1136160000.000000000", false},
41
-		{"2006-01-02", "1136160000.000000000", false},
42
-		{"2015-05-13T20:39:09Z", "1431549549.000000000", false},
43
-
44
-		// unix timestamps returned as is
45
-		{"1136073600", "1136073600", false},
46
-		{"1136073600.000000001", "1136073600.000000001", false},
47
-		// Durations
48
-		{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix()), false},
49
-		{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
50
-		{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
51
-
52
-		// String fallback
53
-		{"invalid", "invalid", false},
54
-	}
55
-
56
-	for _, c := range cases {
57
-		o, err := GetTimestamp(c.in, now)
58
-		if o != c.expected ||
59
-			(err == nil && c.expectedErr) ||
60
-			(err != nil && !c.expectedErr) {
61
-			t.Errorf("wrong value for '%s'. expected:'%s' got:'%s' with error: `%s`", c.in, c.expected, o, err)
62
-			t.Fail()
63
-		}
64
-	}
65
-}
66
-
67
-func TestParseTimestamps(t *testing.T) {
68
-	cases := []struct {
69
-		in                        string
70
-		def, expectedS, expectedN int64
71
-		expectedErr               bool
72
-	}{
73
-		// unix timestamps
74
-		{"1136073600", 0, 1136073600, 0, false},
75
-		{"1136073600.000000001", 0, 1136073600, 1, false},
76
-		{"1136073600.0000000010", 0, 1136073600, 1, false},
77
-		{"1136073600.00000001", 0, 1136073600, 10, false},
78
-		{"foo.bar", 0, 0, 0, true},
79
-		{"1136073600.bar", 0, 1136073600, 0, true},
80
-		{"", -1, -1, 0, false},
81
-	}
82
-
83
-	for _, c := range cases {
84
-		s, n, err := ParseTimestamps(c.in, c.def)
85
-		if s != c.expectedS ||
86
-			n != c.expectedN ||
87
-			(err == nil && c.expectedErr) ||
88
-			(err != nil && !c.expectedErr) {
89
-			t.Errorf("wrong values for input `%s` with default `%d` expected:'%d'seconds and `%d`nanosecond got:'%d'seconds and `%d`nanoseconds with error: `%s`", c.in, c.def, c.expectedS, c.expectedN, s, n, err)
90
-			t.Fail()
91
-		}
92
-	}
93
-}