github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/parseduration.go (about)

     1  package fs
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  // Duration is a time.Duration with some more parsing options
    12  type Duration time.Duration
    13  
    14  // DurationOff is the default value for flags which can be turned off
    15  const DurationOff = Duration((1 << 63) - 1)
    16  
    17  // Turn Duration into a string
    18  func (d Duration) String() string {
    19  	if d == DurationOff {
    20  		return "off"
    21  	}
    22  	for i := len(ageSuffixes) - 2; i >= 0; i-- {
    23  		ageSuffix := &ageSuffixes[i]
    24  		if math.Abs(float64(d)) >= float64(ageSuffix.Multiplier) {
    25  			timeUnits := float64(d) / float64(ageSuffix.Multiplier)
    26  			return strconv.FormatFloat(timeUnits, 'f', -1, 64) + ageSuffix.Suffix
    27  		}
    28  	}
    29  	return time.Duration(d).String()
    30  }
    31  
    32  // IsSet returns if the duration is != DurationOff
    33  func (d Duration) IsSet() bool {
    34  	return d != DurationOff
    35  }
    36  
    37  // We use time conventions
    38  var ageSuffixes = []struct {
    39  	Suffix     string
    40  	Multiplier time.Duration
    41  }{
    42  	{Suffix: "d", Multiplier: time.Hour * 24},
    43  	{Suffix: "w", Multiplier: time.Hour * 24 * 7},
    44  	{Suffix: "M", Multiplier: time.Hour * 24 * 30},
    45  	{Suffix: "y", Multiplier: time.Hour * 24 * 365},
    46  
    47  	// Default to second
    48  	{Suffix: "", Multiplier: time.Second},
    49  }
    50  
    51  // ParseDuration parses a duration string. Accept ms|s|m|h|d|w|M|y suffixes. Defaults to second if not provided
    52  func ParseDuration(age string) (time.Duration, error) {
    53  	var period float64
    54  
    55  	if age == "off" {
    56  		return time.Duration(DurationOff), nil
    57  	}
    58  
    59  	// Attempt to parse as a time.Duration first
    60  	d, err := time.ParseDuration(age)
    61  	if err == nil {
    62  		return d, nil
    63  	}
    64  
    65  	for _, ageSuffix := range ageSuffixes {
    66  		if strings.HasSuffix(age, ageSuffix.Suffix) {
    67  			numberString := age[:len(age)-len(ageSuffix.Suffix)]
    68  			var err error
    69  			period, err = strconv.ParseFloat(numberString, 64)
    70  			if err != nil {
    71  				return time.Duration(0), err
    72  			}
    73  			period *= float64(ageSuffix.Multiplier)
    74  			break
    75  		}
    76  	}
    77  
    78  	return time.Duration(period), nil
    79  }
    80  
    81  // ReadableString parses d into a human readable duration.
    82  // Based on https://github.com/hako/durafmt
    83  func (d Duration) ReadableString() string {
    84  	switch d {
    85  	case DurationOff:
    86  		return "off"
    87  	case 0:
    88  		return "0s"
    89  	}
    90  
    91  	readableString := ""
    92  
    93  	// Check for minus durations.
    94  	if d < 0 {
    95  		readableString += "-"
    96  	}
    97  
    98  	duration := time.Duration(math.Abs(float64(d)))
    99  
   100  	// Convert duration.
   101  	seconds := int64(duration.Seconds()) % 60
   102  	minutes := int64(duration.Minutes()) % 60
   103  	hours := int64(duration.Hours()) % 24
   104  	days := int64(duration/(24*time.Hour)) % 365 % 7
   105  
   106  	// Edge case between 364 and 365 days.
   107  	// We need to calculate weeks from what is left from years
   108  	leftYearDays := int64(duration/(24*time.Hour)) % 365
   109  	weeks := leftYearDays / 7
   110  	if leftYearDays >= 364 && leftYearDays < 365 {
   111  		weeks = 52
   112  	}
   113  
   114  	years := int64(duration/(24*time.Hour)) / 365
   115  	milliseconds := int64(duration/time.Millisecond) -
   116  		(seconds * 1000) - (minutes * 60000) - (hours * 3600000) -
   117  		(days * 86400000) - (weeks * 604800000) - (years * 31536000000)
   118  
   119  	// Create a map of the converted duration time.
   120  	durationMap := map[string]int64{
   121  		"ms": milliseconds,
   122  		"s":  seconds,
   123  		"m":  minutes,
   124  		"h":  hours,
   125  		"d":  days,
   126  		"w":  weeks,
   127  		"y":  years,
   128  	}
   129  
   130  	// Construct duration string.
   131  	for _, u := range [...]string{"y", "w", "d", "h", "m", "s", "ms"} {
   132  		v := durationMap[u]
   133  		strval := strconv.FormatInt(v, 10)
   134  		if v == 0 {
   135  			continue
   136  		}
   137  		readableString += strval + u
   138  	}
   139  
   140  	return readableString
   141  }
   142  
   143  // Set a Duration
   144  func (d *Duration) Set(s string) error {
   145  	duration, err := ParseDuration(s)
   146  	if err != nil {
   147  		return err
   148  	}
   149  	*d = Duration(duration)
   150  	return nil
   151  }
   152  
   153  // Type of the value
   154  func (d Duration) Type() string {
   155  	return "Duration"
   156  }
   157  
   158  // Scan implements the fmt.Scanner interface
   159  func (d *Duration) Scan(s fmt.ScanState, ch rune) error {
   160  	token, err := s.Token(true, nil)
   161  	if err != nil {
   162  		return err
   163  	}
   164  	return d.Set(string(token))
   165  }