github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/api/types/time/timestamp.go (about) 1 package time // import "github.com/demonoid81/moby/api/types/time" 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 // if the string has a Z or a + or three dashes use parse otherwise use parseinlocation 33 parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3) 34 35 if strings.Contains(value, ".") { 36 if parseInLocation { 37 format = rFC3339NanoLocal 38 } else { 39 format = time.RFC3339Nano 40 } 41 } else if strings.Contains(value, "T") { 42 // we want the number of colons in the T portion of the timestamp 43 tcolons := strings.Count(value, ":") 44 // if parseInLocation is off and we have a +/- zone offset (not Z) then 45 // there will be an extra colon in the input for the tz offset subtract that 46 // colon from the tcolons count 47 if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 { 48 tcolons-- 49 } 50 if parseInLocation { 51 switch tcolons { 52 case 0: 53 format = "2006-01-02T15" 54 case 1: 55 format = "2006-01-02T15:04" 56 default: 57 format = rFC3339Local 58 } 59 } else { 60 switch tcolons { 61 case 0: 62 format = "2006-01-02T15Z07:00" 63 case 1: 64 format = "2006-01-02T15:04Z07:00" 65 default: 66 format = time.RFC3339 67 } 68 } 69 } else if parseInLocation { 70 format = dateLocal 71 } else { 72 format = dateWithZone 73 } 74 75 var t time.Time 76 var err error 77 78 if parseInLocation { 79 t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone())) 80 } else { 81 t, err = time.Parse(format, value) 82 } 83 84 if err != nil { 85 // if there is a `-` then it's an RFC3339 like timestamp 86 if strings.Contains(value, "-") { 87 return "", err // was probably an RFC3339 like timestamp but the parser failed with an error 88 } 89 if _, _, err := parseTimestamp(value); err != nil { 90 return "", fmt.Errorf("failed to parse value as time or duration: %q", value) 91 } 92 return value, nil // unix timestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server) 93 } 94 95 return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil 96 } 97 98 // ParseTimestamps returns seconds and nanoseconds from a timestamp that has the 99 // format "%d.%09d", time.Unix(), int64(time.Nanosecond())) 100 // if the incoming nanosecond portion is longer or shorter than 9 digits it is 101 // converted to nanoseconds. The expectation is that the seconds and 102 // seconds will be used to create a time variable. For example: 103 // seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0) 104 // if err == nil since := time.Unix(seconds, nanoseconds) 105 // returns seconds as def(aultSeconds) if value == "" 106 func ParseTimestamps(value string, def int64) (int64, int64, error) { 107 if value == "" { 108 return def, 0, nil 109 } 110 return parseTimestamp(value) 111 } 112 113 func parseTimestamp(value string) (int64, int64, error) { 114 sa := strings.SplitN(value, ".", 2) 115 s, err := strconv.ParseInt(sa[0], 10, 64) 116 if err != nil { 117 return s, 0, err 118 } 119 if len(sa) != 2 { 120 return s, 0, nil 121 } 122 n, err := strconv.ParseInt(sa[1], 10, 64) 123 if err != nil { 124 return s, n, err 125 } 126 // should already be in nanoseconds but just in case convert n to nanoseconds 127 n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1])))) 128 return s, n, nil 129 }