github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  // FormatTimeAsTimestamp formats a time.Time to a given machine readable format.
    72  // Returns a string with the local time formatted or uses the localized tz or
    73  // in UTC timezone.
    74  // The difference between this and `common.FormatTime` is that this version
    75  // drops the date part of the time.Time.
    76  func FormatTimeAsTimestamp(t *time.Time, formatISO bool) string {
    77  	if formatISO {
    78  		return t.UTC().Format("15:04:05")
    79  	}
    80  	return t.Local().Format("15:04:05Z07:00")
    81  }
    82  
    83  // ConformYAML ensures all keys of any nested maps are strings.  This is
    84  // necessary because YAML unmarshals map[interface{}]interface{} in nested
    85  // maps, which cannot be serialized by bson. Also, handle []interface{}.
    86  // cf. gopkg.in/juju/charm.v4/actions.go cleanse
    87  func ConformYAML(input interface{}) (interface{}, error) {
    88  	switch typedInput := input.(type) {
    89  
    90  	case map[string]interface{}:
    91  		newMap := make(map[string]interface{})
    92  		for key, value := range typedInput {
    93  			newValue, err := ConformYAML(value)
    94  			if err != nil {
    95  				return nil, err
    96  			}
    97  			newMap[key] = newValue
    98  		}
    99  		return newMap, nil
   100  
   101  	case map[interface{}]interface{}:
   102  		newMap := make(map[string]interface{})
   103  		for key, value := range typedInput {
   104  			typedKey, ok := key.(string)
   105  			if !ok {
   106  				return nil, errors.New("map keyed with non-string value")
   107  			}
   108  			newMap[typedKey] = value
   109  		}
   110  		return ConformYAML(newMap)
   111  
   112  	case []interface{}:
   113  		newSlice := make([]interface{}, len(typedInput))
   114  		for i, sliceValue := range typedInput {
   115  			newSliceValue, err := ConformYAML(sliceValue)
   116  			if err != nil {
   117  				return nil, errors.New("map keyed with non-string value")
   118  			}
   119  			newSlice[i] = newSliceValue
   120  		}
   121  		return newSlice, nil
   122  
   123  	default:
   124  		return input, nil
   125  	}
   126  }