diff --git a/internal/processing/streaming/notification_test.go b/internal/processing/streaming/notification_test.go
index faaf01621..f26058744 100644
--- a/internal/processing/streaming/notification_test.go
+++ b/internal/processing/streaming/notification_test.go
@@ -44,7 +44,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() {
notification := &apimodel.Notification{
ID: "01FH57SJCMDWQGEAJ0X08CE3WV",
Type: "follow",
- CreatedAt: "2021-10-04T10:52:36+02:00",
+ CreatedAt: "2021-10-04T08:52:36Z",
Account: followAccountAPIModel,
}
@@ -52,7 +52,7 @@ func (suite *NotificationTestSuite) TestStreamNotification() {
suite.NoError(err)
msg := <-openStream.Messages
- suite.Equal(`{"id":"01FH57SJCMDWQGEAJ0X08CE3WV","type":"follow","created_at":"2021-10-04T10:52:36+02:00","account":{"id":"01F8MH5ZK5VRH73AKHQM6Y9VNX","username":"foss_satan","acct":"foss_satan@fossbros-anonymous.io","display_name":"big gerald","locked":false,"bot":false,"created_at":"2021-09-26T12:52:36+02:00","note":"i post about like, i dunno, stuff, or whatever!!!!","url":"http://fossbros-anonymous.io/@foss_satan","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":0,"following_count":0,"statuses_count":1,"last_status_at":"2021-09-20T10:40:37Z","emojis":[],"fields":[]}}`, msg.Payload)
+ suite.Equal(`{"id":"01FH57SJCMDWQGEAJ0X08CE3WV","type":"follow","created_at":"2021-10-04T08:52:36Z","account":{"id":"01F8MH5ZK5VRH73AKHQM6Y9VNX","username":"foss_satan","acct":"foss_satan@fossbros-anonymous.io","display_name":"big gerald","locked":false,"bot":false,"created_at":"2021-09-26T10:52:36Z","note":"i post about like, i dunno, stuff, or whatever!!!!","url":"http://fossbros-anonymous.io/@foss_satan","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":0,"following_count":0,"statuses_count":1,"last_status_at":"2021-09-20T10:40:37Z","emojis":[],"fields":[]}}`, msg.Payload)
}
func TestNotificationTestSuite(t *testing.T) {
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index 9158830ef..9601f3e3a 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -22,7 +22,6 @@ import (
"context"
"fmt"
"strings"
- "time"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
@@ -31,6 +30,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
)
func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmodel.Account) (*model.Account, error) {
@@ -93,7 +93,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
var lastStatusAt string
lastPosted, err := c.db.GetAccountLastPosted(ctx, a.ID)
if err == nil && !lastPosted.IsZero() {
- lastStatusAt = lastPosted.Format(time.RFC3339)
+ lastStatusAt = util.FormatISO8601(lastPosted)
}
// set account avatar fields if available
@@ -140,7 +140,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
Value: f.Value,
}
if !f.VerifiedAt.IsZero() {
- mField.VerifiedAt = f.VerifiedAt.Format(time.RFC3339)
+ mField.VerifiedAt = util.FormatISO8601(f.VerifiedAt)
}
fields = append(fields, mField)
}
@@ -169,7 +169,7 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
DisplayName: a.DisplayName,
Locked: a.Locked,
Bot: a.Bot,
- CreatedAt: a.CreatedAt.Format(time.RFC3339),
+ CreatedAt: util.FormatISO8601(a.CreatedAt),
Note: a.Note,
URL: a.URL,
Avatar: aviURL,
@@ -209,7 +209,7 @@ func (c *converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel.
Acct: acct,
DisplayName: a.DisplayName,
Bot: a.Bot,
- CreatedAt: a.CreatedAt.Format(time.RFC3339),
+ CreatedAt: util.FormatISO8601(a.CreatedAt),
URL: a.URL,
Suspended: suspended,
}, nil
@@ -511,7 +511,7 @@ func (c *converter) StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, r
apiStatus := &model.Status{
ID: s.ID,
- CreatedAt: s.CreatedAt.Format(time.RFC3339),
+ CreatedAt: util.FormatISO8601(s.CreatedAt),
InReplyToID: s.InReplyToID,
InReplyToAccountID: s.InReplyToAccountID,
Sensitive: s.Sensitive,
@@ -695,7 +695,7 @@ func (c *converter) NotificationToAPINotification(ctx context.Context, n *gtsmod
return &model.Notification{
ID: n.ID,
Type: string(n.NotificationType),
- CreatedAt: n.CreatedAt.Format(time.RFC3339),
+ CreatedAt: util.FormatISO8601(n.CreatedAt),
Account: apiAccount,
Status: apiStatus,
}, nil
@@ -714,7 +714,7 @@ func (c *converter) DomainBlockToAPIDomainBlock(ctx context.Context, b *gtsmodel
domainBlock.PrivateComment = b.PrivateComment
domainBlock.SubscriptionID = b.SubscriptionID
domainBlock.CreatedBy = b.CreatedByAccountID
- domainBlock.CreatedAt = b.CreatedAt.Format(time.RFC3339)
+ domainBlock.CreatedAt = util.FormatISO8601(b.CreatedAt)
}
return domainBlock, nil
diff --git a/internal/util/time.go b/internal/util/time.go
new file mode 100644
index 000000000..d4d4ebf79
--- /dev/null
+++ b/internal/util/time.go
@@ -0,0 +1,30 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package util
+
+import "time"
+
+// ISO8601 is a formatter for serializing times that forces ISO8601 behavior.
+const ISO8601 = "2006-01-02T15:04:05Z"
+
+// FormatISO8601 converts the given time to UTC and then formats it
+// using the ISO8601 const, which the Mastodon API is able to understand.
+func FormatISO8601(t time.Time) string {
+ return t.UTC().Format(ISO8601)
+}
diff --git a/internal/util/time_test.go b/internal/util/time_test.go
new file mode 100644
index 000000000..1d49e82af
--- /dev/null
+++ b/internal/util/time_test.go
@@ -0,0 +1,53 @@
+/*
+ GoToSocial
+ Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package util_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
+ "github.com/superseriousbusiness/gotosocial/testrig"
+)
+
+type TimeSuite struct {
+ suite.Suite
+}
+
+func (suite *TimeSuite) TestISO8601Format1() {
+ testTime := testrig.TimeMustParse("2022-05-17T13:10:59Z")
+ testTimeString := util.FormatISO8601(testTime)
+ suite.Equal("2022-05-17T13:10:59Z", testTimeString)
+}
+
+func (suite *TimeSuite) TestISO8601Format2() {
+ testTime := testrig.TimeMustParse("2022-05-09T07:34:35+02:00")
+ testTimeString := util.FormatISO8601(testTime)
+ suite.Equal("2022-05-09T05:34:35Z", testTimeString)
+}
+
+func (suite *TimeSuite) TestISO8601Format3() {
+ testTime := testrig.TimeMustParse("2021-10-04T10:52:36+02:00")
+ testTimeString := util.FormatISO8601(testTime)
+ suite.Equal("2021-10-04T08:52:36Z", testTimeString)
+}
+
+func TestTimeSuite(t *testing.T) {
+ suite.Run(t, &TimeSuite{})
+}