gitlab.com/apertussolutions/u-root@v7.0.0+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  }