Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add time.weekday builtin #800

Merged
merged 2 commits into from
Jun 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ast/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ var DefaultBuiltins = [...]*Builtin{
ParseDurationNanos,
Date,
Clock,
Weekday,

// Crypto
CryptoX509ParseCertificates,
Expand Down Expand Up @@ -860,6 +861,15 @@ var Clock = &Builtin{
),
}

// Weekday returns the day of the week (Monday, Tuesday, ...) for the nanoseconds since epoch.
var Weekday = &Builtin{
Name: "time.weekday",
Decl: types.NewFunction(
types.Args(types.N),
types.S,
),
}

/**
* Crypto.
*/
Expand Down
1 change: 1 addition & 0 deletions docs/book/language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ The input `string` is a JSON Web Token encoded with JWS Compact Serialization. J
| <span class="opa-keep-it-together">``time.parse_duration_ns(duration, output)``</span> | 1 | ``output`` is ``number`` representing the duration ``duration`` in nanoseconds. See the [Go `time` package documentation](https://golang.org/pkg/time/#ParseDuration) for more details on ``duration``. |
| <span class="opa-keep-it-together">``time.date(ns, [year, month, day])``</span> | 1 | outputs the ``year``, ``month`` (0-12), and ``day`` (0-31) as ``number``s representing the date from the nanoseconds since epoch (``ns``). |
| <span class="opa-keep-it-together">``time.clock(ns, [hour, minute, second])``</span> | 1 | outputs the ``hour``, ``minute`` (0-59), and ``second`` (0-59) as ``number``s representing the time of day for the nanoseconds since epoch (``ns``). |
| <span class="opa-keep-it-together">``time.weekday(ns, day)``</span> | 1 | outputs the ``day`` as ``string`` representing the day of the week for the nanoseconds since epoch (``ns``). |

> Multiple calls to the `time.now_ns` built-in function within a single policy
evaluation query will always return the same value.
Expand Down
39 changes: 23 additions & 16 deletions topdown/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,41 +83,47 @@ func builtinParseDurationNanos(a ast.Value) (ast.Value, error) {
}

func builtinDate(a ast.Value) (ast.Value, error) {

value, err := builtins.NumberOperand(a, 1)
t, err := utcTime(a)
if err != nil {
return nil, err
}

f := builtins.NumberToFloat(value)
i64, acc := f.Int64()
if acc != big.Exact {
return nil, fmt.Errorf("timestamp too big")
}

t := time.Unix(0, i64).UTC()
year, month, day := t.Date()
result := ast.Array{ast.IntNumberTerm(year), ast.IntNumberTerm(int(month)), ast.IntNumberTerm(day)}
return result, nil
}

func builtinClock(a ast.Value) (ast.Value, error) {
t, err := utcTime(a)
if err != nil {
return nil, err
}
hour, minute, second := t.Clock()
result := ast.Array{ast.IntNumberTerm(hour), ast.IntNumberTerm(minute), ast.IntNumberTerm(second)}
return result, nil
}

value, err := builtins.NumberOperand(a, 1)
func builtinWeekday(a ast.Value) (ast.Value, error) {
t, err := utcTime(a)
if err != nil {
return nil, err
}
weekday := t.Weekday().String()
return ast.String(weekday), nil
}

func utcTime(a ast.Value) (time.Time, error) {
value, err := builtins.NumberOperand(a, 1)
if err != nil {
return time.Time{}, err
}

f := builtins.NumberToFloat(value)
i64, acc := f.Int64()
if acc != big.Exact {
return nil, fmt.Errorf("timestamp too big")
return time.Time{}, fmt.Errorf("timestamp too big")
}

t := time.Unix(0, i64).UTC()
hour, minute, second := t.Clock()
result := ast.Array{ast.IntNumberTerm(hour), ast.IntNumberTerm(minute), ast.IntNumberTerm(second)}
return result, nil
return time.Unix(0, i64).UTC(), nil
}

func int64ToJSONNumber(i int64) json.Number {
Expand All @@ -131,4 +137,5 @@ func init() {
RegisterFunctionalBuiltin1(ast.ParseDurationNanos.Name, builtinParseDurationNanos)
RegisterFunctionalBuiltin1(ast.Date.Name, builtinDate)
RegisterFunctionalBuiltin1(ast.Clock.Name, builtinClock)
RegisterFunctionalBuiltin1(ast.Weekday.Name, builtinWeekday)
}
9 changes: 9 additions & 0 deletions topdown/topdown_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,15 @@ func TestTopDownTime(t *testing.T) {

runTopDownTestCase(t, data, "clock too big", []string{`
p = [hour, minute, second] { [hour, minute, second] := time.clock(1582977600*1000*1000*1000*1000) }`}, fmt.Errorf("timestamp too big"))

for i, day := range []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} {
ts := 1517832000*1000*1000*1000 + i*24*int(time.Hour)
runTopDownTestCase(t, data, "weekday", []string{fmt.Sprintf(`p = weekday { weekday := time.weekday(%d)}`, ts)},
fmt.Sprintf("%q", day))
}

runTopDownTestCase(t, data, "weekday too big", []string{`
p = weekday { weekday := time.weekday(1582977600*1000*1000*1000*1000) }`}, fmt.Errorf("timestamp too big"))
}

func TestTopDownWalkBuiltin(t *testing.T) {
Expand Down