Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ syntax. **Note**: this operation is done in-place. If you want to preserve the
original string(s), pair the transform with `shift`. This transform also
supports the `$now` operator for `inputFormat`, which will set the current
timestamp at the specified path, formatted according to the `outputFormat`.
`$unix` is supported for both input and output formats as a Unix time, the
number of seconds elapsed since January 1, 1970 UTC as an integer string.
```javascript
{
"operation": "timestamp",
Expand All @@ -237,7 +239,11 @@ timestamp at the specified path, formatted according to the `outputFormat`.
"nowTimestamp": {
"inputFormat": "$now",
"outputFormat": "2006-01-02T15:04:05-0700"
}
},
"epochTimestamp": {
"inputFormat": "2006-01-02T15:04:05-0700",
"outputFormat": "$unix"
}
}

```
Expand Down
31 changes: 26 additions & 5 deletions transform/timestamp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
// This is necessary for testing purposes
var now = time.Now

const unixFormat = "$unix"

// Timestamp parses and formats timestamp strings using the golang syntax
func Timestamp(spec *Config, data []byte) ([]byte, error) {
for k, v := range *spec.Spec {
Expand Down Expand Up @@ -96,10 +98,29 @@ func Timestamp(spec *Config, data []byte) ([]byte, error) {

// parseAndFormatValue generates a properly formatted timestamp
func parseAndFormatValue(inputFormat, outputFormat, unformattedItem string) (string, error) {
parsedItem, err := time.Parse(inputFormat, unformattedItem)
if err != nil {
return "", err
var (
parsedItem time.Time
formattedItem string
err error
)

if inputFormat == unixFormat {
i, err := strconv.ParseInt(unformattedItem, 10, 64)
if err != nil {
return "", err
}
parsedItem = time.Unix(i, 0)
} else {
parsedItem, err = time.Parse(inputFormat, unformattedItem)
if err != nil {
return "", err
}
}

if outputFormat == unixFormat {
formattedItem = strconv.FormatInt(parsedItem.Unix(), 10)
} else {
formattedItem = parsedItem.Format(outputFormat)
}
formattedItem := strings.Join([]string{"\"", parsedItem.Format(outputFormat), "\""}, "")
return formattedItem, nil
return strings.Join([]string{"\"", formattedItem, "\""}, ""), nil
}
25 changes: 25 additions & 0 deletions transform/timestamp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ func TestParseAndFormatValue(t *testing.T) {
{time.UnixDate, "\"Fri Jul 21 08:15:27 +0100 2017\""},
{time.RFC3339, "\"2017-07-21T08:15:27+01:00\""},
{time.StampNano, "\"Jul 21 08:15:27.000000000\""},
{"$unix", "\"1500621327\""},
}
for _, testItem := range parseAndFormatTests {
actual, _ := parseAndFormatValue(inputFormat, testItem.outputFormat, inputTimestamp)
Expand All @@ -179,3 +180,27 @@ func TestParseAndFormatValue(t *testing.T) {
}
}
}

func TestParseAndFormatValueOutputUnix(t *testing.T) {
parseAndFormatTests := []struct {
inputFormat string
inputTimestamp string
expectedOutput string
}{
// test against a sampling of common formats
{"2006-01-02T15:04:05-0700", "2017-07-21T08:15:27+0100", "\"1500621327\""},
{"January _2, 2006", "July 21, 2017", "\"1500595200\""},
{time.ANSIC, "Fri Jul 21 08:15:27 2017", "\"1500624927\""},
{time.UnixDate, "Fri Jul 21 08:15:27 GMT 2017", "\"1500624927\""},
{time.RFC3339, "2017-07-21T08:15:27+01:00", "\"1500621327\""},
{"$unix", "1500621327", "\"1500621327\""},
}
for _, testItem := range parseAndFormatTests {
actual, _ := parseAndFormatValue(testItem.inputFormat, "$unix", testItem.inputTimestamp)
if actual != testItem.expectedOutput {
t.Error("Error data does not match expectation.", testItem.inputFormat)
t.Log("Expected: ", testItem.expectedOutput)
t.Log("Actual: ", actual)
}
}
}