Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix milestone deadline and date related problems #32339

Merged
merged 11 commits into from
Nov 5, 2024
8 changes: 3 additions & 5 deletions models/actions/schedule_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,17 @@ import (
"testing"
"time"

"code.gitea.io/gitea/modules/test"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestActionScheduleSpec_Parse(t *testing.T) {
// Mock the local timezone is not UTC
local := time.Local
tz, err := time.LoadLocation("Asia/Shanghai")
require.NoError(t, err)
defer func() {
time.Local = local
}()
time.Local = tz
defer test.MockVariableValue(&time.Local, tz)()

now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
require.NoError(t, err)
Expand Down
3 changes: 1 addition & 2 deletions models/issues/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,9 @@ func (m *Milestone) BeforeUpdate() {
// this object.
func (m *Milestone) AfterLoad() {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
if m.DeadlineUnix.Year() == 9999 {
if m.DeadlineUnix == 0 {
return
}

m.DeadlineString = m.DeadlineUnix.FormatDate()
if m.IsClosed {
m.IsOverdue = m.ClosedDateUnix >= m.DeadlineUnix
Expand Down
1 change: 1 addition & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ func prepareMigrationTasks() []*migration {
newMigration(304, "Add index for release sha1", v1_23.AddIndexForReleaseSha1),
newMigration(305, "Add Repository Licenses", v1_23.AddRepositoryLicenses),
newMigration(306, "Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection),
newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate),
}
return preparedMigrations
}
Expand Down
21 changes: 21 additions & 0 deletions models/migrations/v1_23/v307.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_23 //nolint

import (
"code.gitea.io/gitea/modules/timeutil"

"xorm.io/xorm"
)

func FixMilestoneNoDueDate(x *xorm.Engine) error {
type Milestone struct {
DeadlineUnix timeutil.TimeStamp
}
// Wednesday, December 1, 9999 12:00:00 AM GMT+00:00
_, err := x.Table("milestone").Where("deadline_unix > 253399622400").
Cols("deadline_unix").
Update(&Milestone{DeadlineUnix: 0})
return err
}
2 changes: 1 addition & 1 deletion modules/gitgraph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
graphCmd.AddArguments("--all")
}

graphCmd.AddArguments("-C", "-M", "--date=iso").
graphCmd.AddArguments("-C", "-M", "--date=iso-strict").
AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page).
AddOptionFormat("--pretty=format:%s", format)

Expand Down
13 changes: 11 additions & 2 deletions modules/gitgraph/graph_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"fmt"
"strings"
"time"

asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
Expand Down Expand Up @@ -192,6 +193,14 @@ var RelationCommit = &Commit{
Row: -1,
}

func parseGitTime(timeStr string) time.Time {
t, err := time.Parse(time.RFC3339, timeStr)
if err != nil {
return time.Unix(0, 0)
}
return t
}

// NewCommit creates a new commit from a provided line
func NewCommit(row, column int, line []byte) (*Commit, error) {
data := bytes.SplitN(line, []byte("|"), 5)
Expand All @@ -206,7 +215,7 @@ func NewCommit(row, column int, line []byte) (*Commit, error) {
// 1 matches git log --pretty=format:%H => commit hash
Rev: string(data[1]),
// 2 matches git log --pretty=format:%ad => author date (format respects --date= option)
Date: string(data[2]),
Date: parseGitTime(string(data[2])),
// 3 matches git log --pretty=format:%h => abbreviated commit hash
ShortRev: string(data[3]),
// 4 matches git log --pretty=format:%s => subject
Expand Down Expand Up @@ -245,7 +254,7 @@ type Commit struct {
Column int
Refs []git.Reference
Rev string
Date string
Date time.Time
ShortRev string
Subject string
}
Expand Down
2 changes: 1 addition & 1 deletion modules/templates/util_date.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func (du *DateUtils) AbsoluteShort(time any) template.HTML {

// AbsoluteLong renders in "January 01, 2006" format
func (du *DateUtils) AbsoluteLong(time any) template.HTML {
return dateTimeFormat("short", time)
return dateTimeFormat("long", time)
}

// FullTime renders in "Jan 01, 2006 20:33:44" format
Expand Down
12 changes: 3 additions & 9 deletions routers/api/v1/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
Expand Down Expand Up @@ -1046,18 +1047,11 @@ func UpdateIssueDeadline(ctx *context.APIContext) {
return
}

var deadlineUnix timeutil.TimeStamp
var deadline time.Time
if form.Deadline != nil && !form.Deadline.IsZero() {
deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
23, 59, 59, 0, time.Local)
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
}

deadlineUnix, _ := common.ParseAPIDeadlineToEndOfDay(form.Deadline)
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
return
}

ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline})
ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: deadlineUnix.AsTimePtr()})
}
14 changes: 6 additions & 8 deletions routers/api/v1/repo/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package repo
import (
"net/http"
"strconv"
"time"

"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
Expand All @@ -16,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
Expand Down Expand Up @@ -155,16 +155,16 @@ func CreateMilestone(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.CreateMilestoneOption)

if form.Deadline == nil {
defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local)
form.Deadline = &defaultDeadline
var deadlineUnix int64
if form.Deadline != nil {
deadlineUnix = form.Deadline.Unix()
}

milestone := &issues_model.Milestone{
RepoID: ctx.Repo.Repository.ID,
Name: form.Title,
Content: form.Description,
DeadlineUnix: timeutil.TimeStamp(form.Deadline.Unix()),
DeadlineUnix: timeutil.TimeStamp(deadlineUnix),
}

if form.State == "closed" {
Expand Down Expand Up @@ -225,9 +225,7 @@ func EditMilestone(ctx *context.APIContext) {
if form.Description != nil {
milestone.Content = *form.Description
}
if form.Deadline != nil && !form.Deadline.IsZero() {
milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
}
milestone.DeadlineUnix, _ = common.ParseAPIDeadlineToEndOfDay(form.Deadline)

oldIsClosed := milestone.IsClosed
if form.State != nil {
Expand Down
31 changes: 31 additions & 0 deletions routers/common/deadline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package common

import (
"time"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
)

func ParseDeadlineDateToEndOfDay(date string) (timeutil.TimeStamp, error) {
if date == "" {
return 0, nil
}
deadline, err := time.ParseInLocation("2006-01-02", date, setting.DefaultUILocation)
if err != nil {
return 0, err
}
deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
return timeutil.TimeStamp(deadline.Unix()), nil
}

func ParseAPIDeadlineToEndOfDay(t *time.Time) (timeutil.TimeStamp, error) {
if t == nil || t.IsZero() || t.Unix() == 0 {
return 0, nil
}
deadline := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, setting.DefaultUILocation)
return timeutil.TimeStamp(deadline.Unix()), nil
}
15 changes: 3 additions & 12 deletions routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"sort"
"strconv"
"strings"
"time"

activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
Expand Down Expand Up @@ -45,9 +44,9 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/routers/utils"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
asymkey_service "code.gitea.io/gitea/services/asymkey"
Expand Down Expand Up @@ -2329,7 +2328,6 @@ func UpdateIssueContent(ctx *context.Context) {

// UpdateIssueDeadline updates an issue deadline
func UpdateIssueDeadline(ctx *context.Context) {
form := web.GetForm(ctx).(*api.EditDeadlineOption)
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
Expand All @@ -2345,20 +2343,13 @@ func UpdateIssueDeadline(ctx *context.Context) {
return
}

var deadlineUnix timeutil.TimeStamp
var deadline time.Time
if form.Deadline != nil && !form.Deadline.IsZero() {
deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
23, 59, 59, 0, time.Local)
deadlineUnix = timeutil.TimeStamp(deadline.Unix())
}

deadlineUnix, _ := common.ParseDeadlineDateToEndOfDay(ctx.FormString("deadline"))
if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
return
}

ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline})
ctx.JSONRedirect("")
lunny marked this conversation as resolved.
Show resolved Hide resolved
}

