"StringUtils": NewStringUtils,
"SliceUtils": NewSliceUtils,
"JsonUtils": NewJsonUtils,
- "DateUtils": NewDateUtils, // TODO: to be replaced by DateUtils
+ "DateUtils": NewDateUtils,
// -----------------------------------------------------------------
// svg / avatar / icon / color
"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"
import (
"context"
"html/template"
+ "time"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
)
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)
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package templates
+
+import (
+ "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"
+)
+
+func TestDateTime(t *testing.T) {
+ testTz, _ := time.LoadLocation("America/New_York")
+ 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 := timeutil.TimeStamp(refTime.Unix())
+
+ assert.EqualValues(t, "-", du.AbsoluteShort(nil))
+ assert.EqualValues(t, "-", du.AbsoluteShort(0))
+ assert.EqualValues(t, "-", du.AbsoluteShort(time.Time{}))
+ assert.EqualValues(t, "-", du.AbsoluteShort(timeutil.TimeStamp(0)))
+
+ actual := dateTimeLegacy("short", "invalid")
+ assert.EqualValues(t, `-`, actual)
+
+ actual = dateTimeLegacy("short", refTimeStr)
+ assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual)
+
+ actual = du.AbsoluteShort(refTime)
+ assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual)
+
+ actual = dateTimeLegacy("short", refDateStr)
+ assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00-05:00">2018-01-01</absolute-date>`, actual)
+
+ actual = du.AbsoluteShort(refTimeStamp)
+ assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2017-12-31T19:00:00-05:00">2017-12-31</absolute-date>`, actual)
+
+ actual = du.FullTime(refTimeStamp)
+ assert.EqualValues(t, `<relative-time weekday="" year="numeric" format="datetime" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" data-tooltip-content data-tooltip-interactive="true" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
+}
)
// 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
}
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 "-"
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"`)
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package timeutil
-
-import (
- "testing"
- "time"
-
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/test"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestDateTime(t *testing.T) {
- testTz, _ := time.LoadLocation("America/New_York")
- defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
-
- refTimeStr := "2018-01-01T00:00:00Z"
- refDateStr := "2018-01-01"
- refTime, _ := time.Parse(time.RFC3339, refTimeStr)
- refTimeStamp := 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)))
-
- actual := DateTime("short", "invalid")
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="invalid">invalid</absolute-date>`, actual)
-
- actual = DateTime("short", refTimeStr)
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01T00:00:00Z</absolute-date>`, actual)
-
- actual = DateTime("short", refTime)
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01T00:00:00Z">2018-01-01</absolute-date>`, actual)
-
- actual = DateTime("short", refDateStr)
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01">2018-01-01</absolute-date>`, actual)
-
- actual = DateTime("short", refTimeStamp)
- assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2017-12-31T19:00:00-05:00">2017-12-31</absolute-date>`, actual)
-
- actual = DateTime("full", refTimeStamp)
- assert.EqualValues(t, `<relative-time weekday="" year="numeric" format="datetime" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" data-tooltip-content data-tooltip-interactive="true" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
-}
{{if .Milestone.DeadlineString}}
<span{{if .IsOverdue}} class="text red"{{end}}>
{{svg "octicon-calendar"}}
- {{DateTime "short" .Milestone.DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.Milestone.DeadlineString|ctx.DateUtils.ParseLegacy)}}
</span>
{{else}}
{{svg "octicon-calendar"}}
{{if .DeadlineString}}
<span class="flex-text-inline {{if .IsOverdue}}text red{{end}}">
{{svg "octicon-calendar" 14}}
- {{DateTime "short" .DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
</span>
{{else}}
{{svg "octicon-calendar" 14}}
{{template "shared/user/avatarlink" dict "user" .Poster}}
<span class="text grey muted-links">
{{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}}
</span>
</div>
{{else if eq .Type 17}}
{{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}}
</span>
{{template "shared/user/avatarlink" dict "user" .Poster}}
<span class="text grey muted-links">
{{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}}
</span>
</div>
{{else if eq .Type 19}}
{{if .DeadlineString}}
<span{{if .IsOverdue}} class="text red"{{end}}>
{{svg "octicon-calendar" 14}}
- {{DateTime "short" .DeadlineString}}
+ {{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
</span>
{{else}}
{{svg "octicon-calendar" 14}}