在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。
答案 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())
}
}