summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2024-11-04 19:30:00 +0800
committerGitHub <noreply@github.com>2024-11-04 11:30:00 +0000
commitb068dbd40ee3b4dc7d18cdcf168f0c24cea234c0 (patch)
tree154bf826f6850bed412c8d94be37bfcec94adbc6 /modules
parent61be51e56baf037aa7902e7cd066b895a10da244 (diff)
downloadgitea-b068dbd40ee3b4dc7d18cdcf168f0c24cea234c0.tar.gz
gitea-b068dbd40ee3b4dc7d18cdcf168f0c24cea234c0.zip
Refactor DateUtils and merge TimeSince (#32409)
Follow #32383 and #32402
Diffstat (limited to 'modules')
-rw-r--r--modules/templates/helper.go15
-rw-r--r--modules/templates/util_date.go111
-rw-r--r--modules/templates/util_date_test.go23
-rw-r--r--modules/timeutil/datetime.go60
-rw-r--r--modules/timeutil/since.go41
5 files changed, 133 insertions, 117 deletions
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index a5168541d8..a01aad06a1 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -20,7 +20,6 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
"code.gitea.io/gitea/modules/templates/eval"
- "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
"code.gitea.io/gitea/services/webtheme"
@@ -67,16 +66,18 @@ func NewFuncMap() template.FuncMap {
// -----------------------------------------------------------------
// time / number / format
- "FileSize": base.FileSize,
- "CountFmt": base.FormatNumberSI,
- "TimeSince": timeutil.TimeSince,
- "TimeSinceUnix": timeutil.TimeSinceUnix,
- "DateTime": dateTimeLegacy, // for backward compatibility only, do not use it anymore
- "Sec2Time": util.SecToTime,
+ "FileSize": base.FileSize,
+ "CountFmt": base.FormatNumberSI,
+ "Sec2Time": util.SecToTime,
"LoadTimes": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
},
+ // for backward compatibility only, do not use them anymore
+ "TimeSince": timeSinceLegacy,
+ "TimeSinceUnix": timeSinceLegacy,
+ "DateTime": dateTimeLegacy,
+
// -----------------------------------------------------------------
// setting
"AppName": func() string {
diff --git a/modules/templates/util_date.go b/modules/templates/util_date.go
index 45dd8da02f..b9e04401f1 100644
--- a/modules/templates/util_date.go
+++ b/modules/templates/util_date.go
@@ -4,35 +4,40 @@
package templates
import (
- "context"
+ "fmt"
+ "html"
"html/template"
+ "strings"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/translation"
)
-type DateUtils struct {
- ctx context.Context
-}
+type DateUtils struct{}
-func NewDateUtils(ctx context.Context) *DateUtils {
- return &DateUtils{ctx}
+func NewDateUtils() *DateUtils {
+ return (*DateUtils)(nil) // the util is stateless, and we do not need to create an instance
}
// AbsoluteShort renders in "Jan 01, 2006" format
func (du *DateUtils) AbsoluteShort(time any) template.HTML {
- return timeutil.DateTime("short", time)
+ return dateTimeFormat("short", time)
}
// AbsoluteLong renders in "January 01, 2006" format
func (du *DateUtils) AbsoluteLong(time any) template.HTML {
- return timeutil.DateTime("short", time)
+ return dateTimeFormat("short", time)
}
// FullTime renders in "Jan 01, 2006 20:33:44" format
func (du *DateUtils) FullTime(time any) template.HTML {
- return timeutil.DateTime("full", time)
+ return dateTimeFormat("full", time)
+}
+
+func (du *DateUtils) TimeSince(time any) template.HTML {
+ return TimeSince(time)
}
// 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 {
if s, ok := datetime.(string); ok {
datetime = parseLegacy(s)
}
- return timeutil.DateTime(format, datetime)
+ return dateTimeFormat(format, datetime)
+}
+
+func timeSinceLegacy(time any, _ translation.Locale) template.HTML {
+ if !setting.IsProd || setting.IsInTesting {
+ panic("timeSinceLegacy is for backward compatibility only, do not use it in new code")
+ }
+ return TimeSince(time)
+}
+
+func anyToTime(any any) (t time.Time, isZero bool) {
+ switch v := any.(type) {
+ case nil:
+ // it is zero
+ case *time.Time:
+ if v != nil {
+ t = *v
+ }
+ case time.Time:
+ t = v
+ case timeutil.TimeStamp:
+ t = v.AsTime()
+ case timeutil.TimeStampNano:
+ t = v.AsTime()
+ case int:
+ t = timeutil.TimeStamp(v).AsTime()
+ case int64:
+ t = timeutil.TimeStamp(v).AsTime()
+ default:
+ panic(fmt.Sprintf("Unsupported time type %T", any))
+ }
+ return t, t.IsZero() || t.Unix() == 0
+}
+
+func dateTimeFormat(format string, datetime any) template.HTML {
+ t, isZero := anyToTime(datetime)
+ if isZero {
+ return "-"
+ }
+ var textEscaped string
+ datetimeEscaped := html.EscapeString(t.Format(time.RFC3339))
+ if format == "full" {
+ textEscaped = html.EscapeString(t.Format("2006-01-02 15:04:05 -07:00"))
+ } else {
+ textEscaped = html.EscapeString(t.Format("2006-01-02"))
+ }
+
+ attrs := []string{`weekday=""`, `year="numeric"`}
+ switch format {
+ case "short", "long": // date only
+ attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
+ return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
+ case "full": // full date including time
+ attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
+ return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
+ default:
+ panic(fmt.Sprintf("Unsupported format %s", format))
+ }
+}
+
+func timeSinceTo(then any, now time.Time) template.HTML {
+ thenTime, isZero := anyToTime(then)
+ if isZero {
+ return "-"
+ }
+
+ friendlyText := thenTime.Format("2006-01-02 15:04:05 -07:00")
+
+ // document: https://github.com/github/relative-time-element
+ attrs := `tense="past"`
+ isFuture := now.Before(thenTime)
+ if isFuture {
+ attrs = `tense="future"`
+ }
+
+ // declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
+ htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`,
+ attrs, thenTime.Format(time.RFC3339), friendlyText)
+ return template.HTML(htm)
+}
+
+// TimeSince renders relative time HTML given a time
+func TimeSince(then any) template.HTML {
+ if setting.UI.PreferredTimestampTense == "absolute" {
+ return dateTimeFormat("full", then)
+ }
+ return timeSinceTo(then, time.Now())
}
diff --git a/modules/templates/util_date_test.go b/modules/templates/util_date_test.go
index 96c3776d39..f3a2409a9f 100644
--- a/modules/templates/util_date_test.go
+++ b/modules/templates/util_date_test.go
@@ -19,7 +19,7 @@ func TestDateTime(t *testing.T) {
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
- du := NewDateUtils(nil)
+ du := NewDateUtils()
refTimeStr := "2018-01-01T00:00:00Z"
refDateStr := "2018-01-01"
@@ -49,3 +49,24 @@ func TestDateTime(t *testing.T) {
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)
}
+
+func TestTimeSince(t *testing.T) {
+ testTz, _ := time.LoadLocation("America/New_York")
+ defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
+ defer test.MockVariableValue(&setting.IsInTesting, false)()
+
+ du := NewDateUtils()
+ assert.EqualValues(t, "-", du.TimeSince(nil))
+
+ refTimeStr := "2018-01-01T00:00:00Z"
+ refTime, _ := time.Parse(time.RFC3339, refTimeStr)
+
+ actual := du.TimeSince(refTime)
+ 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)
+
+ actual = timeSinceTo(&refTime, time.Time{})
+ 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)
+
+ actual = timeSinceLegacy(timeutil.TimeStampNano(refTime.UnixNano()), nil)
+ 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)
+}
diff --git a/modules/timeutil/datetime.go b/modules/timeutil/datetime.go
deleted file mode 100644
index 664e0320b0..0000000000
--- a/modules/timeutil/datetime.go
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package timeutil
-
-import (
- "fmt"
- "html"
- "html/template"
- "strings"
- "time"
-)
-
-// DateTime renders an absolute time HTML element by datetime.
-func DateTime(format string, datetime any) template.HTML {
- if p, ok := datetime.(*time.Time); ok {
- datetime = *p
- }
- if p, ok := datetime.(*TimeStamp); ok {
- datetime = *p
- }
- switch v := datetime.(type) {
- case TimeStamp:
- datetime = v.AsTime()
- case int:
- datetime = TimeStamp(v).AsTime()
- case int64:
- datetime = TimeStamp(v).AsTime()
- }
-
- var datetimeEscaped, textEscaped string
- switch v := datetime.(type) {
- case nil:
- return "-"
- case time.Time:
- if v.IsZero() || v.Unix() == 0 {
- return "-"
- }
- datetimeEscaped = html.EscapeString(v.Format(time.RFC3339))
- if format == "full" {
- textEscaped = html.EscapeString(v.Format("2006-01-02 15:04:05 -07:00"))
- } else {
- textEscaped = html.EscapeString(v.Format("2006-01-02"))
- }
- default:
- panic(fmt.Sprintf("Unsupported time type %T", datetime))
- }
-
- attrs := []string{`weekday=""`, `year="numeric"`}
- switch format {
- case "short", "long": // date only
- attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
- return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
- case "full": // full date including time
- attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
- return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
- default:
- panic(fmt.Sprintf("Unsupported format %s", format))
- }
-}
diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go
index dba42c793a..2c89ae38d5 100644
--- a/modules/timeutil/since.go
+++ b/modules/timeutil/since.go
@@ -4,12 +4,9 @@
package timeutil
import (
- "fmt"
- "html/template"
"strings"
"time"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation"
)
@@ -81,16 +78,11 @@ func computeTimeDiffFloor(diff int64, lang translation.Locale) (int64, string) {
return diff, diffStr
}
-// MinutesToFriendly returns a user friendly string with number of minutes
+// MinutesToFriendly returns a user-friendly string with number of minutes
// converted to hours and minutes.
func MinutesToFriendly(minutes int, lang translation.Locale) string {
duration := time.Duration(minutes) * time.Minute
- return TimeSincePro(time.Now().Add(-duration), lang)
-}
-
-// TimeSincePro calculates the time interval and generate full user-friendly string.
-func TimeSincePro(then time.Time, lang translation.Locale) string {
- return timeSincePro(then, time.Now(), lang)
+ return timeSincePro(time.Now().Add(-duration), time.Now(), lang)
}
func timeSincePro(then, now time.Time, lang translation.Locale) string {
@@ -114,32 +106,3 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string {
}
return strings.TrimPrefix(timeStr, ", ")
}
-
-func timeSinceUnix(then, now time.Time, _ translation.Locale) template.HTML {
- friendlyText := then.Format("2006-01-02 15:04:05 -07:00")
-
- // document: https://github.com/github/relative-time-element
- attrs := `tense="past"`
- isFuture := now.Before(then)
- if isFuture {
- attrs = `tense="future"`
- }
-
- // declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
- htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`,
- attrs, then.Format(time.RFC3339), friendlyText)
- return template.HTML(htm)
-}
-
-// TimeSince renders relative time HTML given a time.Time
-func TimeSince(then time.Time, lang translation.Locale) template.HTML {
- if setting.UI.PreferredTimestampTense == "absolute" {
- return DateTime("full", then)
- }
- return timeSinceUnix(then, time.Now(), lang)
-}
-
-// TimeSinceUnix renders relative time HTML given a TimeStamp
-func TimeSinceUnix(then TimeStamp, lang translation.Locale) template.HTML {
- return TimeSince(then.AsLocalTime(), lang)
-}