使用序数日期字段解析日期

时间:2017-11-24 10:18:28

标签: date parsing go

在Golang中,如何解析每月 序数 日期的日期?例如以下日期:

"Sunday 23rd January 2033 04:38:25 AM"

对于这个特定日期,我可以对 rd 进行硬编码,并在time.Parse()中使用此布局:

"Monday 02rd January 2006 15:04:05 PM"

但是,如果这个月的那一天是21日呢?

"Sunday 21st January 2033 04:38:25 AM"

(忽略此日期不正确的事实)

以下内容适用于案例,但显然是错误的:

"Monday 02st January 2006 15:04:05 PM"

我一直在阅读source code并且还没有达到启蒙。

(我知道缺少时区也需要由程序逻辑来解决)。

这出现在我自己写的一系列问题中,以改善我的日期解析。如果您还需要练习,请参阅https://github.com/soniah/date_practice

2 个答案:

答案 0 :(得分:1)

如果你有锤子,一切看起来像钉子。如果你有一个计算机程序员,一切看起来都像一个函数。

首先,一个序数函数图,

package main

import (
    "fmt"
    "strconv"
)

// generateOrdinalsMap generates map[ordinal]cardinal to stdout
// for the non-negative interval [min, max]
// as var name = map[string]string{}.
func generateOrdinalsMap(name string, min, max int) {
    fmt.Printf("\nvar " + name + " = map[string]string{ // map[ordinal]cardinal\n")
    for i := min; i >= 0 && i <= max; i++ {
        var o string
        switch i % 10 {
        case 1:
            o = "st"
        case 2:
            o = "nd"
        case 3:
            o = "rd"
        default:
            o = "th"
        }
        if 11 <= i && i <= 13 {
            o = "th"
        }
        c := strconv.Itoa(i)
        o = c + o
        fmt.Printf(`"%s": "%s", `, o, c)
        if (i)%5 == 0 || i == max {
            fmt.Printf("\n")
        }
    }
    fmt.Printf("}\n\n")
}

func main() {
    // Generate ordinal map for days.
    generateOrdinalsMap("dayOrdinals", 1, 31)
}

游乐场:https://play.golang.org/p/W7Ad0pjjGu

输出:

var dayOrdinals = map[string]string{ // map[ordinal]cardinal
"1st": "1", "2nd": "2", "3rd": "3", "4th": "4", "5th": "5", 
"6th": "6", "7th": "7", "8th": "8", "9th": "9", "10th": "10", 
"11th": "11", "12th": "12", "13th": "13", "14th": "14", "15th": "15", 
"16th": "16", "17th": "17", "18th": "18", "19th": "19", "20th": "20", 
"21st": "21", "22nd": "22", "23rd": "23", "24th": "24", "25th": "25", 
"26th": "26", "27th": "27", "28th": "28", "29th": "29", "30th": "30", 
"31st": "31", 
}

第二,time.Parse包装函数,它将value中的序数日转换为基数日,然后在基数日layout进行解析,

package main

import (
    "fmt"
    "strconv"
    "strings"
    "time"
)

var dayOrdinals = map[string]string{ // map[ordinal]cardinal
    "1st": "1", "2nd": "2", "3rd": "3", "4th": "4", "5th": "5",
    "6th": "6", "7th": "7", "8th": "8", "9th": "9", "10th": "10",
    "11th": "11", "12th": "12", "13th": "13", "14th": "14", "15th": "15",
    "16th": "16", "17th": "17", "18th": "18", "19th": "19", "20th": "20",
    "21st": "21", "22nd": "22", "23rd": "23", "24th": "24", "25th": "25",
    "26th": "26", "27th": "27", "28th": "28", "29th": "29", "30th": "30",
    "31st": "31",
}

