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  }