Preflight Checklist

  • [x] I have searched the issue tracker for an issue that matches the one I want to file, without success.
  • [x] I am not looking for support or already pursued the available support channels without success.
  • [x] I have checked the troubleshooting guide for my problem, without success.

Viper Version

1.19.1

Go Version

1.24

Config Source

Flags

Format

YAML

Repl.it link

No response

Code reproducing the issue

package main

import (
    "fmt"
    "os"

    "github.com/spf13/cobra"
    "github.com/spf13/viper"
)

var cfgFile string

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "Example application showing Viper configuration",
    Run: func(cmd *cobra.Command, args []string) {
        cfg, err := getConfig()
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            os.Exit(1)
        }
        fmt.Printf("Configuration loaded: %+v\n", cfg)
    },
}

type Config struct {
    ClusterID   string   `mapstructure:"clusterID"`
    ServerPort  int      `mapstructure:"server.port"`
    BackendMode []string `mapstructure:"server.backendMode"`
}

func init() {
    cobra.OnInitialize(initConfig)

    // Define flags
    rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "Load configuration from `filename`")

    rootCmd.PersistentFlags().StringP(
        "cluster-id",
        "i",
        "",
        "Specify the cluster ID",
    )
    viper.BindPFlag("clusterID", rootCmd.PersistentFlags().Lookup("cluster-id"))
    viper.BindEnv("clusterID", "MY_CLUSTER_ID")

    rootCmd.PersistentFlags().IntP(
        "port",
        "p",
        8080,
        "Server port",
    )
    viper.BindPFlag("server.port", rootCmd.PersistentFlags().Lookup("port"))
}

func initConfig() {
    if cfgFile != "" {
        // Use config file from the flag
        viper.SetConfigFile(cfgFile)
    } else {
        // Search config in working directory with name "config" (without extension)
        viper.AddConfigPath(".")
        viper.SetConfigName("config")
    }

    // Read the config file
    if err := viper.ReadInConfig(); err != nil {
        fmt.Printf("Can't read configuration file: %v\n", err)
        os.Exit(1)
    }
    fmt.Println("Using config file:", viper.ConfigFileUsed())
}

func getConfig() (Config, error) {
    var cfg Config

    // Read configuration into struct
    cfg = Config{
        ClusterID:   viper.GetString("clusterID"),
        ServerPort:  viper.GetInt("server.port"),
        BackendMode: viper.GetStringSlice("server.backendMode"),
    }

    // Validate configuration
    if cfg.ClusterID == "" {
        return cfg, fmt.Errorf("cluster ID cannot be empty")
    }

    return cfg, nil
}

func main() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

Expected Behavior

while parsing below configuration file

conf.yaml

clusterID: 2024-03-03
server:
  port: 9090
  backendMode:
    - "mode1"
    - "mode2"

we expect string with - is parsed as string

Configuration loaded: {ClusterID:2022-03-04 ServerPort:9090 BackendMode:[mode1 mode2]}

Actual Behavior

while parsing below configuration file

conf.yaml

clusterID: 2024-03-03
server:
  port: 9090
  backendMode:
    - "mode1"
    - "mode2"

we see string with - is parsed as datetime in UTC

Configuration loaded: {ClusterID:2022-03-04 00:00:00 +0000 UTC ServerPort:9090 BackendMode:[mode1 mode2]}

Steps To Reproduce

mkdir my app

go mod init myapp

vi myapp.go [insert source code]

vi conf.yaml [insert config]

go mod tidy

go build -o myapp myapp.go

./myapp --config config.yaml

Additional Information

For the same configuration file viper 1.7.0 and 1.19.0 are parsing values differently.

with viper v1.7.0 we see it is parsed as string

Configuration loaded: {ClusterID:2022-03-04 ServerPort:9090 BackendMode:[mode1 mode2]}

with viper v1.19.0 we see it is parsed as datetime in UTC

Configuration loaded: {ClusterID:2022-03-04 00:00:00 +0000 UTC ServerPort:9090 BackendMode:[mode1 mode2]}