Loading... # 0x00 时间格式化 相信都听说过Golang时间格式化规则layout`2006-01-02 15:04:05` 是Golang创始纪念日,以前我也是这样认为,还觉得golang太傲慢了(笑)。直到后面仔细观察后发现layout中年、月、日、时、分、秒的数字才发现事情没有那么简单,首先来观察这几组layout: ```golang 2006-01-02 15:04:05 // 24小时格式 有0占位 2006-1-2 15:4:5 // 24小时格式 无0占位 2006-01-02 03:04:05 // 12小时格式 有0占位 2006-1-2 3:4:5 // 12小时格式 无0占位 2006 // 长年份 06 // 短年份 有0占位 6 // 短年份 无0占位 ``` 不知发现其中规则了没有,年、月、日、时、分、秒格式中除了表示占位的0和长年份的2以外,**没有一个数字是重复的**,也就是说只要检查layout中特定的数字就能解析出字块的意义。 为了验证我的猜想,来看一下time的源码: ```golang // nextStdChunk finds the first occurrence of a std string in // layout and returns the text before, the std string, and the text after. func nextStdChunk(layout string) (prefix string, std int, suffix string) { for i := 0; i < len(layout); i++ { switch c := int(layout[i]); c { case 'J': // January, Jan if len(layout) >= i+3 && layout[i:i+3] == "Jan" { if len(layout) >= i+7 && layout[i:i+7] == "January" { return layout[0:i], stdLongMonth, layout[i+7:] } if !startsWithLowerCase(layout[i+3:]) { return layout[0:i], stdMonth, layout[i+3:] } } case 'M': // Monday, Mon, MST if len(layout) >= i+3 { if layout[i:i+3] == "Mon" { if len(layout) >= i+6 && layout[i:i+6] == "Monday" { return layout[0:i], stdLongWeekDay, layout[i+6:] } if !startsWithLowerCase(layout[i+3:]) { return layout[0:i], stdWeekDay, layout[i+3:] } } if layout[i:i+3] == "MST" { return layout[0:i], stdTZ, layout[i+3:] } } case '0': // 01, 02, 03, 04, 05, 06, 002 if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' { return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:] } if len(layout) >= i+3 && layout[i+1] == '0' && layout[i+2] == '2' { return layout[0:i], stdZeroYearDay, layout[i+3:] } case '1': // 15, 1 if len(layout) >= i+2 && layout[i+1] == '5' { return layout[0:i], stdHour, layout[i+2:] } return layout[0:i], stdNumMonth, layout[i+1:] case '2': // 2006, 2 if len(layout) >= i+4 && layout[i:i+4] == "2006" { return layout[0:i], stdLongYear, layout[i+4:] } return layout[0:i], stdDay, layout[i+1:] case '_': // _2, _2006, __2 if len(layout) >= i+2 && layout[i+1] == '2' { //_2006 is really a literal _, followed by stdLongYear if len(layout) >= i+5 && layout[i+1:i+5] == "2006" { return layout[0 : i+1], stdLongYear, layout[i+5:] } return layout[0:i], stdUnderDay, layout[i+2:] } if len(layout) >= i+3 && layout[i+1] == '_' && layout[i+2] == '2' { return layout[0:i], stdUnderYearDay, layout[i+3:] } case '3': return layout[0:i], stdHour12, layout[i+1:] case '4': return layout[0:i], stdMinute, layout[i+1:] case '5': return layout[0:i], stdSecond, layout[i+1:] case 'P': // PM if len(layout) >= i+2 && layout[i+1] == 'M' { return layout[0:i], stdPM, layout[i+2:] } case 'p': // pm if len(layout) >= i+2 && layout[i+1] == 'm' { return layout[0:i], stdpm, layout[i+2:] } case '-': // -070000, -07:00:00, -0700, -07:00, -07 if len(layout) >= i+7 && layout[i:i+7] == "-070000" { return layout[0:i], stdNumSecondsTz, layout[i+7:] } if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" { return layout[0:i], stdNumColonSecondsTZ, layout[i+9:] } if len(layout) >= i+5 && layout[i:i+5] == "-0700" { return layout[0:i], stdNumTZ, layout[i+5:] } if len(layout) >= i+6 && layout[i:i+6] == "-07:00" { return layout[0:i], stdNumColonTZ, layout[i+6:] } if len(layout) >= i+3 && layout[i:i+3] == "-07" { return layout[0:i], stdNumShortTZ, layout[i+3:] } case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00, if len(layout) >= i+7 && layout[i:i+7] == "Z070000" { return layout[0:i], stdISO8601SecondsTZ, layout[i+7:] } if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" { return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:] } if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { return layout[0:i], stdISO8601TZ, layout[i+5:] } if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { return layout[0:i], stdISO8601ColonTZ, layout[i+6:] } if len(layout) >= i+3 && layout[i:i+3] == "Z07" { return layout[0:i], stdISO8601ShortTZ, layout[i+3:] } case '.': // .000 or .999 - repeated digits for fractional seconds. if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { ch := layout[i+1] j := i + 1 for j < len(layout) && layout[j] == ch { j++ } // String of digits must end here - only fractional second is all digits. if !isDigit(layout, j) { std := stdFracSecond0 if layout[i+1] == '9' { std = stdFracSecond9 } std |= (j - (i + 1)) << stdArgShift return layout[0:i], std, layout[j:] } } } } return layout, 0, "" } ``` 可以看到不光年月日时分秒没有重复,甚至连时区也不是重复的。只要遵循这个规则就可以随心自定义layout。 由此可见前面说这是Golang创始纪念日的说法完全是无稽之谈。 而这种设计实际上很科学也很好记。 Last modification:January 4, 2022 © Allow specification reprint Support Appreciate the author AliPayWeChat Like 0 如果觉得我的文章对你有用,请随意赞赏