Skip to content

Commit

Permalink
Add support of timestamps in nanoseconds (google#365)
Browse files Browse the repository at this point in the history
* Add support of timestamps in nanoseconds

* Add a test for the function time.now()

* Overload from_timestamp and improve the documentation

* Improve the documentation of the time package
  • Loading branch information
essobedo authored Apr 6, 2021
1 parent 74c10e2 commit f54874d
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 21 deletions.
70 changes: 49 additions & 21 deletions lib/time/time.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
package time
// Copyright 2021 The Bazel Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package time provides time-related constants and functions.
package time // import "go.starlark.net/lib/time"

import (
"fmt"
Expand All @@ -10,16 +15,49 @@ import (
"go.starlark.net/syntax"
)

// Module time is a Starlark module of time-related functions.
// Module time is a Starlark module of time-related functions and constants.
// The module defines the following functions:
//
// from_timestamp(sec, nsec) - Converts the given Unix time corresponding to the number of seconds
// and (optionally) nanoseconds since January 1, 1970 UTC into an object
// of type Time. For more details, refer to https://pkg.go.dev/time#Unix.
//
// is_valid_timezone(loc) - Reports whether loc is a valid time zone name.
//
// now() - Returns the current local time. Applications may replace this function by a deterministic one.
//
// parse_duration(d) - Parses the given duration string. For more details, refer to
// https://pkg.go.dev/time#ParseDuration.
//
// parseTime(x, format, location) - Parses the given time string using a specific time format and location.
// The expected arguments are a time string (mandatory), a time format
// (optional, set to RFC3339 by default, e.g. "2021-03-22T23:20:50.52Z")
// and a name of location (optional, set to UTC by default). For more details,
// refer to https://pkg.go.dev/time#Parse and https://pkg.go.dev/time#ParseInLocation.
//
// time(year, month, day, hour, minute, second, nanosecond, location) - Returns the Time corresponding to
// yyyy-mm-dd hh:mm:ss + nsec nanoseconds
// in the appropriate zone for that time
// in the given location. All the parameters
// are optional.
// The module also defines the following constants:
//
// nanosecond - A duration representing one nanosecond.
// microsecond - A duration representing one microsecond.
// millisecond - A duration representing one millisecond.
// second - A duration representing one second.
// minute - A duration representing one minute.
// hour - A duration representing one hour.
//
var Module = &starlarkstruct.Module{
Name: "time",
Members: starlark.StringDict{
"parse_duration": starlark.NewBuiltin("parse_duration", parseDuration),
"from_timestamp": starlark.NewBuiltin("from_timestamp", fromTimestamp),
"is_valid_timezone": starlark.NewBuiltin("is_valid_timezone", isValidTimezone),
"now": starlark.NewBuiltin("now", now),
"time": starlark.NewBuiltin("time", newTime),
"parse_duration": starlark.NewBuiltin("parse_duration", parseDuration),
"parse_time": starlark.NewBuiltin("parse_time", parseTime),
"from_timestamp": starlark.NewBuiltin("from_timestamp", fromTimestamp),
"time": starlark.NewBuiltin("time", newTime),

"nanosecond": Duration(time.Nanosecond),
"microsecond": Duration(time.Microsecond),
Expand All @@ -35,19 +73,12 @@ var Module = &starlarkstruct.Module{
// Starlark scripts to be fully deterministic.
var NowFunc = time.Now

// Parses the given duration string.
// A duration string is a possibly signed sequence of
// decimal numbers, each with optional fraction and a unit suffix,
// such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
func parseDuration(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var d Duration
err := starlark.UnpackPositionalArgs("parse_duration", args, kwargs, 1, &d)
return d, err
}

// Indicates whether the given name of location is valid or not.
// Returns true if it is valid, false otherwise.
func isValidTimezone(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var s string
if err := starlark.UnpackPositionalArgs("is_valid_timezone", args, kwargs, 1, &s); err != nil {
Expand All @@ -57,9 +88,6 @@ func isValidTimezone(thread *starlark.Thread, _ *starlark.Builtin, args starlark
return starlark.Bool(err == nil), nil
}

// The expected arguments are a time string (mandatory), a time format (optional, set to RFC3339 by default)
// and a name of location (optional set to UTC by default).
// Parses the given time string using a specific time format and location.
func parseTime(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var (
x string
Expand Down Expand Up @@ -89,17 +117,17 @@ func parseTime(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple
return Time(t), nil
}

// Parses the given timestamp string corresponding to
// the total amount of seconds since January 1, 1970 UTC.
func fromTimestamp(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var x int64
if err := starlark.UnpackPositionalArgs("from_timestamp", args, kwargs, 1, &x); err != nil {
var (
sec int64
nsec int64 = 0
)
if err := starlark.UnpackPositionalArgs("from_timestamp", args, kwargs, 1, &sec, &nsec); err != nil {
return nil, err
}
return Time(time.Unix(x, 0)), nil
return Time(time.Unix(sec, nsec)), nil
}

// Generates the current time current time.
func now(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
return Time(NowFunc()), nil
}
Expand Down
3 changes: 3 additions & 0 deletions starlark/testdata/time.star
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
load('assert.star', 'assert')
load('time.star', 'time')

assert.true(time.now() > time.parse_time("2021-03-20T00:00:00Z"))

assert.eq(time.parse_time("2020-06-26T17:38:36Z"), time.from_timestamp(1593193116))
assert.eq(time.parse_time("2020-06-26T17:38:36.123456789", format="2006-01-02T15:04:05.999999999"), time.from_timestamp(1593193116, 123456789))

assert.eq(time.parse_time("1970-01-01T00:00:00Z").unix, 0)
assert.eq(time.parse_time("1970-01-01T00:00:00Z").unix_nano, 0)
Expand Down

0 comments on commit f54874d

Please sign in to comment.