github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/cmds/core/date/date.go (about) 1 // Copyright 2015-2017 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // date prints the date. 6 // 7 // Synopsis: 8 // date [-u] [+format] | date [-u] [MMDDhhmm[CC]YY[.ss]] 9 package main 10 11 import ( 12 "flag" 13 "fmt" 14 "log" 15 "os" 16 "regexp" 17 "strconv" 18 "strings" 19 "time" 20 ) 21 22 var ( 23 // default format map from format.go on time lib 24 // Help to make format of date with posix compliant 25 fmtMap = map[string]string{ 26 "%a": "Mon", 27 "%A": "Monday", 28 "%b": "Jan", 29 "%h": "Jan", 30 "%B": "January", 31 "%c": time.UnixDate, 32 "%d": "02", 33 "%e": "_2", 34 "%H": "15", 35 "%I": "03", 36 "%m": "1", 37 "%M": "04", 38 "%p": "PM", 39 "%S": "05", 40 "%y": "06", 41 "%Y": "2006", 42 "%z": "-0700", 43 "%Z": "MST", 44 } 45 flags struct { 46 universal bool 47 reference string 48 } 49 ) 50 51 const cmd = "date [-u] [-d FILE] [+format] | date [-u] [-d FILE] [MMDDhhmm[CC]YY[.ss]]" 52 53 func init() { 54 defUsage := flag.Usage 55 flag.Usage = func() { 56 os.Args[0] = cmd 57 defUsage() 58 } 59 flag.BoolVar(&flags.universal, "u", false, "Coordinated Universal Time (UTC)") 60 flag.StringVar(&flags.reference, "r", "", "Display the last midification time of FILE") 61 } 62 63 // regex search for +format POSIX patterns 64 func formatParser(args string) []string { 65 pattern := regexp.MustCompile("%[a-zA-Z]") 66 match := pattern.FindAll([]byte(args), -1) 67 68 var results []string 69 for _, m := range match { 70 results = append(results, string(m[:])) 71 } 72 73 return results 74 } 75 76 // replace map for the format patterns according POSIX and GNU implementations 77 func dateMap(t time.Time, z *time.Location, format string) string { 78 d := t.In(z) 79 var toReplace string 80 for _, match := range formatParser(format) { 81 translate, exists := fmtMap[match] 82 switch { 83 case exists: 84 // Values defined by fmtMap 85 toReplace = d.Format(translate) 86 case match == "%C": 87 // Century (a year divided by 100 and truncated to an integer) 88 // as a decimal number [00,99]. 89 toReplace = strconv.Itoa(d.Year() / 100) 90 case match == "%D": 91 // Date in the format mm/dd/yy. 92 toReplace = dateMap(t, z, "%m/%d/%y") 93 case match == "%j": 94 // Day of the year as a decimal number [001,366]." 95 year, weekYear := d.ISOWeek() 96 firstWeekDay := time.Date(year, 1, 1, 1, 1, 1, 1, time.UTC).Weekday() 97 weekDay := d.Weekday() 98 dayYear := int(weekYear)*7 - (int(firstWeekDay) - 1) + int(weekDay) 99 toReplace = strconv.Itoa(dayYear) 100 case match == "%n": 101 // A <newline>. 102 toReplace = "\n" 103 case match == "%r": 104 // 12-hour clock time [01,12] using the AM/PM notation; 105 // in the POSIX locale, this shall be equivalent to %I : %M : %S %p. 106 toReplace = dateMap(t, z, "%I:%M:%S %p") 107 case match == "%t": 108 // A <tab>. 109 toReplace = "\t" 110 case match == "%T": 111 toReplace = dateMap(t, z, "%H:%M:%S") 112 case match == "%W": 113 // Week of the year (Sunday as the first day of the week) 114 // as a decimal number [00,53]. All days in a new year preceding 115 // the first Sunday shall be considered to be in week 0. 116 _, weekYear := d.ISOWeek() 117 weekDay := int(d.Weekday()) 118 isNotSunday := 1 119 if weekDay == 0 { 120 isNotSunday = 0 121 } 122 toReplace = strconv.Itoa(weekYear - (isNotSunday)) 123 case match == "%w": 124 // Weekday as a decimal number [0,6] (0=Sunday). 125 toReplace = strconv.Itoa(int(d.Weekday())) 126 case match == "%V": 127 // Week of the year (Monday as the first day of the week) 128 // as a decimal number [01,53]. If the week containing January 1 129 // has four or more days in the new year, then it shall be 130 // considered week 1; otherwise, it shall be the last week 131 // of the previous year, and the next week shall be week 1. 132 _, weekYear := d.ISOWeek() 133 toReplace = strconv.Itoa(int(weekYear)) 134 case match == "%x": 135 // Locale's appropriate date representation. 136 toReplace = dateMap(t, z, "%m/%d/%y") // TODO: decision algorithm 137 case match == "%F": 138 // Date yyyy-mm-dd defined by GNU implementation 139 toReplace = dateMap(t, z, "%Y-%m-%d") 140 case match == "%X": 141 // Locale's appropriate time representation. 142 toReplace = dateMap(t, z, "%I:%M:%S %p") // TODO: decision algorithm 143 default: 144 continue 145 } 146 147 format = strings.Replace(format, match, toReplace, 1) 148 // fmt.Printf("Iteration[%d]: %v\n", iter, format) 149 } 150 return format 151 } 152 153 func ints(s string, i ...*int) error { 154 var err error 155 for _, p := range i { 156 if *p, err = strconv.Atoi(s[0:2]); err != nil { 157 return err 158 } 159 s = s[2:] 160 } 161 return nil 162 } 163 164 // getTime gets the desired time as a time.Time. 165 // It derives it from a unix date command string. 166 // Some values in the string are optional, namely 167 // YY and SS. For these values, we use 168 // time.Now(). For the timezone, we use whatever 169 // one we are in, or UTC if desired. 170 func getTime(z *time.Location, s string) (t time.Time, err error) { 171 var MM, DD, hh, mm int 172 // CC is the year / 100, not the "century". 173 // i.e. for 2001, CC is 20, not 21. 174 YY := time.Now().Year() % 100 175 CC := time.Now().Year() / 100 176 SS := time.Now().Second() 177 if err = ints(s, &MM, &DD, &hh, &mm); err != nil { 178 return 179 } 180 s = s[8:] 181 switch len(s) { 182 case 0: 183 case 2: 184 err = ints(s, &YY) 185 case 3: 186 err = ints(s[1:], &SS) 187 case 4: 188 err = ints(s, &CC, &YY) 189 case 5: 190 s = s[0:2] + s[3:] 191 err = ints(s, &YY, &SS) 192 case 7: 193 s = s[0:4] + s[5:] 194 err = ints(s, &CC, &YY, &SS) 195 default: 196 err = fmt.Errorf("optional string is %v instead of [[CC]YY][.ss]", s) 197 } 198 199 if err != nil { 200 return 201 } 202 203 YY = YY + CC*100 204 t = time.Date(YY, time.Month(MM), DD, hh, mm, SS, 0, z) 205 return 206 } 207 208 func date(t time.Time, z *time.Location) string { 209 return t.In(z).Format(time.UnixDate) 210 } 211 212 func main() { 213 flag.Parse() 214 215 t := time.Now() 216 z := time.Local 217 if flags.universal { 218 z = time.UTC 219 } 220 if flags.reference != "" { 221 stat, err := os.Stat(flags.reference) 222 if err != nil { 223 log.Fatalf("Unable to gather stats of file %v", flags.reference) 224 } 225 t = stat.ModTime() 226 } 227 228 a := flag.Args() 229 switch len(a) { 230 case 0: 231 fmt.Printf("%v\n", date(t, z)) 232 case 1: 233 a0 := a[0] 234 if strings.HasPrefix(a0, "+") { 235 fmt.Printf("%v\n", dateMap(t, z, a0[1:])) 236 } else { 237 if err := setDate(a[0], z); err != nil { 238 log.Fatalf("%v: %v", a0, err) 239 } 240 } 241 default: 242 flag.Usage() 243 } 244 }