package main import ( "bufio" "flag" "fmt" "io" "os" "time" ) var ( format = "2006-01-02 15:04" // Format that will be used for times. outFormat = format + " MST" // Output format. tz = "Local" // String descriptor for timezone. fromLoc *time.Location = time.Local // Go time.Location for the named timezone. toLoc *time.Location = time.UTC // Go time.Location for output timezone. ) func usage(w io.Writer) { fmt.Fprintf(w, `Usage: utc [-f format] [-h] [-o format] [-q] [-u] [-z zone] [time(s)...] utc converts times to UTC. If no arguments are provided, prints the current time in UTC. If the only argument provided is "-", utc reads newline-separated timestamps from standard input. If the argument is "help", it will print an extended help message with examples. If both the input and output timezones are the same (e.g., the local time zone is UTC), a warning message will be printed on standard error. This can be suppressed with the -q option. Flags: -f format Go timestamp format for input times. See the Go docs (e.g. https://golang.org/pkg/time/#pkg-constants) for an explanation of this format. Default value: %s -h Print this help message. -o format Go timestamp format for outputting times. It uses the same format as the '-f' argument. Default value: %s -q Suppress the timezone check warning message. -u Timestamps are in UTC format and should be converted to the timezone specified by the -z argument (which defaults to '%s'). Note that this isn't particularly useful with no arguments. -z zone Text form of the time zone; this can be in short time zone abbreviation (e.g. MST) or a location (e.g. America/Los_Angeles). This has no effect when printing the current time. Default value: %s `, format, outFormat, tz, tz) } func usageExamples() { usage(os.Stdout) fmt.Println(` Examples (note that the examples are done in the America/Los_Angeles / PST8PDT time zone): + Getting the current time in UTC: $ utc 2016-06-14 14:30 PDT = 2016-06-14 21:30 UTC + Converting a local timestamp to UTC: $ utc '2016-06-14 21:30' 2016-06-14 21:30 PDT = 2016-06-15 04:30 UTC + Converting a local EST timestamp to UTC (on a machine set to PST8PDT): $ utc -z EST '2016-06-14 21:30' 2016-06-14 21:30 EST = 2016-06-15 02:30 UTC + Converting timestamps in the form '14-06-2016 3:04PM': $ utc -f '02-01-2006 3:04PM' '14-06-2016 9:30PM' 2016-06-14 21:30 PDT = 2016-06-15 04:30 UTC + Converting timestamps from standard input: $ printf "2016-06-14 14:42\n2016-06-13 11:01" | utc - 2016-06-14 14:42 PDT = 2016-06-14 21:42 UTC 2016-06-13 11:01 PDT = 2016-06-13 18:01 UTC + Converting a UTC timestamp to the local time zone: $ utc -u '2016-06-14 21:30' 2016-06-14 21:30 UTC = 2016-06-14 14:30 PDT + Converting a UTC timestamp to EST (on a machine set to PST8PDT): $ utc -u -z EST '2016-06-14 21:30' 2016-06-14 21:30 UTC = 2016-06-14 16:30 EST + Using a different output format: $ utc -o '2006-01-02T15:03:04-0700' '2016-06-14 21:30' 2016-06-14T21:09:30-0700 = 2016-06-15T04:04:30+0000 + Example of the warning message when running utc on a machine where the local time zone is UTC: $ utc ================================================================== Note: both input and output timezone offsets are the same --- this program may not do what you expect it to. (Converting from UTC (offset +0000) to UTC (offset +0000).) ================================================================== 2016-06-14 23:44 = 2016-06-14 23:44 + Example of the warning message when running utc on a machine where the local time zone is GMT: $ utc ================================================================== Note: both input and output timezone offsets are the same --- this program may not do what you expect it to. (Converting from GMT (offset +0000) to UTC (offset +0000).) ================================================================== 2016-06-14 23:46 = 2016-06-14 23:46 `) } func getZone(loc *time.Location) (string, int) { return time.Now().In(loc).Zone() } func checkZones(quiet bool) { if quiet { return } toZone, toOff := getZone(toLoc) fromZone, fromOff := getZone(fromLoc) if toOff == fromOff { fmt.Fprintf(os.Stderr, ` ================================================================== Note: both input and output timezone offsets are the same --- this program may not do what you expect it to. (Converting from %s (offset %+05d) to %s (offset %+05d).) ================================================================== `, fromZone, fromOff, toZone, toOff) } } func init() { var help, quiet, utc bool flag.Usage = func() { usage(os.Stderr) } flag.StringVar(&format, "f", format, "time format") flag.BoolVar(&help, "h", false, "print usage information") flag.StringVar(&outFormat, "o", outFormat, "output time format") flag.BoolVar(&quiet, "q", false, "suppress zone check warning") flag.BoolVar(&utc, "u", false, "timestamps are in UTC format") flag.StringVar(&tz, "z", tz, "time zone to convert from; if blank, the local timezone is used") flag.Parse() if help { if !utc { checkZones(quiet) } usage(os.Stdout) os.Exit(0) } if utc { var err error toLoc, err = time.LoadLocation(tz) if err != nil { fmt.Fprintf(os.Stderr, "Malformed timezone %s: %s\n", tz, err) os.Exit(1) } fromLoc = time.UTC } else { var err error fromLoc, err = time.LoadLocation(tz) if err != nil { fmt.Fprintf(os.Stderr, "Malformed timezone %s: %s\n", tz, err) os.Exit(1) } if fromLoc == time.UTC { } toLoc = time.UTC } checkZones(quiet) } func showTime(t time.Time) { fmt.Printf("%s = %s\n", t.Format(outFormat), t.In(toLoc).Format(outFormat)) } func dumpTimes(times []string) bool { var errored bool for _, t := range times { u, err := time.ParseInLocation(format, t, fromLoc) if err != nil { errored = true fmt.Fprintf(os.Stderr, "Malformed time %s: %s\n", t, err) continue } showTime(u) } return errored } func main() { var times []string n := flag.NArg() switch n { case 0: showTime(time.Now()) os.Exit(0) case 1: if flag.Arg(0) == "-" { s := bufio.NewScanner(os.Stdin) for s.Scan() { times = append(times, s.Text()) } } else if flag.Arg(0) == "help" { usageExamples() } else { times = flag.Args() } default: times = flag.Args() } if dumpTimes(times) { os.Exit(1) } }