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 }