diff --git a/README.md b/README.md index 4c6d808..6ca743b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Australia/Sydney 2023-12-12 04:37:13 entries. - 🔒 _**Robust**_ - tested to work with all [tz database] entries, [`version 2023c`]. +- 🧭 _**Strftime-friendly**_ - accepts ISO C `strftime` with the common + extensions (e.g. `%C`, `%D`, `%G`, `%O*`, `%+`). - 📦 **Self-contained** - zero dependencies, lightweight (`110 lines`, `2458 bytes`). @@ -67,10 +69,16 @@ Options: -h Print in human-readable format -s format - Set desired time format (e.g. "%Y-%m-%d") + Set desired strftime time format + (default "%Y-%m-%dT%H:%M:%S%z") -t timezone specific timezone (e.g. "Asia/Tokyo") +Formatting uses ISO/IEC 9899 (`strftime`) with the usual extensions, +including `%C`, `%D`, `%E*`, `%e`, `%G`, `%g`, `%h`, `%k`, `%l`, `%n`, +`%O*`, `%R`, `%r`, `%s`, `%T`, `%t`, `%u`, `%V`, `%z`, and `%+`, +powered by [`github.com/ncruces/go-strftime`](https://github.com/ncruces/go-strftime). + Examples: Print Tokyo's date in a human-readable format with YY-MM-DD format: diff --git a/go.mod b/go.mod index 066fcc1..b0390d9 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module gotwc go 1.22.5 + +require github.com/ncruces/go-strftime v1.0.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b8e318a --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= +github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= diff --git a/main.go b/main.go index 6778434..6dd095b 100644 --- a/main.go +++ b/main.go @@ -8,12 +8,12 @@ import ( "regexp" "strings" "time" -) -var ( - progName string + "github.com/ncruces/go-strftime" ) +var progName string + func errMsg(err error, message string) { if err != nil { fmt.Printf("%s: %s\n", message, err) @@ -61,7 +61,8 @@ func readTzFile(filePath string) ([]string, error) { re := regexp.MustCompile(`(?m)^[ \t]*(#.*|\n)`) fileContent = re.ReplaceAll(fileContent, []byte{}) - fileContent = []byte(strings.ReplaceAll(string(fileContent), "UTC-0", "UTC")) + normalized := strings.ReplaceAll(string(fileContent), "UTC-0", "UTC") + fileContent = []byte(normalized) var timezones []string for _, line := range strings.Split(string(fileContent), "\n") { @@ -82,7 +83,7 @@ Options: Read config from path (default "$HOME/.config/twc/tz.conf") -h Print in human-readable format -s format - Set desired time format (e.g. "%%Y-%%m-%%d") + Set desired strftime time format (e.g. "%%Y-%%m-%%d") -t timezone Set a specific timezone (e.g. "Asia/Tokyo") ` @@ -95,7 +96,8 @@ func main() { flag.Usage = usage fmtHuman := flag.Bool("h", false, "Print human-readable format") - fmtSpec := flag.String("s", time.RFC3339, "Specify time format") + defaultFmt := "%Y-%m-%dT%H:%M:%S%z" + fmtSpec := flag.String("s", defaultFmt, "Specify time format") filePath := flag.String("f", "", "Specify timezone file") tzFlag := flag.String("t", "", "Specify timezone directly") @@ -103,7 +105,7 @@ func main() { format := *fmtSpec if *fmtHuman { - format = "2006-01-02 15:04:05" + format = "%Y-%m-%d %H:%M:%S" } timezones, err := getTz(*filePath, *tzFlag) @@ -128,6 +130,10 @@ func main() { } tzTime := time.Now().UTC().In(loc) - fmt.Printf("%-*s %s\n", maxWidth, tz, tzTime.Format(format)) + fmt.Printf("%-*s %s\n", maxWidth, tz, formatTime(tzTime, format)) } } + +func formatTime(t time.Time, format string) string { + return strftime.Format(format, t) +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..1b4dad1 --- /dev/null +++ b/main_test.go @@ -0,0 +1,57 @@ +package main + +import ( + "testing" + "time" +) + +func TestFormatTimeDefault(t *testing.T) { + loc := time.FixedZone("UTC", 0) + now := time.Date(2024, time.July, 12, 16, 3, 52, 0, loc) + + got := formatTime(now, "%Y-%m-%dT%H:%M:%S%z") + want := "2024-07-12T16:03:52+0000" + if got != want { + t.Fatalf("formatTime() = %q, want %q", got, want) + } +} + +func TestFormatTimeExtensions(t *testing.T) { + loc := time.FixedZone("TEST", 2*60*60) + now := time.Date(2024, time.March, 5, 14, 3, 2, 0, loc) + + tests := []struct { + format string + want string + }{ + {"%C", "20"}, + {"%D", "03/05/24"}, + {"%e", " 5"}, + {"%G", "2024"}, + {"%g", "24"}, + {"%h", "Mar"}, + {"%k", "14"}, + {"%l", " 2"}, + {"%n", "\n"}, + {"%Oe", " 5"}, + {"%R", "14:03"}, + {"%r", "02:03:02 PM"}, + {"%s", "1709640182"}, + {"%T", "14:03:02"}, + {"%t", "\t"}, + {"%u", "2"}, + {"%V", "10"}, + {"%z", "+0200"}, + {"%+", "Tue Mar 5 14:03:02 TEST 2024"}, + } + + for _, tt := range tests { + got := formatTime(now, tt.format) + if got != tt.want { + t.Fatalf( + "formatTime(%q) = %q, want %q", + tt.format, got, tt.want, + ) + } + } +}