feat(tools/when): simple time-conversion tool
I often need unix timestamps, or have unix timestamps, or need timestamps at certain relative offsets etc. This adds a tool called `when` which can do all of this. It has basically no user interface, you just call it with a time query and it does it if it understands what you meant. This will get smarter over time as I find more uses. Example: ~> when yesterday 5PM Local: Mon 06 May 2024 at 17:00:00 MSK UTC: 2024-05-06T14:00:00Z UNIX: 1715004000 It supports all kinds of queries already, see the usage for details. Change-Id: I694ffef7608586acfb1ff8010ac0fac4d9951e2e Reviewed-on: https://cl.tvl.fyi/c/depot/+/11598 Tested-by: BuildkiteCI Autosubmit: tazjin <tazjin@tvl.su> Reviewed-by: tazjin <tazjin@tvl.su>
This commit is contained in:
parent
368a11ee0a
commit
00135d1c28
2 changed files with 147 additions and 0 deletions
6
tools/when/default.nix
Normal file
6
tools/when/default.nix
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{ depot, ... }:
|
||||||
|
|
||||||
|
depot.nix.buildGo.program {
|
||||||
|
name = "when";
|
||||||
|
srcs = [ ./when.go ];
|
||||||
|
}
|
141
tools/when/when.go
Normal file
141
tools/when/when.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const usage = `usage: when <time>
|
||||||
|
|
||||||
|
This program converts the given time into various formats (currently a local
|
||||||
|
timestamp, UTC timestamp, and UNIX epoch). It tries to guess what the input is.
|
||||||
|
|
||||||
|
Some valid queries:
|
||||||
|
|
||||||
|
2024-01-05
|
||||||
|
1715079241
|
||||||
|
tomorrow 5PM
|
||||||
|
-22h
|
||||||
|
-7h10m
|
||||||
|
|
||||||
|
For now a single timestamp and a single duration (which is added either to the
|
||||||
|
current time, or the given time) is supported.`
|
||||||
|
|
||||||
|
func printTime(t time.Time) {
|
||||||
|
fmt.Println("Local:", t.Format("Mon 02 January 2006 at 15:04:05 MST"))
|
||||||
|
fmt.Println("UTC: ", t.UTC().Format(time.RFC3339))
|
||||||
|
fmt.Println("UNIX: ", t.Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTime(this time.Time, that time.Time) time.Time {
|
||||||
|
return time.Date(
|
||||||
|
this.Year(),
|
||||||
|
this.Month(),
|
||||||
|
this.Day(),
|
||||||
|
that.Hour(),
|
||||||
|
that.Minute(),
|
||||||
|
that.Second(),
|
||||||
|
0,
|
||||||
|
this.Location(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTime(input string) (time.Time, error) {
|
||||||
|
// try unix times
|
||||||
|
if i, err := strconv.ParseInt(input, 10, 64); err == nil {
|
||||||
|
if i < 9999999999 {
|
||||||
|
return time.Unix(i, 0), nil
|
||||||
|
}
|
||||||
|
if i < 9999999999999 {
|
||||||
|
return time.UnixMilli(i), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try simple date/time formats
|
||||||
|
if t, err := time.Parse(time.DateOnly, input); err == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := time.Parse(time.Kitchen, input); err == nil {
|
||||||
|
now := time.Now()
|
||||||
|
return setTime(now, t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := time.Parse(time.TimeOnly, input); err == nil {
|
||||||
|
now := time.Now()
|
||||||
|
return setTime(now, t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := time.Parse("15:04", input); err == nil {
|
||||||
|
now := time.Now()
|
||||||
|
return setTime(now, t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := time.Parse("3PM", input); err == nil {
|
||||||
|
now := time.Now()
|
||||||
|
return setTime(now, t), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Time{}, fmt.Errorf("could not parse time: %q", input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDuration(input string) (time.Duration, error) {
|
||||||
|
// some simple rewriting
|
||||||
|
switch input {
|
||||||
|
case "yesterday":
|
||||||
|
input = "-24h"
|
||||||
|
case "tomorrow":
|
||||||
|
input = "24h"
|
||||||
|
case "today", "now":
|
||||||
|
return time.Duration(0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: days, months, weeks, ...
|
||||||
|
return time.ParseDuration(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Fprintln(os.Stderr, usage)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var d time.Duration
|
||||||
|
var t time.Time
|
||||||
|
var err error
|
||||||
|
var haveTime, haveDuration bool
|
||||||
|
|
||||||
|
for _, arg := range os.Args[1:] {
|
||||||
|
if !haveTime {
|
||||||
|
if t, err = parseTime(arg); err == nil {
|
||||||
|
haveTime = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haveDuration {
|
||||||
|
if d, err = parseDuration(arg); err == nil {
|
||||||
|
haveDuration = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Not sure what you want, try another time.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if haveTime && haveDuration {
|
||||||
|
printTime(t.Add(d))
|
||||||
|
} else if haveTime {
|
||||||
|
printTime(t)
|
||||||
|
} else if haveDuration {
|
||||||
|
printTime(time.Now().Add(d))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "Not sure what you want, try another time.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue