rl404/go-malscraper

View on GitHub
pkg/utils/time.go

Summary

Maintainability
C
1 day
Test Coverage
package utils

import (
    "fmt"
    "regexp"
    "strings"
    "time"
)

// StrToTime to convert MyAnimeList date string to time.Time type.
func StrToTime(str string) (t time.Time, isValid bool) {
    str = strings.TrimSpace(str)
    if str == "" {
        return time.Time{}, false
    }

    now := time.Now()
    now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second(), 0, time.Local)

    // Now
    if strings.ToLower(str) == "now" {
        return now, true
    } else

    // 5 seconds ago || 1 second ago
    if match, _ := regexp.MatchString(`([0-9]+) seconds? ago`, str); match {
        secondStr := GetValueFromSplit(str, " ", 0)
        second := time.Duration(StrToNum(secondStr))
        return now.Add(-(second * time.Second)), true
    } else

    // 27 minutes ago || 1 minute ago
    if match, _ := regexp.MatchString(`([0-9]+) minutes? ago`, str); match {
        minuteStr := GetValueFromSplit(str, " ", 0)
        minute := time.Duration(StrToNum(minuteStr))
        return now.Add(-(minute * time.Minute)), true
    } else

    // 8 hours ago || 1 hour ago
    if match, _ := regexp.MatchString(`([0-9]+) hours? ago`, str); match {
        hourStr := GetValueFromSplit(str, " ", 0)
        hour := time.Duration(StrToNum(hourStr))
        return now.Add(-(hour * time.Hour)), true
    } else

    // Today, 8:48 AM
    if match, _ := regexp.MatchString(`Today, ([0-9]{1,2}):([0-9]{2}) (AM|PM)`, str); match {
        today := now.Format("Jan 2, 2006")
        str = strings.Replace(str, "Today,", today, -1)
        return StrToTime(str)
    } else

    // Yesterday, 2:45 AM
    if match, _ := regexp.MatchString(`Yesterday, ([0-9]{1,2}):([0-9]{2}) (AM|PM)`, str); match {
        today := now.AddDate(0, 0, -1).Format("Jan 2, 2006")
        str = strings.Replace(str, "Yesterday,", today, -1)
        return StrToTime(str)
    } else

    // Apr 17, 8:57 PM
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}), ([0-9]{1,2}):([0-9]{2}) (AM|PM)`, str); match {
        str = strings.Replace(str, ",", now.Format(", 2006"), -1)
        return StrToTime(str)
    } else

    // Apr 9, 2017 10:57 PM
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}), ([0-9]{4}) ([0-9]{1,2}):([0-9]{2}) (AM|PM)`, str); match {
        return ParseTime("Jan 2, 2006 3:04 PM", str), true
    } else

    // Apr 7, 2013, 01:58 (JST)
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}), ([0-9]{4}), ([0-9]{1,2}):([0-9]{2}) \(([A-Z]+)\)`, str); match {
        return ParseTime("Jan 2, 2006, 15:04 (MST)", str), true
    } else

    // Apr 3, 1998
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}), ([0-9]{4})$`, str); match {
        return ParseTime("Jan 2, 2006", str), true
    } else

    // Apr ??, 1998
    if match, _ := regexp.MatchString(`([a-z]+) ([?]{2}), ([0-9]{4})$`, str); match {
        str = strings.Replace(str, "??", "1", -1)
        return StrToTime(str)
    } else

    // ??? ??, 1998
    if match, _ := regexp.MatchString(`([?]+) ([?]{2}), ([0-9]{4})$`, str); match {
        str = GetValueFromSplit(str, " ", 2)
        return StrToTime(str)
    } else

    // Jan, 2021
    if match, _ := regexp.MatchString(`([a-z]+), ([0-9]{4})$`, str); match {
        str = strings.Replace(str, ",", " 1,", -1)
        return StrToTime(str)
    } else

    // Jan 2021
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{4})$`, str); match {
        str = strings.Replace(str, " ", ", ", -1)
        return StrToTime(str)
    } else

    // Jan 3,
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}),$`, str); match {
        return ParseTime("Jan 2,", str), true
    } else

    // Jan 3
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2})$`, str); match {
        return ParseTime("Jan 2", str), true
    } else

    // 2021
    if match, _ := regexp.MatchString(`^([0-9]{4})$`, str); match {
        return ParseTime("Jan 2, 2006", "Jan 1, "+str), true
    } else

    // 10-25-02
    if match, _ := regexp.MatchString(`^([0-9]{2})-([0-9]{2})-([0-9]{2})$`, str); match {
        return ParseTime("01-02-06", str), true
    } else

    // 10-??-02
    if match, _ := regexp.MatchString(`^([0-9]{2})-([?]{2})-([0-9]{2})$`, str); match {
        str = strings.Replace(str, "??", "01", -1)
        return ParseTime("01-02-06", str), true
    } else

    // ??-??-02
    if match, _ := regexp.MatchString(`^([?]{2})-([?]{2})-([0-9]{2})$`, str); match {
        str = strings.Replace(str, "??", "01", -1)
        return ParseTime("01-02-06", str), true
    }

    return time.Time{}, false
}

var months = map[string]int{
    "Jan": 1,
    "Feb": 2,
    "Mar": 3,
    "Apr": 4,
    "May": 5,
    "Jun": 6,
    "Jul": 7,
    "Aug": 8,
    "Sep": 9,
    "Oct": 10,
    "Nov": 11,
    "Dec": 12,
}

// StrToDate to convert MyAnimeList date string to splitted date (year, month, day).
func StrToDate(str string) (y, m, d int) {
    str = strings.TrimSpace(str)
    if str == "" {
        return 0, 0, 0
    }

    str = strings.Replace(str, ",", "", -1)
    spltStr := strings.Split(str, " ")

    // Apr 3 1998
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}) ([0-9]{4})$`, str); match {
        return StrToNum(spltStr[2]), months[spltStr[0]], StrToNum(spltStr[1])
    } else

    // Apr ?? 1998
    if match, _ := regexp.MatchString(`([a-z]+) ([?]{2}) ([0-9]{4})$`, str); match {
        return StrToNum(spltStr[2]), months[spltStr[0]], 0
    } else

    // ??? ?? 1998
    if match, _ := regexp.MatchString(`([?]+) ([?]{2}) ([0-9]{4})$`, str); match {
        return StrToNum(spltStr[2]), 0, 0
    } else

    // Jan 2021
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{4})$`, str); match {
        return StrToNum(spltStr[1]), months[spltStr[0]], 0
    } else

    // Jan 3
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2})$`, str); match {
        return 0, months[spltStr[0]], StrToNum(spltStr[1])
    } else

    // 2021
    if match, _ := regexp.MatchString(`^([0-9]{4})$`, str); match {
        return StrToNum(spltStr[0]), 0, 0
    } else

    // Apr 9 2017 10:57 PM
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}) ([0-9]{4}) ([0-9]{1,2}):([0-9]{2}) (AM|PM)`, str); match {
        return StrToNum(spltStr[2]), months[spltStr[0]], StrToNum(spltStr[1])
    } else

    // Apr 7, 2013, 01:58 (JST)
    if match, _ := regexp.MatchString(`([a-z]+) ([0-9]{1,2}) ([0-9]{4}) ([0-9]{1,2}):([0-9]{2}) \(([A-Z]+)\)`, str); match {
        return StrToNum(spltStr[2]), months[spltStr[0]], StrToNum(spltStr[1])
    } else

    // 10-25-02
    if match, _ := regexp.MatchString(`^([0-9]{2})-([0-9]{2})-([0-9]{2})$`, str); match {
        spltStr = strings.Split(str, "-")
        y := StrToNum(spltStr[2])
        if y >= 69 {
            y += 1900
        } else {
            y += 2000
        }
        return y, StrToNum(spltStr[0]), StrToNum(spltStr[1])
    } else

    // 10-??-02
    if match, _ := regexp.MatchString(`^([0-9]{2})-([?]{2})-([0-9]{2})$`, str); match {
        spltStr = strings.Split(str, "-")
        y := StrToNum(spltStr[2])
        if y >= 69 {
            y += 1900
        } else {
            y += 2000
        }
        return y, StrToNum(spltStr[0]), 0
    } else

    // ??-??-02
    if match, _ := regexp.MatchString(`^([?]{2})-([?]{2})-([0-9]{2})$`, str); match {
        spltStr = strings.Split(str, "-")
        y := StrToNum(spltStr[2])
        if y >= 69 {
            y += 1900
        } else {
            y += 2000
        }
        return y, 0, 0
    }

    return 0, 0, 0
}

// ParseTime is time parser wrapper of built-in library `time.ParseInLocation`.
func ParseTime(layout, str string) time.Time {
    t, _ := time.ParseInLocation(layout, str, time.Local)
    return t
}

// GetDuration to get duration in seconds.
// Example: "3 hr. 30 min. 20 sec." => 12620.
func GetDuration(duration string) (seconds int) {
    duration = strings.TrimSpace(duration)
    if duration == "" {
        return 0
    }

    // Count hour.
    r := regexp.MustCompile(`\d+ hr.`)
    match := r.FindStringSubmatch(duration)
    if len(match) > 0 {
        h := GetValueFromSplit(match[0], " ", 0)
        seconds += StrToNum(h) * 3600
    }

    // Count minute.
    r = regexp.MustCompile(`\d+ min.`)
    match = r.FindStringSubmatch(duration)
    if len(match) > 0 {
        m := GetValueFromSplit(match[0], " ", 0)
        seconds += StrToNum(m) * 60
    }

    // Count minute.
    r = regexp.MustCompile(`\d+ sec.`)
    match = r.FindStringSubmatch(duration)
    if len(match) > 0 {
        s := GetValueFromSplit(match[0], " ", 0)
        seconds += StrToNum(s)
    }

    return seconds
}

// SecondToString to convert int seconds to string duration.
// Example: 12620 => 03:30:20.
func SecondToString(seconds int) string {
    d, _ := time.ParseDuration(fmt.Sprintf("%vs", seconds))
    duration := d.String()

    h, m, s := "00", "00", "00"

    // Count hour.
    r := regexp.MustCompile(`\d+h`)
    match := r.FindStringSubmatch(duration)
    if len(match) > 0 {
        h = strings.Replace(match[0], "h", "", -1)
        if len(h) == 1 {
            h = "0" + h
        }
    }

    // Count minute.
    r = regexp.MustCompile(`\d+m`)
    match = r.FindStringSubmatch(duration)
    if len(match) > 0 {
        m = strings.Replace(match[0], "m", "", -1)
        if len(m) == 1 {
            m = "0" + m
        }
    }

    // Count minute.
    r = regexp.MustCompile(`\d+s`)
    match = r.FindStringSubmatch(duration)
    if len(match) > 0 {
        s = strings.Replace(match[0], "s", "", -1)
        if len(s) == 1 {
            s = "0" + s
        }
    }

    return fmt.Sprintf("%v:%v:%v", h, m, s)
}