github.com/crewjam/saml@v0.4.14/duration.go (about) 1 package saml 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 "strings" 8 "time" 9 ) 10 11 // Duration is a time.Duration that uses the xsd:duration format for text 12 // marshalling and unmarshalling. 13 type Duration time.Duration 14 15 // MarshalText implements the encoding.TextMarshaler interface. 16 func (d Duration) MarshalText() ([]byte, error) { 17 if d == 0 { 18 return nil, nil 19 } 20 21 out := "PT" 22 if d < 0 { 23 d *= -1 24 out = "-" + out 25 } 26 27 h := time.Duration(d) / time.Hour 28 m := time.Duration(d) % time.Hour / time.Minute 29 s := time.Duration(d) % time.Minute / time.Second 30 ns := time.Duration(d) % time.Second 31 if h > 0 { 32 out += fmt.Sprintf("%dH", h) 33 } 34 if m > 0 { 35 out += fmt.Sprintf("%dM", m) 36 } 37 if s > 0 || ns > 0 { 38 out += fmt.Sprintf("%d", s) 39 if ns > 0 { 40 out += strings.TrimRight(fmt.Sprintf(".%09d", ns), "0") 41 } 42 out += "S" 43 } 44 45 return []byte(out), nil 46 } 47 48 const ( 49 day = 24 * time.Hour 50 month = 30 * day // Assumed to be 30 days. 51 year = 365 * day // Assumed to be non-leap year. 52 ) 53 54 var ( 55 durationRegexp = regexp.MustCompile(`^(-?)P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(.+))?$`) 56 durationTimeRegexp = regexp.MustCompile(`^(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?$`) 57 ) 58 59 // UnmarshalText implements the encoding.TextUnmarshaler interface. 60 func (d *Duration) UnmarshalText(text []byte) error { 61 if text == nil { 62 *d = 0 63 return nil 64 } 65 66 var ( 67 out time.Duration 68 sign time.Duration = 1 69 ) 70 match := durationRegexp.FindStringSubmatch(string(text)) 71 if match == nil || strings.Join(match[2:6], "") == "" { 72 return fmt.Errorf("invalid duration (%s)", text) 73 } 74 if match[1] == "-" { 75 sign = -1 76 } 77 if match[2] != "" { 78 y, err := strconv.Atoi(match[2]) 79 if err != nil { 80 return fmt.Errorf("invalid duration years (%s): %s", text, err) 81 } 82 out += time.Duration(y) * year 83 } 84 if match[3] != "" { 85 m, err := strconv.Atoi(match[3]) 86 if err != nil { 87 return fmt.Errorf("invalid duration months (%s): %s", text, err) 88 } 89 out += time.Duration(m) * month 90 } 91 if match[4] != "" { 92 d, err := strconv.Atoi(match[4]) 93 if err != nil { 94 return fmt.Errorf("invalid duration days (%s): %s", text, err) 95 } 96 out += time.Duration(d) * day 97 } 98 if match[5] != "" { 99 match := durationTimeRegexp.FindStringSubmatch(match[5]) 100 if match == nil { 101 return fmt.Errorf("invalid duration (%s)", text) 102 } 103 if match[1] != "" { 104 h, err := strconv.Atoi(match[1]) 105 if err != nil { 106 return fmt.Errorf("invalid duration hours (%s): %s", text, err) 107 } 108 out += time.Duration(h) * time.Hour 109 } 110 if match[2] != "" { 111 m, err := strconv.Atoi(match[2]) 112 if err != nil { 113 return fmt.Errorf("invalid duration minutes (%s): %s", text, err) 114 } 115 out += time.Duration(m) * time.Minute 116 } 117 if match[3] != "" { 118 s, err := strconv.ParseFloat(match[3], 64) 119 if err != nil { 120 return fmt.Errorf("invalid duration seconds (%s): %s", text, err) 121 } 122 out += time.Duration(s * float64(time.Second)) 123 } 124 } 125 126 *d = Duration(sign * out) 127 return nil 128 }