github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/timetype/timestamp.go (about) 1 package timetype 2 3 // code adapted from https://github.com/moby/moby/blob/master/api/types/time/timestamp.go 4 5 import ( 6 "fmt" 7 "math" 8 "strconv" 9 "strings" 10 "time" 11 ) 12 13 // These are additional predefined layouts for use in Time.Format and Time.Parse 14 // with --since and --until parameters for `docker logs` and `docker events` 15 const ( 16 rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone 17 rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone 18 dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00 19 dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00 20 ) 21 22 // GetTimestamp tries to parse given string as golang duration, 23 // then RFC3339 time and finally as a Unix timestamp. If 24 // any of these were successful, it returns a Unix timestamp 25 // as string otherwise returns the given value back. 26 // In case of duration input, the returned timestamp is computed 27 // as the given reference time minus the amount of the duration. 28 func GetTimestamp(value string, reference time.Time) (string, error) { 29 if d, err := time.ParseDuration(value); value != "0" && err == nil { 30 return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil 31 } 32 33 var format string 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 switch { 38 case strings.Contains(value, "."): 39 if parseInLocation { 40 format = rFC3339NanoLocal 41 } else { 42 format = time.RFC3339Nano 43 } 44 case strings.Contains(value, "T"): 45 // we want the number of colons in the T portion of the timestamp 46 tcolons := strings.Count(value, ":") 47 // if parseInLocation is off and we have a +/- zone offset (not Z) then 48 // there will be an extra colon in the input for the tz offset subtract that 49 // colon from the tcolons count 50 if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 { 51 tcolons-- 52 } 53 if parseInLocation { 54 switch tcolons { 55 case 0: 56 format = "2006-01-02T15" 57 case 1: 58 format = "2006-01-02T15:04" 59 default: 60 format = rFC3339Local 61 } 62 } else { 63 switch tcolons { 64 case 0: 65 format = "2006-01-02T15Z07:00" 66 case 1: 67 format = "2006-01-02T15:04Z07:00" 68 default: 69 format = time.RFC3339 70 } 71 } 72 case parseInLocation: 73 format = dateLocal 74 default: 75 format = dateWithZone 76 } 77 78 var t time.Time 79 var err error 80 81 if parseInLocation { 82 t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone())) 83 } else { 84 t, err = time.Parse(format, value) 85 } 86 87 if err != nil { 88 // if there is a `-` then it's an RFC3339 like timestamp 89 if strings.Contains(value, "-") { 90 return "", err // was probably an RFC3339 like timestamp but the parser failed with an error 91 } 92 if _, _, err := parseTimestamp(value); err != nil { 93 return "", fmt.Errorf("failed to parse value as time or duration: %q", value) 94 } 95 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) 96 } 97 98 return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil 99 } 100 101 // ParseTimestamps returns seconds and nanoseconds from a timestamp that has the 102 // format "%d.%09d", time.Unix(), int64(time.Nanosecond())) 103 // if the incoming nanosecond portion is longer or shorter than 9 digits it is 104 // converted to nanoseconds. The expectation is that the seconds and 105 // seconds will be used to create a time variable. For example: 106 // seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0) 107 // if err == nil since := time.Unix(seconds, nanoseconds) 108 // returns seconds as def(aultSeconds) if value == "" 109 func ParseTimestamps(value string, def int64) (int64, int64, error) { 110 if value == "" { 111 return def, 0, nil 112 } 113 return parseTimestamp(value) 114 } 115 116 func parseTimestamp(value string) (int64, int64, error) { 117 sa := strings.SplitN(value, ".", 2) 118 s, err := strconv.ParseInt(sa[0], 10, 64) 119 if err != nil { 120 return s, 0, err 121 } 122 if len(sa) != 2 { 123 return s, 0, nil 124 } 125 n, err := strconv.ParseInt(sa[1], 10, 64) 126 if err != nil { 127 return s, n, err 128 } 129 // should already be in nanoseconds but just in case convert n to nanoseconds 130 n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1])))) 131 return s, n, nil 132 }