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 }