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