Skip to content

Commit b068dbd

Browse files
authored
Refactor DateUtils and merge TimeSince (#32409)
Follow #32383 and #32402
1 parent 61be51e commit b068dbd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+241
-228
lines changed

Diff for: modules/templates/helper.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"code.gitea.io/gitea/modules/setting"
2121
"code.gitea.io/gitea/modules/svg"
2222
"code.gitea.io/gitea/modules/templates/eval"
23-
"code.gitea.io/gitea/modules/timeutil"
2423
"code.gitea.io/gitea/modules/util"
2524
"code.gitea.io/gitea/services/gitdiff"
2625
"code.gitea.io/gitea/services/webtheme"
@@ -67,16 +66,18 @@ func NewFuncMap() template.FuncMap {
6766

6867
// -----------------------------------------------------------------
6968
// time / number / format
70-
"FileSize": base.FileSize,
71-
"CountFmt": base.FormatNumberSI,
72-
"TimeSince": timeutil.TimeSince,
73-
"TimeSinceUnix": timeutil.TimeSinceUnix,
74-
"DateTime": dateTimeLegacy, // for backward compatibility only, do not use it anymore
75-
"Sec2Time": util.SecToTime,
69+
"FileSize": base.FileSize,
70+
"CountFmt": base.FormatNumberSI,
71+
"Sec2Time": util.SecToTime,
7672
"LoadTimes": func(startTime time.Time) string {
7773
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
7874
},
7975

76+
// for backward compatibility only, do not use them anymore
77+
"TimeSince": timeSinceLegacy,
78+
"TimeSinceUnix": timeSinceLegacy,
79+
"DateTime": dateTimeLegacy,
80+
8081
// -----------------------------------------------------------------
8182
// setting
8283
"AppName": func() string {

Diff for: modules/templates/util_date.go

+101-10
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,40 @@
44
package templates
55

66
import (
7-
"context"
7+
"fmt"
8+
"html"
89
"html/template"
10+
"strings"
911
"time"
1012

1113
"code.gitea.io/gitea/modules/setting"
1214
"code.gitea.io/gitea/modules/timeutil"
15+
"code.gitea.io/gitea/modules/translation"
1316
)
1417

15-
type DateUtils struct {
16-
ctx context.Context
17-
}
18+
type DateUtils struct{}
1819

19-
func NewDateUtils(ctx context.Context) *DateUtils {
20-
return &DateUtils{ctx}
20+
func NewDateUtils() *DateUtils {
21+
return (*DateUtils)(nil) // the util is stateless, and we do not need to create an instance
2122
}
2223

2324
// AbsoluteShort renders in "Jan 01, 2006" format
2425
func (du *DateUtils) AbsoluteShort(time any) template.HTML {
25-
return timeutil.DateTime("short", time)
26+
return dateTimeFormat("short", time)
2627
}
2728

2829
// AbsoluteLong renders in "January 01, 2006" format
2930
func (du *DateUtils) AbsoluteLong(time any) template.HTML {
30-
return timeutil.DateTime("short", time)
31+
return dateTimeFormat("short", time)
3132
}
3233

3334
// FullTime renders in "Jan 01, 2006 20:33:44" format
3435
func (du *DateUtils) FullTime(time any) template.HTML {
35-
return timeutil.DateTime("full", time)
36+
return dateTimeFormat("full", time)
37+
}
38+
39+
func (du *DateUtils) TimeSince(time any) template.HTML {
40+
return TimeSince(time)
3641
}
3742

3843
// ParseLegacy parses the datetime in legacy format, eg: "2016-01-02" in server's timezone.
@@ -56,5 +61,91 @@ func dateTimeLegacy(format string, datetime any, _ ...string) template.HTML {
5661
if s, ok := datetime.(string); ok {
5762
datetime = parseLegacy(s)
5863
}
59-
return timeutil.DateTime(format, datetime)
64+
return dateTimeFormat(format, datetime)
65+
}
66+
67+
func timeSinceLegacy(time any, _ translation.Locale) template.HTML {
68+
if !setting.IsProd || setting.IsInTesting {
69+
panic("timeSinceLegacy is for backward compatibility only, do not use it in new code")
70+
}
71+
return TimeSince(time)
72+
}
73+
74+
func anyToTime(any any) (t time.Time, isZero bool) {
75+
switch v := any.(type) {
76+
case nil:
77+
// it is zero
78+
case *time.Time:
79+
if v != nil {
80+
t = *v
81+
}
82+
case time.Time:
83+
t = v
84+
case timeutil.TimeStamp:
85+
t = v.AsTime()
86+
case timeutil.TimeStampNano:
87+
t = v.AsTime()
88+
case int:
89+
t = timeutil.TimeStamp(v).AsTime()
90+
case int64:
91+
t = timeutil.TimeStamp(v).AsTime()
92+
default:
93+
panic(fmt.Sprintf("Unsupported time type %T", any))
94+
}
95+
return t, t.IsZero() || t.Unix() == 0
96+
}
97+
98+
func dateTimeFormat(format string, datetime any) template.HTML {
99+
t, isZero := anyToTime(datetime)
100+
if isZero {
101+
return "-"
102+
}
103+
var textEscaped string
104+
datetimeEscaped := html.EscapeString(t.Format(time.RFC3339))
105+
if format == "full" {
106+
textEscaped = html.EscapeString(t.Format("2006-01-02 15:04:05 -07:00"))
107+
} else {
108+
textEscaped = html.EscapeString(t.Format("2006-01-02"))
109+
}
110+
111+
attrs := []string{`weekday=""`, `year="numeric"`}
112+
switch format {
113+
case "short", "long": // date only
114+
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
115+
return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
116+
case "full": // full date including time
117+
attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
118+
return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
119+
default:
120+
panic(fmt.Sprintf("Unsupported format %s", format))
121+
}
122+
}
123+
124+
func timeSinceTo(then any, now time.Time) template.HTML {
125+
thenTime, isZero := anyToTime(then)
126+
if isZero {
127+
return "-"
128+
}
129+
130+
friendlyText := thenTime.Format("2006-01-02 15:04:05 -07:00")
131+
132+
// document: https://github.com/github/relative-time-element
133+
attrs := `tense="past"`
134+
isFuture := now.Before(thenTime)
135+
if isFuture {
136+
attrs = `tense="future"`
137+
}
138+
139+
// declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
140+
htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`,
141+
attrs, thenTime.Format(time.RFC3339), friendlyText)
142+
return template.HTML(htm)
143+
}
144+
145+
// TimeSince renders relative time HTML given a time
146+
func TimeSince(then any) template.HTML {
147+
if setting.UI.PreferredTimestampTense == "absolute" {
148+
return dateTimeFormat("full", then)
149+
}
150+
return timeSinceTo(then, time.Now())
60151
}

Diff for: modules/templates/util_date_test.go

+22-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestDateTime(t *testing.T) {
1919
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
2020
defer test.MockVariableValue(&setting.IsInTesting, false)()
2121

22-
du := NewDateUtils(nil)
22+
du := NewDateUtils()
2323

2424
refTimeStr := "2018-01-01T00:00:00Z"
2525
refDateStr := "2018-01-01"
@@ -49,3 +49,24 @@ func TestDateTime(t *testing.T) {
4949
actual = du.FullTime(refTimeStamp)
5050
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)
5151
}
52+
53+
func TestTimeSince(t *testing.T) {
54+
testTz, _ := time.LoadLocation("America/New_York")
55+
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
56+
defer test.MockVariableValue(&setting.IsInTesting, false)()
57+
58+
du := NewDateUtils()
59+
assert.EqualValues(t, "-", du.TimeSince(nil))
60+
61+
refTimeStr := "2018-01-01T00:00:00Z"
62+
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
63+
64+
actual := du.TimeSince(refTime)
65+
assert.EqualValues(t, `<relative-time prefix="" tense="past" datetime="2018-01-01T00:00:00Z" data-tooltip-content data-tooltip-interactive="true">2018-01-01 00:00:00 +00:00</relative-time>`, actual)
66+
67+
actual = timeSinceTo(&refTime, time.Time{})
68+
assert.EqualValues(t, `<relative-time prefix="" tense="future" datetime="2018-01-01T00:00:00Z" data-tooltip-content data-tooltip-interactive="true">2018-01-01 00:00:00 +00:00</relative-time>`, actual)
69+
70+
actual = timeSinceLegacy(timeutil.TimeStampNano(refTime.UnixNano()), nil)
71+
assert.EqualValues(t, `<relative-time prefix="" tense="past" datetime="2017-12-31T19:00:00-05:00" data-tooltip-content data-tooltip-interactive="true">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
72+
}

Diff for: modules/timeutil/datetime.go

-60
This file was deleted.

Diff for: modules/timeutil/since.go

+2-39
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,9 @@
44
package timeutil
55

66
import (
7-
"fmt"
8-
"html/template"
97
"strings"
108
"time"
119

12-
"code.gitea.io/gitea/modules/setting"
1310
"code.gitea.io/gitea/modules/translation"
1411
)
1512

@@ -81,16 +78,11 @@ func computeTimeDiffFloor(diff int64, lang translation.Locale) (int64, string) {
8178
return diff, diffStr
8279
}
8380

84-
// MinutesToFriendly returns a user friendly string with number of minutes
81+
// MinutesToFriendly returns a user-friendly string with number of minutes
8582
// converted to hours and minutes.
8683
func MinutesToFriendly(minutes int, lang translation.Locale) string {
8784
duration := time.Duration(minutes) * time.Minute
88-
return TimeSincePro(time.Now().Add(-duration), lang)
89-
}
90-
91-
// TimeSincePro calculates the time interval and generate full user-friendly string.
92-
func TimeSincePro(then time.Time, lang translation.Locale) string {
93-
return timeSincePro(then, time.Now(), lang)
85+
return timeSincePro(time.Now().Add(-duration), time.Now(), lang)
9486
}
9587

9688
func timeSincePro(then, now time.Time, lang translation.Locale) string {
@@ -114,32 +106,3 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string {
114106
}
115107
return strings.TrimPrefix(timeStr, ", ")
116108
}
117-
118-
func timeSinceUnix(then, now time.Time, _ translation.Locale) template.HTML {
119-
friendlyText := then.Format("2006-01-02 15:04:05 -07:00")
120-
121-
// document: https://github.com/github/relative-time-element
122-
attrs := `tense="past"`
123-
isFuture := now.Before(then)
124-
if isFuture {
125-
attrs = `tense="future"`
126-
}
127-
128-
// declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
129-
htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`,
130-
attrs, then.Format(time.RFC3339), friendlyText)
131-
return template.HTML(htm)
132-
}
133-
134-
// TimeSince renders relative time HTML given a time.Time
135-
func TimeSince(then time.Time, lang translation.Locale) template.HTML {
136-
if setting.UI.PreferredTimestampTense == "absolute" {
137-
return DateTime("full", then)
138-
}
139-
return timeSinceUnix(then, time.Now(), lang)
140-
}
141-
142-
// TimeSinceUnix renders relative time HTML given a TimeStamp
143-
func TimeSinceUnix(then TimeStamp, lang translation.Locale) template.HTML {
144-
return TimeSince(then.AsLocalTime(), lang)
145-
}

Diff for: routers/web/repo/blame.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
"code.gitea.io/gitea/modules/log"
1919
"code.gitea.io/gitea/modules/setting"
2020
"code.gitea.io/gitea/modules/templates"
21-
"code.gitea.io/gitea/modules/timeutil"
2221
"code.gitea.io/gitea/modules/util"
2322
"code.gitea.io/gitea/services/context"
2423
files_service "code.gitea.io/gitea/services/repository/files"
@@ -280,7 +279,7 @@ func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames
280279
commitCnt++
281280

282281
// User avatar image
283-
commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Locale)
282+
commitSince := templates.TimeSince(commit.Author.When)
284283

285284
var avatar string
286285
if commit.User != nil {

Diff for: routers/web/repo/issue_content_history.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"code.gitea.io/gitea/modules/log"
1515
"code.gitea.io/gitea/modules/setting"
1616
"code.gitea.io/gitea/modules/templates"
17-
"code.gitea.io/gitea/modules/timeutil"
1817
"code.gitea.io/gitea/services/context"
1918

2019
"github.com/sergi/go-diff/diffmatchpatch"
@@ -73,10 +72,10 @@ func GetContentHistoryList(ctx *context.Context) {
7372
class := avatars.DefaultAvatarClass + " tw-mr-2"
7473
name := html.EscapeString(username)
7574
avatarHTML := string(templates.AvatarHTML(src, 28, class, username))
76-
timeSinceText := string(timeutil.TimeSinceUnix(item.EditedUnix, ctx.Locale))
75+
timeSinceHTML := string(templates.TimeSince(item.EditedUnix))
7776

7877
results = append(results, map[string]any{
79-
"name": avatarHTML + "<strong>" + name + "</strong> " + actionText + " " + timeSinceText,
78+
"name": avatarHTML + "<strong>" + name + "</strong> " + actionText + " " + timeSinceHTML,
8079
"value": item.HistoryID,
8180
})
8281
}

Diff for: services/context/context.go

-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
100100
tmplCtx := NewTemplateContext(ctx)
101101
tmplCtx["Locale"] = ctx.Base.Locale
102102
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
103-
tmplCtx["DateUtils"] = templates.NewDateUtils(ctx)
104103
tmplCtx["RootData"] = ctx.Data
105104
tmplCtx["Consts"] = map[string]any{
106105
"RepoUnitTypeCode": unit.TypeCode,

Diff for: templates/admin/auth/list.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
<td><a href="{{AppSubUrl}}/-/admin/auths/{{.ID}}">{{.Name}}</a></td>
2727
<td>{{.TypeName}}</td>
2828
<td>{{svg (Iif .IsActive "octicon-check" "octicon-x")}}</td>
29-
<td>{{ctx.DateUtils.AbsoluteShort .UpdatedUnix}}</td>
30-
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td>
29+
<td>{{DateUtils.AbsoluteShort .UpdatedUnix}}</td>
30+
<td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
3131
<td><a href="{{AppSubUrl}}/-/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
3232
</tr>
3333
{{end}}

0 commit comments

Comments
 (0)