Procházet zdrojové kódy

dline/kline: Allow year/month/day durations

tags/v0.7.0
Daniel Oaks před 7 roky
rodič
revize
0c86c454c2
4 změnil soubory, kde provedl 189 přidání a 4 odebrání
  1. 183
    0
      irc/custime/parseduration.go
  2. 2
    1
      irc/dline.go
  3. 2
    2
      irc/help.go
  4. 2
    1
      irc/kline.go

+ 183
- 0
irc/custime/parseduration.go Zobrazit soubor

1
+// Copyright 2010 The Go Authors. All rights reserved.
2
+
3
+package custime
4
+
5
+import (
6
+	"errors"
7
+	"time"
8
+)
9
+
10
+// see https://github.com/golang/go/blob/7ad512e7ffe576c4894ea84b02e954846fbda643/src/time/format.go#L1251
11
+
12
+// This is a forked version of the ParseDuration function that also handles days/months/years
13
+
14
+var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
15
+
16
+// leadingInt consumes the leading [0-9]* from s.
17
+func leadingInt(s string) (x int64, rem string, err error) {
18
+	i := 0
19
+	for ; i < len(s); i++ {
20
+		c := s[i]
21
+		if c < '0' || c > '9' {
22
+			break
23
+		}
24
+		if x > (1<<63-1)/10 {
25
+			// overflow
26
+			return 0, "", errLeadingInt
27
+		}
28
+		x = x*10 + int64(c) - '0'
29
+		if x < 0 {
30
+			// overflow
31
+			return 0, "", errLeadingInt
32
+		}
33
+	}
34
+	return x, s[i:], nil
35
+}
36
+
37
+// leadingFraction consumes the leading [0-9]* from s.
38
+// It is used only for fractions, so does not return an error on overflow,
39
+// it just stops accumulating precision.
40
+func leadingFraction(s string) (x int64, scale float64, rem string) {
41
+	i := 0
42
+	scale = 1
43
+	overflow := false
44
+	for ; i < len(s); i++ {
45
+		c := s[i]
46
+		if c < '0' || c > '9' {
47
+			break
48
+		}
49
+		if overflow {
50
+			continue
51
+		}
52
+		if x > (1<<63-1)/10 {
53
+			// It's possible for overflow to give a positive number, so take care.
54
+			overflow = true
55
+			continue
56
+		}
57
+		y := x*10 + int64(c) - '0'
58
+		if y < 0 {
59
+			overflow = true
60
+			continue
61
+		}
62
+		x = y
63
+		scale *= 10
64
+	}
65
+	return x, scale, s[i:]
66
+}
67
+
68
+var unitMap = map[string]int64{
69
+	"ns": int64(time.Nanosecond),
70
+	"us": int64(time.Microsecond),
71
+	"µs": int64(time.Microsecond), // U+00B5 = micro symbol
72
+	"μs": int64(time.Microsecond), // U+03BC = Greek letter mu
73
+	"ms": int64(time.Millisecond),
74
+	"s":  int64(time.Second),
75
+	"m":  int64(time.Minute),
76
+	"h":  int64(time.Hour),
77
+	"d":  int64(time.Hour * 24),
78
+	"mo": int64(time.Hour * 24 * 30),
79
+	"y":  int64(time.Hour * 24 * 265),
80
+}
81
+
82
+// ParseDuration parses a duration string.
83
+// A duration string is a possibly signed sequence of
84
+// decimal numbers, each with optional fraction and a unit suffix,
85
+// such as "300ms", "-1.5h" or "2h45m".
86
+// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
87
+func ParseDuration(s string) (time.Duration, error) {
88
+	// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
89
+	orig := s
90
+	var d int64
91
+	neg := false
92
+
93
+	// Consume [-+]?
94
+	if s != "" {
95
+		c := s[0]
96
+		if c == '-' || c == '+' {
97
+			neg = c == '-'
98
+			s = s[1:]
99
+		}
100
+	}
101
+	// Special case: if all that is left is "0", this is zero.
102
+	if s == "0" {
103
+		return 0, nil
104
+	}
105
+	if s == "" {
106
+		return 0, errors.New("time: invalid duration " + orig)
107
+	}
108
+	for s != "" {
109
+		var (
110
+			v, f  int64       // integers before, after decimal point
111
+			scale float64 = 1 // value = v + f/scale
112
+		)
113
+
114
+		var err error
115
+
116
+		// The next character must be [0-9.]
117
+		if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
118
+			return 0, errors.New("time: invalid duration " + orig)
119
+		}
120
+		// Consume [0-9]*
121
+		pl := len(s)
122
+		v, s, err = leadingInt(s)
123
+		if err != nil {
124
+			return 0, errors.New("time: invalid duration " + orig)
125
+		}
126
+		pre := pl != len(s) // whether we consumed anything before a period
127
+
128
+		// Consume (\.[0-9]*)?
129
+		post := false
130
+		if s != "" && s[0] == '.' {
131
+			s = s[1:]
132
+			pl := len(s)
133
+			f, scale, s = leadingFraction(s)
134
+			post = pl != len(s)
135
+		}
136
+		if !pre && !post {
137
+			// no digits (e.g. ".s" or "-.s")
138
+			return 0, errors.New("time: invalid duration " + orig)
139
+		}
140
+
141
+		// Consume unit.
142
+		i := 0
143
+		for ; i < len(s); i++ {
144
+			c := s[i]
145
+			if c == '.' || '0' <= c && c <= '9' {
146
+				break
147
+			}
148
+		}
149
+		if i == 0 {
150
+			return 0, errors.New("time: missing unit in duration " + orig)
151
+		}
152
+		u := s[:i]
153
+		s = s[i:]
154
+		unit, ok := unitMap[u]
155
+		if !ok {
156
+			return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
157
+		}
158
+		if v > (1<<63-1)/unit {
159
+			// overflow
160
+			return 0, errors.New("time: invalid duration " + orig)
161
+		}
162
+		v *= unit
163
+		if f > 0 {
164
+			// float64 is needed to be nanosecond accurate for fractions of hours.
165
+			// v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
166
+			v += int64(float64(f) * (float64(unit) / scale))
167
+			if v < 0 {
168
+				// overflow
169
+				return 0, errors.New("time: invalid duration " + orig)
170
+			}
171
+		}
172
+		d += v
173
+		if d < 0 {
174
+			// overflow
175
+			return 0, errors.New("time: invalid duration " + orig)
176
+		}
177
+	}
178
+
179
+	if neg {
180
+		d = -d
181
+	}
182
+	return time.Duration(d), nil
183
+}

