dryk50495 2017-11-24 10:18
浏览 36
已采纳

用顺序日期字段解析日期

In Golang how do I parse a date that has an ordinal day of the month? For example the following date:

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

For this particular date I could hardcode the rd and use this layout in time.Parse():

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

But what if the day of the month was the 21st?

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

(ignoring the fact that this date is incorrect)

The following would work for this case but is obviously wrong:

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

I've been reading the source code and haven't yet reached enlightenment.

(I'm aware that the lack of timezone also needs to be accounted for by program logic).


This come up in a set of questions I wrote myself, to improve my date parsing. See https://github.com/soniah/date_practice if you also need practice!

  • 写回答

2条回答 默认 最新

  • dongshuo6185 2017-11-24 13:39
    关注

    If you have a hammer, everything looks like a nail. If you have a computer programmer, everything looks like a function.


    First, a map of ordinals function,

    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("
    var " + name + " = map[string]string{ // map[ordinal]cardinal
    ")
        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("
    ")
            }
        }
        fmt.Printf("}
    
    ")
    }
    
    func main() {
        // Generate ordinal map for days.
        generateOrdinalsMap("dayOrdinals", 1, 31)
    }
    

    Playground: https://play.golang.org/p/W7Ad0pjjGu

    Output:

    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", 
    }
    

    Second, a time.Parse wrapper function, which converts an ordinal day in the value to a cardinal day before parsing with a cardinal day 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
    ", d.layout)
            fmt.Printf("Value:  %q
    ", d.value)
            t, err := parseOrdinalDate(d.layout, d.value)
            fmt.Printf("Time:   %v  Error: %v
    ", t, err)
            fmt.Printf("Unix:   %v  Error: %v
    ", t, err)
            fmt.Println()
        }
    }
    

    Playground: https://play.golang.org/p/5INR83e66T

    Output:

    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>
    

    Third, complete and run your date_practice tests,

    {
        "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条)

报告相同问题?

悬赏问题

  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?
  • ¥15 求daily translation(DT)偏差订正方法的代码
  • ¥15 js调用html页面需要隐藏某个按钮
  • ¥15 ads仿真结果在圆图上是怎么读数的
  • ¥20 Cotex M3的调试和程序执行方式是什么样的?
  • ¥20 java项目连接sqlserver时报ssl相关错误
  • ¥15 一道python难题3