github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/common/format.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/errors" 11 ) 12 13 // LastConnection turns the *time.Time returned from the API server 14 // into a user facing string with either exact time or a user friendly 15 // string based on the args. 16 func LastConnection(connectionTime *time.Time, now time.Time, exact bool) string { 17 if connectionTime == nil { 18 return "never connected" 19 } 20 if exact { 21 return connectionTime.String() 22 } 23 return UserFriendlyDuration(*connectionTime, now) 24 } 25 26 // UserFriendlyDuration translates a time in the past into a user 27 // friendly string representation relative to the "now" time argument. 28 func UserFriendlyDuration(when, now time.Time) string { 29 since := now.Sub(when) 30 // if over 24 hours ago, just say the date. 31 if since.Hours() >= 24 { 32 return when.Format("2006-01-02") 33 } 34 if since.Hours() >= 1 { 35 unit := "hours" 36 if int(since.Hours()) == 1 { 37 unit = "hour" 38 } 39 return fmt.Sprintf("%d %s ago", int(since.Hours()), unit) 40 } 41 if since.Minutes() >= 1 { 42 unit := "minutes" 43 if int(since.Minutes()) == 1 { 44 unit = "minute" 45 } 46 return fmt.Sprintf("%d %s ago", int(since.Minutes()), unit) 47 } 48 if since.Seconds() >= 2 { 49 return fmt.Sprintf("%d seconds ago", int(since.Seconds())) 50 } 51 return "just now" 52 } 53 54 // FormatTime returns a string with the local time formatted 55 // in an arbitrary format used for status or and localized tz 56 // or in UTC timezone and format RFC3339 if u is specified. 57 func FormatTime(t *time.Time, formatISO bool) string { 58 if formatISO { 59 // If requested, use ISO time format. 60 // The format we use is RFC3339 without the "T". From the spec: 61 // NOTE: ISO 8601 defines date and time separated by "T". 62 // Applications using this syntax may choose, for the sake of 63 // readability, to specify a full-date and full-time separated by 64 // (say) a space character. 65 return t.UTC().Format("2006-01-02 15:04:05Z") 66 } 67 // Otherwise use local time. 68 return t.Local().Format("02 Jan 2006 15:04:05Z07:00") 69 } 70 71 // ConformYAML ensures all keys of any nested maps are strings. This is 72 // necessary because YAML unmarshals map[interface{}]interface{} in nested 73 // maps, which cannot be serialized by bson. Also, handle []interface{}. 74 // cf. gopkg.in/juju/charm.v4/actions.go cleanse 75 func ConformYAML(input interface{}) (interface{}, error) { 76 switch typedInput := input.(type) { 77 78 case map[string]interface{}: 79 newMap := make(map[string]interface{}) 80 for key, value := range typedInput { 81 newValue, err := ConformYAML(value) 82 if err != nil { 83 return nil, err 84 } 85 newMap[key] = newValue 86 } 87 return newMap, nil 88 89 case map[interface{}]interface{}: 90 newMap := make(map[string]interface{}) 91 for key, value := range typedInput { 92 typedKey, ok := key.(string) 93 if !ok { 94 return nil, errors.New("map keyed with non-string value") 95 } 96 newMap[typedKey] = value 97 } 98 return ConformYAML(newMap) 99 100 case []interface{}: 101 newSlice := make([]interface{}, len(typedInput)) 102 for i, sliceValue := range typedInput { 103 newSliceValue, err := ConformYAML(sliceValue) 104 if err != nil { 105 return nil, errors.New("map keyed with non-string value") 106 } 107 newSlice[i] = newSliceValue 108 } 109 return newSlice, nil 110 111 default: 112 return input, nil 113 } 114 }