package main import ( "database/sql" "errors" "flag" "fmt" _ "github.com/mattn/go-sqlite3" "log" "math" "os" "strconv" "time" ) type EventsRow struct { description sql.NullString timestamp sql.NullInt64 interval sql.NullString period sql.NullString yearknown sql.NullBool monthknown sql.NullBool dayknown sql.NullBool hourknown sql.NullBool minuteknown sql.NullBool secondknown sql.NullBool } type TimelineDB struct { *sql.DB } func formula(p float64) int64 { yearsAgo := math.Exp(20.3444*math.Pow(p, 3)+3) - math.Exp(3) return int64(yearsAgo * 31556926) } func (db *TimelineDB) closestEvents(t int64, n uint, llimit int64, ulimit int64) ([]EventsRow, error) { query := ` select description, timestamp, yearknown, monthknown, dayknown, hourknown, minuteknown, secondknown from events where timestamp between ? and ? order by abs(? - timestamp) asc limit ? ` rows, err := db.Query(query, llimit, ulimit, t, n) if err != nil { return nil, err } defer rows.Close() events := make([]EventsRow, n) i := 0 for ; rows.Next(); i++ { var event EventsRow err = rows.Scan(&event.description, &event.timestamp, &event.yearknown, &event.monthknown, &event.dayknown, &event.hourknown, &event.minuteknown, &event.secondknown) if err != nil { return events, err } events[i] = event } return events[:i], err } func (event *EventsRow) Output() (int64, string, string, error) { if !event.timestamp.Valid { return 0, "", "", errors.New("null timestamp") } else if !event.description.Valid { return 0, "", "", errors.New("null description") } timestamp := event.timestamp.Int64 desc := event.description.String yearknown := event.yearknown.Valid && event.yearknown.Bool var ago string date := time.Unix(timestamp, 0) if !yearknown { yeardiff := time.Now().Year() - date.Year() ago = strconv.Itoa(yeardiff) + " years ago" } else { ago = date.String() } return timestamp, ago, desc, nil } func main() { percent := flag.Float64("p", -1, "percentage") nevents := flag.Uint("n", 1, "number of events") after := flag.Bool("a", false, "only return events occurring after computed timestamp") before := flag.Bool("b", false, "only return events occurring before computed timestamp") flag.Parse() if *percent < 0 || *percent > 1 { log.Fatal("invalid percentage") } t := time.Now().Unix() - formula(*percent) llimit := int64(math.MinInt64) ulimit := int64(math.MaxInt64) if *after && *before { log.Fatal("cannot pass both -a and -b") } else if *after { llimit = t } else if *before { ulimit = t } dbpath := os.Getenv("XBIT_DB") if dbpath == "" { log.Fatal("XBIT_DB not set") } var db TimelineDB var err error db.DB, err = sql.Open("sqlite3", dbpath) if err != nil { log.Fatal(err) } defer db.Close() events, err := db.closestEvents(t, *nevents, llimit, ulimit) if err != nil { log.Fatal(err) } for _, event := range events { timestamp, date, desc, err := event.Output() if err != nil { log.Fatal(err) } fmt.Printf("%v\t%v\t%v\n", timestamp, date, desc) } }