Skip to content

Commit 1f152f7

Browse files
Merge pull request #105 from invopop/date-time
Configure date and time formatting independently
2 parents 3bedd8b + 65d61b6 commit 1f152f7

6 files changed

Lines changed: 104 additions & 17 deletions

File tree

cmd/gobl.html/serve.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ func (s *serveOpts) render(c echo.Context, req *options, env *gobl.Envelope, opt
124124
var err error
125125

126126
// Prepare the request options
127-
if req.DateFormat != "" {
128-
opts = append(opts, goblhtml.WithCalFormatter(req.DateFormat, "", time.UTC))
127+
if req.DateFormat != "" || req.TimeFormat != "" {
128+
opts = append(opts, goblhtml.WithCalFormatter(req.DateFormat, req.TimeFormat, time.UTC))
129129
}
130130
opts = append(opts, goblhtml.WithLocale(req.Locale))
131131

@@ -176,6 +176,7 @@ type options struct {
176176
Filename string `param:"filename"`
177177
Locale i18n.Code `query:"locale"`
178178
DateFormat string `query:"date_format"`
179+
TimeFormat string `query:"time_format"`
179180
LogoURL string `query:"logo_url"`
180181
LogoHeight int32 `query:"logo_height"`
181182
Notes string `query:"notes"`

components/t/i18n.templ

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"reflect"
77

88
"github.com/invopop/ctxi18n/i18n"
9+
"github.com/invopop/gobl.html/internal"
910
"github.com/invopop/gobl/cal"
1011
"github.com/invopop/gobl/currency"
1112
"github.com/invopop/gobl/num"
@@ -69,7 +70,8 @@ func Localize(ctx context.Context, a any) string {
6970
return nf.Percentage(v)
7071
case cal.DateTime:
7172
cf := calFormatter(ctx)
72-
return v.In(cf.Location).Format(cf.DateTime)
73+
t := v.In(cf.Location)
74+
return t.Format(cf.Date) + internal.DateTimeSeparator + t.Format(cf.Time)
7375
case cal.Date:
7476
cf := calFormatter(ctx)
7577
return v.Time().Format(cf.Date)

components/t/i18n_templ.go

Lines changed: 9 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/t/i18n_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package t_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
calT "github.com/invopop/gobl.html/components/t"
9+
"github.com/invopop/gobl.html/internal"
10+
"github.com/invopop/gobl/cal"
11+
"github.com/invopop/gobl/num"
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestLocalizeDateTime(t *testing.T) {
16+
// 2026-03-25T12:34 – matches the co-dian-invoice example that has both
17+
// issue_date and issue_time, which is the scenario fixed by APP-472.
18+
dt := cal.MakeDateTime(2026, time.March, 25, 12, 34, 0)
19+
20+
nf := num.Formatter{}
21+
tests := []struct {
22+
name string
23+
date string
24+
timeF string
25+
expected string
26+
}{
27+
{
28+
name: "ISO defaults",
29+
date: "",
30+
timeF: "",
31+
expected: "2026-03-25 · 12:34",
32+
},
33+
{
34+
name: "custom date format keeps date_format when issue_time present",
35+
date: "02/01/2006",
36+
timeF: "",
37+
expected: "25/03/2026 · 12:34",
38+
},
39+
{
40+
name: "custom date and time format",
41+
date: "02/01/2006",
42+
timeF: "3:04 PM",
43+
expected: "25/03/2026 · 12:34 PM",
44+
},
45+
{
46+
name: "default date with custom time format",
47+
date: "",
48+
timeF: "3:04 PM",
49+
expected: "2026-03-25 · 12:34 PM",
50+
},
51+
}
52+
53+
for _, tc := range tests {
54+
t.Run(tc.name, func(t *testing.T) {
55+
cf := internal.CalFormatterISO
56+
if tc.date != "" {
57+
cf.Date = tc.date
58+
}
59+
if tc.timeF != "" {
60+
cf.Time = tc.timeF
61+
}
62+
63+
opts := &internal.Opts{
64+
CalFormatter: &cf,
65+
NumFormatter: &nf,
66+
}
67+
ctx := internal.WithOptions(context.Background(), opts)
68+
69+
got := calT.Localize(ctx, dt)
70+
assert.Equal(t, tc.expected, got)
71+
})
72+
}
73+
}

goblhtml.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,18 @@ func WithLocale(locale i18n.Code) Option {
6262
}
6363
}
6464

65-
// WithCalFormatter prepares simple date and datetime formatting.
66-
func WithCalFormatter(date, dateTime string, loc *time.Location) Option {
65+
// WithCalFormatter prepares simple date and time formatting. The date and
66+
// time formats are configured independently; date-time values render as
67+
// "<date><sep><time>" using the provided formats. Pass an empty string to
68+
// keep the ISO default for a given field.
69+
func WithCalFormatter(date, timeFormat string, loc *time.Location) Option {
6770
return func(o *internal.Opts) {
6871
cf := internal.CalFormatterISO
6972
if date != "" {
7073
cf.Date = date
7174
}
72-
if dateTime != "" {
73-
cf.DateTime = dateTime
75+
if timeFormat != "" {
76+
cf.Time = timeFormat
7477
}
7578
if loc != nil {
7679
cf.Location = loc

internal/options.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,23 @@ func Options(ctx context.Context) *Opts {
7777
return nil
7878
}
7979

80-
// CalFormatter defines a simple date and datetime formatter
80+
// CalFormatter defines a simple date and time formatter. Date and time
81+
// formats are configured independently; date-time values are rendered by
82+
// composing the two with a separator.
8183
type CalFormatter struct {
8284
Date string // Golang date format for dates, e.g. `02/01/2006`
85+
Time string // Golang time format for times, e.g. `15:04`
8386
Location *time.Location
84-
DateTime string // Date-time format
8587
}
8688

8789
// CalFormatterISO is the default formatter for dates and times based on
8890
// the recommended ISO 8601 formatting.
8991
var CalFormatterISO = CalFormatter{
9092
Date: "2006-01-02",
91-
DateTime: "2006-01-02 · 15:04",
93+
Time: "15:04",
9294
Location: time.UTC,
9395
}
96+
97+
// DateTimeSeparator is used to join the formatted date and time when
98+
// rendering a cal.DateTime value.
99+
const DateTimeSeparator = " · "

0 commit comments

Comments
 (0)