diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 5b5470b87e..a1f3f1aa2d 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -52,6 +52,7 @@ func NewFuncMap() template.FuncMap {
"StringUtils": NewStringUtils,
"SliceUtils": NewSliceUtils,
"JsonUtils": NewJsonUtils,
+ "DateUtils": NewDateUtils,
// -----------------------------------------------------------------
// svg / avatar / icon / color
@@ -68,7 +69,7 @@ func NewFuncMap() template.FuncMap {
"CountFmt": base.FormatNumberSI,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
- "DateTime": timeutil.DateTime,
+ "DateTime": dateTimeLegacy, // for backward compatibility only, do not use it anymore
"Sec2Time": util.SecToTime,
"LoadTimes": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
diff --git a/modules/templates/util_date.go b/modules/templates/util_date.go
new file mode 100644
index 0000000000..10f7cdb508
--- /dev/null
+++ b/modules/templates/util_date.go
@@ -0,0 +1,60 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package templates
+
+import (
+ "context"
+ "html/template"
+ "time"
+
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/timeutil"
+)
+
+type DateUtils struct {
+ ctx context.Context
+}
+
+func NewDateUtils(ctx context.Context) *DateUtils {
+ return &DateUtils{ctx}
+}
+
+// AbsoluteShort renders in "Jan 01, 2006" format
+func (du *DateUtils) AbsoluteShort(time any) template.HTML {
+ return timeutil.DateTime("short", time)
+}
+
+// AbsoluteLong renders in "January 01, 2006" format
+func (du *DateUtils) AbsoluteLong(time any) template.HTML {
+ return timeutil.DateTime("long", time)
+}
+
+// FullTime renders in "Jan 01, 2006 20:33:44" format
+func (du *DateUtils) FullTime(time any) template.HTML {
+ return timeutil.DateTime("full", time)
+}
+
+// ParseLegacy parses the datetime in legacy format, eg: "2016-01-02" in server's timezone.
+// It shouldn't be used in new code. New code should use Time or TimeStamp as much as possible.
+func (du *DateUtils) ParseLegacy(datetime string) time.Time {
+ return parseLegacy(datetime)
+}
+
+func parseLegacy(datetime string) time.Time {
+ t, err := time.Parse(time.RFC3339, datetime)
+ if err != nil {
+ t, _ = time.ParseInLocation(time.DateOnly, datetime, setting.DefaultUILocation)
+ }
+ return t
+}
+
+func dateTimeLegacy(format string, datetime any, _ ...string) template.HTML {
+ if !setting.IsProd || setting.IsInTesting {
+ panic("dateTimeLegacy is for backward compatibility only, do not use it in new code")
+ }
+ if s, ok := datetime.(string); ok {
+ datetime = parseLegacy(s)
+ }
+ return timeutil.DateTime(format, datetime)
+}
diff --git a/modules/timeutil/datetime_test.go b/modules/templates/util_date_test.go
similarity index 50%
rename from modules/timeutil/datetime_test.go
rename to modules/templates/util_date_test.go
index ac2ce35ba2..e3dec5c8db 100644
--- a/modules/timeutil/datetime_test.go
+++ b/modules/templates/util_date_test.go
@@ -1,47 +1,61 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
-package timeutil
+package templates
import (
+ "html/template"
"testing"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
+ "code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestDateTime(t *testing.T) {
- testTz, _ := time.LoadLocation("America/New_York")
+ testTz, err := time.LoadLocation("America/New_York")
+ require.NoError(t, err)
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsInTesting, false)()
+
+ du := NewDateUtils(nil)
refTimeStr := "2018-01-01T00:00:00Z"
refDateStr := "2018-01-01"
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
- refTimeStamp := TimeStamp(refTime.Unix())
+ refTimeStamp := timeutil.TimeStamp(refTime.Unix())
- assert.EqualValues(t, "-", DateTime("short", nil))
- assert.EqualValues(t, "-", DateTime("short", 0))
- assert.EqualValues(t, "-", DateTime("short", time.Time{}))
- assert.EqualValues(t, "-", DateTime("short", TimeStamp(0)))
+ for _, val := range []any{nil, 0, time.Time{}, timeutil.TimeStamp(0)} {
+ for _, fun := range []func(val any) template.HTML{du.AbsoluteLong, du.AbsoluteShort, du.FullTime} {
+ assert.EqualValues(t, "-", fun(val))
+ }
+ }
- actual := DateTime("short", "invalid")
- assert.EqualValues(t, `invalid`, actual)
+ actual := dateTimeLegacy("short", "invalid")
+ assert.EqualValues(t, `-`, actual)
- actual = DateTime("short", refTimeStr)
- assert.EqualValues(t, `2018-01-01T00:00:00Z`, actual)
-
- actual = DateTime("short", refTime)
+ actual = dateTimeLegacy("short", refTimeStr)
assert.EqualValues(t, `2018-01-01`, actual)
- actual = DateTime("short", refDateStr)
- assert.EqualValues(t, `2018-01-01`, actual)
+ actual = du.AbsoluteShort(refTime)
+ assert.EqualValues(t, `2018-01-01`, actual)
- actual = DateTime("short", refTimeStamp)
+ actual = du.AbsoluteLong(refTime)
+ assert.EqualValues(t, `2018-01-01`, actual)
+
+ actual = dateTimeLegacy("short", refDateStr)
+ assert.EqualValues(t, `2018-01-01`, actual)
+
+ actual = du.AbsoluteShort(refTimeStamp)
assert.EqualValues(t, `2017-12-31`, actual)
- actual = DateTime("full", refTimeStamp)
+ actual = du.AbsoluteLong(refTimeStamp)
+ assert.EqualValues(t, `2017-12-31`, actual)
+
+ actual = du.FullTime(refTimeStamp)
assert.EqualValues(t, `2017-12-31 19:00:00 -05:00`, actual)
}
diff --git a/modules/timeutil/datetime.go b/modules/timeutil/datetime.go
index c089173560..664e0320b0 100644
--- a/modules/timeutil/datetime.go
+++ b/modules/timeutil/datetime.go
@@ -12,9 +12,7 @@ import (
)
// DateTime renders an absolute time HTML element by datetime.
-func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
- // TODO: remove the extraAttrs argument, it's not used in any call to DateTime
-
+func DateTime(format string, datetime any) template.HTML {
if p, ok := datetime.(*time.Time); ok {
datetime = *p
}
@@ -34,9 +32,6 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
switch v := datetime.(type) {
case nil:
return "-"
- case string:
- datetimeEscaped = html.EscapeString(v)
- textEscaped = datetimeEscaped
case time.Time:
if v.IsZero() || v.Unix() == 0 {
return "-"
@@ -51,10 +46,7 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
panic(fmt.Sprintf("Unsupported time type %T", datetime))
}
- attrs := make([]string, 0, 10+len(extraAttrs))
- attrs = append(attrs, extraAttrs...)
- attrs = append(attrs, `weekday=""`, `year="numeric"`)
-
+ attrs := []string{`weekday=""`, `year="numeric"`}
switch format {
case "short", "long": // date only
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go
index ba776c84d3..954446be36 100644
--- a/routers/web/repo/activity.go
+++ b/routers/web/repo/activity.go
@@ -48,8 +48,8 @@ func Activity(ctx *context.Context) {
ctx.Data["Period"] = "weekly"
timeFrom = timeUntil.Add(-time.Hour * 168)
}
- ctx.Data["DateFrom"] = timeFrom.UTC().Format(time.RFC3339)
- ctx.Data["DateUntil"] = timeUntil.UTC().Format(time.RFC3339)
+ ctx.Data["DateFrom"] = timeFrom
+ ctx.Data["DateUntil"] = timeUntil
ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + ctx.Data["Period"].(string))
var err error
diff --git a/services/context/context.go b/services/context/context.go
index 91e7b1849d..65796c3ee7 100644
--- a/services/context/context.go
+++ b/services/context/context.go
@@ -102,6 +102,7 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
tmplCtx := NewTemplateContext(ctx)
tmplCtx["Locale"] = ctx.Base.Locale
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
+ tmplCtx["DateUtils"] = templates.NewDateUtils(ctx)
return tmplCtx
}
diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl
index 6483ec800c..5b5affad8a 100644
--- a/templates/admin/auth/list.tmpl
+++ b/templates/admin/auth/list.tmpl
@@ -26,8 +26,8 @@
{{.Name}} |
{{.TypeName}} |
{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
- {{DateTime "short" .UpdatedUnix}} |
- {{DateTime "short" .CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .UpdatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .CreatedUnix}} |
{{svg "octicon-pencil"}} |
{{end}}
diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl
index 3cb641488c..64ae7858ba 100644
--- a/templates/admin/cron.tmpl
+++ b/templates/admin/cron.tmpl
@@ -23,8 +23,8 @@
|
{{ctx.Locale.Tr (printf "admin.dashboard.%s" .Name)}} |
{{.Spec}} |
- {{DateTime "full" .Next}} |
- {{if gt .Prev.Year 1}}{{DateTime "full" .Prev}}{{else}}-{{end}} |
+ {{ctx.DateUtils.FullTime .Next}} |
+ {{if gt .Prev.Year 1}}{{ctx.DateUtils.FullTime .Prev}}{{else}}-{{end}} |
{{.ExecTimes}} |
{{if eq .Status ""}}—{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}} |
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index 33d8a2f963..2db0384f6d 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -21,7 +21,7 @@
{{.ID}} |
{{ctx.Locale.Tr .TrStr}} |
{{.Description}} |
- {{DateTime "short" .CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .CreatedUnix}} |
{{svg "octicon-note" 16}} |
{{end}}
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index 987ceab1e0..6a6dc14609 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -63,7 +63,7 @@
{{.NumTeams}} |
{{.NumMembers}} |
{{.NumRepos}} |
- {{DateTime "short" .CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .CreatedUnix}} |
{{svg "octicon-pencil"}} |
{{end}}
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl
index 4ff49b8c43..9cc08772b7 100644
--- a/templates/admin/packages/list.tmpl
+++ b/templates/admin/packages/list.tmpl
@@ -71,7 +71,7 @@
{{end}}
{{ctx.Locale.TrSize .CalculateBlobSize}} |
- {{DateTime "short" .Version.CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .Version.CreatedUnix}} |
{{svg "octicon-trash"}} |
{{end}}
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl
index 1ea6183d80..992933154d 100644
--- a/templates/admin/repo/list.tmpl
+++ b/templates/admin/repo/list.tmpl
@@ -82,8 +82,8 @@
{{.NumIssues}} |
{{ctx.Locale.TrSize .GitSize}} |
{{ctx.Locale.TrSize .LFSSize}} |
- {{DateTime "short" .UpdatedUnix}} |
- {{DateTime "short" .CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .UpdatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .CreatedUnix}} |
{{svg "octicon-trash"}} |
{{end}}
diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl
index e5d429952f..42879d7917 100644
--- a/templates/admin/user/list.tmpl
+++ b/templates/admin/user/list.tmpl
@@ -96,9 +96,9 @@
{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
- {{DateTime "short" .CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .CreatedUnix}} |
{{if .LastLoginUnix}}
- {{DateTime "short" .LastLoginUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .LastLoginUnix}} |
{{else}}
{{ctx.Locale.Tr "admin.users.never_login"}} |
{{end}}
diff --git a/templates/explore/user_list.tmpl b/templates/explore/user_list.tmpl
index f2cf939ffb..ff46f13c17 100644
--- a/templates/explore/user_list.tmpl
+++ b/templates/explore/user_list.tmpl
@@ -21,7 +21,7 @@
{{.Email}}
{{end}}
- {{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix)}}
+ {{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (ctx.DateUtils.AbsoluteShort .CreatedUnix)}}
diff --git a/templates/package/shared/cleanup_rules/preview.tmpl b/templates/package/shared/cleanup_rules/preview.tmpl
index 0d9c4b0d46..ced1e5c11b 100644
--- a/templates/package/shared/cleanup_rules/preview.tmpl
+++ b/templates/package/shared/cleanup_rules/preview.tmpl
@@ -22,7 +22,7 @@
{{.Version.Version}} |
{{.Creator.Name}} |
{{ctx.Locale.TrSize .CalculateBlobSize}} |
- {{DateTime "short" .Version.CreatedUnix}} |
+ {{ctx.DateUtils.AbsoluteShort .Version.CreatedUnix}} |
{{else}}
diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl
index fe88e54317..59e4e53f12 100644
--- a/templates/package/view.tmpl
+++ b/templates/package/view.tmpl
@@ -94,7 +94,7 @@
{{range .LatestVersions}}
{{.Version}}
-
{{DateTime "short" .CreatedUnix}}
+
{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}
{{end}}
diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl
index 110f8ac60b..26c6b77971 100644
--- a/templates/repo/diff/compare.tmpl
+++ b/templates/repo/diff/compare.tmpl
@@ -212,7 +212,7 @@
{{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}}
{{else}}
- {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}}
+ {{ctx.Locale.Tr "repo.archive.title_date" (ctx.DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}}
{{end}}
diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl
index 7613643351..d7f05223af 100644
--- a/templates/repo/empty.tmpl
+++ b/templates/repo/empty.tmpl
@@ -10,7 +10,7 @@
{{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}}
{{else}}
- {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}}
+ {{ctx.Locale.Tr "repo.archive.title_date" (ctx.DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}}
{{end}}
diff --git a/templates/repo/graph/commits.tmpl b/templates/repo/graph/commits.tmpl
index 5c768f32bb..2ec4166308 100644
--- a/templates/repo/graph/commits.tmpl
+++ b/templates/repo/graph/commits.tmpl
@@ -71,7 +71,7 @@
{{$userName}}
{{end}}
- {{DateTime "full" $commit.Date}}
+ {{ctx.DateUtils.FullTime $commit.Date}}
{{end}}
{{end}}
diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl
index f2a61794a6..d16e616a81 100644
--- a/templates/repo/home.tmpl
+++ b/templates/repo/home.tmpl
@@ -53,7 +53,7 @@
{{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}}
{{else}}
- {{ctx.Locale.Tr "repo.archive.title_date" (DateTime "long" .Repository.ArchivedUnix)}}
+ {{ctx.Locale.Tr "repo.archive.title_date" (ctx.DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}}
{{end}}
diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl
index e5dc8cb605..bfd64a3205 100644
--- a/templates/repo/issue/milestone_issues.tmpl
+++ b/templates/repo/issue/milestone_issues.tmpl
@@ -38,7 +38,7 @@
{{if .Milestone.DeadlineString}}
{{svg "octicon-calendar"}}
- {{DateTime "short" .Milestone.DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.Milestone.DeadlineString|ctx.DateUtils.ParseLegacy)}}
{{else}}
{{svg "octicon-calendar"}}
diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl
index 63a6f6b26a..978f81598e 100644
--- a/templates/repo/issue/milestones.tmpl
+++ b/templates/repo/issue/milestones.tmpl
@@ -61,7 +61,7 @@
{{if .DeadlineString}}
{{svg "octicon-calendar" 14}}
- {{DateTime "short" .DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
{{else}}
{{svg "octicon-calendar" 14}}
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index c634615ef3..828191e5e1 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -300,7 +300,8 @@
{{template "shared/user/avatarlink" dict "user" .Poster}}
{{template "shared/user/authorlink" .Poster}}
- {{ctx.Locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content) $createdStr}}
+ {{$dueDate := ctx.DateUtils.AbsoluteLong (.Content|ctx.DateUtils.ParseLegacy)}}
+ {{ctx.Locale.Tr "repo.issues.due_date_added" $dueDate $createdStr}}
{{else if eq .Type 17}}
@@ -311,8 +312,8 @@
{{template "shared/user/authorlink" .Poster}}
{{$parsedDeadline := StringUtils.Split .Content "|"}}
{{if eq (len $parsedDeadline) 2}}
- {{$from := DateTime "long" (index $parsedDeadline 1)}}
- {{$to := DateTime "long" (index $parsedDeadline 0)}}
+ {{$to := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 0)|ctx.DateUtils.ParseLegacy)}}
+ {{$from := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 1)|ctx.DateUtils.ParseLegacy)}}
{{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}}
{{end}}
@@ -323,7 +324,8 @@
{{template "shared/user/avatarlink" dict "user" .Poster}}
{{template "shared/user/authorlink" .Poster}}
- {{ctx.Locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content) $createdStr}}
+ {{$dueDate := ctx.DateUtils.AbsoluteLong (.Content|ctx.DateUtils.ParseLegacy)}}
+ {{ctx.Locale.Tr "repo.issues.due_date_remove" $dueDate $createdStr}}
{{else if eq .Type 19}}
diff --git a/templates/repo/issue/view_content/sidebar/due_deadline.tmpl b/templates/repo/issue/view_content/sidebar/due_deadline.tmpl
index 2de836b4ed..1a87af6973 100644
--- a/templates/repo/issue/view_content/sidebar/due_deadline.tmpl
+++ b/templates/repo/issue/view_content/sidebar/due_deadline.tmpl
@@ -9,7 +9,7 @@
{{svg "octicon-calendar" 16 "tw-mr-2"}}
- {{DateTime "long" .Issue.DeadlineUnix.FormatDate}}
+ {{ctx.DateUtils.AbsoluteLong .Issue.DeadlineUnix}}
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl
index 3554cf6a19..a630c7c3f2 100644
--- a/templates/repo/pulse.tmpl
+++ b/templates/repo/pulse.tmpl
@@ -1,5 +1,5 @@