Repository: go-openapi/strfmt
Pull request #146 (2025-03-12 09:12:40)

Relint
* updated linter config
* fixed linting issues:
  * wrapped errors
  * used explicitly named constants instead of "magic numbers"
  * used more recent stdlib API to replace bson time conversions


Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 22f8d21..d2fafb8 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,12 +1,6 @@
 linters-settings:
-  govet:
-    check-shadowing: true
-  golint:
-    min-confidence: 0
   gocyclo:
     min-complexity: 45
-  maligned:
-    suggest-new: true
   dupl:
     threshold: 200
   goconst:
@@ -16,7 +10,7 @@ linters-settings:
 linters:
   enable-all: true
   disable:
-    - maligned
+    - recvcheck
     - unparam
     - lll
     - gochecknoinits
@@ -29,9 +23,6 @@ linters:
     - wrapcheck
     - testpackage
     - nlreturn
-    - gomnd
-    - exhaustivestruct
-    - goerr113
     - errorlint
     - nestif
     - godot
@@ -39,7 +30,6 @@ linters:
     - paralleltest
     - tparallel
     - thelper
-    - ifshort
     - exhaustruct
     - varnamelen
     - gci
@@ -52,10 +42,15 @@ linters:
     - forcetypeassert
     - cyclop
     # deprecated linters
