1package protocol 2 3import ( 4 "bytes" 5 "fmt" 6 "math" 7 "strconv" 8 "time" 9 10 "github.com/aws/aws-sdk-go/internal/sdkmath" 11) 12 13// Names of time formats supported by the SDK 14const ( 15 RFC822TimeFormatName = "rfc822" 16 ISO8601TimeFormatName = "iso8601" 17 UnixTimeFormatName = "unixTimestamp" 18) 19 20// Time formats supported by the SDK 21// Output time is intended to not contain decimals 22const ( 23 // RFC 7231#section-7.1.1.1 timetamp format. e.g Tue, 29 Apr 2014 18:30:38 GMT 24 RFC822TimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT" 25 rfc822TimeFormatSingleDigitDay = "Mon, _2 Jan 2006 15:04:05 GMT" 26 rfc822TimeFormatSingleDigitDayTwoDigitYear = "Mon, _2 Jan 06 15:04:05 GMT" 27 28 // This format is used for output time without seconds precision 29 RFC822OutputTimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" 30 31 // RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z 32 ISO8601TimeFormat = "2006-01-02T15:04:05.999999999Z" 33 34 // This format is used for output time with fractional second precision up to milliseconds 35 ISO8601OutputTimeFormat = "2006-01-02T15:04:05.999999999Z" 36) 37 38// IsKnownTimestampFormat returns if the timestamp format name 39// is know to the SDK's protocols. 40func IsKnownTimestampFormat(name string) bool { 41 switch name { 42 case RFC822TimeFormatName: 43 fallthrough 44 case ISO8601TimeFormatName: 45 fallthrough 46 case UnixTimeFormatName: 47 return true 48 default: 49 return false 50 } 51} 52 53// FormatTime returns a string value of the time. 54func FormatTime(name string, t time.Time) string { 55 t = t.UTC().Truncate(time.Millisecond) 56 57 switch name { 58 case RFC822TimeFormatName: 59 return t.Format(RFC822OutputTimeFormat) 60 case ISO8601TimeFormatName: 61 return t.Format(ISO8601OutputTimeFormat) 62 case UnixTimeFormatName: 63 ms := t.UnixNano() / int64(time.Millisecond) 64 return strconv.FormatFloat(float64(ms)/1e3, 'f', -1, 64) 65 default: 66 panic("unknown timestamp format name, " + name) 67 } 68} 69 70// ParseTime attempts to parse the time given the format. Returns 71// the time if it was able to be parsed, and fails otherwise. 72func ParseTime(formatName, value string) (time.Time, error) { 73 switch formatName { 74 case RFC822TimeFormatName: // Smithy HTTPDate format 75 return tryParse(value, 76 RFC822TimeFormat, 77 rfc822TimeFormatSingleDigitDay, 78 rfc822TimeFormatSingleDigitDayTwoDigitYear, 79 time.RFC850, 80 time.ANSIC, 81 ) 82 case ISO8601TimeFormatName: // Smithy DateTime format 83 return tryParse(value, 84 ISO8601TimeFormat, 85 time.RFC3339Nano, 86 time.RFC3339, 87 ) 88 case UnixTimeFormatName: 89 v, err := strconv.ParseFloat(value, 64) 90 _, dec := math.Modf(v) 91 dec = sdkmath.Round(dec*1e3) / 1e3 //Rounds 0.1229999 to 0.123 92 if err != nil { 93 return time.Time{}, err 94 } 95 return time.Unix(int64(v), int64(dec*(1e9))), nil 96 default: 97 panic("unknown timestamp format name, " + formatName) 98 } 99} 100 101func tryParse(v string, formats ...string) (time.Time, error) { 102 var errs parseErrors 103 for _, f := range formats { 104 t, err := time.Parse(f, v) 105 if err != nil { 106 errs = append(errs, parseError{ 107 Format: f, 108 Err: err, 109 }) 110 continue 111 } 112 return t, nil 113 } 114 115 return time.Time{}, fmt.Errorf("unable to parse time string, %v", errs) 116} 117 118type parseErrors []parseError 119 120func (es parseErrors) Error() string { 121 var s bytes.Buffer 122 for _, e := range es { 123 fmt.Fprintf(&s, "\n * %q: %v", e.Format, e.Err) 124 } 125 126 return "parse errors:" + s.String() 127} 128 129type parseError struct { 130 Format string 131 Err error 132} 133