diff --git a/internal/project/datetime.go b/internal/project/datetime.go new file mode 100644 index 000000000..fd76fcc7c --- /dev/null +++ b/internal/project/datetime.go @@ -0,0 +1,25 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package project + +const ( + // RFC3339Date formats time as RFC3339 but without a time component (e.g. + // 2020-11-24 for November 24, 20202). + RFC3339Date = "2006-01-02" + + // RFC3339Squish is the RFC3339 datetime but all dashes and timezone + // indicators are removed. This is useful for filenames. + RFC3339Squish = "20060102150405" +) diff --git a/internal/project/random.go b/internal/project/random.go index 4b775cbd2..d036aaaf8 100644 --- a/internal/project/random.go +++ b/internal/project/random.go @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package project defines global project helpers. package project import ( diff --git a/pkg/clients/e2e.go b/pkg/clients/e2e.go index b75b31ceb..482a928fd 100644 --- a/pkg/clients/e2e.go +++ b/pkg/clients/e2e.go @@ -22,6 +22,7 @@ import ( "net/http" "time" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/api" "github.com/google/exposure-notifications-verification-server/pkg/config" "github.com/google/exposure-notifications-verification-server/pkg/jsonclient" @@ -56,7 +57,7 @@ func RunEndToEnd(ctx context.Context, config *config.E2ETestConfig) error { testType = "likely" iterations++ } - symptomDate := time.Now().UTC().Add(-48 * time.Hour).Format("2006-01-02") + symptomDate := time.Now().UTC().Add(-48 * time.Hour).Format(project.RFC3339Date) adminID := "" revisionToken := "" diff --git a/pkg/controller/admin/events_test.go b/pkg/controller/admin/events_test.go index 32682e754..61ab21ade 100644 --- a/pkg/controller/admin/events_test.go +++ b/pkg/controller/admin/events_test.go @@ -28,7 +28,7 @@ import ( ) // This goes to the value of a -const RFC3339PartialLocal = "2006-01-02T15:04:05" +const rfc3339PartialLocal = "2006-01-02T15:04:05" func TestShowAdminEvents(t *testing.T) { t.Parallel() @@ -98,16 +98,16 @@ func TestShowAdminEvents(t *testing.T) { chromedp.WaitVisible(`body#admin-events-index`, chromedp.ByQuery), // Search from and hour before to and hour after our event - chromedp.SetValue(`#from`, eventTime.Add(-time.Hour).Format(RFC3339PartialLocal), chromedp.ByQuery), - chromedp.SetValue(`#to`, eventTime.Add(time.Hour).Format(RFC3339PartialLocal), chromedp.ByQuery), + chromedp.SetValue(`#from`, eventTime.Add(-time.Hour).Format(rfc3339PartialLocal), chromedp.ByQuery), + chromedp.SetValue(`#to`, eventTime.Add(time.Hour).Format(rfc3339PartialLocal), chromedp.ByQuery), chromedp.Submit(`form#search-form`, chromedp.ByQuery), // Wait for the search result. chromedp.WaitVisible(`#results #event`, chromedp.ByQuery), // Search an hour before the event. - chromedp.SetValue(`#from`, eventTime.Add(-2*time.Hour).Format(RFC3339PartialLocal), chromedp.ByQuery), - chromedp.SetValue(`#to`, eventTime.Add(-time.Hour).Format(RFC3339PartialLocal), chromedp.ByQuery), + chromedp.SetValue(`#from`, eventTime.Add(-2*time.Hour).Format(rfc3339PartialLocal), chromedp.ByQuery), + chromedp.SetValue(`#to`, eventTime.Add(-time.Hour).Format(rfc3339PartialLocal), chromedp.ByQuery), chromedp.Submit(`form#search-form`, chromedp.ByQuery), // Assert no event found diff --git a/pkg/controller/codes/issue.go b/pkg/controller/codes/issue.go index 5f69bd0a6..ae49cd37b 100644 --- a/pkg/controller/codes/issue.go +++ b/pkg/controller/codes/issue.go @@ -20,6 +20,7 @@ import ( "net/http" "time" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/controller" ) @@ -52,8 +53,8 @@ func (c *Controller) HandleIssue() http.Handler { now := time.Now().UTC() pastDaysDuration := -1 * c.serverconfig.AllowedSymptomAge displayAllowedDays := fmt.Sprintf("%.0f", c.serverconfig.AllowedSymptomAge.Hours()/24.0) - m["maxDate"] = now.Format("2006-01-02") - m["minDate"] = now.Add(pastDaysDuration).Format("2006-01-02") + m["maxDate"] = now.Format(project.RFC3339Date) + m["minDate"] = now.Add(pastDaysDuration).Format(project.RFC3339Date) m["maxSymptomDays"] = displayAllowedDays m["duration"] = realm.CodeDuration.Duration.String() m["hasSMSConfig"] = hasSMSConfig diff --git a/pkg/controller/codes/issue_test.go b/pkg/controller/codes/issue_test.go index 1727e0787..a8b375c0c 100644 --- a/pkg/controller/codes/issue_test.go +++ b/pkg/controller/codes/issue_test.go @@ -21,6 +21,7 @@ import ( "github.com/google/exposure-notifications-verification-server/internal/browser" "github.com/google/exposure-notifications-verification-server/internal/envstest" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/api" "github.com/google/exposure-notifications-verification-server/pkg/controller" "github.com/google/exposure-notifications-verification-server/pkg/database" @@ -70,7 +71,7 @@ func TestHandleIssue_IssueCode(t *testing.T) { taskCtx, done := context.WithTimeout(browserCtx, 30*time.Second) defer done() - yesterday := time.Now().Add(-24 * time.Hour).Format("2006-01-02") + yesterday := time.Now().Add(-24 * time.Hour).Format(project.RFC3339Date) var code string if err := chromedp.Run(taskCtx, diff --git a/pkg/controller/issueapi/issue_test.go b/pkg/controller/issueapi/issue_test.go index ff2d3306a..5e640c86f 100644 --- a/pkg/controller/issueapi/issue_test.go +++ b/pkg/controller/issueapi/issue_test.go @@ -17,6 +17,8 @@ package issueapi import ( "testing" "time" + + "github.com/google/exposure-notifications-verification-server/internal/project" ) func TestDateValidation(t *testing.T) { @@ -25,7 +27,7 @@ func TestDateValidation(t *testing.T) { t.Fatalf("error loading utc") } var aug1 time.Time - aug1, err = time.ParseInLocation("2006-01-02", "2020-08-01", utc) + aug1, err = time.ParseInLocation(project.RFC3339Date, "2020-08-01", utc) if err != nil { t.Fatalf("error parsing date") } @@ -47,7 +49,7 @@ func TestDateValidation(t *testing.T) { {"2020-07-29", aug1, -60, true, "2020-07-30"}, } for i, test := range tests { - date, err := time.ParseInLocation("2006-01-02", test.v, utc) + date, err := time.ParseInLocation(project.RFC3339Date, test.v, utc) if err != nil { t.Fatalf("[%d] error parsing date %q", i, test.v) } @@ -61,7 +63,7 @@ func TestDateValidation(t *testing.T) { } else { t.Fatalf("[%d] expected error", i) } - } else if s := newDate.Format("2006-01-02"); s != test.expected { + } else if s := newDate.Format(project.RFC3339Date); s != test.expected { t.Fatalf("[%d] validateDate returned a different date %q != %q", i, s, test.expected) } } diff --git a/pkg/controller/issueapi/logic.go b/pkg/controller/issueapi/logic.go index e2f11ec8c..619e6d365 100644 --- a/pkg/controller/issueapi/logic.go +++ b/pkg/controller/issueapi/logic.go @@ -100,7 +100,7 @@ func (c *Controller) issue(ctx context.Context, request *api.IssueCodeRequest) ( dateSettings := []*dateParseSettings{&onsetSettings, &testSettings} for i, d := range input { if d != "" { - parsed, err := time.Parse("2006-01-02", d) + parsed, err := time.Parse(project.RFC3339Date, d) if err != nil { return &issueResult{ obsBlame: observability.BlameClient, @@ -117,9 +117,9 @@ func (c *Controller) issue(ctx context.Context, request *api.IssueCodeRequest) ( if err != nil { err := fmt.Errorf("%s date must be on/after %v and on/before %v %v", dateSettings[i].Name, - minDate.Format("2006-01-02"), - maxDate.Format("2006-01-02"), - parsed.Format("2006-01-02"), + minDate.Format(project.RFC3339Date), + maxDate.Format(project.RFC3339Date), + parsed.Format(project.RFC3339Date), ) return &issueResult{ obsBlame: observability.BlameClient, diff --git a/pkg/controller/realmadmin/show.go b/pkg/controller/realmadmin/show.go index 5ddf428cb..197a913fa 100644 --- a/pkg/controller/realmadmin/show.go +++ b/pkg/controller/realmadmin/show.go @@ -24,6 +24,7 @@ import ( "time" "github.com/google/exposure-notifications-verification-server/internal/icsv" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/cache" "github.com/google/exposure-notifications-verification-server/pkg/controller" "github.com/google/exposure-notifications-verification-server/pkg/database" @@ -51,7 +52,7 @@ func (c *Controller) HandleShow() http.Handler { var stats icsv.Marshaler var err error - nowFormatted := now.Format("20060102150405") + nowFormatted := now.Format(project.RFC3339Squish) switch r.URL.Query().Get("scope") { case "external": diff --git a/pkg/database/external_issuer_stats.go b/pkg/database/external_issuer_stats.go index beed114cb..222c5388b 100644 --- a/pkg/database/external_issuer_stats.go +++ b/pkg/database/external_issuer_stats.go @@ -24,6 +24,7 @@ import ( "time" "github.com/google/exposure-notifications-verification-server/internal/icsv" + "github.com/google/exposure-notifications-verification-server/internal/project" ) var _ icsv.Marshaler = (ExternalIssuerStats)(nil) @@ -55,7 +56,7 @@ func (s ExternalIssuerStats) MarshalCSV() ([]byte, error) { for i, stat := range s { if err := w.Write([]string{ - stat.Date.Format("2006-01-02"), + stat.Date.Format(project.RFC3339Date), strconv.FormatUint(uint64(stat.RealmID), 10), stat.IssuerID, strconv.FormatUint(uint64(stat.CodesIssued), 10), diff --git a/pkg/database/realm_stats.go b/pkg/database/realm_stats.go index 33c0d848d..e58212cf6 100644 --- a/pkg/database/realm_stats.go +++ b/pkg/database/realm_stats.go @@ -24,6 +24,7 @@ import ( "time" "github.com/google/exposure-notifications-verification-server/internal/icsv" + "github.com/google/exposure-notifications-verification-server/internal/project" ) var _ icsv.Marshaler = (RealmStats)(nil) @@ -56,7 +57,7 @@ func (s RealmStats) MarshalCSV() ([]byte, error) { for i, stat := range s { if err := w.Write([]string{ - stat.Date.Format("2006-01-02"), + stat.Date.Format(project.RFC3339Date), strconv.FormatUint(uint64(stat.CodesIssued), 10), strconv.FormatUint(uint64(stat.CodesClaimed), 10), strconv.FormatUint(uint64(stat.DailyActiveUsers), 10), diff --git a/pkg/database/realm_user_stats.go b/pkg/database/realm_user_stats.go index 592ea24a5..e08f7d67a 100644 --- a/pkg/database/realm_user_stats.go +++ b/pkg/database/realm_user_stats.go @@ -24,6 +24,7 @@ import ( "time" "github.com/google/exposure-notifications-verification-server/internal/icsv" + "github.com/google/exposure-notifications-verification-server/internal/project" ) var _ icsv.Marshaler = (RealmUserStats)(nil) @@ -59,7 +60,7 @@ func (s RealmUserStats) MarshalCSV() ([]byte, error) { for i, stat := range s { if err := w.Write([]string{ - stat.Date.Format("2006-01-02"), + stat.Date.Format(project.RFC3339Date), strconv.FormatUint(uint64(stat.RealmID), 10), strconv.FormatUint(uint64(stat.UserID), 10), stat.Name, diff --git a/pkg/database/token.go b/pkg/database/token.go index 20131d02f..db2b7554c 100644 --- a/pkg/database/token.go +++ b/pkg/database/token.go @@ -23,6 +23,7 @@ import ( "time" "github.com/google/exposure-notifications-server/pkg/timeutils" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/api" "github.com/jinzhu/gorm" ) @@ -67,10 +68,10 @@ func (s *Subject) String() string { parts[0] = s.TestType if s.SymptomDate != nil { - parts[1] = s.SymptomDate.Format("2006-01-02") + parts[1] = s.SymptomDate.Format(project.RFC3339Date) } if s.TestDate != nil { - parts[2] = s.TestDate.Format("2006-01-02") + parts[2] = s.TestDate.Format(project.RFC3339Date) } return strings.Join(parts, ".") @@ -90,7 +91,7 @@ func ParseSubject(sub string) (*Subject, error) { } var symptomDate *time.Time if parts[1] != "" { - parsedDate, err := time.Parse("2006-01-02", parts[1]) + parsedDate, err := time.Parse(project.RFC3339Date, parts[1]) if err != nil { return nil, fmt.Errorf("subject contains invalid symptom date: %w", err) } @@ -99,7 +100,7 @@ func ParseSubject(sub string) (*Subject, error) { var testDate *time.Time if len(parts) == 3 && parts[2] != "" { - parsedDate, err := time.Parse("2006-01-02", parts[2]) + parsedDate, err := time.Parse(project.RFC3339Date, parts[2]) if err != nil { return nil, fmt.Errorf("subject contains invalid test date: %w", err) } @@ -118,7 +119,7 @@ func (t *Token) FormatSymptomDate() string { if t.SymptomDate == nil { return "" } - return t.SymptomDate.Format("2006-01-02") + return t.SymptomDate.Format(project.RFC3339Date) } // FormatTestDate returns YYYY-MM-DD formatted test date, or "" if nil. @@ -126,7 +127,7 @@ func (t *Token) FormatTestDate() string { if t.TestDate == nil { return "" } - return t.TestDate.Format("2006-01-02") + return t.TestDate.Format(project.RFC3339Date) } func (t *Token) Subject() *Subject { diff --git a/pkg/database/token_test.go b/pkg/database/token_test.go index 5913586d3..15b2f1cb8 100644 --- a/pkg/database/token_test.go +++ b/pkg/database/token_test.go @@ -21,6 +21,7 @@ import ( "time" "github.com/google/exposure-notifications-server/pkg/timeutils" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/api" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -29,7 +30,7 @@ import ( func TestSubject(t *testing.T) { t.Parallel() - testDay, err := time.Parse("2006-01-02", "2020-07-07") + testDay, err := time.Parse(project.RFC3339Date, "2020-07-07") if err != nil { t.Fatalf("test setup error: %v", err) } diff --git a/pkg/database/vercode.go b/pkg/database/vercode.go index 07fab782f..04d3953aa 100644 --- a/pkg/database/vercode.go +++ b/pkg/database/vercode.go @@ -179,7 +179,7 @@ func (v *VerificationCode) FormatSymptomDate() string { if v.SymptomDate == nil { return "" } - return v.SymptomDate.Format("2006-01-02") + return v.SymptomDate.Format(project.RFC3339Date) } // IsCodeExpired checks to see if the actual code provided is the short or long diff --git a/pkg/database/vercode_test.go b/pkg/database/vercode_test.go index 9619713b4..2db93cb89 100644 --- a/pkg/database/vercode_test.go +++ b/pkg/database/vercode_test.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" @@ -422,9 +423,8 @@ func TestStatDatesOnCreate(t *testing.T) { db, _ := testDatabaseInstance.NewDatabase(t, nil) - fmtString := "2006-01-02" now := time.Now() - nowStr := now.Format(fmtString) + nowStr := now.Format(project.RFC3339Date) maxAge := time.Hour tests := []struct { @@ -469,7 +469,7 @@ func TestStatDatesOnCreate(t *testing.T) { if stats[0].CodesIssued != uint(i+1) { t.Errorf("[%d] expected stat.CodesIssued = %d, expected %d", i, stats[0].CodesIssued, i+1) } - if f := stats[0].Date.Format(fmtString); f != test.statDate { + if f := stats[0].Date.Format(project.RFC3339Date); f != test.statDate { t.Errorf("[%d] expected stat.Date = %s, expected %s", i, f, test.statDate) } } @@ -491,7 +491,7 @@ func TestStatDatesOnCreate(t *testing.T) { if stats[0].CodesIssued != uint(i+1) { t.Errorf("[%d] expected stat.CodesIssued = %d, expected %d", i, stats[0].CodesIssued, i+1) } - if f := stats[0].Date.Format(fmtString); f != test.statDate { + if f := stats[0].Date.Format(project.RFC3339Date); f != test.statDate { t.Errorf("[%d] expected stat.Date = %s, expected %s", i, f, test.statDate) } } @@ -513,7 +513,7 @@ func TestStatDatesOnCreate(t *testing.T) { if stats[0].CodesIssued != uint(i+1) { t.Errorf("[%d] expected stat.CodesIssued = %d, expected %d", i, stats[0].CodesIssued, i+1) } - if f := stats[0].Date.Format(fmtString); f != test.statDate { + if f := stats[0].Date.Format(project.RFC3339Date); f != test.statDate { t.Errorf("[%d] expected stat.Date = %s, expected %s", i, f, test.statDate) } } @@ -535,7 +535,7 @@ func TestStatDatesOnCreate(t *testing.T) { if stats[0].CodesIssued != uint(i+1) { t.Errorf("[%d] expected stat.CodesIssued = %d, expected %d", i, stats[0].CodesIssued, i+1) } - if f := stats[0].Date.Format(fmtString); f != test.statDate { + if f := stats[0].Date.Format(project.RFC3339Date); f != test.statDate { t.Errorf("[%d] expected stat.Date = %s, expected %s", i, f, test.statDate) } } diff --git a/pkg/integration/integration_test.go b/pkg/integration/integration_test.go index b8fc237f5..33b61cb6d 100644 --- a/pkg/integration/integration_test.go +++ b/pkg/integration/integration_test.go @@ -25,6 +25,7 @@ import ( verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1" "github.com/google/exposure-notifications-server/pkg/util" "github.com/google/exposure-notifications-server/pkg/verification" + "github.com/google/exposure-notifications-verification-server/internal/project" "github.com/google/exposure-notifications-verification-server/pkg/api" "github.com/google/exposure-notifications-verification-server/pkg/testsuite" ) @@ -80,7 +81,7 @@ func TestIntegration(t *testing.T) { now := time.Now().UTC() curDayInterval := timeToInterval(now) nextInterval := curDayInterval - symptomDate := time.Now().UTC().Add(-48 * time.Hour).Format("2006-01-02") + symptomDate := time.Now().UTC().Add(-48 * time.Hour).Format(project.RFC3339Date) testType := "confirmed" tzMinOffset := 0