time.go (4285B)
1 package sdp 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "strconv" 8 "strings" 9 "time" 10 ) 11 12 // number of seconds from the zero time used in SDP; 1900-01-01T00:00Z 13 // to the Unix epoch. 14 const sinceTimeZero = 2208988800 15 16 func parseTimes(s string) ([2]time.Time, error) { 17 var times [2]time.Time 18 fields := strings.Fields(s) 19 if len(fields) != 2 { 20 return times, fmt.Errorf("bad number of fields %d: need 2", len(fields)) 21 } 22 start, err := strconv.Atoi(fields[0]) 23 if err != nil { 24 return times, fmt.Errorf("parse start time: %w", err) 25 } 26 if start != 0 { 27 times[0] = time.Unix(int64(start-sinceTimeZero), 0).UTC() 28 } 29 end, err := strconv.Atoi(fields[1]) 30 if err != nil { 31 return times, fmt.Errorf("parse end time: %w", err) 32 } 33 if end != 0 { 34 times[1] = time.Unix(int64(end-sinceTimeZero), 0).UTC() 35 } 36 return times, nil 37 } 38 39 // Repeat represents a session's repetition cycle as described in 40 // RFC 8866 section 5.10. 41 type Repeat struct { 42 Interval time.Duration // duration between each repetition cycle 43 Active time.Duration // planned duration of each session 44 Offsets []time.Duration // duration(s) between each session 45 } 46 47 func (rp *Repeat) String() string { 48 interval := math.Round(rp.Interval.Round(time.Second).Seconds()) 49 active := math.Round(rp.Active.Round(time.Second).Seconds()) 50 s := fmt.Sprintf("r=%d %d ", int(interval), int(active)) 51 if len(rp.Offsets) > 0 { 52 ss := make([]string, len(rp.Offsets)) 53 for i := range rp.Offsets { 54 ss[i] = fmt.Sprintf("%f", rp.Offsets[i].Round(time.Second).Seconds()) 55 } 56 s += strings.Join(ss, " ") 57 } 58 return strings.TrimSpace(s) 59 } 60 61 func parseRepeat(s string) (Repeat, error) { 62 // guard against negative durations, decimals. 63 // these are valid for time.ParseDuration, but not for our Repeat. 64 if strings.Contains(s, "-") || strings.Contains(s, ".") { 65 return Repeat{}, errors.New("invalid duration") 66 } 67 68 fields := strings.Fields(s) 69 if len(fields) < 3 { 70 return Repeat{}, fmt.Errorf("short line: have %d, want at least %d fields", len(fields), 3) 71 } 72 73 var repeat Repeat 74 var err error 75 repeat.Interval, err = parseDuration(fields[0]) 76 if err != nil { 77 return Repeat{}, fmt.Errorf("parse interval %s: %w", fields[0], err) 78 } 79 repeat.Active, err = parseDuration(fields[1]) 80 if err != nil { 81 return Repeat{}, fmt.Errorf("parse active duration %s: %w", fields[1], err) 82 } 83 for _, s := range fields[2:] { 84 offset, err := parseDuration(s) 85 if err != nil { 86 return Repeat{}, fmt.Errorf("parse offset %s: %w", s, err) 87 } 88 repeat.Offsets = append(repeat.Offsets, offset) 89 } 90 return repeat, nil 91 } 92 93 // parseDuration wraps time.ParseDuration to account for 94 // bare integers (seconds) and 95 // the day prefix ("d"). 96 func parseDuration(s string) (time.Duration, error) { 97 // a bare int, like 86400 98 i, err := strconv.Atoi(s) 99 if err == nil { 100 return time.Duration(i) * time.Second, nil 101 } 102 103 // a duration string like 24h 104 dur, err := time.ParseDuration(s) 105 if err == nil { 106 return dur, nil 107 } 108 109 // a duration string with days suffix, like 1d 110 // [0-9]+d 111 if !strings.HasSuffix(s, "d") { 112 return 0, fmt.Errorf("bad duration: expected d suffix for days") 113 } 114 j, err := strconv.Atoi(s[:len(s)-1]) 115 if err != nil { 116 return 0, fmt.Errorf("parse days: %w", err) 117 } 118 return time.Duration(j) * 24 * time.Hour, nil 119 } 120 121 type TimeAdjustment struct { 122 When time.Time 123 Offset time.Duration 124 } 125 126 func (t TimeAdjustment) String() string { 127 return fmt.Sprintf("%d %f", t.When.Unix()+sinceTimeZero, t.Offset.Round(time.Second).Seconds()) 128 } 129 130 func parseAdjustments(line string) ([]TimeAdjustment, error) { 131 fields := strings.Fields(line) 132 if len(fields)%2 != 0 { 133 return nil, fmt.Errorf("odd field count %d", len(fields)) 134 } 135 var adjustments []TimeAdjustment 136 for i := 0; i < len(fields); i += 2 { 137 var adj TimeAdjustment 138 t, err := strconv.Atoi(fields[i]) 139 if err != nil { 140 return nil, fmt.Errorf("time %s: %w", fields[i], err) 141 } 142 adj.When = time.Unix(int64(t-sinceTimeZero), 0).UTC() 143 adj.Offset, err = parseDuration(fields[i+1]) 144 if err != nil { 145 return nil, fmt.Errorf("offset %s: %w", fields[i+1], err) 146 } 147 adjustments = append(adjustments, adj) 148 } 149 return adjustments, nil 150 } 151 152 // Now returns the current SDP timestamp; the number of seconds 153 // since 1900-01-01T00:00Z. 154 func Now() int { 155 return int(time.Now().Unix() + sinceTimeZero) 156 }