-    - deadcode
-    - interfacer
-    - scopelint
-    - varcheck
-    - structcheck
-    - golint
-    - nosnakecase
+    #- deadcode
+    #- interfacer
+    #- scopelint
+    #- varcheck
+    #- structcheck
+    #- golint
+    #- nosnakecase
+    #- maligned
+    #- goerr113
+    #- ifshort
+    #- gomnd
+    #- exhaustivestruct
diff --git a/bson.go b/bson.go
index cfa9a52..685eaf6 100644
--- a/bson.go
+++ b/bson.go
@@ -83,7 +83,7 @@ func (id *ObjectId) Scan(raw interface{}) error {
 	case string:
 		data = []byte(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v: %w", v, ErrFormat)
 	}
 
 	return id.UnmarshalText(data)
diff --git a/date.go b/date.go
index 3c93381..a8f52ff 100644
--- a/date.go
+++ b/date.go
@@ -17,7 +17,6 @@ package strfmt
 import (
 	"database/sql/driver"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"time"
 
@@ -84,7 +83,7 @@ func (d *Date) Scan(raw interface{}) error {
 		*d = Date{}
 		return nil
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.Date from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.Date from: %#v: %w", v, ErrFormat)
 	}
 }
 
@@ -134,7 +133,7 @@ func (d *Date) UnmarshalBSON(data []byte) error {
 		return nil
 	}
 
-	return errors.New("couldn't unmarshal bson bytes value as Date")
+	return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
diff --git a/default.go b/default.go
index 2813714..eb9a852 100644
--- a/default.go
+++ b/default.go
@@ -18,7 +18,6 @@ import (
 	"database/sql/driver"
 	"encoding/base64"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"net/mail"
 	"regexp"
@@ -96,15 +95,17 @@ func IsHostname(str string) bool {
 	}
 
 	// the sum of all label octets and label lengths is limited to 255.
-	if len(str) > 255 {
+	const maxHostnameLength = 255
+	if len(str) > maxHostnameLength {
 		return false
 	}
 
 	// Each node has a label, which is zero to 63 octets in length
+	const maxNodeLength = 63
 	parts := strings.Split(str, ".")
 	valid := true
 	for _, p := range parts {
-		if len(p) > 63 {
+		if len(p) > maxNodeLength {
 			valid = false
 		}
 	}
@@ -117,22 +118,28 @@ func IsUUID(str string) bool {
 	return err == nil
 }
 
+const (
+	uuidV3 = 3
+	uuidV4 = 4
+	uuidV5 = 5
+)
+
 // IsUUID3 returns true is the string matches a UUID v3, upper case is allowed
 func IsUUID3(str string) bool {
 	id, err := uuid.Parse(str)
-	return err == nil && id.Version() == uuid.Version(3)
+	return err == nil && id.Version() == uuid.Version(uuidV3)
 }
 
 // IsUUID4 returns true is the string matches a UUID v4, upper case is allowed
 func IsUUID4(str string) bool {
 	id, err := uuid.Parse(str)
-	return err == nil && id.Version() == uuid.Version(4)
+	return err == nil && id.Version() == uuid.Version(uuidV4)
 }
 
 // IsUUID5 returns true is the string matches a UUID v5, upper case is allowed
 func IsUUID5(str string) bool {
 	id, err := uuid.Parse(str)
-	return err == nil && id.Version() == uuid.Version(5)
+	return err == nil && id.Version() == uuid.Version(uuidV5)
 }
 
 // IsEmail validates an email address.
@@ -269,7 +276,7 @@ func (b *Base64) Scan(raw interface{}) error {
 		}
 		*b = Base64(vv)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.Base64 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.Base64 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -323,7 +330,7 @@ func (b *Base64) UnmarshalBSON(data []byte) error {
 		*b = Base64(vb)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as base64")
+	return fmt.Errorf("couldn't unmarshal bson bytes as base64: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -365,7 +372,7 @@ func (u *URI) Scan(raw interface{}) error {
 	case string:
 		*u = URI(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.URI from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -411,7 +418,7 @@ func (u *URI) UnmarshalBSON(data []byte) error {
 		*u = URI(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as uri")
+	return fmt.Errorf("couldn't unmarshal bson bytes as uri: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -453,7 +460,7 @@ func (e *Email) Scan(raw interface{}) error {
 	case string:
 		*e = Email(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.Email from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.Email from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -499,7 +506,7 @@ func (e *Email) UnmarshalBSON(data []byte) error {
 		*e = Email(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as email")
+	return fmt.Errorf("couldn't unmarshal bson bytes as email: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -541,7 +548,7 @@ func (h *Hostname) Scan(raw interface{}) error {
 	case string:
 		*h = Hostname(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.Hostname from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.Hostname from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -587,7 +594,7 @@ func (h *Hostname) UnmarshalBSON(data []byte) error {
 		*h = Hostname(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as hostname")
+	return fmt.Errorf("couldn't unmarshal bson bytes as hostname: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -629,7 +636,7 @@ func (u *IPv4) Scan(raw interface{}) error {
 	case string:
 		*u = IPv4(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.IPv4 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.IPv4 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -675,7 +682,7 @@ func (u *IPv4) UnmarshalBSON(data []byte) error {
 		*u = IPv4(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as ipv4")
+	return fmt.Errorf("couldn't unmarshal bson bytes as ipv4: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -717,7 +724,7 @@ func (u *IPv6) Scan(raw interface{}) error {
 	case string:
 		*u = IPv6(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.IPv6 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.IPv6 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -763,7 +770,7 @@ func (u *IPv6) UnmarshalBSON(data []byte) error {
 		*u = IPv6(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as ipv6")
+	return fmt.Errorf("couldn't unmarshal bson bytes as ipv6: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -805,7 +812,7 @@ func (u *CIDR) Scan(raw interface{}) error {
 	case string:
 		*u = CIDR(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.CIDR from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.CIDR from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -851,7 +858,7 @@ func (u *CIDR) UnmarshalBSON(data []byte) error {
 		*u = CIDR(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as CIDR")
+	return fmt.Errorf("couldn't unmarshal bson bytes as CIDR: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -893,7 +900,7 @@ func (u *MAC) Scan(raw interface{}) error {
 	case string:
 		*u = MAC(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.IPv4 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.IPv4 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -939,7 +946,7 @@ func (u *MAC) UnmarshalBSON(data []byte) error {
 		*u = MAC(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as MAC")
+	return fmt.Errorf("couldn't unmarshal bson bytes as MAC: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -981,7 +988,7 @@ func (u *UUID) Scan(raw interface{}) error {
 	case string:
 		*u = UUID(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.UUID from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.UUID from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1030,7 +1037,7 @@ func (u *UUID) UnmarshalBSON(data []byte) error {
 		*u = UUID(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as UUID")
+	return fmt.Errorf("couldn't unmarshal bson bytes as UUID: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1072,7 +1079,7 @@ func (u *UUID3) Scan(raw interface{}) error {
 	case string:
 		*u = UUID3(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.UUID3 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.UUID3 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1121,7 +1128,7 @@ func (u *UUID3) UnmarshalBSON(data []byte) error {
 		*u = UUID3(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as UUID3")
+	return fmt.Errorf("couldn't unmarshal bson bytes as UUID3: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1163,7 +1170,7 @@ func (u *UUID4) Scan(raw interface{}) error {
 	case string:
 		*u = UUID4(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.UUID4 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.UUID4 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1212,7 +1219,7 @@ func (u *UUID4) UnmarshalBSON(data []byte) error {
 		*u = UUID4(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as UUID4")
+	return fmt.Errorf("couldn't unmarshal bson bytes as UUID4: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1254,7 +1261,7 @@ func (u *UUID5) Scan(raw interface{}) error {
 	case string:
 		*u = UUID5(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.UUID5 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.UUID5 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1303,7 +1310,7 @@ func (u *UUID5) UnmarshalBSON(data []byte) error {
 		*u = UUID5(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as UUID5")
+	return fmt.Errorf("couldn't unmarshal bson bytes as UUID5: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1345,7 +1352,7 @@ func (u *ISBN) Scan(raw interface{}) error {
 	case string:
 		*u = ISBN(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.ISBN from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.ISBN from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1394,7 +1401,7 @@ func (u *ISBN) UnmarshalBSON(data []byte) error {
 		*u = ISBN(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as ISBN")
+	return fmt.Errorf("couldn't unmarshal bson bytes as ISBN: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1436,7 +1443,7 @@ func (u *ISBN10) Scan(raw interface{}) error {
 	case string:
 		*u = ISBN10(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.ISBN10 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.ISBN10 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1485,7 +1492,7 @@ func (u *ISBN10) UnmarshalBSON(data []byte) error {
 		*u = ISBN10(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as ISBN10")
+	return fmt.Errorf("couldn't unmarshal bson bytes as ISBN10: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1527,7 +1534,7 @@ func (u *ISBN13) Scan(raw interface{}) error {
 	case string:
 		*u = ISBN13(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.ISBN13 from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.ISBN13 from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1576,7 +1583,7 @@ func (u *ISBN13) UnmarshalBSON(data []byte) error {
 		*u = ISBN13(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as ISBN13")
+	return fmt.Errorf("couldn't unmarshal bson bytes as ISBN13: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1618,7 +1625,7 @@ func (u *CreditCard) Scan(raw interface{}) error {
 	case string:
 		*u = CreditCard(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.CreditCard from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.CreditCard from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1667,7 +1674,7 @@ func (u *CreditCard) UnmarshalBSON(data []byte) error {
 		*u = CreditCard(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as CreditCard")
+	return fmt.Errorf("couldn't unmarshal bson bytes as CreditCard: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1709,7 +1716,7 @@ func (u *SSN) Scan(raw interface{}) error {
 	case string:
 		*u = SSN(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.SSN from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.SSN from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1758,7 +1765,7 @@ func (u *SSN) UnmarshalBSON(data []byte) error {
 		*u = SSN(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as SSN")
+	return fmt.Errorf("couldn't unmarshal bson bytes as SSN: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1800,7 +1807,7 @@ func (h *HexColor) Scan(raw interface{}) error {
 	case string:
 		*h = HexColor(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.HexColor from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.HexColor from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1849,7 +1856,7 @@ func (h *HexColor) UnmarshalBSON(data []byte) error {
 		*h = HexColor(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as HexColor")
+	return fmt.Errorf("couldn't unmarshal bson bytes as HexColor: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1891,7 +1898,7 @@ func (r *RGBColor) Scan(raw interface{}) error {
 	case string:
 		*r = RGBColor(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.RGBColor from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.RGBColor from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -1940,7 +1947,7 @@ func (r *RGBColor) UnmarshalBSON(data []byte) error {
 		*r = RGBColor(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as RGBColor")
+	return fmt.Errorf("couldn't unmarshal bson bytes as RGBColor: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
@@ -1983,7 +1990,7 @@ func (r *Password) Scan(raw interface{}) error {
 	case string:
 		*r = Password(v)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.Password from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.Password from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -2032,7 +2039,7 @@ func (r *Password) UnmarshalBSON(data []byte) error {
 		*r = Password(ud)
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as Password")
+	return fmt.Errorf("couldn't unmarshal bson bytes as Password: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
diff --git a/duration.go b/duration.go
index 6284b82..0d85140 100644
--- a/duration.go
+++ b/duration.go
@@ -17,7 +17,6 @@ package strfmt
 import (
 	"database/sql/driver"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"regexp"
 	"strconv"
@@ -33,6 +32,11 @@ func init() {
 	Default.Add("duration", &d, IsDuration)
 }
 
+const (
+	hoursInDay = 24
+	daysInWeek = 7
+)
+
 var (
 	timeUnits = [][]string{
 		{"ns", "nano"},
@@ -52,8 +56,8 @@ var (
 		"s":  time.Second,
 		"m":  time.Minute,
 		"h":  time.Hour,
-		"d":  24 * time.Hour,
-		"w":  7 * 24 * time.Hour,
+		"d":  hoursInDay * time.Hour,
+		"w":  hoursInDay * daysInWeek * time.Hour,
 	}
 
 	durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`)
@@ -120,7 +124,7 @@ func ParseDuration(cand string) (time.Duration, error) {
 	if ok {
 		return dur, nil
 	}
-	return 0, fmt.Errorf("unable to parse %s as duration", cand)
+	return 0, fmt.Errorf("unable to parse %s as duration: %w", cand, ErrFormat)
 }
 
 // Scan reads a Duration value from database driver type.
@@ -134,7 +138,7 @@ func (d *Duration) Scan(raw interface{}) error {
 	case nil:
 		*d = Duration(0)
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -192,7 +196,7 @@ func (d *Duration) UnmarshalBSON(data []byte) error {
 		return nil
 	}
 
-	return errors.New("couldn't unmarshal bson bytes value as Date")
+	return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.
diff --git a/errors.go b/errors.go
new file mode 100644
index 0000000..9a92403
--- /dev/null
+++ b/errors.go
@@ -0,0 +1,10 @@
+package strfmt
+
+type strfmtError string
+
+// ErrFormat is an error raised by the strfmt package
+const ErrFormat strfmtError = "format error"
+
+func (e strfmtError) Error() string {
+	return string(e)
+}
diff --git a/format.go b/format.go
index 888e107..2c45cd2 100644
--- a/format.go
+++ b/format.go
@@ -16,7 +16,6 @@ package strfmt
 
 import (
 	"encoding"
-	stderrors "errors"
 	"fmt"
 	"reflect"
 	"strings"
@@ -102,7 +101,7 @@ func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc {
 		}
 		data, ok := obj.(string)
 		if !ok {
-			return nil, fmt.Errorf("failed to cast %+v to string", obj)
+			return nil, fmt.Errorf("failed to cast %+v to string: %w", obj, ErrFormat)
 		}
 
 		for _, v := range f.data {
@@ -118,7 +117,7 @@ func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc {
 				case "datetime":
 					input := data
 					if len(input) == 0 {
-						return nil, stderrors.New("empty string is an invalid datetime format")
+						return nil, fmt.Errorf("empty string is an invalid datetime format: %w", ErrFormat)
 					}
 					return ParseDateTime(input)
 				case "duration":
diff --git a/time.go b/time.go
index 2f83187..310b112 100644
--- a/time.go
+++ b/time.go
@@ -18,7 +18,6 @@ import (
 	"database/sql/driver"
 	"encoding/binary"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"regexp"
 	"strings"
@@ -40,13 +39,19 @@ func init() {
 	Default.Add("datetime", &dt, IsDateTime)
 }
 
-// IsDateTime returns true when the string is a valid date-time
+// IsDateTime returns true when the string is a valid date-time.
+//
+// JSON datetime format consist of a date and a time separated by a "T", e.g. 2012-04-23T18:25:43.511Z.
 func IsDateTime(str string) bool {
-	if len(str) < 4 {
+	const (
+		minDateTimeLength = 4
+		minParts          = 2
+	)
+	if len(str) < minDateTimeLength {
 		return false
 	}
 	s := strings.Split(strings.ToLower(str), "t")
-	if len(s) < 2 || !IsDate(s[0]) {
+	if len(s) < minParts || !IsDate(s[0]) {
 		return false
 	}
 
@@ -91,7 +96,7 @@ var (
 	// MarshalFormat sets the time resolution format used for marshaling time (set to milliseconds)
 	MarshalFormat = RFC3339Millis
 
-	// NormalizeTimeForMarshal provides a normalization function on time befeore marshalling (e.g. time.UTC).
+	// NormalizeTimeForMarshal provides a normalization function on time before marshalling (e.g. time.UTC).
 	// By default, the time value is not changed.
 	NormalizeTimeForMarshal = func(t time.Time) time.Time { return t }
 
@@ -172,7 +177,7 @@ func (t *DateTime) Scan(raw interface{}) error {
 	case nil:
 		*t = DateTime{}
 	default:
-		return fmt.Errorf("cannot sql.Scan() strfmt.DateTime from: %#v", v)
+		return fmt.Errorf("cannot sql.Scan() strfmt.DateTime from: %#v: %w", v, ErrFormat)
 	}
 
 	return nil
@@ -226,20 +231,23 @@ func (t *DateTime) UnmarshalBSON(data []byte) error {
 	return nil
 }
 
+const bsonDateLength = 8
+
 // MarshalBSONValue is an interface implemented by types that can marshal themselves
 // into a BSON document represented as bytes. The bytes returned must be a valid
 // BSON document if the error is nil.
+//
 // Marshals a DateTime as a bsontype.DateTime, an int64 representing
 // milliseconds since epoch.
 func (t DateTime) MarshalBSONValue() (bsontype.Type, []byte, error) {
 	// UnixNano cannot be used directly, the result of calling UnixNano on the zero
-	// Time is undefined. Thats why we use time.Nanosecond() instead.
-
+	// Time is undefined. That's why we use time.Nanosecond() instead.
 	tNorm := NormalizeTimeForMarshal(time.Time(t))
-	i64 := tNorm.Unix()*1000 + int64(tNorm.Nanosecond())/1e6
+	i64 := tNorm.UnixMilli()
 
-	buf := make([]byte, 8)
-	binary.LittleEndian.PutUint64(buf, uint64(i64))
+	buf := make([]byte, bsonDateLength)
+	// int64 -> uint64 conversion is safe here
+	binary.LittleEndian.PutUint64(buf, uint64(i64)) //nolint:gosec
 
 	return bson.TypeDateTime, buf, nil
 }
@@ -254,13 +262,14 @@ func (t *DateTime) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error {
 		return nil
 	}
 
-	if len(data) != 8 {
-		return errors.New("bson date field length not exactly 8 bytes")
+	if len(data) != bsonDateLength {
+		return fmt.Errorf("bson date field length not exactly 8 bytes: %w", ErrFormat)
 	}
 
-	i64 := int64(binary.LittleEndian.Uint64(data))
+	// it's ok to get negative values after conversion
+	i64 := int64(binary.LittleEndian.Uint64(data)) //nolint:gosec
 	// TODO: Use bsonprim.DateTime.Time() method
-	*t = DateTime(time.Unix(i64/1000, i64%1000*1000000))
+	*t = DateTime(time.UnixMilli(i64))
 
 	return nil
 }
diff --git a/ulid.go b/ulid.go
index e71aff7..434eb01 100644
--- a/ulid.go
+++ b/ulid.go
@@ -4,7 +4,6 @@ import (
 	cryptorand "crypto/rand"
 	"database/sql/driver"
 	"encoding/json"
-	"errors"
 	"fmt"
 	"io"
 	"sync"
@@ -98,7 +97,7 @@ func NewULID() (ULID, error) {
 	obj := ulidEntropyPool.Get()
 	entropy, ok := obj.(io.Reader)
 	if !ok {
-		return u, fmt.Errorf("failed to cast %+v to io.Reader", obj)
+		return u, fmt.Errorf("failed to cast %+v to io.Reader: %w", obj, ErrFormat)
 	}
 
 	id, err := ulid.New(ulid.Now(), entropy)
@@ -181,12 +180,12 @@ func (u *ULID) UnmarshalBSON(data []byte) error {
 	if ud, ok := m["data"].(string); ok {
 		id, err := ulid.ParseStrict(ud)
 		if err != nil {
-			return fmt.Errorf("couldn't parse bson bytes as ULID: %w", err)
+			return fmt.Errorf("couldn't parse bson bytes as ULID: %w: %w", err, ErrFormat)
 		}
 		u.ULID = id
 		return nil
 	}
-	return errors.New("couldn't unmarshal bson bytes as ULID")
+	return fmt.Errorf("couldn't unmarshal bson bytes as ULID: %w", ErrFormat)
 }
 
 // DeepCopyInto copies the receiver and writes its value into out.


Test patch:
diff --git a/default_test.go b/default_test.go
index 8b2e2f5..87371d8 100644
--- a/default_test.go
+++ b/default_test.go
@@ -440,7 +440,7 @@ func testStringFormat(t *testing.T, what testableFormat, format, with string, va
 
 	// Stringer
 	strVal = what.String()
-	assert.Equalf(t, []byte(with), b, "[%s]String: expected %v and %v to be equal", strVal, with)
+	assert.Equalf(t, []byte(with), b, "[%s]String: expected %v and %v to be equal", format, strVal, with)
 
 	// JSON encoding interface
 	bj := []byte("\"" + with + "\"")
diff --git a/ulid_test.go b/ulid_test.go
index 02f4879..8be6714 100644
--- a/ulid_test.go
+++ b/ulid_test.go
@@ -201,9 +201,9 @@ func TestFormatULID_Scan(t *testing.T) {
 			case [16]byte:
 				return u, u.ULID.UnmarshalBinary(x[:])
 			case int: // just for linter
-				return u, fmt.Errorf("cannot sql.Scan() strfmt.ULID from: %#v", raw)
+				return u, fmt.Errorf("cannot sql.Scan() strfmt.ULID from: %#v: %w", raw, ErrFormat)
 			}
-			return u, fmt.Errorf("cannot sql.Scan() strfmt.ULID from: %#v", raw)
+			return u, fmt.Errorf("cannot sql.Scan() strfmt.ULID from: %#v: %w", raw, ErrFormat)
 		}
 
 		// get underlying binary implementation which is actually [16]byte