From 739e7b1a9c94b7ce9a5f5180f9d68d4afa502739 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 21 May 2024 00:36:21 +0200 Subject: [PATCH] feat: Add Unix timestamp conversion --- README.md | 2 ++ cli.go | 27 ++++++++++++++++++++++++++- unixtime.go | 30 ++++++++++++++++++++++++++++++ unixtime_test.go | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 unixtime.go create mode 100644 unixtime_test.go diff --git a/README.md b/README.md index 1467e63..fadd19e 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Commands: sort Sort the input by line split Split a string tail Returns the last n lines + time from-unix Convert from Unix time to normal time url encode Encode string to valid URL url decode Decode URL to string version Show version information @@ -90,6 +91,7 @@ Most of the commands work with stdin so you have to pipe content, for example: ```sh echo "hello world" | gstring case camel gstring hash sha256 < main.go # equivalent to sha256sum main.go +date +%s | gstring time from-unix -f "2006-01-02 15:04" ``` However, some commands read from named arguments, for example: diff --git a/cli.go b/cli.go index 4fe352e..869f460 100644 --- a/cli.go +++ b/cli.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "strconv" "strings" ) @@ -50,7 +51,10 @@ type CLI struct { Sort sortCmd `cmd:"" help:"Sort the input by line"` Split splitCmd `cmd:"" help:"Split a string"` Tail tailCmd `cmd:"" help:"Returns the last n lines"` - URL struct { + Time struct { + FromUnixTime fromUnixTimeCmd `cmd:"" name:"from-unix" help:"Convert from Unix time to normal time"` + } `cmd:"" help:"Time conversions"` + URL struct { Encode encodeURLCmd `cmd:"" help:"Encode string to valid URL"` Decode decodeURLCmd `cmd:"" help:"Decode URL to string"` } `cmd:"" help:"URL Encoding and Decoding"` @@ -421,3 +425,24 @@ func (c *sortCmd) Run(globals *Globals) error { printOutput(sortLines(in, c.Desc, c.IgnoreEmptyLines, c.Unique), globals.Trim) return nil } + +type fromUnixTimeCmd struct { + Format string `default:"2006-01-02 15:04:05.000000000" short:"f" help:"Time format as Go reference time"` +} + +func (c *fromUnixTimeCmd) Run(globals *Globals) error { + in, err := readFromSTDIN() + if err != nil { + return err + } + timestamp, err := strconv.ParseInt(strings.TrimSpace(in), 10, 64) + if err != nil { + return err + } + t, err := convertUnixTimestamp(timestamp, c.Format) + if err != nil { + return err + } + printOutput(t, globals.Trim) + return nil +} diff --git a/unixtime.go b/unixtime.go new file mode 100644 index 0000000..dc039e8 --- /dev/null +++ b/unixtime.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "time" +) + +func convertUnixTimestamp(timestamp int64, format string) (string, error) { + var t time.Time + + // Determine the precision based on the length of the timestamp + switch { + case timestamp >= 1e18: + // Nanoseconds + t = time.Unix(0, timestamp) + case timestamp >= 1e15: + // Microseconds + t = time.UnixMicro(timestamp) + case timestamp >= 1e12: + // Milliseconds + t = time.UnixMilli(timestamp) + case timestamp >= 1e9: + // Seconds + t = time.Unix(timestamp, 0) + default: + return "", fmt.Errorf("invalid timestamp: %d", timestamp) + } + + return t.Format(format), nil +} diff --git a/unixtime_test.go b/unixtime_test.go new file mode 100644 index 0000000..dab6ae7 --- /dev/null +++ b/unixtime_test.go @@ -0,0 +1,37 @@ +package main + +import ( + "testing" +) + +func TestConvertUnixTimestamp(t *testing.T) { + format := "2006-01-02 15:04:05.000000000" + tests := []struct { + timestamp int64 + expected string + }{ + {1627670400005, "2021-07-30 20:40:00.005000000"}, + {1627670400000005, "2021-07-30 20:40:00.000005000"}, + {1627670400000000005, "2021-07-30 20:40:00.000000005"}, + } + + for _, tt := range tests { + t.Run("", func(t *testing.T) { + result, err := convertUnixTimestamp(tt.timestamp, format) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if result != tt.expected { + t.Errorf("convertUnixTimestamp(%d, %q) = %q; want %q", tt.timestamp, format, result, tt.expected) + } + }) + } +} + +func TestConvertUnixTimestampInvalid(t *testing.T) { + format := "2006-01-02 15:04:05.999999999" + _, err := convertUnixTimestamp(1627670, format) + if err == nil { + t.Error("expected error for invalid timestamp, got nil") + } +}