Repository: prometheus/prometheus
File: template/template.go (Go)

Code before:
				if err != nil {
					return "", err
				}
				return fmt.Sprintf("%.4g%%", v*100), nil
			},
			"humanizeTimestamp": common_templates.HumanizeTimestamp,
			"toTime": func(i interface{}) (*time.Time, error) {
				v, err := common_templates.ConvertToFloat(i)
				if err != nil {
					return nil, err
				}

				return floatToTime(v)
			},
			"toDuration": func(i interface{}) (*time.Duration, error) {
				v, err := common_templates.ConvertToFloat(i)
				if err != nil {
					return nil, err
				}
				d := time.Duration(v * float64(time.Second))
				return &d, nil
			},
			//nolint:gocritic // must be a function to avoid template panics (as in Loki).
			"now": func() model.Time {
				return model.Now()
			},
			"pathPrefix": func() string {
				return externalURL.Path
			},
			"externalURL": func() string {
				return externalURL.String()
			},
			"parseDuration": func(d string) (float64, error) {
				v, err := model.ParseDurationAllowNegative(d)
				if err != nil {
					return 0, err
				}
				return float64(time.Duration(v)) / float64(time.Second), nil
			},
		},
		options: options,
	}
}

// AlertTemplateData returns the interface to be used in expanding the template.
func AlertTemplateData(labels, externalLabels map[string]string, externalURL string, smpl promql.Sample) interface{} {
	res := struct {
		Labels         map[string]string
		ExternalLabels map[string]string
		ExternalURL    string
		Value          interface{}

Review comment (suggestion):
After some consideration, I think we should better return a float representing the number of seconds since epoch to make this useful for `toTime` and `humanizeTime` and generally the time and duration arithmetic, which is based on seconds, not milliseconds.

```suggestion
			"now": func() float64 {
				now := time.Now()
				return float64(now.Unix()) + float64(now.Nanosecond())/float64(time.Second)
			},
```

Code after:
				if err != nil {
					return "", err
				}
				return fmt.Sprintf("%.4g%%", v*100), nil
			},
			"humanizeTimestamp": common_templates.HumanizeTimestamp,
			"toTime": func(i interface{}) (*time.Time, error) {
				v, err := common_templates.ConvertToFloat(i)
				if err != nil {
					return nil, err
				}

				return floatToTime(v)
			},
			"toDuration": func(i interface{}) (*time.Duration, error) {
				v, err := common_templates.ConvertToFloat(i)
				if err != nil {
					return nil, err
				}
				d := time.Duration(v * float64(time.Second))
				return &d, nil
			},
			"now": func() float64 {
				return float64(timestamp) / 1000.0
			},
			"pathPrefix": func() string {
				return externalURL.Path
			},
			"externalURL": func() string {
				return externalURL.String()
			},
			"parseDuration": func(d string) (float64, error) {
				v, err := model.ParseDurationAllowNegative(d)
				if err != nil {
					return 0, err
				}
				return float64(time.Duration(v)) / float64(time.Second), nil
			},
		},
		options: options,
	}
}

// AlertTemplateData returns the interface to be used in expanding the template.
func AlertTemplateData(labels, externalLabels map[string]string, externalURL string, smpl promql.Sample) interface{} {
	res := struct {
		Labels         map[string]string
		ExternalLabels map[string]string
		ExternalURL    string
		Value          interface{}
	}{