utility.go (3325B)
1 package exifcommon 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/dsoprea/go-logging" 12 ) 13 14 var ( 15 timeType = reflect.TypeOf(time.Time{}) 16 ) 17 18 // DumpBytes prints a list of hex-encoded bytes. 19 func DumpBytes(data []byte) { 20 fmt.Printf("DUMP: ") 21 for _, x := range data { 22 fmt.Printf("%02x ", x) 23 } 24 25 fmt.Printf("\n") 26 } 27 28 // DumpBytesClause prints a list like DumpBytes(), but encapsulated in 29 // "[]byte { ... }". 30 func DumpBytesClause(data []byte) { 31 fmt.Printf("DUMP: ") 32 33 fmt.Printf("[]byte { ") 34 35 for i, x := range data { 36 fmt.Printf("0x%02x", x) 37 38 if i < len(data)-1 { 39 fmt.Printf(", ") 40 } 41 } 42 43 fmt.Printf(" }\n") 44 } 45 46 // DumpBytesToString returns a stringified list of hex-encoded bytes. 47 func DumpBytesToString(data []byte) string { 48 b := new(bytes.Buffer) 49 50 for i, x := range data { 51 _, err := b.WriteString(fmt.Sprintf("%02x", x)) 52 log.PanicIf(err) 53 54 if i < len(data)-1 { 55 _, err := b.WriteRune(' ') 56 log.PanicIf(err) 57 } 58 } 59 60 return b.String() 61 } 62 63 // DumpBytesClauseToString returns a comma-separated list of hex-encoded bytes. 64 func DumpBytesClauseToString(data []byte) string { 65 b := new(bytes.Buffer) 66 67 for i, x := range data { 68 _, err := b.WriteString(fmt.Sprintf("0x%02x", x)) 69 log.PanicIf(err) 70 71 if i < len(data)-1 { 72 _, err := b.WriteString(", ") 73 log.PanicIf(err) 74 } 75 } 76 77 return b.String() 78 } 79 80 // ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a 81 // `time.Time` struct. It will attempt to convert to UTC first. 82 func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) { 83 t = t.UTC() 84 85 return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) 86 } 87 88 // ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC 89 // `time.Time` struct. 90 func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error) { 91 defer func() { 92 if state := recover(); state != nil { 93 err = log.Wrap(state.(error)) 94 } 95 }() 96 97 parts := strings.Split(fullTimestampPhrase, " ") 98 datestampValue, timestampValue := parts[0], parts[1] 99 100 // Normalize the separators. 101 datestampValue = strings.ReplaceAll(datestampValue, "-", ":") 102 timestampValue = strings.ReplaceAll(timestampValue, "-", ":") 103 104 dateParts := strings.Split(datestampValue, ":") 105 106 year, err := strconv.ParseUint(dateParts[0], 10, 16) 107 if err != nil { 108 log.Panicf("could not parse year") 109 } 110 111 month, err := strconv.ParseUint(dateParts[1], 10, 8) 112 if err != nil { 113 log.Panicf("could not parse month") 114 } 115 116 day, err := strconv.ParseUint(dateParts[2], 10, 8) 117 if err != nil { 118 log.Panicf("could not parse day") 119 } 120 121 timeParts := strings.Split(timestampValue, ":") 122 123 hour, err := strconv.ParseUint(timeParts[0], 10, 8) 124 if err != nil { 125 log.Panicf("could not parse hour") 126 } 127 128 minute, err := strconv.ParseUint(timeParts[1], 10, 8) 129 if err != nil { 130 log.Panicf("could not parse minute") 131 } 132 133 second, err := strconv.ParseUint(timeParts[2], 10, 8) 134 if err != nil { 135 log.Panicf("could not parse second") 136 } 137 138 timestamp = time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.UTC) 139 return timestamp, nil 140 } 141 142 // IsTime returns true if the value is a `time.Time`. 143 func IsTime(v interface{}) bool { 144 145 // TODO(dustin): Add test 146 147 return reflect.TypeOf(v) == timeType 148 }