mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-25 14:35:40 +00:00
[PORT] Replace DateTime with proper functions (gitea#32402)
Follow https://github.com/go-gitea/gitea/pull/32383 This PR cleans up the "Deadline" usages in templates, make them call `ParseLegacy` first to get a `Time` struct then display by `DateUtils`. Now it should be pretty clear how "deadline string" works, it makes it possible to do further refactoring and correcting. (cherry picked from commit 259811617ba15c77ddd89360178a59251d611af2)
This commit is contained in:
parent
f2eabf6308
commit
fddde93759
|
@ -52,7 +52,7 @@ func NewFuncMap() template.FuncMap {
|
||||||
"StringUtils": NewStringUtils,
|
"StringUtils": NewStringUtils,
|
||||||
"SliceUtils": NewSliceUtils,
|
"SliceUtils": NewSliceUtils,
|
||||||
"JsonUtils": NewJsonUtils,
|
"JsonUtils": NewJsonUtils,
|
||||||
"DateUtils": NewDateUtils, // TODO: to be replaced by DateUtils
|
"DateUtils": NewDateUtils,
|
||||||
|
|
||||||
// -----------------------------------------------------------------
|
// -----------------------------------------------------------------
|
||||||
// svg / avatar / icon / color
|
// svg / avatar / icon / color
|
||||||
|
@ -69,7 +69,7 @@ func NewFuncMap() template.FuncMap {
|
||||||
"CountFmt": base.FormatNumberSI,
|
"CountFmt": base.FormatNumberSI,
|
||||||
"TimeSince": timeutil.TimeSince,
|
"TimeSince": timeutil.TimeSince,
|
||||||
"TimeSinceUnix": timeutil.TimeSinceUnix,
|
"TimeSinceUnix": timeutil.TimeSinceUnix,
|
||||||
"DateTime": timeutil.DateTime,
|
"DateTime": dateTimeLegacy, // for backward compatibility only, do not use it anymore
|
||||||
"Sec2Time": util.SecToTime,
|
"Sec2Time": util.SecToTime,
|
||||||
"LoadTimes": func(startTime time.Time) string {
|
"LoadTimes": func(startTime time.Time) string {
|
||||||
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
|
||||||
|
|
|
@ -6,7 +6,9 @@ package templates
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,3 +34,27 @@ func (du *DateUtils) AbsoluteLong(time any) template.HTML {
|
||||||
func (du *DateUtils) FullTime(time any) template.HTML {
|
func (du *DateUtils) FullTime(time any) template.HTML {
|
||||||
return timeutil.DateTime("full", time)
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
package timeutil
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -16,32 +17,35 @@ import (
|
||||||
func TestDateTime(t *testing.T) {
|
func TestDateTime(t *testing.T) {
|
||||||
testTz, _ := time.LoadLocation("America/New_York")
|
testTz, _ := time.LoadLocation("America/New_York")
|
||||||
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
|
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
|
||||||
|
defer test.MockVariableValue(&setting.IsInTesting, false)()
|
||||||
|
|
||||||
|
du := NewDateUtils(nil)
|
||||||
|
|
||||||
refTimeStr := "2018-01-01T00:00:00Z"
|
refTimeStr := "2018-01-01T00:00:00Z"
|
||||||
refDateStr := "2018-01-01"
|
refDateStr := "2018-01-01"
|
||||||
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
|
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
|
||||||
refTimeStamp := TimeStamp(refTime.Unix())
|
refTimeStamp := timeutil.TimeStamp(refTime.Unix())
|
||||||
|
|
||||||
assert.EqualValues(t, "-", DateTime("short", nil))
|
assert.EqualValues(t, "-", du.AbsoluteShort(nil))
|
||||||
assert.EqualValues(t, "-", DateTime("short", 0))
|
assert.EqualValues(t, "-", du.AbsoluteShort(0))
|
||||||
assert.EqualValues(t, "-", DateTime("short", time.Time{}))
|
assert.EqualValues(t, "-", du.AbsoluteShort(time.Time{}))
|
||||||
assert.EqualValues(t, "-", DateTime("short", TimeStamp(0)))
|
assert.EqualValues(t, "-", du.AbsoluteShort(timeutil.TimeStamp(0)))
|
||||||
|
|
||||||
actual := DateTime("short", "invalid")
|
actual := dateTimeLegacy("short", "invalid")
|
||||||
assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="invalid">invalid</absolute-date>`, actual)
|
assert.EqualValues(t, `-`, actual)
|
||||||
|
|
||||||
actual = DateTime("short", refTimeStr)
|
actual = dateTimeLegacy("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)
|
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)
|
actual = du.AbsoluteShort(refTime)
|
||||||
assert.EqualValues(t, `<absolute-date weekday="" year="numeric" month="short" day="numeric" date="2018-01-01">2018-01-01</absolute-date>`, actual)
|
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", refTimeStamp)
|
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)
|
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)
|
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)
|
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)
|
||||||
}
|
}
|
|
@ -12,9 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// DateTime renders an absolute time HTML element by datetime.
|
// DateTime renders an absolute time HTML element by datetime.
|
||||||
func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
|
func DateTime(format string, datetime any) template.HTML {
|
||||||
// TODO: remove the extraAttrs argument, it's not used in any call to DateTime
|
|
||||||
|
|
||||||
if p, ok := datetime.(*time.Time); ok {
|
if p, ok := datetime.(*time.Time); ok {
|
||||||
datetime = *p
|
datetime = *p
|
||||||
}
|
}
|
||||||
|
@ -34,9 +32,6 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
|
||||||
switch v := datetime.(type) {
|
switch v := datetime.(type) {
|
||||||
case nil:
|
case nil:
|
||||||
return "-"
|
return "-"
|
||||||
case string:
|
|
||||||
datetimeEscaped = html.EscapeString(v)
|
|
||||||
textEscaped = datetimeEscaped
|
|
||||||
case time.Time:
|
case time.Time:
|
||||||
if v.IsZero() || v.Unix() == 0 {
|
if v.IsZero() || v.Unix() == 0 {
|
||||||
return "-"
|
return "-"
|
||||||
|
@ -51,10 +46,7 @@ func DateTime(format string, datetime any, extraAttrs ...string) template.HTML {
|
||||||
panic(fmt.Sprintf("Unsupported time type %T", datetime))
|
panic(fmt.Sprintf("Unsupported time type %T", datetime))
|
||||||
}
|
}
|
||||||
|
|
||||||
attrs := make([]string, 0, 10+len(extraAttrs))
|
attrs := []string{`weekday=""`, `year="numeric"`}
|
||||||
attrs = append(attrs, extraAttrs...)
|
|
||||||
attrs = append(attrs, `weekday=""`, `year="numeric"`)
|
|
||||||
|
|
||||||
switch format {
|
switch format {
|
||||||
case "short", "long": // date only
|
case "short", "long": // date only
|
||||||
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
|
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<td>{{.ID}}</td>
|
<td>{{.ID}}</td>
|
||||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
|
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
|
||||||
<td>{{.TypeName}}</td>
|
<td>{{.TypeName}}</td>
|
||||||
<td>{{svg (Iif .IsActive "octicon-check" "octicon-x")}}</td>
|
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
|
||||||
<td>{{ctx.DateUtils.AbsoluteShort .UpdatedUnix}}</td>
|
<td>{{ctx.DateUtils.AbsoluteShort .UpdatedUnix}}</td>
|
||||||
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td>
|
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td>
|
||||||
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
|
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
{{if .Milestone.DeadlineString}}
|
{{if .Milestone.DeadlineString}}
|
||||||
<span{{if .IsOverdue}} class="text red"{{end}}>
|
<span{{if .IsOverdue}} class="text red"{{end}}>
|
||||||
{{svg "octicon-calendar"}}
|
{{svg "octicon-calendar"}}
|
||||||
{{DateTime "short" .Milestone.DeadlineString}}
|
{{ctx.DateUtils.AbsoluteShort (.Milestone.DeadlineString|ctx.DateUtils.ParseLegacy)}}
|
||||||
</span>
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-calendar"}}
|
{{svg "octicon-calendar"}}
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
{{if .DeadlineString}}
|
{{if .DeadlineString}}
|
||||||
<span class="flex-text-inline {{if .IsOverdue}}text red{{end}}">
|
<span class="flex-text-inline {{if .IsOverdue}}text red{{end}}">
|
||||||
{{svg "octicon-calendar" 14}}
|
{{svg "octicon-calendar" 14}}
|
||||||
{{DateTime "short" .DeadlineString}}
|
{{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
|
||||||
</span>
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-calendar" 14}}
|
{{svg "octicon-calendar" 14}}
|
||||||
|
|
|
@ -300,7 +300,8 @@
|
||||||
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .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}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 17}}
|
{{else if eq .Type 17}}
|
||||||
|
@ -311,8 +312,8 @@
|
||||||
{{template "shared/user/authorlink" .Poster}}
|
{{template "shared/user/authorlink" .Poster}}
|
||||||
{{$parsedDeadline := StringUtils.Split .Content "|"}}
|
{{$parsedDeadline := StringUtils.Split .Content "|"}}
|
||||||
{{if eq (len $parsedDeadline) 2}}
|
{{if eq (len $parsedDeadline) 2}}
|
||||||
{{$from := DateTime "long" (index $parsedDeadline 1)}}
|
{{$to := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 0)|ctx.DateUtils.ParseLegacy)}}
|
||||||
{{$to := DateTime "long" (index $parsedDeadline 0)}}
|
{{$from := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 1)|ctx.DateUtils.ParseLegacy)}}
|
||||||
{{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}}
|
{{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -323,7 +324,8 @@
|
||||||
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
{{template "shared/user/avatarlink" dict "user" .Poster}}
|
||||||
<span class="text grey muted-links">
|
<span class="text grey muted-links">
|
||||||
{{template "shared/user/authorlink" .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}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{{else if eq .Type 19}}
|
{{else if eq .Type 19}}
|
||||||
|
|
|
@ -116,7 +116,7 @@
|
||||||
{{if .DeadlineString}}
|
{{if .DeadlineString}}
|
||||||
<span{{if .IsOverdue}} class="text red"{{end}}>
|
<span{{if .IsOverdue}} class="text red"{{end}}>
|
||||||
{{svg "octicon-calendar" 14}}
|
{{svg "octicon-calendar" 14}}
|
||||||
{{DateTime "short" .DeadlineString}}
|
{{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}}
|
||||||
</span>
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{svg "octicon-calendar" 14}}
|
{{svg "octicon-calendar" 14}}
|
||||||
|
|
Loading…
Reference in a new issue