David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 1 | // Copyright 2016 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 4 | |
| 5 | package ptypes |
| 6 | |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 7 | import ( |
| 8 | "errors" |
| 9 | "fmt" |
| 10 | "time" |
| 11 | |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 12 | durationpb "github.com/golang/protobuf/ptypes/duration" |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 13 | ) |
| 14 | |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 15 | // Range of google.protobuf.Duration as specified in duration.proto. |
| 16 | // This is about 10,000 years in seconds. |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 17 | const ( |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 18 | maxSeconds = int64(10000 * 365.25 * 24 * 60 * 60) |
| 19 | minSeconds = -maxSeconds |
| 20 | ) |
| 21 | |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 22 | // Duration converts a durationpb.Duration to a time.Duration. |
| 23 | // Duration returns an error if dur is invalid or overflows a time.Duration. |
| 24 | // |
| 25 | // Deprecated: Call the dur.AsDuration and dur.CheckValid methods instead. |
| 26 | func Duration(dur *durationpb.Duration) (time.Duration, error) { |
| 27 | if err := validateDuration(dur); err != nil { |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 28 | return 0, err |
| 29 | } |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 30 | d := time.Duration(dur.Seconds) * time.Second |
| 31 | if int64(d/time.Second) != dur.Seconds { |
| 32 | return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur) |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 33 | } |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 34 | if dur.Nanos != 0 { |
| 35 | d += time.Duration(dur.Nanos) * time.Nanosecond |
| 36 | if (d < 0) != (dur.Nanos < 0) { |
| 37 | return 0, fmt.Errorf("duration: %v is out of range for time.Duration", dur) |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 38 | } |
| 39 | } |
| 40 | return d, nil |
| 41 | } |
| 42 | |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 43 | // DurationProto converts a time.Duration to a durationpb.Duration. |
| 44 | // |
| 45 | // Deprecated: Call the durationpb.New function instead. |
| 46 | func DurationProto(d time.Duration) *durationpb.Duration { |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 47 | nanos := d.Nanoseconds() |
| 48 | secs := nanos / 1e9 |
| 49 | nanos -= secs * 1e9 |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 50 | return &durationpb.Duration{ |
| 51 | Seconds: int64(secs), |
Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame] | 52 | Nanos: int32(nanos), |
| 53 | } |
| 54 | } |
David K. Bainbridge | bd6b288 | 2021-08-26 13:31:02 +0000 | [diff] [blame] | 55 | |
| 56 | // validateDuration determines whether the durationpb.Duration is valid |
| 57 | // according to the definition in google/protobuf/duration.proto. |
| 58 | // A valid durpb.Duration may still be too large to fit into a time.Duration |
| 59 | // Note that the range of durationpb.Duration is about 10,000 years, |
| 60 | // while the range of time.Duration is about 290 years. |
| 61 | func validateDuration(dur *durationpb.Duration) error { |
| 62 | if dur == nil { |
| 63 | return errors.New("duration: nil Duration") |
| 64 | } |
| 65 | if dur.Seconds < minSeconds || dur.Seconds > maxSeconds { |
| 66 | return fmt.Errorf("duration: %v: seconds out of range", dur) |
| 67 | } |
| 68 | if dur.Nanos <= -1e9 || dur.Nanos >= 1e9 { |
| 69 | return fmt.Errorf("duration: %v: nanos out of range", dur) |
| 70 | } |
| 71 | // Seconds and Nanos must have the same sign, unless d.Nanos is zero. |
| 72 | if (dur.Seconds < 0 && dur.Nanos > 0) || (dur.Seconds > 0 && dur.Nanos < 0) { |
| 73 | return fmt.Errorf("duration: %v: seconds and nanos have different signs", dur) |
| 74 | } |
| 75 | return nil |
| 76 | } |