time.go (3600B)
1 // Copyright 2021 The Hugo Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package htime
15
16 import (
17 "strings"
18 "time"
19
20 "github.com/bep/clock"
21 "github.com/spf13/cast"
22
23 "github.com/gohugoio/locales"
24 )
25
26 var (
27 longDayNames = []string{
28 "Sunday",
29 "Monday",
30 "Tuesday",
31 "Wednesday",
32 "Thursday",
33 "Friday",
34 "Saturday",
35 }
36
37 shortDayNames = []string{
38 "Sun",
39 "Mon",
40 "Tue",
41 "Wed",
42 "Thu",
43 "Fri",
44 "Sat",
45 }
46
47 shortMonthNames = []string{
48 "Jan",
49 "Feb",
50 "Mar",
51 "Apr",
52 "May",
53 "Jun",
54 "Jul",
55 "Aug",
56 "Sep",
57 "Oct",
58 "Nov",
59 "Dec",
60 }
61
62 longMonthNames = []string{
63 "January",
64 "February",
65 "March",
66 "April",
67 "May",
68 "June",
69 "July",
70 "August",
71 "September",
72 "October",
73 "November",
74 "December",
75 }
76
77 Clock = clock.System()
78 )
79
80 func NewTimeFormatter(ltr locales.Translator) TimeFormatter {
81 if ltr == nil {
82 panic("must provide a locales.Translator")
83 }
84 return TimeFormatter{
85 ltr: ltr,
86 }
87 }
88
89 // TimeFormatter is locale aware.
90 type TimeFormatter struct {
91 ltr locales.Translator
92 }
93
94 func (f TimeFormatter) Format(t time.Time, layout string) string {
95 if layout == "" {
96 return ""
97 }
98
99 if layout[0] == ':' {
100 // It may be one of Hugo's custom layouts.
101 switch strings.ToLower(layout[1:]) {
102 case "date_full":
103 return f.ltr.FmtDateFull(t)
104 case "date_long":
105 return f.ltr.FmtDateLong(t)
106 case "date_medium":
107 return f.ltr.FmtDateMedium(t)
108 case "date_short":
109 return f.ltr.FmtDateShort(t)
110 case "time_full":
111 return f.ltr.FmtTimeFull(t)
112 case "time_long":
113 return f.ltr.FmtTimeLong(t)
114 case "time_medium":
115 return f.ltr.FmtTimeMedium(t)
116 case "time_short":
117 return f.ltr.FmtTimeShort(t)
118 }
119 }
120
121 s := t.Format(layout)
122
123 monthIdx := t.Month() - 1 // Month() starts at 1.
124 dayIdx := t.Weekday()
125
126 s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
127 if !strings.Contains(s, f.ltr.MonthWide(t.Month())) {
128 s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthAbbreviated(t.Month()))
129 }
130 s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
131 if !strings.Contains(s, f.ltr.WeekdayWide(t.Weekday())) {
132 s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdayAbbreviated(t.Weekday()))
133 }
134
135 return s
136 }
137
138 func ToTimeInDefaultLocationE(i any, location *time.Location) (tim time.Time, err error) {
139 switch vv := i.(type) {
140 case AsTimeProvider:
141 return vv.AsTime(location), nil
142 // issue #8895
143 // datetimes parsed by `go-toml` have empty zone name
144 // convert back them into string and use `cast`
145 // TODO(bep) add tests, make sure we really need this.
146 case time.Time:
147 i = vv.Format(time.RFC3339)
148 }
149 return cast.ToTimeInDefaultLocationE(i, location)
150 }
151
152 // Now returns time.Now() or time value based on the `clock` flag.
153 // Use this function to fake time inside hugo.
154 func Now() time.Time {
155 return Clock.Now()
156 }
157
158 func Since(t time.Time) time.Duration {
159 return Clock.Since(t)
160 }
161
162 // AsTimeProvider is implemented by go-toml's LocalDate and LocalDateTime.
163 type AsTimeProvider interface {
164 AsTime(zone *time.Location) time.Time
165 }