Repository: go-openapi/strfmt (go)

Issue:
Parse negative special durations as negative durations
Simply put, if you parse `strfmt.ParseDuration("-1h")`, you will correctly get minus 1 hour. However, when using the "enhanced" durations like `d`/`w`, you will not get a negative duration, but a positive one. So `strfmt.ParseDuration("-1d")` will result in a _positive_ day duration.

Pull request:
Fix negative duration parsing
Only the one the negative one that could be parsed with strconv.ParseDuration were working.
Now all negative durations are working.

Fixes #143

Patch:
diff --git a/duration.go b/duration.go
index 0d85140..7b14a69 100644
--- a/duration.go
+++ b/duration.go
@@ -60,7 +60,7 @@ var (
 		"w":  hoursInDay * daysInWeek * time.Hour,
 	}
 
-	durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`)
+	durationMatcher = regexp.MustCompile(`(((?:-\s?)?\d+)\s*([A-Za-zµ]+))`)
 )
 
 // IsDuration returns true if the provided string is a valid duration
@@ -102,10 +102,19 @@ func ParseDuration(cand string) (time.Duration, error) {
 	ok := false
 	for _, match := range durationMatcher.FindAllStringSubmatch(cand, -1) {
 
-		factor, err := strconv.Atoi(match[2]) // converts string to int
+		// remove possible leading - and spaces
+		value, negative := strings.CutPrefix(match[2], "-")
+
+		// if the string is a valid duration, parse it
+		factor, err := strconv.Atoi(strings.TrimSpace(value)) // converts string to int
 		if err != nil {
 			return 0, err
 		}
+
+		if negative {
+			factor = -factor
+		}
+
 		unit := strings.ToLower(strings.TrimSpace(match[3]))
 
 		for _, variants := range timeUnits {


Test patch:
diff --git a/duration_test.go b/duration_test.go
index 75562d2..cfee1bc 100644
--- a/duration_test.go
+++ b/duration_test.go
@@ -123,7 +123,6 @@ func TestDurationScanner_Nil(t *testing.T) {
 
 func TestDurationParser(t *testing.T) {
 	testcases := map[string]time.Duration{
-
 		// parse the short forms without spaces
 		"1ns": 1 * time.Nanosecond,
 		"1us": 1 * time.Microsecond,
@@ -182,10 +181,18 @@ func TestDurationParser(t *testing.T) {
 	}
 
 	for str, dur := range testcases {
-		testDurationParser(t, str, dur)
-		testDurationSQLScanner(t, dur)
+		t.Run(str, func(t *testing.T) {
+			testDurationParser(t, str, dur)
+
+			// negative duration
+			testDurationParser(t, "-"+str, -dur)
+			testDurationParser(t, "- "+str, -dur)
+
+			testDurationSQLScanner(t, dur)
+		})
 	}
 }
+
 func TestIsDuration_Caveats(t *testing.T) {
 	// This works too
 	e := IsDuration("45 weeks")
@@ -206,7 +213,6 @@ func TestIsDuration_Caveats(t *testing.T) {
 	// This does not work
 	e = IsDuration("12 phours")
 	assert.False(t, e)
-
 }
 
 func TestDeepCopyDuration(t *testing.T) {