+ 2
- 1
irc/dline.go Zobrazit soubor

14
 	"encoding/json"
14
 	"encoding/json"
15
 
15
 
16
 	"github.com/DanielOaks/girc-go/ircmsg"
16
 	"github.com/DanielOaks/girc-go/ircmsg"
17
+	"github.com/DanielOaks/oragono/irc/custime"
17
 	"github.com/tidwall/buntdb"
18
 	"github.com/tidwall/buntdb"
18
 )
19
 )
19
 
20
 
201
 	}
202
 	}
202
 
203
 
203
 	// duration
204
 	// duration
204
-	duration, err := time.ParseDuration(msg.Params[currentArg])
205
+	duration, err := custime.ParseDuration(msg.Params[currentArg])
205
 	durationIsUsed := err == nil
206
 	durationIsUsed := err == nil
206
 	if durationIsUsed {
207
 	if durationIsUsed {
207
 		currentArg++
208
 		currentArg++

+ 2
- 2
irc/help.go Zobrazit soubor

112
 from. If "MYSELF" is not given, trying to DLINE yourself will result in an error.
112
 from. If "MYSELF" is not given, trying to DLINE yourself will result in an error.
113
 
113
 
114
 [duration] can be of the following forms:
114
 [duration] can be of the following forms:
115
-	10h 8m 13s
115
+	1y 12mo 31d 10h 8m 13s
116
 
116
 
117
 <net> is specified in typical CIDR notation. For example:
117
 <net> is specified in typical CIDR notation. For example:
118
 	127.0.0.1/8
118
 	127.0.0.1/8
172
 from. If "MYSELF" is not given, trying to KLINE yourself will result in an error.
172
 from. If "MYSELF" is not given, trying to KLINE yourself will result in an error.
173
 
173
 
174
 [duration] can be of the following forms:
174
 [duration] can be of the following forms:
175
-	10h 8m 13s
175
+	1y 12mo 31d 10h 8m 13s
176
 
176
 
177
 <mask> is specified in typical IRC format. For example:
177
 <mask> is specified in typical IRC format. For example:
178
 	dan
178
 	dan

+ 2
- 1
irc/kline.go Zobrazit soubor

11
 
11
 
12
 	"github.com/DanielOaks/girc-go/ircmatch"
12
 	"github.com/DanielOaks/girc-go/ircmatch"
13
 	"github.com/DanielOaks/girc-go/ircmsg"
13
 	"github.com/DanielOaks/girc-go/ircmsg"
14
+	"github.com/DanielOaks/oragono/irc/custime"
14
 	"github.com/tidwall/buntdb"
15
 	"github.com/tidwall/buntdb"
15
 )
16
 )
16
 
17
 
128
 	}
129
 	}
129
 
130
 
130
 	// duration
131
 	// duration
131
-	duration, err := time.ParseDuration(msg.Params[currentArg])
132
+	duration, err := custime.ParseDuration(msg.Params[currentArg])
132
 	durationIsUsed := err == nil
133
 	durationIsUsed := err == nil
133
 	if durationIsUsed {
134
 	if durationIsUsed {
134
 		currentArg++
135
 		currentArg++

Načítá se…
Zrušit
Uložit