相对日期解析

How to parse relative datetime in GO?

Example of relative dates:

today at 9:17 AM
yesterday at 9:58 PM
Saturday at 9:44 PM
Wednesday at 11:01 AM

So format is DAY (in the past) at TIME. I tried next example:

const longForm = "Monday at 3:04 PM"
t, _ := time.Parse(longForm, "Saturday at 3:50 PM")
fmt.Println(t)

demo

Time is parsed correctly, but day/date is ignored...

Expanding on my comment:

Just Monday without further date reference is meaningless in the eyes of the parser, so it is discarded. Which Monday? The parser is strict, not fuzzy. Assuming Monday refers to the current week is not something that such a parser can do. You will not to write your own more sophisticated parser for that.

So it would have to be along these lines - one function that converts a relative fuzzy day to a real date, and replaces that in the original expression, and another one that parses the whole thing:

const dateFormat = "2006-01-02"
const longForm = "2006-01-02 at 3:04 PM"

func parseFuzzyDate(fuzzyTime string) (time.Time, error) {

    formattedTime, err := parseDayAndReplaceIt(fuzzyTime)
    if err != nil {
       return nil, err
    }

    return time.Parse(longForm, formattedTime)
}

and the second function gets the fuzzy time, finds the day, parses it and returns. I'm not going to implement it, just write in comments what should be done:

func parseDayAndReplaceIt(fuzzyTime string) (string, error) {
   // 1. Extract the day

   // 2. Parse weekday names to relative time

   // 3. if it's not a weekday name, parse things like "tomorrow" "yesterday"

   // 4. replace the day string in the original fuzzyTime with a formatted date that the parser can understand

   // 5. return the formatted date
}

I tweaked something that I wrote a while back and consolidated it into this example code:

func lastDateOf(targetDay time.Weekday, timeOfDay time.Time) time.Time {
    const oneDay = 24 * time.Hour
    var dayIndex time.Duration

    //dayIndex -= oneDay
    for {
        if time.Now().Add(dayIndex).Weekday() == targetDay {
            y, m, d := time.Now().Add(dayIndex).Date()
            return timeOfDay.AddDate(y, int(m)-1, d-1)
        }
        dayIndex -= oneDay
    }
}

It returns the date, relative to now, of the previous targetDay, added to timeOfDay, assuming that timeOfDay consists of hours, minutes and seconds, and the zero time values for year, month and day it will give you a suitable answer.

It's not very flexible but I believe it suits your example reasonably well. Although it doesn't address relative terms like "tomorrow", "yesterday" or "next Saturday".

runnable version in the playground.

Custom parser:

func RelativeDateParse(s string) (time.Time, error) {
    for n := 0; n < 7; n++ {
        day := time.Now().AddDate(0, 0, -n)
        dayName := day.Format("Monday")
        switch n {
        case 0:
            dayName = "today"
        case 1:
            dayName = "yesterday"
        }
        s = strings.Replace(s, dayName + " at", day.Format("2006-01-02"), -1)
    }
    return time.Parse("2006-01-02 3:04 PM", s)
}

demo