mirror of https://github.com/dsoprea/go-exif.git
backwards incompatible: Move time utilities to exifcommon
parent
83b844408c
commit
3f7ee4ce89
|
@ -3,11 +3,18 @@ package exifcommon
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
var (
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
)
|
||||
|
||||
// DumpBytes prints a list of hex-encoded bytes.
|
||||
func DumpBytes(data []byte) {
|
||||
fmt.Printf("DUMP: ")
|
||||
|
@ -77,3 +84,61 @@ func ExifFullTimestampString(t time.Time) (fullTimestampPhrase string) {
|
|||
|
||||
return fmt.Sprintf("%04d:%02d:%02d %02d:%02d:%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
||||
}
|
||||
|
||||
// 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]
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// IsTime returns true if the value is a `time.Time`.
|
||||
func IsTime(v interface{}) bool {
|
||||
|
||||
// TODO(dustin): Add test
|
||||
|
||||
return reflect.TypeOf(v) == timeType
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package exifcommon
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
// "github.com/dsoprea/go-logging"
|
||||
"time"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
func TestDumpBytes(t *testing.T) {
|
||||
|
@ -26,3 +29,52 @@ func TestDumpBytesClauseToString(t *testing.T) {
|
|||
t.Fatalf("Stringified clause is not correct: [%s]", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExifFullTimestampString(t *testing.T) {
|
||||
originalPhrase := "2018:11:30 13:01:49"
|
||||
|
||||
timestamp, err := ParseExifFullTimestamp(originalPhrase)
|
||||
log.PanicIf(err)
|
||||
|
||||
restoredPhrase := ExifFullTimestampString(timestamp)
|
||||
if restoredPhrase != originalPhrase {
|
||||
t.Fatalf("Final phrase [%s] does not equal original phrase [%s]", restoredPhrase, originalPhrase)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleExifFullTimestampString() {
|
||||
originalPhrase := "2018:11:30 13:01:49"
|
||||
|
||||
timestamp, err := ParseExifFullTimestamp(originalPhrase)
|
||||
log.PanicIf(err)
|
||||
|
||||
restoredPhrase := ExifFullTimestampString(timestamp)
|
||||
fmt.Printf("To EXIF timestamp: [%s]\n", restoredPhrase)
|
||||
|
||||
// Output:
|
||||
// To EXIF timestamp: [2018:11:30 13:01:49]
|
||||
}
|
||||
|
||||
func TestParseExifFullTimestamp(t *testing.T) {
|
||||
timestamp, err := ParseExifFullTimestamp("2018:11:30 13:01:49")
|
||||
log.PanicIf(err)
|
||||
|
||||
actual := timestamp.Format(time.RFC3339)
|
||||
expected := "2018-11-30T13:01:49Z"
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("time not formatted correctly: [%s] != [%s]", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleParseExifFullTimestamp() {
|
||||
originalPhrase := "2018:11:30 13:01:49"
|
||||
|
||||
timestamp, err := ParseExifFullTimestamp(originalPhrase)
|
||||
log.PanicIf(err)
|
||||
|
||||
fmt.Printf("To Go timestamp: [%s]\n", timestamp.Format(time.RFC3339))
|
||||
|
||||
// Output:
|
||||
// To Go timestamp: [2018-11-30T13:01:49Z]
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ func (it *IndexedTag) Is(ifdPath string, id uint16) bool {
|
|||
// GetEncodingType returns the largest type that this tag's value can occupy.
|
||||
func (it *IndexedTag) GetEncodingType(value interface{}) exifcommon.TagTypePrimitive {
|
||||
// For convenience, we handle encoding a `time.Time` directly.
|
||||
if IsTime(value) == true {
|
||||
if exifcommon.IsTime(value) == true {
|
||||
// Timestamps are encoded as ASCII.
|
||||
value = ""
|
||||
}
|
||||
|
|
|
@ -2,12 +2,7 @@ package exif
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
|
||||
|
@ -19,69 +14,6 @@ var (
|
|||
utilityLogger = log.NewLogger("exif.utility")
|
||||
)
|
||||
|
||||
var (
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
)
|
||||
|
||||
// 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]
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
// RELEASE(dustin): Dump this for the next release. It duplicates the same function now in exifcommon.
|
||||
|
||||
return exifcommon.ExifFullTimestampString(t)
|
||||
}
|
||||
|
||||
// ExifTag is one simple representation of a tag in a flat list of all of them.
|
||||
type ExifTag struct {
|
||||
// IfdPath is the fully-qualified IFD path (even though it is not named as
|
||||
|
@ -231,16 +163,3 @@ func GpsDegreesEquals(gi1, gi2 GpsDegrees) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsTime returns true if the value is a `time.Time`.
|
||||
func IsTime(v interface{}) bool {
|
||||
return reflect.TypeOf(v) == timeType
|
||||
}
|
||||
|
||||
// TODO(dustin): !! For debugging
|
||||
func mustGetCurrentOffset(s io.Seeker) int64 {
|
||||
offset, err := s.Seek(0, io.SeekCurrent)
|
||||
log.PanicIf(err)
|
||||
|
||||
return offset
|
||||
}
|
||||
|
|
|
@ -1,62 +1,9 @@
|
|||
package exif
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dsoprea/go-logging"
|
||||
)
|
||||
|
||||
func TestParseExifFullTimestamp(t *testing.T) {
|
||||
timestamp, err := ParseExifFullTimestamp("2018:11:30 13:01:49")
|
||||
log.PanicIf(err)
|
||||
|
||||
actual := timestamp.Format(time.RFC3339)
|
||||
expected := "2018-11-30T13:01:49Z"
|
||||
|
||||
if actual != expected {
|
||||
t.Fatalf("time not formatted correctly: [%s] != [%s]", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExifFullTimestampString(t *testing.T) {
|
||||
originalPhrase := "2018:11:30 13:01:49"
|
||||
|
||||
timestamp, err := ParseExifFullTimestamp(originalPhrase)
|
||||
log.PanicIf(err)
|
||||
|
||||
restoredPhrase := ExifFullTimestampString(timestamp)
|
||||
if restoredPhrase != originalPhrase {
|
||||
t.Fatalf("Final phrase [%s] does not equal original phrase [%s]", restoredPhrase, originalPhrase)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleParseExifFullTimestamp() {
|
||||
originalPhrase := "2018:11:30 13:01:49"
|
||||
|
||||
timestamp, err := ParseExifFullTimestamp(originalPhrase)
|
||||
log.PanicIf(err)
|
||||
|
||||
fmt.Printf("To Go timestamp: [%s]\n", timestamp.Format(time.RFC3339))
|
||||
|
||||
// Output:
|
||||
// To Go timestamp: [2018-11-30T13:01:49Z]
|
||||
}
|
||||
|
||||
func ExampleExifFullTimestampString() {
|
||||
originalPhrase := "2018:11:30 13:01:49"
|
||||
|
||||
timestamp, err := ParseExifFullTimestamp(originalPhrase)
|
||||
log.PanicIf(err)
|
||||
|
||||
restoredPhrase := ExifFullTimestampString(timestamp)
|
||||
fmt.Printf("To EXIF timestamp: [%s]\n", restoredPhrase)
|
||||
|
||||
// Output:
|
||||
// To EXIF timestamp: [2018:11:30 13:01:49]
|
||||
}
|
||||
|
||||
func TestGpsDegreesEquals_Equals(t *testing.T) {
|
||||
gi := GpsDegrees{
|
||||
Orientation: 'A',
|
||||
|
|
Loading…
Reference in New Issue