Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexible date/time parsing in Go (Adding default values in parsing)

Tags:

datetime

go

Further to this question, I want to parse a date/time passed on the command line to a Go program. At the moment, I use the flag package to populate a string variable ts and then the following code:

if ts == "" {
    config.Until = time.Now()
} else {
    const layout = "2006-01-02T15:04:05"
    if config.Until, err = time.Parse(layout, ts); err != nil {
        log.Errorf("Could not parse %s as a time string: %s. Using current date/time instead.", ts, err.Error())
        config.Until = time.Now()
    }
}

This works OK, provided the user passes exactly the right format - e.g. 2019-05-20T09:07:33 or some such.

However, what I would like, if possible, is the flexibility to pass e.g. 2019-05-20T09:07 or 2019-05-20T09 or maybe even 2019-05-20 and have the hours, minutes and seconds default to 0 where appropriate.

Is there a sane1 way to do this?


1 not requiring me to essentially write my own parser


UPDATE

I've kind of got a solution to this, although it's not particularly elegant, it does appear to work for most of the cases I am likely to encounter.

package main

import (
    "fmt"
    "time"
)

func main() {

    const layout = "2006-01-02T15:04:05"
    var l string
    var input string

    for _, input = range []string{"2019-05-30", "2019-05-30T16", "2019-05-30T16:04", "2019-05-30T16:04:34",
                                "This won't work", "This is extravagantly long and won't work either"} {
        if len(input) < len(layout) {
            l = layout[:len(input)]
        } else {
            l = layout
        }
        if d, err := time.Parse(l, input); err != nil {
            fmt.Printf("Error %s\n", err.Error())
        } else {
            fmt.Printf("Layout %-20s gives time %v\n", l, d)
        }
    }
}
like image 922
TimGJ Avatar asked May 30 '19 08:05

TimGJ


3 Answers

Just try each format, until one works. If none work, return an error.

var formats = []string{"2006-01-02T15:04:05", "2006-01-02", ...}

func parseTime(input string) (time.Time, error) {
    for _, format := range formats {
        t, err := time.Parse(format, input)
        if err == nil {
            return t, nil
        }
    }
    return time.Time{}, errors.New("Unrecognized time format")
}
like image 72
Flimzy Avatar answered Oct 27 '22 00:10

Flimzy


I think this library is what you are looking for https://github.com/araddon/dateparse

Parse many date strings without knowing the format in advance. Uses a scanner to read bytes and use a state machine to find format.

t, err := dateparse.ParseAny("3/1/2014")
like image 29
zdebra Avatar answered Oct 26 '22 22:10

zdebra


In the specific scenario you describe, you could check the length of the input datestamp string, and add the proper length of zero stuff at the end of it to correspond to your layout. So basically you could append as much of the string "T00:00:00" (counting from the end), to the input as is missing in length compared to the layout format string.

like image 27
FooF Avatar answered Oct 27 '22 00:10

FooF