Repository: uber-go/zap
File: zapcore/encoder.go (Go)

Code before:
	}
}

// UnmarshalText unmarshals text to a TimeEncoder.
// "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder.
// "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder.
// "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder.
// "millis" is unmarshaled to EpochMillisTimeEncoder.
// "nanos" is unmarshaled to EpochNanosEncoder.
// "format=<time-layout-format>" is unmarshaled to TimeEncoder with given format.
// Anything else is unmarshaled to EpochTimeEncoder.
func (e *TimeEncoder) UnmarshalText(text []byte) error {
	vals := strings.SplitAfterN(string(text), "=", 2)
	switch vals[0] {
	case "rfc3339nano", "RFC3339Nano":
		*e = RFC3339NanoTimeEncoder
	case "rfc3339", "RFC3339":
		*e = RFC3339TimeEncoder
	case "iso8601", "ISO8601":
		*e = ISO8601TimeEncoder
	case "millis":
		*e = EpochMillisTimeEncoder
	case "nanos":
		*e = EpochNanosTimeEncoder
	case "format=":
		*e = TimeEncoderOfFormat(vals[1])
	default:
		*e = EpochTimeEncoder
	}
	return nil
}

// A DurationEncoder serializes a time.Duration to a primitive type.
type DurationEncoder func(time.Duration, PrimitiveArrayEncoder)

// SecondsDurationEncoder serializes a time.Duration to a floating-point number of seconds elapsed.
func SecondsDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
	enc.AppendFloat64(float64(d) / float64(time.Second))
}

// NanosDurationEncoder serializes a time.Duration to an integer number of
// nanoseconds elapsed.
func NanosDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
	enc.AppendInt64(int64(d))
}

// MillisDurationEncoder serializes a time.Duration to an integer number of
// milliseconds elapsed.
func MillisDurationEncoder(d time.Duration, enc PrimitiveArrayEncoder) {
	enc.AppendInt64(d.Nanoseconds() / 1e6)
}

Review comment (bug):
I see the concern about the `=` splitting thing. This could become a problem
if we want more parameters in the future, or if escaping parameters becomes
necessary.

Perhaps we can accept objects for when the format is specified:

```
timeEncoder: rfc3339
# or
timeEncoder:
  format: "06/01/02 03:04pm"
```

We can accomplish this by implementing [`yaml.Unmarshaler`][2] to decode into
an object, and if that fails, to fall back to UnmarshalText.

  [1]: https://golang.org/pkg/encoding/json/#Unmarshaler
  [2]: https://godoc.org/gopkg.in/yaml.v2#Unmarshaler

Something like the following should suffice:

    func (e *TimeEncoder) UnmarshalYAML(unmarshal func(interface{}) error) error {
      # timeEncoder:
      #   format: "..."
      var o struct {
        Format string `yaml:"format" json:"format"`
      }
      if err := unmarshal(&o); err == nil {
        *e = TimeEncoderOfFormat(o.Format)
        return nil
      }
      
      # timeEncoder: "..."
      var s string
      if err := unmarshal(&s); err != nil {
        return err
      }
      return e.UnmarshalText([]byte(s))
    }

We can even reuse the `UnmarshalYAML` method for JSON support:

    func (e *TimeEncoder) UnmarshalJSON(bs []byte) error {
      return e.UnmarshalYAML(func(v interface{}) error {
        return json.Unmarshal(bs, v)
      })
    }

Code after:
	}
}

// UnmarshalText unmarshals text to a TimeEncoder.
// "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder.
// "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder.
// "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder.
// "millis" is unmarshaled to EpochMillisTimeEncoder.
// "nanos" is unmarshaled to EpochNanosEncoder.
// Anything else is unmarshaled to EpochTimeEncoder.
func (e *TimeEncoder) UnmarshalText(text []byte) error {
	switch string(text) {
	case "rfc3339nano", "RFC3339Nano":
		*e = RFC3339NanoTimeEncoder
	case "rfc3339", "RFC3339":
		*e = RFC3339TimeEncoder
	case "iso8601", "ISO8601":
		*e = ISO8601TimeEncoder
	case "millis":
		*e = EpochMillisTimeEncoder
	case "nanos":
		*e = EpochNanosTimeEncoder
	default:
		*e = EpochTimeEncoder
	}
	return nil
}

// UnmarshalYAML unmarshals YAML to a TimeEncoder.
// If value is an object with a "layout" field, it will be unmarshaled to  TimeEncoder with given layout.
//     timeEncoder:
//       layout: 06/01/02 03:04pm
// If value is string, it uses UnmarshalText.
//     timeEncoder: iso8601
func (e *TimeEncoder) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var o struct {
		Layout string `json:"layout" yaml:"layout"`
	}
	if err := unmarshal(&o); err == nil {
		*e = TimeEncoderOfLayout(o.Layout)
		return nil
	}

	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	return e.UnmarshalText([]byte(s))
}

// UnmarshalJSON unmarshals JSON to a TimeEncoder as same way UnmarshalYAML does.