- 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>
| ... | ... |
@@ -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 |
-} |