// parseOrdinalDate parses a string time value using an ordinary package time layout.
// Before parsing, an ordinal day, [1st, 31st], is converted to a cardinal day, [1, 31].
// For example, "1st August 2017" is converted to "1 August 2017" before parsing, and
// "August 1st, 2017" is converted to "August 1, 2017" before parsing.
func parseOrdinalDate(layout, value string) (time.Time, error) {
    const ( // day number
        cardMinLen = len("1")
        cardMaxLen = len("31")
        ordSfxLen  = len("th")
        ordMinLen  = cardMinLen + ordSfxLen
    )

    for k := 0; k < len(value)-ordMinLen; {
        // i number start
        for ; k < len(value) && (value[k] > '9' || value[k] < '0'); k++ {
        }
        i := k
        // j cardinal end
        for ; k < len(value) && (value[k] <= '9' && value[k] >= '0'); k++ {
        }
        j := k
        if j-i > cardMaxLen || j-i < cardMinLen {
            continue
        }
        // k ordinal end
        // ASCII Latin (uppercase | 0x20) = lowercase
        for ; k < len(value) && (value[k]|0x20 >= 'a' && value[k]|0x20 <= 'z'); k++ {
        }
        if k-j != ordSfxLen {
            continue
        }

        // day ordinal to cardinal
        for ; i < j-1 && (value[i] == '0'); i++ {
        }
        o := strings.ToLower(value[i:k])
        c, ok := dayOrdinals[o]
        if ok {
            value = value[:i] + c + value[k:]
            break
        }
    }

    return time.ParseInLocation(layout, value, defaultLocation)
}

// Times without a timezone are Hong Kong times.
var defaultLocation = func(name string) *time.Location {
    loc, err := time.LoadLocation(name)
    if err != nil {
        loc = time.UTC
    }
    return loc
}(`Asia/Hong_Kong`)

func main() {
    var dates = []struct {
        layout, value string
    }{
        {"Monday 02 January 2006 15:04:05 PM", "Friday 24th November 2017 13:14:07 PM"},
        {"2 January 2006", "1st August 2017"},   // ISO
        {"January 2, 2006", "August 1st, 2017"}, // USA
    }
    fmt.Println()
    for _, d := range dates {
        fmt.Printf("Layout: %q\n", d.layout)
        fmt.Printf("Value:  %q\n", d.value)
        t, err := parseOrdinalDate(d.layout, d.value)
        fmt.Printf("Time:   %v  Error: %v\n", t, err)
        fmt.Printf("Unix:   %v  Error: %v\n", t, err)
        fmt.Println()
    }
}

游乐场:https://play.golang.org/p/5INR83e66T

输出:

Layout: "Monday 02 January 2006 15:04:05 PM"
Value:  "Friday 24th November 2017 13:14:07 PM"
Time:   2017-11-24 13:14:07 +0800 HKT  Error: <nil>
Unix:   2017-11-24 13:14:07 +0800 HKT  Error: <nil>

Layout: "2 January 2006"
Value:  "1st August 2017"
Time:   2017-08-01 00:00:00 +0800 HKT  Error: <nil>
Unix:   2017-08-01 00:00:00 +0800 HKT  Error: <nil>

Layout: "January 2, 2006"
Value:  "August 1st, 2017"
Time:   2017-08-01 00:00:00 +0800 HKT  Error: <nil>
Unix:   2017-08-01 00:00:00 +0800 HKT  Error: <nil>

第三,完成并运行date_practice测试,

{
    "wombat",
    "Sunday 23rd January 2033 04:38:25 AM",
    "Monday 2 January 2006 03:04:05 PM",
    1990039105,
    parseOrdinalDate,
    nil,
},

{
    "kangaroo",
    "Tuesday 7th November 2017 03:18:25 PM",
    "Monday 2 January 2006 03:04:05 PM",
    1510039105,
    parseOrdinalDate,
    nil,
},

答案 1 :(得分:1)

如果输入格式一致,这是一个简单的解决方案:

https://play.golang.org/p/EB-hruEGW5

package main

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

func main() {
    input := []string{
        "Sunday 23rd January 2033 04:38:25 AM",
        "Sunday 21st January 2033 04:38:25 AM",
        "Sunday 04th January 2033 04:38:25 AM",
        "Sunday 02nd January 2033 04:38:25 AM",
        "Thursday 23rd November 2017 5:06:55 PM",
        "Friday 24th November 2017 08:10:30 AM",
    }
    ordinal := []string{"nd", "rd", "st", "th"}
    form := "Monday 02 January 2006 15:04:05 PM"

    for _, d := range input {
        for _, o := range ordinal {
            if strings.Index(d, o+" ") > -1 {
                d = strings.Replace(d, o+" ", " ", -1)
                break
            }
        }
        t, _ := time.Parse(form, d)
        fmt.Println(t.String())
    }
}