cadc8ad72d
I notice that the Hugo use this project to convert the string to time.Time. When I migrate to Hugo from Hexo (another static blog generator). The Hugo said : > page.go:750: Failed to parse date '2016-03-06 15:28:01' in page xxxxx.md I hope you can merge this Pull request, so that those people who migrate to Hugo from Hexo are easy to use Hugo to generate the static blog. Thanks.
528 lines
12 KiB
Go
528 lines
12 KiB
Go
// Copyright © 2014 Steve Francia <spf@spf13.com>.
|
|
//
|
|
// Use of this source code is governed by an MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cast
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// ToTimeE casts an empty interface to time.Time.
|
|
func ToTimeE(i interface{}) (tim time.Time, err error) {
|
|
i = indirect(i)
|
|
|
|
switch s := i.(type) {
|
|
case time.Time:
|
|
return s, nil
|
|
case string:
|
|
d, e := StringToDate(s)
|
|
if e == nil {
|
|
return d, nil
|
|
}
|
|
return time.Time{}, fmt.Errorf("Could not parse Date/Time format: %v\n", e)
|
|
default:
|
|
return time.Time{}, fmt.Errorf("Unable to Cast %#v to Time\n", i)
|
|
}
|
|
}
|
|
|
|
// ToDurationE casts an empty interface to time.Duration.
|
|
func ToDurationE(i interface{}) (d time.Duration, err error) {
|
|
i = indirect(i)
|
|
|
|
switch s := i.(type) {
|
|
case time.Duration:
|
|
return s, nil
|
|
case int64, int32, int16, int8, int:
|
|
d = time.Duration(ToInt64(s))
|
|
return
|
|
case float32, float64:
|
|
d = time.Duration(ToFloat64(s))
|
|
return
|
|
case string:
|
|
if strings.ContainsAny(s, "nsuµmh") {
|
|
d, err = time.ParseDuration(s)
|
|
} else {
|
|
d, err = time.ParseDuration(s + "ns")
|
|
}
|
|
return
|
|
default:
|
|
err = fmt.Errorf("Unable to Cast %#v to Duration\n", i)
|
|
return
|
|
}
|
|
}
|
|
|
|
// ToBoolE casts an empty interface to a bool.
|
|
func ToBoolE(i interface{}) (bool, error) {
|
|
|
|
i = indirect(i)
|
|
|
|
switch b := i.(type) {
|
|
case bool:
|
|
return b, nil
|
|
case nil:
|
|
return false, nil
|
|
case int:
|
|
if i.(int) != 0 {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
case string:
|
|
return strconv.ParseBool(i.(string))
|
|
default:
|
|
return false, fmt.Errorf("Unable to Cast %#v to bool", i)
|
|
}
|
|
}
|
|
|
|
// ToFloat64E casts an empty interface to a float64.
|
|
func ToFloat64E(i interface{}) (float64, error) {
|
|
i = indirect(i)
|
|
|
|
switch s := i.(type) {
|
|
case float64:
|
|
return s, nil
|
|
case float32:
|
|
return float64(s), nil
|
|
case int64:
|
|
return float64(s), nil
|
|
case int32:
|
|
return float64(s), nil
|
|
case int16:
|
|
return float64(s), nil
|
|
case int8:
|
|
return float64(s), nil
|
|
case int:
|
|
return float64(s), nil
|
|
case string:
|
|
v, err := strconv.ParseFloat(s, 64)
|
|
if err == nil {
|
|
return float64(v), nil
|
|
}
|
|
return 0.0, fmt.Errorf("Unable to Cast %#v to float", i)
|
|
default:
|
|
return 0.0, fmt.Errorf("Unable to Cast %#v to float", i)
|
|
}
|
|
}
|
|
|
|
// ToInt64E casts an empty interface to an int64.
|
|
func ToInt64E(i interface{}) (int64, error) {
|
|
i = indirect(i)
|
|
|
|
switch s := i.(type) {
|
|
case int64:
|
|
return s, nil
|
|
case int:
|
|
return int64(s), nil
|
|
case int32:
|
|
return int64(s), nil
|
|
case int16:
|
|
return int64(s), nil
|
|
case int8:
|
|
return int64(s), nil
|
|
case string:
|
|
v, err := strconv.ParseInt(s, 0, 0)
|
|
if err == nil {
|
|
return v, nil
|
|
}
|
|
return 0, fmt.Errorf("Unable to Cast %#v to int64", i)
|
|
case float64:
|
|
return int64(s), nil
|
|
case bool:
|
|
if bool(s) {
|
|
return int64(1), nil
|
|
}
|
|
return int64(0), nil
|
|
case nil:
|
|
return int64(0), nil
|
|
default:
|
|
return int64(0), fmt.Errorf("Unable to Cast %#v to int64", i)
|
|
}
|
|
}
|
|
|
|
// ToIntE casts an empty interface to an int.
|
|
func ToIntE(i interface{}) (int, error) {
|
|
i = indirect(i)
|
|
|
|
switch s := i.(type) {
|
|
case int:
|
|
return s, nil
|
|
case int64:
|
|
return int(s), nil
|
|
case int32:
|
|
return int(s), nil
|
|
case int16:
|
|
return int(s), nil
|
|
case int8:
|
|
return int(s), nil
|
|
case string:
|
|
v, err := strconv.ParseInt(s, 0, 0)
|
|
if err == nil {
|
|
return int(v), nil
|
|
}
|
|
return 0, fmt.Errorf("Unable to Cast %#v to int", i)
|
|
case float64:
|
|
return int(s), nil
|
|
case bool:
|
|
if bool(s) {
|
|
return 1, nil
|
|
}
|
|
return 0, nil
|
|
case nil:
|
|
return 0, nil
|
|
default:
|
|
return 0, fmt.Errorf("Unable to Cast %#v to int", i)
|
|
}
|
|
}
|
|
|
|
// From html/template/content.go
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// indirect returns the value, after dereferencing as many times
|
|
// as necessary to reach the base type (or nil).
|
|
func indirect(a interface{}) interface{} {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
|
|
// Avoid creating a reflect.Value if it's not a pointer.
|
|
return a
|
|
}
|
|
v := reflect.ValueOf(a)
|
|
for v.Kind() == reflect.Ptr && !v.IsNil() {
|
|
v = v.Elem()
|
|
}
|
|
return v.Interface()
|
|
}
|
|
|
|
// From html/template/content.go
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// indirectToStringerOrError returns the value, after dereferencing as many times
|
|
// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
|
|
// or error,
|
|
func indirectToStringerOrError(a interface{}) interface{} {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
|
|
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
var fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
|
|
|
|
v := reflect.ValueOf(a)
|
|
for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
|
|
v = v.Elem()
|
|
}
|
|
return v.Interface()
|
|
}
|
|
|
|
// ToStringE casts an empty interface to a string.
|
|
func ToStringE(i interface{}) (string, error) {
|
|
i = indirectToStringerOrError(i)
|
|
|
|
switch s := i.(type) {
|
|
case string:
|
|
return s, nil
|
|
case bool:
|
|
return strconv.FormatBool(s), nil
|
|
case float64:
|
|
return strconv.FormatFloat(i.(float64), 'f', -1, 64), nil
|
|
case int64:
|
|
return strconv.FormatInt(i.(int64), 10), nil
|
|
case int:
|
|
return strconv.FormatInt(int64(i.(int)), 10), nil
|
|
case []byte:
|
|
return string(s), nil
|
|
case template.HTML:
|
|
return string(s), nil
|
|
case template.URL:
|
|
return string(s), nil
|
|
case template.JS:
|
|
return string(s), nil
|
|
case template.CSS:
|
|
return string(s), nil
|
|
case template.HTMLAttr:
|
|
return string(s), nil
|
|
case nil:
|
|
return "", nil
|
|
case fmt.Stringer:
|
|
return s.String(), nil
|
|
case error:
|
|
return s.Error(), nil
|
|
default:
|
|
return "", fmt.Errorf("Unable to Cast %#v to string", i)
|
|
}
|
|
}
|
|
|
|
// ToStringMapStringE casts an empty interface to a map[string]string.
|
|
func ToStringMapStringE(i interface{}) (map[string]string, error) {
|
|
|
|
var m = map[string]string{}
|
|
|
|
switch v := i.(type) {
|
|
case map[string]string:
|
|
return v, nil
|
|
case map[string]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToString(val)
|
|
}
|
|
return m, nil
|
|
case map[interface{}]string:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToString(val)
|
|
}
|
|
return m, nil
|
|
case map[interface{}]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToString(val)
|
|
}
|
|
return m, nil
|
|
default:
|
|
return m, fmt.Errorf("Unable to Cast %#v to map[string]string", i)
|
|
}
|
|
}
|
|
|
|
// ToStringMapStringSliceE casts an empty interface to a map[string][]string.
|
|
func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) {
|
|
|
|
var m = map[string][]string{}
|
|
|
|
switch v := i.(type) {
|
|
case map[string][]string:
|
|
return v, nil
|
|
case map[string][]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToStringSlice(val)
|
|
}
|
|
return m, nil
|
|
case map[string]string:
|
|
for k, val := range v {
|
|
m[ToString(k)] = []string{val}
|
|
}
|
|
case map[string]interface{}:
|
|
for k, val := range v {
|
|
switch vt := val.(type) {
|
|
case []interface{}:
|
|
m[ToString(k)] = ToStringSlice(vt)
|
|
case []string:
|
|
m[ToString(k)] = vt
|
|
default:
|
|
m[ToString(k)] = []string{ToString(val)}
|
|
}
|
|
}
|
|
return m, nil
|
|
case map[interface{}][]string:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToStringSlice(val)
|
|
}
|
|
return m, nil
|
|
case map[interface{}]string:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToStringSlice(val)
|
|
}
|
|
return m, nil
|
|
case map[interface{}][]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToStringSlice(val)
|
|
}
|
|
return m, nil
|
|
case map[interface{}]interface{}:
|
|
for k, val := range v {
|
|
key, err := ToStringE(k)
|
|
if err != nil {
|
|
return m, fmt.Errorf("Unable to Cast %#v to map[string][]string", i)
|
|
}
|
|
value, err := ToStringSliceE(val)
|
|
if err != nil {
|
|
return m, fmt.Errorf("Unable to Cast %#v to map[string][]string", i)
|
|
}
|
|
m[key] = value
|
|
}
|
|
default:
|
|
return m, fmt.Errorf("Unable to Cast %#v to map[string][]string", i)
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// ToStringMapBoolE casts an empty interface to a map[string]bool.
|
|
func ToStringMapBoolE(i interface{}) (map[string]bool, error) {
|
|
|
|
var m = map[string]bool{}
|
|
|
|
switch v := i.(type) {
|
|
case map[interface{}]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToBool(val)
|
|
}
|
|
return m, nil
|
|
case map[string]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = ToBool(val)
|
|
}
|
|
return m, nil
|
|
case map[string]bool:
|
|
return v, nil
|
|
default:
|
|
return m, fmt.Errorf("Unable to Cast %#v to map[string]bool", i)
|
|
}
|
|
}
|
|
|
|
// ToStringMapE casts an empty interface to a map[string]interface{}.
|
|
func ToStringMapE(i interface{}) (map[string]interface{}, error) {
|
|
|
|
var m = map[string]interface{}{}
|
|
|
|
switch v := i.(type) {
|
|
case map[interface{}]interface{}:
|
|
for k, val := range v {
|
|
m[ToString(k)] = val
|
|
}
|
|
return m, nil
|
|
case map[string]interface{}:
|
|
return v, nil
|
|
default:
|
|
return m, fmt.Errorf("Unable to Cast %#v to map[string]interface{}", i)
|
|
}
|
|
}
|
|
|
|
// ToSliceE casts an empty interface to a []interface{}.
|
|
func ToSliceE(i interface{}) ([]interface{}, error) {
|
|
|
|
var s []interface{}
|
|
|
|
switch v := i.(type) {
|
|
case []interface{}:
|
|
for _, u := range v {
|
|
s = append(s, u)
|
|
}
|
|
return s, nil
|
|
case []map[string]interface{}:
|
|
for _, u := range v {
|
|
s = append(s, u)
|
|
}
|
|
return s, nil
|
|
default:
|
|
return s, fmt.Errorf("Unable to Cast %#v of type %v to []interface{}", i, reflect.TypeOf(i))
|
|
}
|
|
}
|
|
|
|
// ToBoolSliceE casts an empty interface to a []bool.
|
|
func ToBoolSliceE(i interface{}) ([]bool, error) {
|
|
|
|
if i == nil {
|
|
return []bool{}, fmt.Errorf("Unable to Cast %#v to []bool", i)
|
|
}
|
|
|
|
switch v := i.(type) {
|
|
case []bool:
|
|
return v, nil
|
|
}
|
|
|
|
kind := reflect.TypeOf(i).Kind()
|
|
switch kind {
|
|
case reflect.Slice, reflect.Array:
|
|
s := reflect.ValueOf(i)
|
|
a := make([]bool, s.Len())
|
|
for j := 0; j < s.Len(); j++ {
|
|
val, err := ToBoolE(s.Index(j).Interface())
|
|
if err != nil {
|
|
return []bool{}, fmt.Errorf("Unable to Cast %#v to []bool", i)
|
|
}
|
|
a[j] = val
|
|
}
|
|
return a, nil
|
|
default:
|
|
return []bool{}, fmt.Errorf("Unable to Cast %#v to []bool", i)
|
|
}
|
|
}
|
|
|
|
// ToStringSliceE casts an empty interface to a []string.
|
|
func ToStringSliceE(i interface{}) ([]string, error) {
|
|
|
|
var a []string
|
|
|
|
switch v := i.(type) {
|
|
case []interface{}:
|
|
for _, u := range v {
|
|
a = append(a, ToString(u))
|
|
}
|
|
return a, nil
|
|
case []string:
|
|
return v, nil
|
|
case string:
|
|
return strings.Fields(v), nil
|
|
case interface{}:
|
|
str, err := ToStringE(v)
|
|
if err != nil {
|
|
return a, fmt.Errorf("Unable to Cast %#v to []string", i)
|
|
}
|
|
return []string{str}, nil
|
|
default:
|
|
return a, fmt.Errorf("Unable to Cast %#v to []string", i)
|
|
}
|
|
}
|
|
|
|
// ToIntSliceE casts an empty interface to a []int.
|
|
func ToIntSliceE(i interface{}) ([]int, error) {
|
|
|
|
if i == nil {
|
|
return []int{}, fmt.Errorf("Unable to Cast %#v to []int", i)
|
|
}
|
|
|
|
switch v := i.(type) {
|
|
case []int:
|
|
return v, nil
|
|
}
|
|
|
|
kind := reflect.TypeOf(i).Kind()
|
|
switch kind {
|
|
case reflect.Slice, reflect.Array:
|
|
s := reflect.ValueOf(i)
|
|
a := make([]int, s.Len())
|
|
for j := 0; j < s.Len(); j++ {
|
|
val, err := ToIntE(s.Index(j).Interface())
|
|
if err != nil {
|
|
return []int{}, fmt.Errorf("Unable to Cast %#v to []int", i)
|
|
}
|
|
a[j] = val
|
|
}
|
|
return a, nil
|
|
default:
|
|
return []int{}, fmt.Errorf("Unable to Cast %#v to []int", i)
|
|
}
|
|
}
|
|
|
|
// StringToDate casts an empty interface to a time.Time.
|
|
func StringToDate(s string) (time.Time, error) {
|
|
return parseDateWith(s, []string{
|
|
time.RFC3339,
|
|
"2006-01-02T15:04:05", // iso8601 without timezone
|
|
time.RFC1123Z,
|
|
time.RFC1123,
|
|
time.RFC822Z,
|
|
time.RFC822,
|
|
time.ANSIC,
|
|
time.UnixDate,
|
|
time.RubyDate,
|
|
"2006-01-02 15:04:05Z07:00",
|
|
"02 Jan 06 15:04 MST",
|
|
"2006-01-02",
|
|
"02 Jan 2006",
|
|
"2006-01-02 15:04:05 -07:00",
|
|
"2006-01-02 15:04:05 -0700",
|
|
"2006-01-02 15:04:05",
|
|
})
|
|
}
|
|
|
|
func parseDateWith(s string, dates []string) (d time.Time, e error) {
|
|
for _, dateType := range dates {
|
|
if d, e = time.Parse(dateType, s); e == nil {
|
|
return
|
|
}
|
|
}
|
|
return d, fmt.Errorf("Unable to parse date: %s", s)
|
|
}
|