-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
164 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package utils | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
"time" | ||
|
||
timefmt "github.com/itchyny/timefmt-go" | ||
) | ||
|
||
const ( | ||
Iso8601 string = "iso8601" | ||
Rfc2822 string = "rfc2822" // timezone name | ||
Rfc2822z string = "rfc2822z" // explicit timezone | ||
Rfc3339 string = "rfc3339" | ||
TimestampSecs string = "unix_timestamp_secs" | ||
TimestampMillis string = "unix_timestamp_millis" | ||
TimestampMicros string = "unix_timestamp_micros" | ||
TimestampNanos string = "unix_timestamp_nanos" | ||
) | ||
|
||
const Rfc2822Layout string = "%a, %d %b %Y %T %Z" | ||
const Rfc2822zLayout string = "%a, %d %b %Y %T %z" | ||
|
||
// Parses a value into Time given a timeOutputFormat. The conversion | ||
// only works with float64 as this is what we get when parsing a response. | ||
func ParseTime(value any, timeOutputFormat string) (time.Time, error) { | ||
switch timeOutputFormat { | ||
case Iso8601, Rfc3339: | ||
value_string := value.(string) | ||
timeValue, err := time.Parse(time.RFC3339, value_string) | ||
if err != nil { | ||
return time.Time{}, err | ||
} | ||
return timeValue, nil | ||
|
||
case Rfc2822: | ||
// XXX: the time package's layout for RFC2822 is bogus, don't use that. | ||
value_string := value.(string) | ||
timeValue, err := timefmt.Parse(value_string, Rfc2822Layout) | ||
if err != nil { | ||
return time.Time{}, err | ||
} | ||
return timeValue, nil | ||
case Rfc2822z: | ||
// XXX: the time package's layout for RFC2822 is bogus, don't use that. | ||
value_string := value.(string) | ||
timeValue, err := timefmt.Parse(value_string, Rfc2822zLayout) | ||
if err != nil { | ||
return time.Time{}, err | ||
} | ||
return timeValue, nil | ||
|
||
case TimestampSecs, TimestampMillis, TimestampMicros, TimestampNanos: | ||
var value_i64 int64 | ||
switch value.(type) { | ||
case int, int8, int16, int32, int64: | ||
value_i64 = reflect.ValueOf(value).Int() | ||
case float32, float64: | ||
value_f64 := reflect.ValueOf(value).Float() | ||
value_i64 = int64(value_f64) | ||
default: | ||
return time.Time{}, errors.New("parseTime only accepts float64 or int64 values with timestamp based formats") | ||
} | ||
|
||
if timeOutputFormat == TimestampSecs { | ||
return time.Unix(value_i64, 0), nil | ||
} else if timeOutputFormat == TimestampMillis { | ||
return time.Unix(0, value_i64*1_000_000), nil | ||
} else if timeOutputFormat == TimestampMicros { | ||
return time.Unix(0, value_i64*1_000), nil | ||
} else if timeOutputFormat == TimestampNanos { | ||
return time.Unix(0, value_i64), nil | ||
} | ||
default: | ||
value_string := value.(string) | ||
timeValue, err := timefmt.Parse(value_string, timeOutputFormat) | ||
if err != nil { | ||
return time.Time{}, err | ||
} | ||
return timeValue, nil | ||
} | ||
return time.Time{}, fmt.Errorf("timeOutputFormat not supported yet %s", timeOutputFormat) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package utils | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
const ( | ||
testYear int = 2024 | ||
testMonth int = 3 | ||
testDay int = 28 | ||
testHour int = 12 | ||
testMinute int = 34 | ||
testSecond int = 56 | ||
testUnixSeconds int = 1711629296 | ||
testMilli int = testUnixSeconds*1000 + 987 | ||
testMicro int = testMilli*1000 + 654 | ||
testNano int = testMicro*1000 + 321 | ||
) | ||
|
||
var successTests = []struct { | ||
value any | ||
timeOutputFormat string | ||
}{ | ||
// RFC3339 | ||
{"2024-03-28T12:34:56.987Z", Rfc3339}, | ||
// RFC2822 | ||
{"Thu, 28 Mar 2024 12:34:56 GMT", Rfc2822}, | ||
{"Thu, 28 Mar 2024 12:34:56 +0000", Rfc2822z}, | ||
// Custom layout | ||
{"2024-03-28 12:34:56", "%Y-%m-%d %H:%M:%S"}, | ||
{"2024-03-28 12:34:56.987", "%Y-%m-%d %H:%M:%S.%f"}, | ||
// Int timestamps | ||
{1711629296, TimestampSecs}, | ||
{1711629296987, TimestampMillis}, | ||
{1711629296987654, TimestampMicros}, | ||
{1711629296987654321, TimestampNanos}, | ||
// Float timestamps | ||
{1711629296., TimestampSecs}, | ||
{1711629296987., TimestampMillis}, | ||
{1711629296987654., TimestampMicros}, | ||
// {1711629296987654321., TimestampNanos}, // Float precision fail | ||
} | ||
|
||
func TestParseTime(t *testing.T) { | ||
assert := assert.New(t) | ||
for _, tt := range successTests { | ||
t.Run(fmt.Sprintf("Parse %s", tt.timeOutputFormat), func(t *testing.T) { | ||
time, err := ParseTime(tt.value, tt.timeOutputFormat) | ||
assert.Nil(err) | ||
assert.NotNil(time) | ||
// Check day | ||
assert.Equal(testYear, int(time.UTC().Year()), "Year mismatch") | ||
assert.Equal(testMonth, int(time.UTC().Month()), "Month mismatch") | ||
assert.Equal(testDay, int(time.UTC().Day()), "Day mismatch") | ||
assert.Equal(testHour, int(time.UTC().Hour()), "Hour mismatch") | ||
assert.Equal(testMinute, int(time.UTC().Minute()), "Minute mismatch") | ||
assert.Equal(testSecond, int(time.UTC().Second()), "Second mismatch") | ||
|
||
switch tt.timeOutputFormat { | ||
case TimestampNanos: | ||
assert.Equal(testNano, int(time.UTC().UnixNano()), "Nanosecond mismatch") | ||
fallthrough | ||
case TimestampMicros: | ||
assert.Equal(testMicro, int(time.UTC().UnixMicro()), "Microsecond mismatch") | ||
fallthrough | ||
case Rfc3339, TimestampMillis: | ||
assert.Equal(testMilli, int(time.UTC().UnixMilli()), "Millisecond mismatch") | ||
} | ||
}) | ||
} | ||
} |