Add clock cli flag

Close #8787
This commit is contained in:
satotake 2022-04-27 02:57:04 +09:00 committed by Bjørn Erik Pedersen
parent f2946da9e8
commit e77ca3c105
26 changed files with 193 additions and 37 deletions

View file

@ -24,6 +24,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
@ -295,7 +296,7 @@ func (c *Cache) isExpired(modTime time.Time) bool {
if c.maxAge < 0 { if c.maxAge < 0 {
return false return false
} }
return c.maxAge == 0 || time.Since(modTime) > c.maxAge return c.maxAge == 0 || htime.Since(modTime) > c.maxAge
} }
// For testing // For testing

View file

@ -15,6 +15,7 @@ package commands
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -28,9 +29,11 @@ import (
"golang.org/x/sync/semaphore" "golang.org/x/sync/semaphore"
"github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/paths"
"github.com/spf13/cast"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
@ -41,6 +44,7 @@ import (
"github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/hugolib"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/bep/clock"
"github.com/bep/debounce" "github.com/bep/debounce"
"github.com/bep/overlayfs" "github.com/bep/overlayfs"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
@ -164,6 +168,20 @@ func (c *commandeer) initFs(fs *hugofs.Fs) error {
return nil return nil
} }
func (c *commandeer) initClock() error {
bt := c.Cfg.GetString("clock")
if bt == "" {
return nil
}
t, err := cast.StringToDateInDefaultLocation(bt, nil)
if err != nil {
return fmt.Errorf(`failed to parse "clock" flag: %s`, err)
}
htime.Clock = clock.Start(t)
return nil
}
func newCommandeer(mustHaveConfigFile, failOnInitErr, running bool, h *hugoBuilderCommon, f flagsToConfigHandler, cfgInit func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) { func newCommandeer(mustHaveConfigFile, failOnInitErr, running bool, h *hugoBuilderCommon, f flagsToConfigHandler, cfgInit func(c *commandeer) error, subCmdVs ...*cobra.Command) (*commandeer, error) {
var rebuildDebouncer func(f func()) var rebuildDebouncer func(f func())
if running { if running {
@ -341,6 +359,11 @@ func (c *commandeer) loadConfig() error {
c.configFiles = configFiles c.configFiles = configFiles
err = c.initClock()
if err != nil {
return err
}
if l, ok := c.Cfg.Get("languagesSorted").(langs.Languages); ok { if l, ok := c.Cfg.Get("languagesSorted").(langs.Languages); ok {
c.languagesConfigured = true c.languagesConfigured = true
c.languages = l c.languages = l

View file

@ -18,6 +18,7 @@ import (
"os" "os"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/hugo"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
hpaths "github.com/gohugoio/hugo/common/paths" hpaths "github.com/gohugoio/hugo/common/paths"
@ -151,7 +152,7 @@ built with love by spf13 and friends in Go.
Complete documentation is available at https://gohugo.io/.`, Complete documentation is available at https://gohugo.io/.`,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
defer cc.timeTrack(time.Now(), "Total") defer cc.timeTrack(htime.Now(), "Total")
cfgInit := func(c *commandeer) error { cfgInit := func(c *commandeer) error {
if cc.buildWatch { if cc.buildWatch {
c.Set("disableLiveReload", true) c.Set("disableLiveReload", true)
@ -210,6 +211,7 @@ type hugoBuilderCommon struct {
buildWatch bool buildWatch bool
poll string poll string
clock string
gc bool gc bool
@ -236,7 +238,7 @@ func (cc *hugoBuilderCommon) timeTrack(start time.Time, name string) {
if cc.quiet { if cc.quiet {
return return
} }
elapsed := time.Since(start) elapsed := htime.Since(start)
fmt.Printf("%s in %v ms\n", name, int(1000*elapsed.Seconds())) fmt.Printf("%s in %v ms\n", name, int(1000*elapsed.Seconds()))
} }
@ -279,6 +281,7 @@ func (cc *hugoBuilderCommon) handleCommonBuilderFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment") cmd.PersistentFlags().StringVarP(&cc.environment, "environment", "e", "", "build environment")
cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory") cmd.PersistentFlags().StringP("themesDir", "", "", "filesystem path to themes directory")
cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern") cmd.PersistentFlags().StringP("ignoreVendorPaths", "", "", "ignores any _vendor for module paths matching the given Glob pattern")
cmd.PersistentFlags().StringVar(&cc.clock, "clock", "", "set clock inside hugo, e.g. --clock 2021-11-06T22:30:00.00+09:00")
} }
func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) { func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {

View file

@ -33,6 +33,7 @@ import (
"github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/tpl" "github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
@ -187,6 +188,7 @@ func initializeFlags(cmd *cobra.Command, cfg config.Provider) {
"buildDrafts", "buildDrafts",
"buildFuture", "buildFuture",
"buildExpired", "buildExpired",
"clock",
"uglyURLs", "uglyURLs",
"canonifyURLs", "canonifyURLs",
"enableRobotsTXT", "enableRobotsTXT",
@ -679,7 +681,7 @@ func (c *commandeer) firstPathSpec() *helpers.PathSpec {
} }
func (c *commandeer) timeTrack(start time.Time, name string) { func (c *commandeer) timeTrack(start time.Time, name string) {
elapsed := time.Since(start) elapsed := htime.Since(start)
c.logger.Printf("%s in %v ms", name, int(1000*elapsed.Seconds())) c.logger.Printf("%s in %v ms", name, int(1000*elapsed.Seconds()))
} }
@ -790,7 +792,7 @@ func (c *commandeer) fullRebuild(changeType string) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
}() }()
defer c.timeTrack(time.Now(), "Rebuilt") defer c.timeTrack(htime.Now(), "Rebuilt")
c.commandeerHugoState = newCommandeerHugoState() c.commandeerHugoState = newCommandeerHugoState()
err := c.loadConfig() err := c.loadConfig()
@ -900,7 +902,7 @@ func (c *commandeer) printChangeDetected(typ string) {
c.logger.Println(msg) c.logger.Println(msg)
const layout = "2006-01-02 15:04:05.000 -0700" const layout = "2006-01-02 15:04:05.000 -0700"
c.logger.Println(time.Now().Format(layout)) c.logger.Println(htime.Now().Format(layout))
} }
const ( const (
@ -1135,7 +1137,7 @@ func (c *commandeer) handleEvents(watcher *watcher.Batcher,
c.changeDetector.PrepareNew() c.changeDetector.PrepareNew()
func() { func() {
defer c.timeTrack(time.Now(), "Total") defer c.timeTrack(htime.Now(), "Total")
if err := c.rebuildSites(dynamicEvents); err != nil { if err := c.rebuildSites(dynamicEvents); err != nil {
c.handleBuildErr(err, "Rebuild failed") c.handleBuildErr(err, "Rebuild failed")
} }

View file

@ -29,7 +29,6 @@ import (
// Issue #5662 // Issue #5662
func TestHugoWithContentDirOverride(t *testing.T) { func TestHugoWithContentDirOverride(t *testing.T) {
t.Parallel()
c := qt.New(t) c := qt.New(t)
files := ` files := `
@ -51,7 +50,6 @@ Page: {{ .Title }}|
// Issue #9794 // Issue #9794
func TestHugoStaticFilesMultipleStaticAndManyFolders(t *testing.T) { func TestHugoStaticFilesMultipleStaticAndManyFolders(t *testing.T) {
t.Parallel()
c := qt.New(t) c := qt.New(t)
files := ` files := `
@ -95,6 +93,36 @@ Home.
} }
// Issue #8787
func TestHugoListCommandsWithClockFlag(t *testing.T) {
c := qt.New(t)
files := `
-- config.toml --
baseURL = "https://example.org"
title = "Hugo Commands"
-- content/past.md --
---
title: "Past"
date: 2000-11-06
---
-- content/future.md --
---
title: "Future"
date: 2200-11-06
---
-- layouts/_default/single.html --
Page: {{ .Title }}|
`
s := newTestHugoCmdBuilder(c, files, []string{"list", "future"}).Build()
p := filepath.Join("content", "future.md")
s.AssertStdout(p + ",2200-11-06T00:00:00Z")
s = newTestHugoCmdBuilder(c, files, []string{"list", "future", "--clock", "2300-11-06"}).Build()
s.AssertStdout("")
}
type testHugoCmdBuilder struct { type testHugoCmdBuilder struct {
*qt.C *qt.C
@ -102,6 +130,7 @@ type testHugoCmdBuilder struct {
dir string dir string
files string files string
args []string args []string
out string
} }
func newTestHugoCmdBuilder(c *qt.C, files string, args []string) *testHugoCmdBuilder { func newTestHugoCmdBuilder(c *qt.C, files string, args []string) *testHugoCmdBuilder {
@ -127,8 +156,12 @@ func (s *testHugoCmdBuilder) Build() *testHugoCmdBuilder {
args := append(s.args, "-s="+s.dir, "--quiet") args := append(s.args, "-s="+s.dir, "--quiet")
cmd.SetArgs(args) cmd.SetArgs(args)
_, err := cmd.ExecuteC() out, err := captureStdout(func() error {
_, err := cmd.ExecuteC()
return err
})
s.Assert(err, qt.IsNil) s.Assert(err, qt.IsNil)
s.out = out
return s return s
} }
@ -149,3 +182,9 @@ func (s *testHugoCmdBuilder) AssertFileContent(filename string, matches ...strin
} }
} }
} }
func (s *testHugoCmdBuilder) AssertStdout(match string) {
s.Helper()
content := strings.TrimSpace(s.out)
s.Assert(content, qt.Contains, strings.TrimSpace(match))
}

View file

@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/parser/pageparser" "github.com/gohugoio/hugo/parser/pageparser"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/parser/metadecoders" "github.com/gohugoio/hugo/parser/metadecoders"
@ -362,12 +363,12 @@ func parseJekyllFilename(filename string) (time.Time, string, error) {
re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`) re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`)
r := re.FindAllStringSubmatch(filename, -1) r := re.FindAllStringSubmatch(filename, -1)
if len(r) == 0 { if len(r) == 0 {
return time.Now(), "", errors.New("filename not match") return htime.Now(), "", errors.New("filename not match")
} }
postDate, err := time.Parse("2006-1-2", r[0][1]) postDate, err := time.Parse("2006-1-2", r[0][1])
if err != nil { if err != nil {
return time.Now(), "", err return htime.Now(), "", err
} }
postName := r[0][2] postName := r[0][2]

View file

@ -99,6 +99,10 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
return newSystemError("Error building sites", err) return newSystemError("Error building sites", err)
} }
if err != nil {
return newSystemError("Error building sites", err)
}
writer := csv.NewWriter(os.Stdout) writer := csv.NewWriter(os.Stdout)
defer writer.Flush() defer writer.Flush()
@ -127,6 +131,10 @@ List requires a subcommand, e.g. ` + "`hugo list drafts`.",
return newSystemError("Error building sites", err) return newSystemError("Error building sites", err)
} }
if err != nil {
return newSystemError("Error building sites", err)
}
writer := csv.NewWriter(os.Stdout) writer := csv.NewWriter(os.Stdout)
defer writer.Flush() defer writer.Flush()

View file

@ -34,9 +34,9 @@ func TestListAll(t *testing.T) {
hugoCmd := newCommandsBuilder().addAll().build() hugoCmd := newCommandsBuilder().addAll().build()
cmd := hugoCmd.getCommand() cmd := hugoCmd.getCommand()
defer func() { t.Cleanup(func() {
os.RemoveAll(dir) os.RemoveAll(dir)
}() })
cmd.SetArgs([]string{"-s=" + dir, "list", "all"}) cmd.SetArgs([]string{"-s=" + dir, "list", "all"})

View file

@ -18,8 +18,8 @@ import (
"errors" "errors"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -113,7 +113,7 @@ func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
by := []byte(`The MIT License (MIT) by := []byte(`The MIT License (MIT)
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE Copyright (c) ` + htime.Now().Format("2006") + ` YOUR_NAME_HERE
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

View file

@ -33,6 +33,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/paths"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
@ -255,7 +256,7 @@ func (sc *serverCmd) server(cmd *cobra.Command, args []string) error {
} }
err = func() error { err = func() error {
defer c.timeTrack(time.Now(), "Built") defer c.timeTrack(htime.Now(), "Built")
err := c.serverBuild() err := c.serverBuild()
if err != nil { if err != nil {
cmd.PrintErrln("Error:", err.Error()) cmd.PrintErrln("Error:", err.Error())
@ -668,13 +669,13 @@ func memStats() error {
go func() { go func() {
var stats runtime.MemStats var stats runtime.MemStats
start := time.Now().UnixNano() start := htime.Now().UnixNano()
for { for {
runtime.ReadMemStats(&stats) runtime.ReadMemStats(&stats)
if fileMemStats != nil { if fileMemStats != nil {
fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n", fileMemStats.WriteString(fmt.Sprintf("%d\t%d\t%d\t%d\t%d\n",
(time.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased)) (htime.Now().UnixNano()-start)/1000000, stats.HeapSys, stats.HeapAlloc, stats.HeapIdle, stats.HeapReleased))
time.Sleep(interval) time.Sleep(interval)
} else { } else {
break break

View file

@ -17,6 +17,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/bep/clock"
"github.com/spf13/cast" "github.com/spf13/cast"
toml "github.com/pelletier/go-toml/v2" toml "github.com/pelletier/go-toml/v2"
@ -74,6 +75,7 @@ var (
"November", "November",
"December", "December",
} }
Clock = clock.Start(time.Now())
) )
func NewTimeFormatter(ltr locales.Translator) TimeFormatter { func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
@ -148,3 +150,13 @@ func ToTimeInDefaultLocationE(i any, location *time.Location) (tim time.Time, er
} }
return cast.ToTimeInDefaultLocationE(i, location) return cast.ToTimeInDefaultLocationE(i, location)
} }
// Now returns time.Now() or time value based on`clock` flag.
// Use this function to fake time inside hugo.
func Now() time.Time {
return Clock.Now()
}
func Since(t time.Time) time.Duration {
return Clock.Now().Sub(t)
}

View file

@ -24,6 +24,7 @@ import (
"runtime" "runtime"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/terminal" "github.com/gohugoio/hugo/common/terminal"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
@ -176,7 +177,7 @@ func (l *logger) Out() io.Writer {
// PrintTimerIfDelayed prints a time statement to the FEEDBACK logger // PrintTimerIfDelayed prints a time statement to the FEEDBACK logger
// if considerable time is spent. // if considerable time is spent.
func (l *logger) PrintTimerIfDelayed(start time.Time, name string) { func (l *logger) PrintTimerIfDelayed(start time.Time, name string) {
elapsed := time.Since(start) elapsed := htime.Since(start)
milli := int(1000 * elapsed.Seconds()) milli := int(1000 * elapsed.Seconds())
if milli < 500 { if milli < 500 {
return return
@ -185,7 +186,7 @@ func (l *logger) PrintTimerIfDelayed(start time.Time, name string) {
} }
func (l *logger) PrintTimer(start time.Time, name string) { func (l *logger) PrintTimer(start time.Time, name string) {
elapsed := time.Since(start) elapsed := htime.Since(start)
milli := int(1000 * elapsed.Seconds()) milli := int(1000 * elapsed.Seconds())
l.Printf("%s in %v ms", name, milli) l.Printf("%s in %v ms", name, milli)
} }

1
go.mod
View file

@ -91,6 +91,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect
github.com/aws/smithy-go v1.8.0 // indirect github.com/aws/smithy-go v1.8.0 // indirect
github.com/bep/clock v0.1.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect

2
go.sum
View file

@ -212,6 +212,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.7.0/go.mod h1:0qcSMCyASQPN2sk/1KQLQ2
github.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc= github.com/aws/smithy-go v1.8.0 h1:AEwwwXQZtUwP5Mz506FeXXrKBe0jA8gVM+1gEcSRooc=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bep/clock v0.1.0 h1:sdvJ08SsgwTY/ejA705YKBlktFfj2uVpmQSSQspZJ2c=
github.com/bep/clock v0.1.0/go.mod h1:shVP9tZ3cXpbVj60SnlU1pMwKjFxECBRm9vZfpoA0Gs=
github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo= github.com/bep/debounce v1.2.0 h1:wXds8Kq8qRfwAOpAxHrJDbCXgC5aHSzgQb/0gKsHQqo=
github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= github.com/bep/debounce v1.2.0/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840= github.com/bep/gitmap v1.1.2 h1:zk04w1qc1COTZPPYWDQHvns3y1afOsdRfraFQ3qI840=

View file

@ -31,6 +31,7 @@ import (
"errors" "errors"
"github.com/gohugoio/hugo/common/hreflect" "github.com/gohugoio/hugo/common/hreflect"
"github.com/gohugoio/hugo/common/htime"
"github.com/spf13/afero" "github.com/spf13/afero"
) )
@ -203,7 +204,7 @@ func newDirNameOnlyFileInfo(name string, meta *FileMeta, fileOpener func() (afer
m.IsOrdered = false m.IsOrdered = false
return NewFileMetaInfo( return NewFileMetaInfo(
&dirNameOnlyFileInfo{name: base, modTime: time.Now()}, &dirNameOnlyFileInfo{name: base, modTime: htime.Now()},
m, m,
) )
} }

View file

@ -20,6 +20,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/source"
@ -70,7 +71,7 @@ func (f ContentFactory) ApplyArchetypeTemplate(w io.Writer, p page.Page, archety
d := &archetypeFileData{ d := &archetypeFileData{
Type: archetypeKind, Type: archetypeKind,
Date: time.Now().Format(time.RFC3339), Date: htime.Now().Format(time.RFC3339),
Page: p, Page: p,
File: p.File(), File: p.File(),
} }

View file

@ -22,12 +22,14 @@ import (
"testing" "testing"
"time" "time"
"github.com/bep/clock"
"github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/htesting"
"github.com/gohugoio/hugo/markup/asciidocext" "github.com/gohugoio/hugo/markup/asciidocext"
"github.com/gohugoio/hugo/markup/rst" "github.com/gohugoio/hugo/markup/rst"
"github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs"
@ -1536,7 +1538,6 @@ Content.
} }
func TestShouldBuild(t *testing.T) { func TestShouldBuild(t *testing.T) {
t.Parallel()
past := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) past := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
future := time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC) future := time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC)
zero := time.Time{} zero := time.Time{}
@ -1582,6 +1583,54 @@ func TestShouldBuild(t *testing.T) {
} }
} }
func TestShouldBuildWithClock(t *testing.T) {
htime.Clock = clock.Start(time.Date(2021, 11, 17, 20, 34, 58, 651387237, time.UTC))
t.Cleanup(func() { htime.Clock = clock.Start(time.Now()) })
past := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
future := time.Date(2037, 11, 17, 20, 34, 58, 651387237, time.UTC)
zero := time.Time{}
publishSettings := []struct {
buildFuture bool
buildExpired bool
buildDrafts bool
draft bool
publishDate time.Time
expiryDate time.Time
out bool
}{
// publishDate and expiryDate
{false, false, false, false, zero, zero, true},
{false, false, false, false, zero, future, true},
{false, false, false, false, past, zero, true},
{false, false, false, false, past, future, true},
{false, false, false, false, past, past, false},
{false, false, false, false, future, future, false},
{false, false, false, false, future, past, false},
// buildFuture and buildExpired
{false, true, false, false, past, past, true},
{true, true, false, false, past, past, true},
{true, false, false, false, past, past, false},
{true, false, false, false, future, future, true},
{true, true, false, false, future, future, true},
{false, true, false, false, future, past, false},
// buildDrafts and draft
{true, true, false, true, past, future, false},
{true, true, true, true, past, future, true},
{true, true, true, true, past, future, true},
}
for _, ps := range publishSettings {
s := shouldBuild(ps.buildFuture, ps.buildExpired, ps.buildDrafts, ps.draft,
ps.publishDate, ps.expiryDate)
if s != ps.out {
t.Errorf("AssertShouldBuildWithClock unexpected output with params: %+v", ps)
}
}
}
// "dot" in path: #1885 and #2110 // "dot" in path: #1885 and #2110
// disablePathToLower regression: #3374 // disablePathToLower regression: #3374
func TestPathIssues(t *testing.T) { func TestPathIssues(t *testing.T) {

View file

@ -30,6 +30,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/modules" "github.com/gohugoio/hugo/modules"
@ -1910,10 +1911,11 @@ func shouldBuild(buildFuture bool, buildExpired bool, buildDrafts bool, Draft bo
if !(buildDrafts || !Draft) { if !(buildDrafts || !Draft) {
return false return false
} }
if !buildFuture && !publishDate.IsZero() && publishDate.After(time.Now()) { hnow := htime.Now()
if !buildFuture && !publishDate.IsZero() && publishDate.After(hnow) {
return false return false
} }
if !buildExpired && !expiryDate.IsZero() && expiryDate.Before(time.Now()) { if !buildExpired && !expiryDate.IsZero() && expiryDate.Before(hnow) {
return false return false
} }
return true return true

View file

@ -25,6 +25,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/compare" "github.com/gohugoio/hugo/compare"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
@ -129,7 +130,7 @@ func (s *Store) TrackValue(key string, value any, cached bool) {
// MeasureSince adds a measurement for key to the metric store. // MeasureSince adds a measurement for key to the metric store.
func (s *Store) MeasureSince(key string, start time.Time) { func (s *Store) MeasureSince(key string, start time.Time) {
s.mu.Lock() s.mu.Lock()
s.metrics[key] = append(s.metrics[key], time.Since(start)) s.metrics[key] = append(s.metrics[key], htime.Since(start))
s.mu.Unlock() s.mu.Unlock()
} }

View file

@ -23,6 +23,7 @@ import (
"time" "time"
"github.com/bep/debounce" "github.com/bep/debounce"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/loggers" "github.com/gohugoio/hugo/common/loggers"
"github.com/spf13/cast" "github.com/spf13/cast"
@ -505,7 +506,7 @@ func (c *collector) applyThemeConfig(tc *moduleAdapter) error {
} }
func (c *collector) collect() { func (c *collector) collect() {
defer c.logger.PrintTimerIfDelayed(time.Now(), "hugo: collected modules") defer c.logger.PrintTimerIfDelayed(htime.Now(), "hugo: collected modules")
d := debounce.New(2 * time.Second) d := debounce.New(2 * time.Second)
d(func() { d(func() {
c.logger.Println("hugo: downloading modules …") c.logger.Println("hugo: downloading modules …")

View file

@ -13,7 +13,11 @@
package resource package resource
import "time" import (
"time"
"github.com/gohugoio/hugo/common/htime"
)
var _ Dated = Dates{} var _ Dated = Dates{}
@ -55,7 +59,8 @@ func IsFuture(d Dated) bool {
if d.PublishDate().IsZero() { if d.PublishDate().IsZero() {
return false return false
} }
return d.PublishDate().After(time.Now())
return d.PublishDate().After(htime.Now())
} }
// IsExpired returns whether the argument is expired. // IsExpired returns whether the argument is expired.
@ -63,7 +68,7 @@ func IsExpired(d Dated) bool {
if d.ExpiryDate().IsZero() { if d.ExpiryDate().IsZero() {
return false return false
} }
return d.ExpiryDate().Before(time.Now()) return d.ExpiryDate().Before(htime.Now())
} }
// IsZeroDates returns true if all of the dates are zero. // IsZeroDates returns true if all of the dates are zero.

View file

@ -35,6 +35,7 @@ import (
) )
func init() { func init() {
// htime.Now cannot be used here
rand.Seed(time.Now().UTC().UnixNano()) rand.Seed(time.Now().UTC().UnixNano())
} }

View file

@ -25,8 +25,8 @@ import (
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"time"
"github.com/gohugoio/hugo/common/htime"
texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate" texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
@ -222,7 +222,7 @@ func createKey(name string, variants ...any) (partialCacheKey, error) {
var errUnHashable = errors.New("unhashable") var errUnHashable = errors.New("unhashable")
func (ns *Namespace) getOrCreate(ctx context.Context, key partialCacheKey, context any) (result any, err error) { func (ns *Namespace) getOrCreate(ctx context.Context, key partialCacheKey, context any) (result any, err error) {
start := time.Now() start := htime.Now()
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
err = r.(error) err = r.(error)

View file

@ -28,6 +28,7 @@ func init() {
if d.Language == nil { if d.Language == nil {
panic("Language must be set") panic("Language must be set")
} }
ctx := New(langs.GetTimeFormatter(d.Language), langs.GetLocation(d.Language)) ctx := New(langs.GetTimeFormatter(d.Language), langs.GetLocation(d.Language))
ns := &internal.TemplateFuncsNamespace{ ns := &internal.TemplateFuncsNamespace{

View file

@ -68,9 +68,9 @@ func (ns *Namespace) Format(layout string, v any) (string, error) {
return ns.timeFormatter.Format(t, layout), nil return ns.timeFormatter.Format(t, layout), nil
} }
// Now returns the current local time. // Now returns the current local time or `clock` time
func (ns *Namespace) Now() _time.Time { func (ns *Namespace) Now() _time.Time {
return _time.Now() return htime.Now()
} }
// ParseDuration parses the duration string s. // ParseDuration parses the duration string s.

View file

@ -27,10 +27,10 @@ import (
"sort" "sort"
"strings" "strings"
"sync" "sync"
"time"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
"github.com/gohugoio/hugo/common/htime"
"github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
@ -235,7 +235,7 @@ func (t *templateExec) ExecuteWithContext(ctx context.Context, templ tpl.Templat
defer rlocker.RUnlock() defer rlocker.RUnlock()
} }
if t.Metrics != nil { if t.Metrics != nil {
defer t.Metrics.MeasureSince(templ.Name(), time.Now()) defer t.Metrics.MeasureSince(templ.Name(), htime.Now())
} }
if t.templateUsageTracker != nil { if t.templateUsageTracker != nil {