diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2024-11-04 19:30:00 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-04 11:30:00 +0000 |
commit | b068dbd40ee3b4dc7d18cdcf168f0c24cea234c0 (patch) | |
tree | 154bf826f6850bed412c8d94be37bfcec94adbc6 /modules | |
parent | 61be51e56baf037aa7902e7cd066b895a10da244 (diff) | |
download | gitea-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.go | 15 | ||||
-rw-r--r-- | modules/templates/util_date.go | 111 | ||||
-rw-r--r-- | modules/templates/util_date_test.go | 23 | ||||
-rw-r--r-- | modules/timeutil/datetime.go | 60 | ||||
-rw-r--r-- | modules/timeutil/since.go | 41 |
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) -} |