// UpdateIssueMilestone change issue's milestone
Expand Down
21 changes: 6 additions & 15 deletions routers/web/repo/milestone.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"net/http"
"net/url"
"time"

"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
Expand All @@ -16,8 +15,8 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/issue"
Expand Down Expand Up @@ -134,22 +133,18 @@ func NewMilestonePost(ctx *context.Context) {
return
}

if len(form.Deadline) == 0 {
form.Deadline = "9999-12-31"
}
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
deadlineUnix, err := common.ParseDeadlineDateToEndOfDay(form.Deadline)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
return
}

deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
if err = issues_model.NewMilestone(ctx, &issues_model.Milestone{
if err := issues_model.NewMilestone(ctx, &issues_model.Milestone{
RepoID: ctx.Repo.Repository.ID,
Name: form.Title,
Content: form.Content,
DeadlineUnix: timeutil.TimeStamp(deadline.Unix()),
DeadlineUnix: deadlineUnix,
}); err != nil {
ctx.ServerError("NewMilestone", err)
return
Expand Down Expand Up @@ -194,17 +189,13 @@ func EditMilestonePost(ctx *context.Context) {
return
}

if len(form.Deadline) == 0 {
form.Deadline = "9999-12-31"
}
deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
deadlineUnix, err := common.ParseDeadlineDateToEndOfDay(form.Deadline)
if err != nil {
ctx.Data["Err_Deadline"] = true
ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
return
}

deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
m, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":id"))
if err != nil {
if issues_model.IsErrMilestoneNotExist(err) {
Expand All @@ -216,7 +207,7 @@ func EditMilestonePost(ctx *context.Context) {
}
m.Name = form.Title
m.Content = form.Content
m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
m.DeadlineUnix = deadlineUnix
if err = issues_model.UpdateMilestone(ctx, m, m.IsClosed); err != nil {
ctx.ServerError("UpdateMilestone", err)
return
Expand Down
2 changes: 1 addition & 1 deletion routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,7 @@ func registerRoutes(m *web.Router) {
m.Group("/{index}", func() {
m.Post("/title", repo.UpdateIssueTitle)
m.Post("/content", repo.UpdateIssueContent)
m.Post("/deadline", web.Bind(structs.EditDeadlineOption{}), repo.UpdateIssueDeadline)
m.Post("/deadline", repo.UpdateIssueDeadline)
m.Post("/watch", repo.IssueWatch)
m.Post("/ref", repo.UpdateIssueRef)
m.Post("/pin", reqRepoAdmin, repo.IssuePinOrUnpin)
Expand Down
2 changes: 1 addition & 1 deletion services/convert/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ func ToAPIMilestone(m *issues_model.Milestone) *api.Milestone {
if m.IsClosed {
apiMilestone.Closed = m.ClosedDateUnix.AsTimePtr()
}
if m.DeadlineUnix.Year() < 9999 {
if m.DeadlineUnix > 0 {
apiMilestone.Deadline = m.DeadlineUnix.AsTimePtr()
}
return apiMilestone
Expand Down
2 changes: 1 addition & 1 deletion templates/repo/graph/commits.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
{{end}}
{{end}}
</span>
<span class="author tw-flex tw-items-center tw-mr-2 tw-gap-[1px]">
<span class="author tw-flex tw-items-center tw-mr-2 tw-gap-1">
{{$userName := $commit.Commit.Author.Name}}
{{if $commit.User}}
{{if and $commit.User.FullName DefaultShowFullName}}
Expand Down
4 changes: 2 additions & 2 deletions templates/repo/issue/milestone_new.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
<div class="field {{if .Err_Deadline}}error{{end}}">
<label>
{{ctx.Locale.Tr "repo.milestones.due_date"}}
<a id="clear-date">{{ctx.Locale.Tr "repo.milestones.clear"}}</a>
<a id="milestone-clear-deadline">{{ctx.Locale.Tr "repo.milestones.clear"}}</a>
</label>
<input type="date" id="deadline" name="deadline" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
<input type="date" name="deadline" class="tw-w-auto" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
</div>
<div class="field">
<label>{{ctx.Locale.Tr "repo.milestones.desc"}}</label>
Expand Down
Loading
Loading