Browse code

modifying docker --since and --until to support nanoseconds and time zones

Signed-off-by: Mike Brown <brownwm@us.ibm.com>

Mike Brown authored on 2015/10/30 02:51:36
Showing 11 changed files
... ...
@@ -40,10 +40,18 @@ func (cli *DockerCli) CmdEvents(args ...string) error {
40 40
 	}
41 41
 	ref := time.Now()
42 42
 	if *since != "" {
43
-		v.Set("since", timeutils.GetTimestamp(*since, ref))
43
+		ts, err := timeutils.GetTimestamp(*since, ref)
44
+		if err != nil {
45
+			return err
46
+		}
47
+		v.Set("since", ts)
44 48
 	}
45 49
 	if *until != "" {
46
-		v.Set("until", timeutils.GetTimestamp(*until, ref))
50
+		ts, err := timeutils.GetTimestamp(*until, ref)
51
+		if err != nil {
52
+			return err
53
+		}
54
+		v.Set("until", ts)
47 55
 	}
48 56
 	if len(eventFilterArgs) > 0 {
49 57
 		filterJSON, err := filters.ToParam(eventFilterArgs)
... ...
@@ -41,7 +41,11 @@ func (cli *DockerCli) CmdLogs(args ...string) error {
41 41
 	v.Set("stderr", "1")
42 42
 
43 43
 	if *since != "" {
44
-		v.Set("since", timeutils.GetTimestamp(*since, time.Now()))
44
+		ts, err := timeutils.GetTimestamp(*since, time.Now())
45
+		if err != nil {
46
+			return err
47
+		}
48
+		v.Set("since", ts)
45 49
 	}
46 50
 
47 51
 	if *times {
... ...
@@ -17,6 +17,7 @@ import (
17 17
 	derr "github.com/docker/docker/errors"
18 18
 	"github.com/docker/docker/pkg/ioutils"
19 19
 	"github.com/docker/docker/pkg/signal"
20
+	"github.com/docker/docker/pkg/timeutils"
20 21
 	"github.com/docker/docker/runconfig"
21 22
 	"github.com/docker/docker/utils"
22 23
 	"golang.org/x/net/context"
... ...
@@ -100,11 +101,11 @@ func (s *router) getContainersLogs(ctx context.Context, w http.ResponseWriter, r
100 100
 
101 101
 	var since time.Time
102 102
 	if r.Form.Get("since") != "" {
103
-		s, err := strconv.ParseInt(r.Form.Get("since"), 10, 64)
103
+		s, n, err := timeutils.ParseTimestamps(r.Form.Get("since"), 0)
104 104
 		if err != nil {
105 105
 			return err
106 106
 		}
107
-		since = time.Unix(s, 0)
107
+		since = time.Unix(s, n)
108 108
 	}
109 109
 
110 110
 	var closeNotifier <-chan bool
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/docker/docker/pkg/jsonmessage"
16 16
 	"github.com/docker/docker/pkg/parsers/filters"
17 17
 	"github.com/docker/docker/pkg/parsers/kernel"
18
+	"github.com/docker/docker/pkg/timeutils"
18 19
 	"github.com/docker/docker/utils"
19 20
 	"golang.org/x/net/context"
20 21
 )
... ...
@@ -56,19 +57,19 @@ func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.R
56 56
 	if err := httputils.ParseForm(r); err != nil {
57 57
 		return err
58 58
 	}
59
-	since, err := httputils.Int64ValueOrDefault(r, "since", -1)
59
+	since, sinceNano, err := timeutils.ParseTimestamps(r.Form.Get("since"), -1)
60 60
 	if err != nil {
61 61
 		return err
62 62
 	}
63
-	until, err := httputils.Int64ValueOrDefault(r, "until", -1)
63
+	until, untilNano, err := timeutils.ParseTimestamps(r.Form.Get("until"), -1)
64 64
 	if err != nil {
65 65
 		return err
66 66
 	}
67 67
 
68 68
 	timer := time.NewTimer(0)
69 69
 	timer.Stop()
70
-	if until > 0 {
71
-		dur := time.Unix(until, 0).Sub(time.Now())
70
+	if until > 0 || untilNano > 0 {
71
+		dur := time.Unix(until, untilNano).Sub(time.Now())
72 72
 		timer = time.NewTimer(dur)
73 73
 	}
74 74
 
... ...
@@ -108,7 +109,7 @@ func (s *router) getEvents(ctx context.Context, w http.ResponseWriter, r *http.R
108 108
 		current = nil
109 109
 	}
110 110
 	for _, ev := range current {
111
-		if ev.Time < since {
111
+		if ev.Time < since || ((ev.Time == since) && (ev.TimeNano < sinceNano)) {
112 112
 			continue
113 113
 		}
114 114
 		if err := handleEvent(ev); err != nil {
... ...
@@ -27,10 +27,18 @@ and Docker images will report:
27 27
 
28 28
     delete, import, pull, push, tag, untag
29 29
 
30
-The `--since` and `--until` parameters can be Unix timestamps, RFC3339
31
-dates or Go duration strings (e.g. `10m`, `1h30m`) computed relative to
32
-client machine’s time. If you do not provide the --since option, the command
33
-returns only new and/or live events.
30
+The `--since` and `--until` parameters can be Unix timestamps, date formated
31
+timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed
32
+relative to the client machine’s time. If you do not provide the --since option,
33
+the command returns only new and/or live events.  Supported formats for date
34
+formated time stamps include RFC3339Nano, RFC3339, `2006-01-02T15:04:05`,
35
+`2006-01-02T15:04:05.999999999`, `2006-01-02Z07:00`, and `2006-01-02`. The local
36
+timezone on the client will be used if you do not provide either a `Z` or a
37
+`+-00:00` timezone offset at the end of the timestamp.  When providing Unix
38
+timestamps enter seconds[.nanoseconds], where seconds is the number of seconds
39
+that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap
40
+seconds (aka Unix epoch or Unix time), and the optional .nanoseconds field is a
41
+fraction of a second no more than nine digits long.
34 42
 
35 43
 ## Filtering
36 44
 
... ...
@@ -31,13 +31,20 @@ the container's `STDOUT` and `STDERR`.
31 31
 Passing a negative number or a non-integer to `--tail` is invalid and the
32 32
 value is set to `all` in that case.
33 33
 
34
-The `docker logs --timestamp` commands will add an [RFC3339Nano timestamp](https://golang.org/pkg/time/#pkg-constants)
34
+The `docker logs --timestamps` command will add an [RFC3339Nano timestamp](https://golang.org/pkg/time/#pkg-constants)
35 35
 , for example `2014-09-16T06:17:46.000000000Z`, to each
36
-log entry. To ensure that the timestamps for are aligned the
36
+log entry. To ensure that the timestamps are aligned the
37 37
 nano-second part of the timestamp will be padded with zero when necessary.
38 38
 
39 39
 The `--since` option shows only the container logs generated after
40 40
 a given date. You can specify the date as an RFC 3339 date, a UNIX
41
-timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Docker computes
42
-the date relative to the client machine’s time. You can combine
43
-the `--since` option with either or both of the `--follow` or `--tail` options.
41
+timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Besides RFC3339 date
42
+format you may also use RFC3339Nano, `2006-01-02T15:04:05`,
43
+`2006-01-02T15:04:05.999999999`, `2006-01-02Z07:00`, and `2006-01-02`. The local
44
+timezone on the client will be used if you do not provide either a `Z` or a
45
+`+-00:00` timezone offset at the end of the timestamp. When providing Unix
46
+timestamps enter seconds[.nanoseconds], where seconds is the number of seconds
47
+that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap
48
+seconds (aka Unix epoch or Unix time), and the optional .nanoseconds field is a
49
+fraction of a second no more than nine digits long. You can combine the
50
+`--since` option with either or both of the `--follow` or `--tail` options.
... ...
@@ -182,6 +182,11 @@ func (s *DockerSuite) TestLogsSince(c *check.C) {
182 182
 	for _, v := range unexpected {
183 183
 		c.Assert(out, checker.Not(checker.Contains), v, check.Commentf("unexpected log message returned, since=%v", since))
184 184
 	}
185
+
186
+	// Test to make sure a bad since format is caught by the client
187
+	out, _, _ = dockerCmdWithError("logs", "-t", "--since=2006-01-02T15:04:0Z", name)
188
+	c.Assert(out, checker.Contains, "cannot parse \"0Z\" as \"05\"", check.Commentf("bad since format passed to server"))
189
+
185 190
 	// Test with default value specified and parameter omitted
186 191
 	expected := []string{"log1", "log2", "log3"}
187 192
 	for _, cmd := range []*exec.Cmd{
... ...
@@ -37,10 +37,19 @@ and Docker images will report:
37 37
 **--until**=""
38 38
    Stream events until this timestamp
39 39
 
40
-You can specify `--since` and `--until` parameters as an RFC 3339 date,
41
-a UNIX timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Docker computes
42
-the date relative to the client machine’s time.
43
-
40
+The `--since` and `--until` parameters can be Unix timestamps, date formated
41
+timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed
42
+relative to the client machine’s time. If you do not provide the --since option,
43
+the command returns only new and/or live events.  Supported formats for date
44
+formated time stamps include RFC3339Nano, RFC3339, `2006-01-02T15:04:05`,
45
+`2006-01-02T15:04:05.999999999`, `2006-01-02Z07:00`, and `2006-01-02`. The local
46
+timezone on the client will be used if you do not provide either a `Z` or a
47
+`+-00:00` timezone offset at the end of the timestamp.  When providing Unix
48
+timestamps enter seconds[.nanoseconds], where seconds is the number of seconds
49
+that have elapsed since January 1, 1970 (midnight UTC/GMT), not counting leap
50
+seconds (aka Unix epoch or Unix time), and the optional .nanoseconds field is a
51
+fraction of a second no more than nine digits long.
52
+   
44 53
 # EXAMPLES
45 54
 
46 55
 ## Listening for Docker events
... ...
@@ -71,8 +80,8 @@ The following example outputs all events that were generated in the last 3 minut
71 71
 relative to the current time on the client machine:
72 72
 
73 73
     # docker events --since '3m'
74
-    2015-05-12T11:51:30.999999999Z07:00 4386fb97867d: (from ubuntu-1:14.04) die
75
-    2015-05-12T15:52:12.999999999Z07:00 4 4386fb97867d: (from ubuntu-1:14.04) stop
74
+    2015-05-12T11:51:30.999999999Z07:00  4386fb97867d: (from ubuntu-1:14.04) die
75
+    2015-05-12T15:52:12.999999999Z07:00  4386fb97867d: (from ubuntu-1:14.04) stop
76 76
     2015-05-12T15:53:45.999999999Z07:00  7805c1d35632: (from redis:2.8) die
77 77
     2015-05-12T15:54:03.999999999Z07:00  7805c1d35632: (from redis:2.8) stop
78 78
 
... ...
@@ -84,3 +93,4 @@ April 2014, Originally compiled by William Henry (whenry at redhat dot com)
84 84
 based on docker.com source material and internal work.
85 85
 June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
86 86
 June 2015, updated by Brian Goff <cpuguy83@gmail.com>
87
+October 2015, updated by Mike Brown <mikebrow@gmail.com>
... ...
@@ -42,11 +42,18 @@ logging drivers.
42 42
 **--tail**="all"
43 43
    Output the specified number of lines at the end of logs (defaults to all logs)
44 44
 
45
-The `--since` option shows only the container logs generated after
46
-a given date. You can specify the date as an RFC 3339 date, a UNIX
47
-timestamp, or a Go duration string (e.g. `1m30s`, `3h`). Docker computes
48
-the date relative to the client machine’s time. You can combine
49
-the `--since` option with either or both of the `--follow` or `--tail` options.
45
+The `--since` option can be Unix timestamps, date formated timestamps, or Go
46
+duration strings (e.g. `10m`, `1h30m`) computed relative to the client machine’s
47
+time. Supported formats for date formated time stamps include RFC3339Nano,
48
+RFC3339, `2006-01-02T15:04:05`, `2006-01-02T15:04:05.999999999`,
49
+`2006-01-02Z07:00`, and `2006-01-02`. The local timezone on the client will be
50
+used if you do not provide either a `Z` or a `+-00:00` timezone offset at the
51
+end of the timestamp.  When providing Unix timestamps enter
52
+seconds[.nanoseconds], where seconds is the number of seconds that have elapsed
53
+since January 1, 1970 (midnight UTC/GMT), not counting leap  seconds (aka Unix
54
+epoch or Unix time), and the optional .nanoseconds field is a fraction of a
55
+second no more than nine digits long. You can combine the `--since` option with
56
+either or both of the `--follow` or `--tail` options.
50 57
 
51 58
 # HISTORY
52 59
 April 2014, Originally compiled by William Henry (whenry at redhat dot com)
... ...
@@ -54,3 +61,4 @@ based on docker.com source material and internal work.
54 54
 June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
55 55
 July 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
56 56
 April 2015, updated by Ahmet Alp Balkan <ahmetalpbalkan@gmail.com>
57
+October 2015, updated by Mike Brown <mikebrow@gmail.com>
... ...
@@ -1,36 +1,124 @@
1 1
 package timeutils
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"math"
4 6
 	"strconv"
5 7
 	"strings"
6 8
 	"time"
7 9
 )
8 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
+
9 20
 // GetTimestamp tries to parse given string as golang duration,
10 21
 // then RFC3339 time and finally as a Unix timestamp. If
11 22
 // any of these were successful, it returns a Unix timestamp
12 23
 // as string otherwise returns the given value back.
13 24
 // In case of duration input, the returned timestamp is computed
14 25
 // as the given reference time minus the amount of the duration.
15
-func GetTimestamp(value string, reference time.Time) string {
26
+func GetTimestamp(value string, reference time.Time) (string, error) {
16 27
 	if d, err := time.ParseDuration(value); value != "0" && err == nil {
17
-		return strconv.FormatInt(reference.Add(-d).Unix(), 10)
28
+		return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
18 29
 	}
19 30
 
20 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
+
21 37
 	if strings.Contains(value, ".") {
22
-		format = time.RFC3339Nano
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 subract 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()))
23 82
 	} else {
24
-		format = time.RFC3339
83
+		t, err = time.Parse(format, value)
25 84
 	}
26 85
 
27
-	loc := time.FixedZone(time.Now().Zone())
28
-	if len(value) < len(format) {
29
-		format = format[:len(value)]
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
30 116
 	}
31
-	t, err := time.ParseInLocation(format, value, loc)
117
+	n, err := strconv.ParseInt(sa[1], 10, 64)
32 118
 	if err != nil {
33
-		return value
119
+		return s, n, err
34 120
 	}
35
-	return strconv.FormatInt(t.Unix(), 10)
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
36 124
 }
... ...
@@ -8,37 +8,86 @@ import (
8 8
 
9 9
 func TestGetTimestamp(t *testing.T) {
10 10
 	now := time.Now()
11
-	cases := []struct{ in, expected string }{
12
-		{"0", "-62167305600"}, // 0 gets parsed year 0
13
-
11
+	cases := []struct {
12
+		in, expected string
13
+		expectedErr  bool
14
+	}{
14 15
 		// Partial RFC3339 strings get parsed with second precision
15
-		{"2006-01-02T15:04:05.999999999+07:00", "1136189045"},
16
-		{"2006-01-02T15:04:05.999999999Z", "1136214245"},
17
-		{"2006-01-02T15:04:05.999999999", "1136214245"},
18
-		{"2006-01-02T15:04:05", "1136214245"},
19
-		{"2006-01-02T15:04", "1136214240"},
20
-		{"2006-01-02T15", "1136214000"},
21
-		{"2006-01-02T", "1136160000"},
22
-		{"2006-01-02", "1136160000"},
23
-		{"2006", "1136073600"},
24
-		{"2015-05-13T20:39:09Z", "1431549549"},
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},
25 43
 
26 44
 		// unix timestamps returned as is
27
-		{"1136073600", "1136073600"},
28
-
45
+		{"1136073600", "1136073600", false},
46
+		{"1136073600.000000001", "1136073600.000000001", false},
29 47
 		// Durations
30
-		{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix())},
31
-		{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix())},
32
-		{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix())},
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},
33 51
 
34 52
 		// String fallback
35
-		{"invalid", "invalid"},
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},
36 81
 	}
37 82
 
38 83
 	for _, c := range cases {
39
-		o := GetTimestamp(c.in, now)
40
-		if o != c.expected {
41
-			t.Fatalf("wrong value for '%s'. expected:'%s' got:'%s'", c.in, c.expected, o)
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()
42 91
 		}
43 92
 	}
44 93
 }