diff --git a/connection.go b/connection.go
index 99eb8a808..c170114fe 100644
--- a/connection.go
+++ b/connection.go
@@ -251,7 +251,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 				buf = append(buf, "'0000-00-00'"...)
 			} else {
 				buf = append(buf, '\'')
-				buf, err = appendDateTime(buf, v.In(mc.cfg.Loc), mc.cfg.TimeTruncate)
+				buf, err = appendDateTime(buf, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
 				if err != nil {
 					return "", err
 				}
diff --git a/dsn.go b/dsn.go
index ce5d85ff0..d0fbf3bd9 100644
--- a/dsn.go
+++ b/dsn.go
@@ -34,6 +34,8 @@ var (
 // If a new Config is created instead of being parsed from a DSN string,
 // the NewConfig function should be used, which sets default values.
 type Config struct {
+	// non boolean fields
+
 	User                 string            // Username
 	Passwd               string            // Password (requires User)
 	Net                  string            // Network (e.g. "tcp", "tcp6", "unix". default: "tcp")
@@ -45,15 +47,15 @@ type Config struct {
 	Loc                  *time.Location    // Location for time.Time values
 	MaxAllowedPacket     int               // Max packet size allowed
 	ServerPubKey         string            // Server public key name
-	pubKey               *rsa.PublicKey    // Server public key
 	TLSConfig            string            // TLS configuration name
 	TLS                  *tls.Config       // TLS configuration, its priority is higher than TLSConfig
-	TimeTruncate         time.Duration     // Truncate time.Time values to the specified duration
 	Timeout              time.Duration     // Dial timeout
 	ReadTimeout          time.Duration     // I/O read timeout
 	WriteTimeout         time.Duration     // I/O write timeout
 	Logger               Logger            // Logger
 
+	// boolean fields
+
 	AllowAllFiles            bool // Allow all files to be used with LOAD DATA LOCAL INFILE
 	AllowCleartextPasswords  bool // Allows the cleartext client side plugin
 	AllowFallbackToPlaintext bool // Allows fallback to unencrypted connection if server does not support TLS
@@ -66,17 +68,48 @@ type Config struct {
 	MultiStatements          bool // Allow multiple statements in one query
 	ParseTime                bool // Parse time values to time.Time
 	RejectReadOnly           bool // Reject read-only connections
+
+	// unexported fields. new options should be come here
+
+	pubKey       *rsa.PublicKey // Server public key
+	timeTruncate time.Duration  // Truncate time.Time values to the specified duration
 }
 
+// Functional Options Pattern
+// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis
+type Option func(*Config) error
+
 // NewConfig creates a new Config and sets default values.
 func NewConfig() *Config {
-	return &Config{
+	cfg := &Config{
 		Loc:                  time.UTC,
 		MaxAllowedPacket:     defaultMaxAllowedPacket,
 		Logger:               defaultLogger,
 		AllowNativePasswords: true,
 		CheckConnLiveness:    true,
 	}
+
+	return cfg
+}
+
+// Apply applies the given options to the Config object.
+func (c *Config) Apply(opts ...Option) error {
+	for _, opt := range opts {
+		err := opt(c)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// TimeTruncate sets the time duration to truncate time.Time values in
+// query parameters.
+func TimeTruncate(d time.Duration) Option {
+	return func(cfg *Config) error {
+		cfg.timeTruncate = d
+		return nil
+	}
 }
 
 func (cfg *Config) Clone() *Config {
@@ -263,8 +296,8 @@ func (cfg *Config) FormatDSN() string {
 		writeDSNParam(&buf, &hasParam, "parseTime", "true")
 	}
 
-	if cfg.TimeTruncate > 0 {
-		writeDSNParam(&buf, &hasParam, "timeTruncate", cfg.TimeTruncate.String())
+	if cfg.timeTruncate > 0 {
+		writeDSNParam(&buf, &hasParam, "timeTruncate", cfg.timeTruncate.String())
 	}
 
 	if cfg.ReadTimeout > 0 {
@@ -509,9 +542,9 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 
 		// time.Time truncation
 		case "timeTruncate":
-			cfg.TimeTruncate, err = time.ParseDuration(value)
+			cfg.timeTruncate, err = time.ParseDuration(value)
 			if err != nil {
-				return
+				return fmt.Errorf("invalid timeTruncate value: %v, error: %w", value, err)
 			}
 
 		// I/O read Timeout
diff --git a/dsn_test.go b/dsn_test.go
index 75cbda700..dd8cd935c 100644
--- a/dsn_test.go
+++ b/dsn_test.go
@@ -76,7 +76,7 @@ var testDSNs = []struct {
 	&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true},
 }, {
 	"user:password@/dbname?loc=UTC&timeout=30s&parseTime=true&timeTruncate=1h",
-	&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, Timeout: 30 * time.Second, ParseTime: true, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, TimeTruncate: time.Hour},
+	&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Loc: time.UTC, Timeout: 30 * time.Second, ParseTime: true, MaxAllowedPacket: defaultMaxAllowedPacket, Logger: defaultLogger, AllowNativePasswords: true, CheckConnLiveness: true, timeTruncate: time.Hour},
 },
 }
 
diff --git a/packets.go b/packets.go
index e5a6e4727..3d6e5308c 100644
--- a/packets.go
+++ b/packets.go
@@ -1172,7 +1172,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				if v.IsZero() {
 					b = append(b, "0000-00-00"...)
 				} else {
-					b, err = appendDateTime(b, v.In(mc.cfg.Loc), mc.cfg.TimeTruncate)
+					b, err = appendDateTime(b, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
 					if err != nil {
 						return err
 					}
diff --git a/result.go b/result.go
index 36a432e81..d51631468 100644
--- a/result.go
+++ b/result.go
@@ -15,9 +15,8 @@ import "database/sql/driver"
 // This is accessible by executing statements using sql.Conn.Raw() and
 // downcasting the returned result:
 //
-//    res, err := rawConn.Exec(...)
-//    res.(mysql.Result).AllRowsAffected()
-//
+//	res, err := rawConn.Exec(...)
+//	res.(mysql.Result).AllRowsAffected()
 type Result interface {
 	driver.Result
 	// AllRowsAffected returns a slice containing the affected rows for each