k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/validation/strfmt/duration.go (about)

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package strfmt
    16  
    17  import (
    18  	"encoding/json"
    19  	"fmt"
    20  	"regexp"
    21  	"strconv"
    22  	"strings"
    23  	"time"
    24  )
    25  
    26  func init() {
    27  	d := Duration(0)
    28  	// register this format in the default registry
    29  	Default.Add("duration", &d, IsDuration)
    30  }
    31  
    32  var (
    33  	timeUnits = [][]string{
    34  		{"ns", "nano"},
    35  		{"us", "µs", "micro"},
    36  		{"ms", "milli"},
    37  		{"s", "sec"},
    38  		{"m", "min"},
    39  		{"h", "hr", "hour"},
    40  		{"d", "day"},
    41  		{"w", "wk", "week"},
    42  	}
    43  
    44  	timeMultiplier = map[string]time.Duration{
    45  		"ns": time.Nanosecond,
    46  		"us": time.Microsecond,
    47  		"ms": time.Millisecond,
    48  		"s":  time.Second,
    49  		"m":  time.Minute,
    50  		"h":  time.Hour,
    51  		"d":  24 * time.Hour,
    52  		"w":  7 * 24 * time.Hour,
    53  	}
    54  
    55  	durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`)
    56  )
    57  
    58  // IsDuration returns true if the provided string is a valid duration
    59  func IsDuration(str string) bool {
    60  	_, err := ParseDuration(str)
    61  	return err == nil
    62  }
    63  
    64  // Duration represents a duration
    65  //
    66  // Duration stores a period of time as a nanosecond count, with the largest
    67  // repesentable duration being approximately 290 years.
    68  //
    69  // swagger:strfmt duration
    70  type Duration time.Duration
    71  
    72  // MarshalText turns this instance into text
    73  func (d Duration) MarshalText() ([]byte, error) {
    74  	return []byte(time.Duration(d).String()), nil
    75  }
    76  
    77  // UnmarshalText hydrates this instance from text
    78  func (d *Duration) UnmarshalText(data []byte) error { // validation is performed later on
    79  	dd, err := ParseDuration(string(data))
    80  	if err != nil {
    81  		return err
    82  	}
    83  	*d = Duration(dd)
    84  	return nil
    85  }
    86  
    87  // ParseDuration parses a duration from a string, compatible with scala duration syntax
    88  func ParseDuration(cand string) (time.Duration, error) {
    89  	if dur, err := time.ParseDuration(cand); err == nil {
    90  		return dur, nil
    91  	}
    92  
    93  	var dur time.Duration
    94  	ok := false
    95  	for _, match := range durationMatcher.FindAllStringSubmatch(cand, -1) {
    96  
    97  		factor, err := strconv.Atoi(match[2]) // converts string to int
    98  		if err != nil {
    99  			return 0, err
   100  		}
   101  		unit := strings.ToLower(strings.TrimSpace(match[3]))
   102  
   103  		for _, variants := range timeUnits {
   104  			last := len(variants) - 1
   105  			multiplier := timeMultiplier[variants[0]]
   106  
   107  			for i, variant := range variants {
   108  				if (last == i && strings.HasPrefix(unit, variant)) || strings.EqualFold(variant, unit) {
   109  					ok = true
   110  					dur += time.Duration(factor) * multiplier
   111  				}
   112  			}
   113  		}
   114  	}
   115  
   116  	if ok {
   117  		return dur, nil
   118  	}
   119  	return 0, fmt.Errorf("unable to parse %s as duration", cand)
   120  }
   121  
   122  // Scan reads a Duration value from database driver type.
   123  func (d *Duration) Scan(raw interface{}) error {
   124  	switch v := raw.(type) {
   125  	// TODO: case []byte: // ?
   126  	case int64:
   127  		*d = Duration(v)
   128  	case float64:
   129  		*d = Duration(int64(v))
   130  	case nil:
   131  		*d = Duration(0)
   132  	default:
   133  		return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v", v)
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  // String converts this duration to a string
   140  func (d Duration) String() string {
   141  	return time.Duration(d).String()
   142  }
   143  
   144  // MarshalJSON returns the Duration as JSON
   145  func (d Duration) MarshalJSON() ([]byte, error) {
   146  	return json.Marshal(time.Duration(d).String())
   147  }
   148  
   149  // UnmarshalJSON sets the Duration from JSON
   150  func (d *Duration) UnmarshalJSON(data []byte) error {
   151  	if string(data) == jsonNull {
   152  		return nil
   153  	}
   154  
   155  	var dstr string
   156  	if err := json.Unmarshal(data, &dstr); err != nil {
   157  		return err
   158  	}
   159  	tt, err := ParseDuration(dstr)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	*d = Duration(tt)
   164  	return nil
   165  }
   166  
   167  // DeepCopyInto copies the receiver and writes its value into out.
   168  func (d *Duration) DeepCopyInto(out *Duration) {
   169  	*out = *d
   170  }
   171  
   172  // DeepCopy copies the receiver into a new Duration.
   173  func (d *Duration) DeepCopy() *Duration {
   174  	if d == nil {
   175  		return nil
   176  	}
   177  	out := new(Duration)
   178  	d.DeepCopyInto(out)
   179  	return out
   180  }