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  }