1
2
3
4
5
6 package time
7
8 import (
9 "fmt"
10 "sort"
11 "time"
12
13 "go.starlark.net/starlark"
14 "go.starlark.net/starlarkstruct"
15 "go.starlark.net/syntax"
16 )
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 var Module = &starlarkstruct.Module{
53 Name: "time",
54 Members: starlark.StringDict{
55 "from_timestamp": starlark.NewBuiltin("from_timestamp", fromTimestamp),
56 "is_valid_timezone": starlark.NewBuiltin("is_valid_timezone", isValidTimezone),
57 "now": starlark.NewBuiltin("now", now),
58 "parse_duration": starlark.NewBuiltin("parse_duration", parseDuration),
59 "parse_time": starlark.NewBuiltin("parse_time", parseTime),
60 "time": starlark.NewBuiltin("time", newTime),
61
62 "nanosecond": Duration(time.Nanosecond),
63 "microsecond": Duration(time.Microsecond),
64 "millisecond": Duration(time.Millisecond),
65 "second": Duration(time.Second),
66 "minute": Duration(time.Minute),
67 "hour": Duration(time.Hour),
68 },
69 }
70
71
72
73
74 var NowFunc = time.Now
75
76 func parseDuration(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
77 var d Duration
78 err := starlark.UnpackPositionalArgs("parse_duration", args, kwargs, 1, &d)
79 return d, err
80 }
81
82 func isValidTimezone(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
83 var s string
84 if err := starlark.UnpackPositionalArgs("is_valid_timezone", args, kwargs, 1, &s); err != nil {
85 return nil, err
86 }
87 _, err := time.LoadLocation(s)
88 return starlark.Bool(err == nil), nil
89 }
90
91 func parseTime(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
92 var (
93 x string
94 location = "UTC"
95 format = time.RFC3339
96 )
97 if err := starlark.UnpackArgs("parse_time", args, kwargs, "x", &x, "format?", &format, "location?", &location); err != nil {
98 return nil, err
99 }
100
101 if location == "UTC" {
102 t, err := time.Parse(format, x)
103 if err != nil {
104 return nil, err
105 }
106 return Time(t), nil
107 }
108
109 loc, err := time.LoadLocation(location)
110 if err != nil {
111 return nil, err
112 }
113 t, err := time.ParseInLocation(format, x, loc)
114 if err != nil {
115 return nil, err
116 }
117 return Time(t), nil
118 }
119
120 func fromTimestamp(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
121 var (
122 sec int64
123 nsec int64 = 0
124 )
125 if err := starlark.UnpackPositionalArgs("from_timestamp", args, kwargs, 1, &sec, &nsec); err != nil {
126 return nil, err
127 }
128 return Time(time.Unix(sec, nsec)), nil
129 }
130
131 func now(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
132 return Time(NowFunc()), nil
133 }
134
135
136 type Duration time.Duration
137
138
139 var _ starlark.Unpacker = (*Duration)(nil)
140
141
142 func (d *Duration) Unpack(v starlark.Value) error {
143 switch x := v.(type) {
144 case Duration:
145 *d = x
146 return nil
147 case starlark.String:
148 dur, err := time.ParseDuration(string(x))
149 if err != nil {
150 return err
151 }
152
153 *d = Duration(dur)
154 return nil
155 }
156
157 return fmt.Errorf("got %s, want a duration, string, or int", v.Type())
158 }
159
160
161 func (d Duration) String() string { return time.Duration(d).String() }
162
163
164 func (d Duration) Type() string { return "time.duration" }
165
166
167
168 func (d Duration) Freeze() {}
169
170
171
172 func (d Duration) Hash() (uint32, error) {
173 return uint32(d) ^ uint32(int64(d)>>32), nil
174 }
175
176
177 func (d Duration) Truth() starlark.Bool { return d != 0 }
178
179
180
181 func (d Duration) Attr(name string) (starlark.Value, error) {
182 switch name {
183 case "hours":
184 return starlark.Float(time.Duration(d).Hours()), nil
185 case "minutes":
186 return starlark.Float(time.Duration(d).Minutes()), nil
187 case "seconds":
188 return starlark.Float(time.Duration(d).Seconds()), nil
189 case "milliseconds":
190 return starlark.MakeInt64(time.Duration(d).Milliseconds()), nil
191 case "microseconds":
192 return starlark.MakeInt64(time.Duration(d).Microseconds()), nil
193 case "nanoseconds":
194 return starlark.MakeInt64(time.Duration(d).Nanoseconds()), nil
195 }
196 return nil, fmt.Errorf("unrecognized %s attribute %q", d.Type(), name)
197 }
198
199
200
201 func (d Duration) AttrNames() []string {
202 return []string{
203 "hours",
204 "minutes",
205 "seconds",
206 "milliseconds",
207 "microseconds",
208 "nanoseconds",
209 }
210 }
211
212
213
214 func (d Duration) Cmp(v starlark.Value, depth int) (int, error) {
215 if x, y := d, v.(Duration); x < y {
216 return -1, nil
217 } else if x > y {
218 return 1, nil
219 }
220 return 0, nil
221 }
222
223
224
225
226
227
228
229
230
231
232
233 func (d Duration) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) {
234 x := time.Duration(d)
235
236 switch op {
237 case syntax.PLUS:
238 switch y := y.(type) {
239 case Duration:
240 return Duration(x + time.Duration(y)), nil
241 case Time:
242 return Time(time.Time(y).Add(x)), nil
243 }
244
245 case syntax.MINUS:
246 switch y := y.(type) {
247 case Duration:
248 return Duration(x - time.Duration(y)), nil
249 }
250
251 case syntax.SLASH:
252 switch y := y.(type) {
253 case Duration:
254 if y == 0 {
255 return nil, fmt.Errorf("%s division by zero", d.Type())
256 }
257 return starlark.Float(x.Nanoseconds()) / starlark.Float(time.Duration(y).Nanoseconds()), nil
258 case starlark.Int:
259 if side == starlark.Right {
260 return nil, fmt.Errorf("unsupported operation")
261 }
262 i, ok := y.Int64()
263 if !ok {
264 return nil, fmt.Errorf("int value out of range (want signed 64-bit value)")
265 }
266 if i == 0 {
267 return nil, fmt.Errorf("%s division by zero", d.Type())
268 }
269 return d / Duration(i), nil
270 case starlark.Float:
271 f := float64(y)
272 if f == 0 {
273 return nil, fmt.Errorf("%s division by zero", d.Type())
274 }
275 return Duration(float64(x.Nanoseconds()) / f), nil
276 }
277
278 case syntax.SLASHSLASH:
279 switch y := y.(type) {
280 case Duration:
281 if y == 0 {
282 return nil, fmt.Errorf("%s division by zero", d.Type())
283 }
284 return starlark.MakeInt64(x.Nanoseconds() / time.Duration(y).Nanoseconds()), nil
285 }
286
287 case syntax.STAR:
288 switch y := y.(type) {
289 case starlark.Int:
290 i, ok := y.Int64()
291 if !ok {
292 return nil, fmt.Errorf("int value out of range (want signed 64-bit value)")
293 }
294 return d * Duration(i), nil
295 }
296 }
297
298 return nil, nil
299 }
300
301
302 type Time time.Time
303
304 func newTime(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
305 var (
306 year, month, day, hour, min, sec, nsec int
307 loc string
308 )
309 if err := starlark.UnpackArgs("time", args, kwargs,
310 "year?", &year,
311 "month?", &month,
312 "day?", &day,
313 "hour?", &hour,
314 "minute?", &min,
315 "second?", &sec,
316 "nanosecond?", &nsec,
317 "location?", &loc,
318 ); err != nil {
319 return nil, err
320 }
321 if len(args) > 0 {
322 return nil, fmt.Errorf("time: unexpected positional arguments")
323 }
324 location, err := time.LoadLocation(loc)
325 if err != nil {
326 return nil, err
327 }
328 return Time(time.Date(year, time.Month(month), day, hour, min, sec, nsec, location)), nil
329 }
330
331
332
333 func (t Time) String() string { return time.Time(t).String() }
334
335
336 func (t Time) Type() string { return "time.time" }
337
338
339
340 func (t Time) Freeze() {}
341
342
343
344 func (t Time) Hash() (uint32, error) {
345 return uint32(time.Time(t).UnixNano()) ^ uint32(int64(time.Time(t).UnixNano())>>32), nil
346 }
347
348
349
350 func (t Time) Truth() starlark.Bool { return !starlark.Bool(time.Time(t).IsZero()) }
351
352
353
354 func (t Time) Attr(name string) (starlark.Value, error) {
355 switch name {
356 case "year":
357 return starlark.MakeInt(time.Time(t).Year()), nil
358 case "month":
359 return starlark.MakeInt(int(time.Time(t).Month())), nil
360 case "day":
361 return starlark.MakeInt(time.Time(t).Day()), nil
362 case "hour":
363 return starlark.MakeInt(time.Time(t).Hour()), nil
364 case "minute":
365 return starlark.MakeInt(time.Time(t).Minute()), nil
366 case "second":
367 return starlark.MakeInt(time.Time(t).Second()), nil
368 case "nanosecond":
369 return starlark.MakeInt(time.Time(t).Nanosecond()), nil
370 case "unix":
371 return starlark.MakeInt64(time.Time(t).Unix()), nil
372 case "unix_nano":
373 return starlark.MakeInt64(time.Time(t).UnixNano()), nil
374 }
375 return builtinAttr(t, name, timeMethods)
376 }
377
378
379
380 func (t Time) AttrNames() []string {
381 return append(builtinAttrNames(timeMethods),
382 "year",
383 "month",
384 "day",
385 "hour",
386 "minute",
387 "second",
388 "nanosecond",
389 "unix",
390 "unix_nano",
391 )
392 }
393
394
395
396 func (t Time) Cmp(yV starlark.Value, depth int) (int, error) {
397 x := time.Time(t)
398 y := time.Time(yV.(Time))
399 if x.Before(y) {
400 return -1, nil
401 } else if x.After(y) {
402 return 1, nil
403 }
404 return 0, nil
405 }
406
407
408
409
410
411
412 func (t Time) Binary(op syntax.Token, y starlark.Value, side starlark.Side) (starlark.Value, error) {
413 x := time.Time(t)
414
415 switch op {
416 case syntax.PLUS:
417 switch y := y.(type) {
418 case Duration:
419 return Time(x.Add(time.Duration(y))), nil
420 }
421 case syntax.MINUS:
422 switch y := y.(type) {
423 case Duration:
424 return Time(x.Add(time.Duration(-y))), nil
425 case Time:
426
427 return Duration(x.Sub(time.Time(y))), nil
428 }
429 }
430
431 return nil, nil
432 }
433
434 var timeMethods = map[string]builtinMethod{
435 "in_location": timeIn,
436 "format": timeFormat,
437 }
438
439 func timeFormat(fnname string, recV starlark.Value, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
440 var x string
441 if err := starlark.UnpackPositionalArgs("format", args, kwargs, 1, &x); err != nil {
442 return nil, err
443 }
444
445 recv := time.Time(recV.(Time))
446 return starlark.String(recv.Format(x)), nil
447 }
448
449 func timeIn(fnname string, recV starlark.Value, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
450 var x string
451 if err := starlark.UnpackPositionalArgs("in_location", args, kwargs, 1, &x); err != nil {
452 return nil, err
453 }
454 loc, err := time.LoadLocation(x)
455 if err != nil {
456 return nil, err
457 }
458
459 recv := time.Time(recV.(Time))
460 return Time(recv.In(loc)), nil
461 }
462
463 type builtinMethod func(fnname string, recv starlark.Value, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)
464
465 func builtinAttr(recv starlark.Value, name string, methods map[string]builtinMethod) (starlark.Value, error) {
466 method := methods[name]
467 if method == nil {
468 return nil, nil
469 }
470
471
472 impl := func(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
473 return method(b.Name(), b.Receiver(), args, kwargs)
474 }
475 return starlark.NewBuiltin(name, impl).BindReceiver(recv), nil
476 }
477
478 func builtinAttrNames(methods map[string]builtinMethod) []string {
479 names := make([]string, 0, len(methods))
480 for name := range methods {
481 names = append(names, name)
482 }
483 sort.Strings(names)
484 return names
485 }
486
View as plain text