diff --git a/encoding/toml/decode.go b/encoding/toml/decode.go index ba60c28cc..74c91663d 100644 --- a/encoding/toml/decode.go +++ b/encoding/toml/decode.go @@ -463,7 +463,39 @@ func (d *Decoder) decodeExpr(rkey rootedKey, tnode *toml.Node) (ast.Expr, error) strct.Elts = append(strct.Elts, field) } expr = strct - // TODO(mvdan): dates and times + case toml.LocalDate, toml.LocalTime, toml.LocalDateTime, toml.DateTime: + // CUE does not have native date nor time litera kinds, + // so we decode these as strings exactly as they came in. + // The user can use CUE's `time` package to interact with these, + // such as time.Split. + // We could consider adding a field attribute to signal that the original TOML + // TODO: should we produce a CUE field attribute to signal what th + var format ast.Expr + switch tnode.Kind { + case toml.LocalDate: + // TODO(mvdan): rename time.RFC3339Date to time.DateOnly to mirror Go + format = ast.NewSel(&ast.Ident{ + Name: "time", + Node: ast.NewImport(nil, "time"), + }, "RFC3339Date") + case toml.LocalTime: + // TODO(mvdan): add time.TimeOnly to mirror Go + format = ast.NewString("15:04:05") + case toml.LocalDateTime: + // RFC3339 minus the timezone; this seems like a format peculiar to TOML. + format = ast.NewString("2006-01-02T15:04:05") + default: // DateTime + format = ast.NewSel(&ast.Ident{ + Name: "time", + Node: ast.NewImport(nil, "time"), + }, "RFC3339") + } + expr = ast.NewBinExpr(token.AND, ast.NewString(data), ast.NewCall( + ast.NewSel(&ast.Ident{ + Name: "time", + Node: ast.NewImport(nil, "time"), + }, "Format"), format), + ) default: return nil, fmt.Errorf("encoding/toml.Decoder.decodeExpr: unknown %s %#v", tnode.Kind, tnode) } diff --git a/encoding/toml/decode_test.go b/encoding/toml/decode_test.go index c7d94d57e..792321def 100644 --- a/encoding/toml/decode_test.go +++ b/encoding/toml/decode_test.go @@ -316,6 +316,40 @@ line two.\ positive: true negative: false `, + }, { + name: "DateTimes", + input: ` + offsetDateTime1 = 1979-05-27T07:32:00Z + offsetDateTime2 = 1979-05-27T00:32:00-07:00 + offsetDateTime3 = 1979-05-27T00:32:00.999999-07:00 + localDateTime1 = 1979-05-27T07:32:00 + localDateTime2 = 1979-05-27T00:32:00.999999 + localDate1 = 1979-05-27 + localTime1 = 07:32:00 + localTime2 = 00:32:00.999999 + + inlineArray = [1979-05-27, 07:32:00] + + notActuallyDate = "1979-05-27" + notActuallyTime = "07:32:00" + inlineArrayNotActually = ["1979-05-27", "07:32:00"] + `, + wantCUE: ` + import "time" + + offsetDateTime1: "1979-05-27T07:32:00Z" & time.Format(time.RFC3339) + offsetDateTime2: "1979-05-27T00:32:00-07:00" & time.Format(time.RFC3339) + offsetDateTime3: "1979-05-27T00:32:00.999999-07:00" & time.Format(time.RFC3339) + localDateTime1: "1979-05-27T07:32:00" & time.Format("2006-01-02T15:04:05") + localDateTime2: "1979-05-27T00:32:00.999999" & time.Format("2006-01-02T15:04:05") + localDate1: "1979-05-27" & time.Format(time.RFC3339Date) + localTime1: "07:32:00" & time.Format("15:04:05") + localTime2: "00:32:00.999999" & time.Format("15:04:05") + inlineArray: ["1979-05-27" & time.Format(time.RFC3339Date), "07:32:00" & time.Format("15:04:05")] + notActuallyDate: "1979-05-27" + notActuallyTime: "07:32:00" + inlineArrayNotActually: ["1979-05-27", "07:32:00"] + `, }, { name: "Arrays", input: ` diff --git a/encoding/toml/testdata/decode/example.txtar b/encoding/toml/testdata/decode/example.txtar index 405bef1cc..c7b991d83 100644 --- a/encoding/toml/testdata/decode/example.txtar +++ b/encoding/toml/testdata/decode/example.txtar @@ -6,8 +6,7 @@ title = "TOML Example" [owner] name = "Tom Preston-Werner" -# TODO: support dates -# dob = 1979-05-27T07:32:00-08:00 +dob = 1979-05-27T07:32:00-08:00 [database] enabled = true @@ -62,60 +61,65 @@ color = "gray" ValuePos: token.Pos("example.toml:7:8") } } + *ast.Field{ + Label: *ast.Ident{ + NamePos: token.Pos("example.toml:8:1", newline) + } + } } Rbrace: token.Pos("-", newline) } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:11:2", newline) + NamePos: token.Pos("example.toml:10:2", newline) } Value: *ast.StructLit{ Lbrace: token.Pos("-", blank) Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:12:1", newline) + NamePos: token.Pos("example.toml:11:1", newline) } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:13:1", newline) + NamePos: token.Pos("example.toml:12:1", newline) } Value: *ast.ListLit{ Elts: []ast.Expr{ *ast.BasicLit{ - ValuePos: token.Pos("example.toml:13:11") + ValuePos: token.Pos("example.toml:12:11") } *ast.BasicLit{ - ValuePos: token.Pos("example.toml:13:17") + ValuePos: token.Pos("example.toml:12:17") } *ast.BasicLit{ - ValuePos: token.Pos("example.toml:13:23") + ValuePos: token.Pos("example.toml:12:23") } } } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:14:1", newline) + NamePos: token.Pos("example.toml:13:1", newline) } Value: *ast.ListLit{ Elts: []ast.Expr{ *ast.ListLit{ Elts: []ast.Expr{ *ast.BasicLit{ - ValuePos: token.Pos("example.toml:14:11") + ValuePos: token.Pos("example.toml:13:11") } *ast.BasicLit{ - ValuePos: token.Pos("example.toml:14:20") + ValuePos: token.Pos("example.toml:13:20") } } } *ast.ListLit{ Elts: []ast.Expr{ *ast.BasicLit{ - ValuePos: token.Pos("example.toml:14:29") + ValuePos: token.Pos("example.toml:13:29") } } } @@ -124,25 +128,25 @@ color = "gray" } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:15:1", newline) + NamePos: token.Pos("example.toml:14:1", newline) } Value: *ast.StructLit{ - Lbrace: token.Pos("example.toml:15:16") + Lbrace: token.Pos("example.toml:14:16") Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:15:18", blank) + NamePos: token.Pos("example.toml:14:18", blank) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:15:24") + ValuePos: token.Pos("example.toml:14:24") } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:15:30", blank) + NamePos: token.Pos("example.toml:14:30", blank) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:15:37") + ValuePos: token.Pos("example.toml:14:37") } } } @@ -155,7 +159,7 @@ color = "gray" } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:17:2", newline) + NamePos: token.Pos("example.toml:16:2", newline) } Value: *ast.StructLit{ Lbrace: token.Pos("-", blank) @@ -164,31 +168,31 @@ color = "gray" } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:19:2", newline) + NamePos: token.Pos("example.toml:18:2", newline) } Value: *ast.StructLit{ Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:19:10", blank) + NamePos: token.Pos("example.toml:18:10", blank) } Value: *ast.StructLit{ Lbrace: token.Pos("-", blank) Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:20:1", newline) + NamePos: token.Pos("example.toml:19:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:20:6") + ValuePos: token.Pos("example.toml:19:6") } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:21:1", newline) + NamePos: token.Pos("example.toml:20:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:21:8") + ValuePos: token.Pos("example.toml:20:8") } } } @@ -200,31 +204,31 @@ color = "gray" } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:23:2", newline) + NamePos: token.Pos("example.toml:22:2", newline) } Value: *ast.StructLit{ Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:23:10", blank) + NamePos: token.Pos("example.toml:22:10", blank) } Value: *ast.StructLit{ Lbrace: token.Pos("-", blank) Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:24:1", newline) + NamePos: token.Pos("example.toml:23:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:24:6") + ValuePos: token.Pos("example.toml:23:6") } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:25:1", newline) + NamePos: token.Pos("example.toml:24:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:25:8") + ValuePos: token.Pos("example.toml:24:8") } } } @@ -236,7 +240,7 @@ color = "gray" } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:27:3", newline) + NamePos: token.Pos("example.toml:26:3", newline) } Value: *ast.ListLit{ Lbrack: token.Pos("-", blank) @@ -246,18 +250,18 @@ color = "gray" Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:28:1", newline) + NamePos: token.Pos("example.toml:27:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:28:8") + ValuePos: token.Pos("example.toml:27:8") } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:29:1", newline) + NamePos: token.Pos("example.toml:28:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:29:7") + ValuePos: token.Pos("example.toml:28:7") } } } @@ -272,26 +276,26 @@ color = "gray" Elts: []ast.Decl{ *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:34:1", newline) + NamePos: token.Pos("example.toml:33:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:34:8") + ValuePos: token.Pos("example.toml:33:8") } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:35:1", newline) + NamePos: token.Pos("example.toml:34:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:35:7") + ValuePos: token.Pos("example.toml:34:7") } } *ast.Field{ Label: *ast.Ident{ - NamePos: token.Pos("example.toml:37:1", newline) + NamePos: token.Pos("example.toml:36:1", newline) } Value: *ast.BasicLit{ - ValuePos: token.Pos("example.toml:37:9") + ValuePos: token.Pos("example.toml:36:9") } } }