github.com/honeycombio/honeytail@v1.9.0/httime/httime_test.go (about) 1 package httime 2 3 import ( 4 "math" 5 "testing" 6 "time" 7 8 "github.com/honeycombio/honeytail/httime/httimetest" 9 ) 10 11 func init() { 12 DefaultNower = &httimetest.FakeNower{} 13 } 14 15 func TestFormat(t *testing.T) { 16 tsts := []struct { 17 strftime string 18 expected string 19 }{ 20 {"%Y-%m-%d %H:%M:%S", "2006-01-02 15:04:05"}, 21 {"%Y-%m-%d %H:%M", "2006-01-02 15:04"}, 22 {"%Y-%m-%d %k:%M", "2006-01-02 15:04"}, 23 {"%m/%d/%y %I:%M %p", "01/02/06 03:04 PM"}, 24 {"%m/%d/%y %I:%M %P%t%z", "01/02/06 03:04 pm\t-0700"}, 25 {"%a %B %d %C %r", "Mon January 02 06 03:04:05 PM"}, 26 {"%c %G %g %O %u %V %w %X", " "}, 27 {"%H:%M:%S.%f", "15:04:05.999"}, 28 } 29 30 for _, tt := range tsts { 31 gotime := convertTimeFormat(tt.strftime) 32 if gotime != tt.expected { 33 t.Errorf("strftime format '%s' was parsed to go time '%s', expected '%s'", 34 tt.strftime, gotime, tt.expected) 35 } 36 } 37 } 38 39 type testTimestamp struct { 40 format string // the format this test's time is in 41 fieldName string // the field in the map containing the time 42 input interface{} // the value corresponding to the fieldName 43 tz *time.Location // the expected time zone 44 auto bool // whether the input should be parsable even without specifying format/fieldName 45 expected time.Time // the expected time object to get back 46 diffThreshold time.Duration // the epsilon for which different times are the same, to handle floats 47 } 48 49 var utc = time.UTC 50 var pacific, _ = time.LoadLocation("America/Los_Angeles") 51 52 var tts = []testTimestamp{ 53 { 54 format: "2006-01-02 15:04:05.999999999 -0700 MST", 55 fieldName: "time", 56 input: "2014-04-10 19:57:38.123456789 -0800 PST", 57 tz: utc, 58 auto: true, 59 expected: time.Unix(1397188658, 123456789), 60 diffThreshold: 0, 61 }, 62 { 63 format: time.RFC3339Nano, 64 fieldName: "timestamp", 65 input: "2014-04-10T19:57:38.123456789-08:00", 66 tz: utc, 67 auto: true, 68 expected: time.Unix(1397188658, 123456789), 69 diffThreshold: 0, 70 }, 71 { 72 format: time.RFC3339, 73 fieldName: "Date", 74 input: "2014-04-10T19:57:38-08:00", 75 tz: utc, 76 auto: true, 77 expected: time.Unix(1397188658, 0), 78 diffThreshold: 0, 79 }, 80 { 81 format: time.RFC3339, 82 fieldName: "Date", 83 input: "2014-04-10T19:57:38Z", 84 tz: utc, 85 auto: true, 86 expected: time.Unix(1397159858, 0), 87 diffThreshold: 0, 88 }, 89 { 90 format: time.RubyDate, 91 fieldName: "datetime", 92 input: "Thu Apr 10 19:57:38.123456789 -0800 2014", 93 tz: utc, 94 auto: true, 95 expected: time.Unix(1397188658, 123456789), 96 diffThreshold: 0, 97 }, 98 { 99 format: "%Y-%m-%d %H:%M", 100 fieldName: "time", 101 input: "2014-07-30 07:02", 102 tz: utc, 103 expected: time.Unix(1406703720, 0), 104 diffThreshold: 0, 105 }, 106 { 107 format: "%Y-%m-%d %H:%M", 108 fieldName: "time", 109 input: "2014-07-30 07:02", 110 tz: pacific, 111 expected: time.Unix(1406728920, 0), 112 diffThreshold: 0, 113 }, 114 { 115 format: "%Y-%m-%d %k:%M", // check trailing space behavior 116 fieldName: "time", 117 input: "2014-07-30 7:02", 118 tz: utc, 119 expected: time.Unix(1406703720, 0), 120 diffThreshold: 0, 121 }, 122 { 123 format: "%Y-%m-%d %H:%M:%S", 124 fieldName: "time", 125 input: "2014-07-30 07:02:15", 126 tz: utc, 127 expected: time.Unix(1406703735, 0), 128 diffThreshold: 0, 129 }, 130 { 131 format: UnixTimestampFmt, 132 fieldName: "time", 133 input: "1440116565", 134 tz: utc, 135 expected: time.Unix(1440116565, 0), 136 diffThreshold: 0, 137 }, 138 { 139 format: UnixTimestampFmt, 140 fieldName: "time", 141 input: 1440116565, 142 tz: utc, 143 expected: time.Unix(1440116565, 0), 144 diffThreshold: 0, 145 }, 146 // millis 147 { 148 format: UnixTimestampFmt, 149 fieldName: "time", 150 input: "1440116565.123", 151 tz: utc, 152 expected: time.Unix(1440116565, 123000000), 153 diffThreshold: time.Millisecond, 154 }, 155 { 156 format: UnixTimestampFmtAlt, 157 fieldName: "time", 158 input: "1440116565.123456", 159 tz: utc, 160 expected: time.Unix(1440116565, 123456000), 161 diffThreshold: time.Microsecond, 162 }, 163 { 164 format: UnixTimestampFmtTxt, 165 fieldName: "time", 166 input: "1440116565.12345678", 167 tz: utc, 168 expected: time.Unix(1440116565, 123456780), 169 diffThreshold: 100 * time.Nanosecond, 170 }, 171 { 172 format: "%Y-%m-%d %z", 173 input: "2014-04-10 -0700", 174 tz: utc, 175 fieldName: "time", 176 expected: time.Unix(1397113200, 0), 177 diffThreshold: 0, 178 }, 179 { 180 format: "", 181 input: "1538860697500", 182 tz: utc, 183 fieldName: "timestamp", 184 expected: time.Unix(1538860697, 500000000), 185 diffThreshold: 0, 186 }, 187 } 188 189 func TestGetTimestampValid(t *testing.T) { 190 for i, tTimeSet := range tts { 191 Location = tTimeSet.tz 192 if tTimeSet.auto { 193 fields := map[string]interface{}{tTimeSet.fieldName: tTimeSet.input} 194 resp := GetTimestamp(fields, "", "") 195 if !resp.Equal(tTimeSet.expected) { 196 t.Errorf("time %d: should've been parsed automatically, without required config", i) 197 } 198 if len(fields) != 0 { 199 t.Error("time field should have been deleted, but was not") 200 } 201 } 202 203 fields := map[string]interface{}{tTimeSet.fieldName: tTimeSet.input} 204 resp := GetTimestamp(fields, tTimeSet.fieldName, tTimeSet.format) 205 if !approxEqual(resp, tTimeSet.expected, tTimeSet.diffThreshold) { 206 t.Errorf("time %d: resp time %s didn't match expected time %s", i, resp, tTimeSet.expected) 207 } 208 if len(fields) != 0 { 209 t.Error("time field should have been deleted, but was not") 210 } 211 } 212 } 213 214 func approxEqual(t1, t2 time.Time, threshold time.Duration) bool { 215 diff := int64(math.Abs(float64(t1.Sub(t2)))) 216 return diff <= int64(threshold) 217 } 218 219 func TestGetTimestampInvalid(t *testing.T) { 220 // time field missing 221 resp := GetTimestamp(map[string]interface{}{"noTimeField": "not used"}, "", "") 222 if !resp.Equal(Now()) { 223 t.Errorf("resp time %s didn't match expected time %s", resp, Now()) 224 } 225 // time field unparsable 226 resp = GetTimestamp(map[string]interface{}{"time": "not a valid date"}, "", "") 227 if !resp.Equal(Now()) { 228 t.Errorf("resp time %s didn't match expected time %s", resp, Now()) 229 } 230 fields := map[string]interface{}{"time": "not a valid date", "key": "value"} 231 resp = GetTimestamp(fields, "", "") 232 if !resp.Equal(Now()) { 233 t.Errorf("resp time %s didn't match expected time %s", resp, Now()) 234 } 235 if len(fields) != 2 { 236 t.Error("fields was modified despite not having a valid timestamp") 237 } 238 } 239 240 func TestGetTimestampCustomFormat(t *testing.T) { 241 weirdFormat := "Mon // 02 ---- Jan ... 06 15:04:05 -0700" 242 243 testStr := "Mon // 09 ---- Aug ... 10 15:34:56 -0800" 244 expected := time.Date(2010, 8, 9, 15, 34, 56, 0, time.FixedZone("PST", -28800)) 245 246 // with just Format defined 247 resp := GetTimestamp(map[string]interface{}{"timestamp": testStr}, "", weirdFormat) 248 if !resp.Equal(expected) { 249 t.Errorf("resp time %s didn't match expected time %s", resp, expected) 250 } 251 252 // with just TimeFieldName defined -- use one of the expected/fallback 253 // formats 254 resp = GetTimestamp(map[string]interface{}{"funkyTime": expected.Format(time.RubyDate)}, "funkyTime", "") 255 if !resp.Equal(expected) { 256 t.Errorf("resp time %s didn't match expected time %s", resp, expected) 257 } 258 resp = GetTimestamp(map[string]interface{}{"funkyTime": testStr}, "funkyTime", weirdFormat) 259 if !resp.Equal(expected) { 260 t.Errorf("resp time %s didn't match expected time %s", resp, expected) 261 } 262 // don't parse the "time" field if we're told to look for time in "funkyTime" 263 resp = GetTimestamp(map[string]interface{}{"time": "2014-04-10 19:57:38.123456789 -0800 PST"}, "funkyTime", weirdFormat) 264 if !resp.Equal(Now()) { 265 t.Errorf("resp time %s didn't match expected time %s", resp, Now()) 266 } 267 } 268 269 func TestGetTimestampTypeTime(t *testing.T) { 270 expected := time.Now() 271 resp := GetTimestamp(map[string]interface{}{"real_time_key": expected}, "real_time_key", "") 272 if !resp.Equal(expected) { 273 t.Errorf("resp time %s didn't match expected time %s", resp, expected) 274 } 275 } 276 277 func TestCommaInTimestamp(t *testing.T) { 278 commaTimes := []testTimestamp{ 279 { // test commas as the fractional portion separator 280 format: "2006-01-02 15:04:05,999999999 -0700 MST", 281 fieldName: "time", 282 input: "2014-03-10 12:57:38,123456789 -0700 PDT", 283 expected: time.Unix(1394481458, 123456789), 284 }, 285 { 286 format: "2006-01-02 15:04:05.999999999 -0700 MST", 287 fieldName: "time", 288 input: "2014-03-10 12:57:38,123456789 -0700 PDT", 289 expected: time.Unix(1394481458, 123456789), 290 }, 291 } 292 for i, tTimeSet := range commaTimes { 293 expectedTime := tTimeSet.expected 294 resp := GetTimestamp(map[string]interface{}{tTimeSet.fieldName: tTimeSet.input}, tTimeSet.fieldName, tTimeSet.format) 295 if !resp.Equal(expectedTime) { 296 t.Errorf("time %d: resp time %s didn't match expected time %s", i, resp, expectedTime) 297 } 298 } 299 300 }