Skip to content

Commit

Permalink
Add strftime function (#5197)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattnibs authored Aug 1, 2024
1 parent 57a04bd commit efcb3db
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 0 deletions.
67 changes: 67 additions & 0 deletions docs/language/functions/strftime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
### Function

  **strftime** — format time values

### Synopsis
```
strftime(format: string, t: time) -> string
```

### Description
The _strftime_ function returns a string represenation of time `t`
as specified by the provided string `format`. `format` is a string
containing format directives that dictate how the time string is
formatted.

These directives are supported:

| Directive | Explanation | Example |
|-----------|-------------|---------|
| %A | Weekday as full name | Sunday, Monday, ..., Saturday |
| %a | Weekday as abbreviated name | Sun, Mon, ..., Sat |
| %B | Month as full name | January, February, ..., December |
| %b | Month as abbreviated name | Jan, Feb, ..., Dec |
| %C | Century number (year / 100) as a 2-digit integer | 20 |
| %c | Locale's appropriate date and time representation | Tue Jul 30 14:30:15 2024 |
| %D | Equivalent to `%m/%d/%y` | 7/30/24 |
| %d | Day of the month as a zero-padded decimal number | 01, 02, ..., 31 |
| %e | Day of the month as a decimal number (1-31); single digits are preceded by a blank | 1, 2, ..., 31 |
| %F | Equivalent to `%Y-%m-%d` | 2024-07-30 |
| %H | Hour (24-hour clock) as a zero-padded decimal number | 00, 01, ..., 23 |
| %I | Hour (12-hour clock) as a zero-padded decimal number | 00, 01, ..., 12 |
| %j | Day of the year as a zero-padded decimal number | 001, 002, ..., 366 |
| %k | Hour (24-hour clock) as a decimal number; single digits are preceded by a blank | 0, 1, ..., 23 |
| %l | Hour (12-hour clock) as a decimal number; single digits are preceded by a blank | 0, 1, ..., 12 |
| %M | Minute as a zero-padded decimal number | 00, 01, ..., 59 |
| %m | Month as a zero-padded decimal number | 01, 02, ..., 12 |
| %n | Newline character | \n |
| %p | "ante meridiem" (a.m.) or "post meridiem" (p.m.) | AM, PM |
| %R | Equivalent to `%H:%M` | 18:49 |
| %r | Equivalent to `%I:%M:%S %p` | 06:50:58 PM |
| %S | Second as a zero-padded decimal number | 00, 01, ..., 59 |
| %T | Equivalent to `%H:%M:%S` | 18:50:58 |
| %t | Tab character | \t |
| %U | Week number of the year (Sunday as the first day of the week) | 00, 01, ..., 53 |
| %u | Weekday as a decimal number, range 1 to 7, with Monday being 1 | 1, 2, ..., 7 |
| %V | Week number of the year (Monday as the first day of the week) as a decimal number (01-53) | 01, 02, ..., 53 |
| %v | Equivalent to `%e-%b-%Y` | 31-Jul-2024 |
| %W | Week number of the year (Monday as the first day of the week) | 00, 01, ..., 53 |
| %w | Weekday as a decimal number, range 0 to 6, with Sunday being 0 | 0, 1, ..., 6 |
| %X | Locale's appropriate time representation | 14:30:15 |
| %x | Locale's appropriate date representation | 07/30/24 |
| %Y | Year with century as a decimal number | 2024 |
| %y | Year without century as a decimal number | 24, 23 |
| %Z | Timezone name | UTC |
| %z | +hhmm or -hhmm numeric timezone (that is, the hour and minute offset from UTC) | +0000 |
| %% | A literal '%' character | % |

### Examples

Print the year number as a string
```mdtest-command
echo 2024-07-30T20:05:15.118252Z | zq -z 'strftime("%Y", this)' -
```
=>
```mdtest-output
"2024"
```
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/gosuri/uilive v0.0.4
github.com/hashicorp/golang-lru/v2 v2.0.1
github.com/kr/text v0.2.0
github.com/lestrrat-go/strftime v1.0.6
github.com/paulbellamy/ratecounter v0.2.0
github.com/pbnjay/memory v0.0.0-20190104145345-974d429e7ae4
github.com/peterh/liner v1.1.0
Expand Down Expand Up @@ -61,6 +62,7 @@ require (
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rivo/uniseg v0.1.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
Expand Down
3 changes: 3 additions & 0 deletions runtime/sam/expr/function/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ func New(zctx *zed.Context, name string, narg int) (expr.Function, field.Path, e
case "regexp_replace":
argmin, argmax = 3, 3
f = &RegexpReplace{zctx: zctx}
case "strftime":
argmin, argmax = 2, 2
f = &Strftime{zctx: zctx}
case "under":
f = &Under{zctx: zctx}
case "unflatten":
Expand Down
26 changes: 26 additions & 0 deletions runtime/sam/expr/function/time.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/brimdata/zed/pkg/nano"
"github.com/brimdata/zed/runtime/sam/expr"
"github.com/brimdata/zed/runtime/sam/expr/coerce"
"github.com/lestrrat-go/strftime"
)

// https://github.com/brimdata/zed/blob/main/docs/language/functions.md#now
Expand Down Expand Up @@ -46,3 +47,28 @@ func (b *Bucket) Call(ectx expr.Context, args []zed.Value) zed.Value {
}
return zed.NewTime(nano.Ts(v).Trunc(bin))
}

// https://github.com/brimdata/zed/blob/main/docs/language/functions.md#strftime
type Strftime struct {
zctx *zed.Context
formatter *strftime.Strftime
}

func (s *Strftime) Call(ectx expr.Context, args []zed.Value) zed.Value {
formatArg, timeArg := args[0], args[1]
if !formatArg.IsString() {
return s.zctx.WrapError(ectx.Arena(), "strftime: string value required for format arg", formatArg)
}
if zed.TypeUnder(timeArg.Type()) != zed.TypeTime {
return s.zctx.WrapError(ectx.Arena(), "strftime: time value required for time arg", args[1])
}
format := formatArg.AsString()
if s.formatter == nil || s.formatter.Pattern() != format {
var err error
if s.formatter, err = strftime.New(format); err != nil {
return s.zctx.WrapError(ectx.Arena(), "strftime: "+err.Error(), formatArg)
}
}
out := s.formatter.FormatString(timeArg.AsTime().Time())
return ectx.Arena().NewString(out)
}
13 changes: 13 additions & 0 deletions runtime/sam/expr/function/ztests/strftime.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
zed: 'strftime(f, v)'

input: |
{f:"%Y-%a",v:2024-07-30T06:15:01.062681Z}
{f:1,v:2024-07-30T06:15:01.062681Z}
{f:"%H",v:"foo"}
{f:"%1",v:2024-07-30T06:15:01.062681Z}
output: |
"2024-Tue"
error({message:"strftime: string value required for format arg",on:1})
error({message:"strftime: time value required for time arg",on:"foo"})
error({message:"strftime: failed to compile format: pattern compilation failed: lookup failed: '%1' was not found in specification set",on:"%1"})

0 comments on commit efcb3db

Please sign in to comment.