2022-01-23 13:41:31 +00:00
|
|
|
package exifcommon
|
2021-08-12 20:03:24 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2022-01-23 13:41:31 +00:00
|
|
|
"reflect"
|
2021-08-12 20:03:24 +01:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/dsoprea/go-logging"
|
|
|
|
)
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
var (
|
|
|
|
timeType = reflect.TypeOf(time.Time{})
|
|
|
|
)
|
|
|
|
|
|
|
|
// DumpBytes prints a list of hex-encoded bytes.
|
2021-08-12 20:03:24 +01:00
|
|
|
func DumpBytes(data []byte) {
|
|
|
|
fmt.Printf("DUMP: ")
|
|
|
|
for _, x := range data {
|
|
|
|
fmt.Printf("%02x ", x)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("\n")
|
|
|
|
}
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// DumpBytesClause prints a list like DumpBytes(), but encapsulated in
|
|
|
|
// "[]byte { ... }".
|
2021-08-12 20:03:24 +01:00
|
|
|
func DumpBytesClause(data []byte) {
|
|
|
|
fmt.Printf("DUMP: ")
|
|
|
|
|
|
|
|
fmt.Printf("[]byte { ")
|
|
|
|
|
|
|
|
for i, x := range data {
|
|
|
|
fmt.Printf("0x%02x", x)
|
|
|
|
|
|
|
|
if i < len(data)-1 {
|
|
|
|
fmt.Printf(", ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf(" }\n")
|
|
|
|
}
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// DumpBytesToString returns a stringified list of hex-encoded bytes.
|
2021-08-12 20:03:24 +01:00
|
|
|
func DumpBytesToString(data []byte) string {
|
|
|
|
b := new(bytes.Buffer)
|
|
|
|
|
|
|
|
for i, x := range data {
|
|
|
|
_, err := b.WriteString(fmt.Sprintf("%02x", x))
|
|
|
|
log.PanicIf(err)
|
|
|
|
|
|
|
|
if i < len(data)-1 {
|
|
|
|
_, err := b.WriteRune(' ')
|
|
|
|
log.PanicIf(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// DumpBytesClauseToString returns a comma-separated list of hex-encoded bytes.
|
2021-08-12 20:03:24 +01:00
|
|
|
func DumpBytesClauseToString(data []byte) string {
|
|
|
|
b := new(bytes.Buffer)
|
|
|
|
|
|
|
|
for i, x := range data {
|
|
|
|
_, err := b.WriteString(fmt.Sprintf("0x%02x", x))
|
|
|
|
log.PanicIf(err)
|
|
|
|
|
|
|
|
if i < len(data)-1 {
|
|
|
|
_, err := b.WriteString(", ")
|
|
|
|
log.PanicIf(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.String()
|
|
|
|
}
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// ExifFullTimestampString produces a string like "2018:11:30 13:01:49" from a
|
|
|
|
// `time.Time` struct. It will attempt to convert to UTC first.
|
|
|
|
func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
|
|
|
|
t = t.UTC()
|
|
|
|
|
|
|
|
return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
|
|
|
}
|
|
|
|
|
2021-08-12 20:03:24 +01:00
|
|
|
// ParseExifFullTimestamp parses dates like "2018:11:30 13:01:49" into a UTC
|
|
|
|
// `time.Time` struct.
|
|
|
|
func ParseExifFullTimestamp(fullTimestampPhrase string) (timestamp time.Time, err error) {
|
|
|
|
defer func() {
|
|
|
|
if state := recover(); state != nil {
|
|
|
|
err = log.Wrap(state.(error))
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
parts := strings.Split(fullTimestampPhrase, " ")
|
|
|
|
datestampValue, timestampValue := parts[0], parts[1]
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// Normalize the separators.
|
|
|
|
datestampValue = strings.ReplaceAll(datestampValue, "-", ":")
|
|
|
|
timestampValue = strings.ReplaceAll(timestampValue, "-", ":")
|
|
|
|
|
2021-08-12 20:03:24 +01:00
|
|
|
dateParts := strings.Split(datestampValue, ":")
|
|
|
|
|
|
|
|
year, err := strconv.ParseUint(dateParts[0], 10, 16)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("could not parse year")
|
|
|
|
}
|
|
|
|
|
|
|
|
month, err := strconv.ParseUint(dateParts[1], 10, 8)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("could not parse month")
|
|
|
|
}
|
|
|
|
|
|
|
|
day, err := strconv.ParseUint(dateParts[2], 10, 8)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("could not parse day")
|
|
|
|
}
|
|
|
|
|
|
|
|
timeParts := strings.Split(timestampValue, ":")
|
|
|
|
|
|
|
|
hour, err := strconv.ParseUint(timeParts[0], 10, 8)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("could not parse hour")
|
|
|
|
}
|
|
|
|
|
|
|
|
minute, err := strconv.ParseUint(timeParts[1], 10, 8)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("could not parse minute")
|
|
|
|
}
|
|
|
|
|
|
|
|
second, err := strconv.ParseUint(timeParts[2], 10, 8)
|
|
|
|
if err != nil {
|
|
|
|
log.Panicf("could not parse second")
|
|
|
|
}
|
|
|
|
|
|
|
|
timestamp = time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(second), 0, time.UTC)
|
|
|
|
return timestamp, nil
|
|
|
|
}
|
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// IsTime returns true if the value is a `time.Time`.
|
|
|
|
func IsTime(v interface{}) bool {
|
2021-08-12 20:03:24 +01:00
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
// TODO(dustin): Add test
|
2021-08-12 20:03:24 +01:00
|
|
|
|
2022-01-23 13:41:31 +00:00
|
|
|
return reflect.TypeOf(v) == timeType
|
2021-08-12 20:03:24 +01:00
|
|
|
}
|