@@ -79,6 +79,50 @@ const (
7979 TimeMaxValueSeconds = TimeMaxHour * 3600 + TimeMaxMinute * 60 + TimeMaxSecond
8080)
8181
82+ const (
83+ // YearIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
84+ YearIndex = 0 + iota
85+ // MonthIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
86+ MonthIndex
87+ // DayIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
88+ DayIndex
89+ // HourIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
90+ HourIndex
91+ // MinuteIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
92+ MinuteIndex
93+ // SecondIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
94+ SecondIndex
95+ // MicrosecondIndex is index of 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
96+ MicrosecondIndex
97+ )
98+
99+ const (
100+ // YearMonthMaxCnt is max parameters count 'YEARS-MONTHS' expr Format allowed
101+ YearMonthMaxCnt = 2
102+ // DayHourMaxCnt is max parameters count 'DAYS HOURS' expr Format allowed
103+ DayHourMaxCnt = 2
104+ // DayMinuteMaxCnt is max parameters count 'DAYS HOURS:MINUTES' expr Format allowed
105+ DayMinuteMaxCnt = 3
106+ // DaySecondMaxCnt is max parameters count 'DAYS HOURS:MINUTES:SECONDS' expr Format allowed
107+ DaySecondMaxCnt = 4
108+ // DayMicrosecondMaxCnt is max parameters count 'DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format allowed
109+ DayMicrosecondMaxCnt = 5
110+ // HourMinuteMaxCnt is max parameters count 'HOURS:MINUTES' expr Format allowed
111+ HourMinuteMaxCnt = 2
112+ // HourSecondMaxCnt is max parameters count 'HOURS:MINUTES:SECONDS' expr Format allowed
113+ HourSecondMaxCnt = 3
114+ // HourMicrosecondMaxCnt is max parameters count 'HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format allowed
115+ HourMicrosecondMaxCnt = 4
116+ // MinuteSecondMaxCnt is max parameters count 'MINUTES:SECONDS' expr Format allowed
117+ MinuteSecondMaxCnt = 2
118+ // MinuteMicrosecondMaxCnt is max parameters count 'MINUTES:SECONDS.MICROSECONDS' expr Format allowed
119+ MinuteMicrosecondMaxCnt = 3
120+ // SecondMicrosecondMaxCnt is max parameters count 'SECONDS.MICROSECONDS' expr Format allowed
121+ SecondMicrosecondMaxCnt = 2
122+ // TimeValueCnt is parameters count 'YEARS-MONTHS DAYS HOURS:MINUTES:SECONDS.MICROSECONDS' expr Format
123+ TimeValueCnt = 7
124+ )
125+
82126// Zero values for different types.
83127var (
84128 // ZeroDuration is the zero value for Duration type.
@@ -1574,234 +1618,67 @@ func extractSingleTimeValue(unit string, format string) (int64, int64, int64, fl
15741618 return 0 , 0 , 0 , 0 , errors .Errorf ("invalid singel timeunit - %s" , unit )
15751619}
15761620
1577- // extractSecondMicrosecond extracts second and microsecond from a string and its format is `SS.FFFFFF`.
1578- func extractSecondMicrosecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1579- fields := strings .Split (format , "." )
1580- if len (fields ) != 2 {
1581- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1582- }
1583-
1584- seconds , err := strconv .ParseFloat (fields [0 ], 64 )
1585- if err != nil {
1586- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1587- }
1588-
1589- microseconds , err := strconv .ParseFloat (alignFrac (fields [1 ], MaxFsp ), 64 )
1590- if err != nil {
1591- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1592- }
1593-
1594- return 0 , 0 , 0 , seconds * float64 (gotime .Second ) + microseconds * float64 (gotime .Microsecond ), nil
1595- }
1596-
1597- // extractMinuteMicrosecond extracts minutes and microsecond from a string and its format is `MM:SS.FFFFFF`.
1598- func extractMinuteMicrosecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1599- fields := strings .Split (format , ":" )
1600- if len (fields ) != 2 {
1601- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1602- }
1603-
1604- minutes , err := strconv .ParseFloat (fields [0 ], 64 )
1605- if err != nil {
1606- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1607- }
1608-
1609- _ , _ , _ , value , err := extractSecondMicrosecond (fields [1 ])
1610- if err != nil {
1611- return 0 , 0 , 0 , 0 , errors .Trace (err )
1612- }
1613-
1614- return 0 , 0 , 0 , minutes * float64 (gotime .Minute ) + value , nil
1615- }
1616-
1617- // extractMinuteSecond extracts minutes and second from a string and its format is `MM:SS`.
1618- func extractMinuteSecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1619- fields := strings .Split (format , ":" )
1620- if len (fields ) != 2 {
1621- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1622- }
1623-
1624- minutes , err := strconv .ParseFloat (fields [0 ], 64 )
1625- if err != nil {
1626- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1627- }
1628-
1629- seconds , err := strconv .ParseFloat (fields [1 ], 64 )
1630- if err != nil {
1631- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1632- }
1633-
1634- return 0 , 0 , 0 , minutes * float64 (gotime .Minute ) + seconds * float64 (gotime .Second ), nil
1635- }
1636-
1637- // extractHourMicrosecond extracts hour and microsecond from a string and its format is `HH:MM:SS.FFFFFF`.
1638- func extractHourMicrosecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1639- fields := strings .Split (format , ":" )
1640- if len (fields ) != 3 {
1641- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1642- }
1643-
1644- hours , err := strconv .ParseFloat (fields [0 ], 64 )
1645- if err != nil {
1646- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1647- }
1648-
1649- minutes , err := strconv .ParseFloat (fields [1 ], 64 )
1650- if err != nil {
1651- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1652- }
1653-
1654- _ , _ , _ , value , err := extractSecondMicrosecond (fields [2 ])
1655- if err != nil {
1656- return 0 , 0 , 0 , 0 , errors .Trace (err )
1657- }
1658-
1659- return 0 , 0 , 0 , hours * float64 (gotime .Hour ) + minutes * float64 (gotime .Minute ) + value , nil
1660- }
1661-
1662- // extractHourSecond extracts hour and second from a string and its format is `HH:MM:SS`.
1663- func extractHourSecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1664- fields := strings .Split (format , ":" )
1665- if len (fields ) != 3 {
1666- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1667- }
1668-
1669- hours , err := strconv .ParseFloat (fields [0 ], 64 )
1670- if err != nil {
1671- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1672- }
1673-
1674- minutes , err := strconv .ParseFloat (fields [1 ], 64 )
1675- if err != nil {
1676- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1677- }
1678-
1679- seconds , err := strconv .ParseFloat (fields [2 ], 64 )
1680- if err != nil {
1681- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1682- }
1683-
1684- return 0 , 0 , 0 , hours * float64 (gotime .Hour ) + minutes * float64 (gotime .Minute ) + seconds * float64 (gotime .Second ), nil
1685- }
1686-
1687- // extractHourMinute extracts hour and minute from a string and its format is `HH:MM`.
1688- func extractHourMinute (format string ) (int64 , int64 , int64 , float64 , error ) {
1689- fields := strings .Split (format , ":" )
1690- if len (fields ) != 2 {
1691- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1692- }
1693-
1694- hours , err := strconv .ParseFloat (fields [0 ], 64 )
1695- if err != nil {
1696- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1697- }
1698-
1699- minutes , err := strconv .ParseFloat (fields [1 ], 64 )
1700- if err != nil {
1701- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1702- }
1703-
1704- return 0 , 0 , 0 , hours * float64 (gotime .Hour ) + minutes * float64 (gotime .Minute ), nil
1705- }
1706-
1707- // extractDayMicrosecond extracts day and microsecond from a string and its format is `DD HH:MM:SS.FFFFFF`.
1708- func extractDayMicrosecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1709- fields := strings .Split (format , " " )
1710- if len (fields ) != 2 {
1711- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1712- }
1713-
1714- days , err := strconv .ParseInt (fields [0 ], 10 , 64 )
1715- if err != nil {
1716- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1621+ // extractTimeValue extracts years, months, days, microseconds from a string
1622+ // MySQL permits any punctuation delimiter in the expr format.
1623+ // See https://dev.mysql.com/doc/refman/8.0/en/expressions.html#temporal-intervals
1624+ func extractTimeValue (format string , index , cnt int ) (int64 , int64 , int64 , float64 , error ) {
1625+ neg := false
1626+ originalFmt := format
1627+ format = strings .TrimSpace (format )
1628+ if len (format ) > 0 && format [0 ] == '-' {
1629+ neg = true
1630+ format = format [1 :]
17171631 }
1718-
1719- _ , _ , _ , value , err := extractHourMicrosecond (fields [1 ])
1720- if err != nil {
1721- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1632+ fields := make ([]string , TimeValueCnt )
1633+ for i := range fields {
1634+ fields [i ] = "0"
17221635 }
1723-
1724- return 0 , 0 , days , value , nil
1725- }
1726-
1727- // extractDaySecond extracts day and hour from a string and its format is `DD HH:MM:SS`.
1728- func extractDaySecond (format string ) (int64 , int64 , int64 , float64 , error ) {
1729- fields := strings .Split (format , " " )
1730- if len (fields ) != 2 {
1731- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1636+ matches := numericRegex .FindAllString (format , - 1 )
1637+ if len (matches ) > cnt {
1638+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17321639 }
1733-
1734- days , err := strconv .ParseInt (fields [0 ], 10 , 64 )
1735- if err != nil {
1736- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1640+ for i := range matches {
1641+ if neg {
1642+ fields [index ] = "-" + matches [len (matches )- 1 - i ]
1643+ } else {
1644+ fields [index ] = matches [len (matches )- 1 - i ]
1645+ }
1646+ index --
17371647 }
17381648
1739- _ , _ , _ , value , err := extractHourSecond (fields [1 ] )
1649+ years , err := strconv . ParseInt (fields [YearIndex ], 10 , 64 )
17401650 if err != nil {
1741- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1651+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17421652 }
1743-
1744- return 0 , 0 , days , value , nil
1745- }
1746-
1747- // extractDayMinute extracts day and minute from a string and its format is `DD HH:MM`.
1748- func extractDayMinute (format string ) (int64 , int64 , int64 , float64 , error ) {
1749- fields := strings .Split (format , " " )
1750- if len (fields ) != 2 {
1751- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1752- }
1753-
1754- days , err := strconv .ParseInt (fields [0 ], 10 , 64 )
1653+ months , err := strconv .ParseInt (fields [MonthIndex ], 10 , 64 )
17551654 if err != nil {
1756- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1655+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17571656 }
1758-
1759- _ , _ , _ , value , err := extractHourMinute (fields [1 ])
1657+ days , err := strconv .ParseInt (fields [DayIndex ], 10 , 64 )
17601658 if err != nil {
1761- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1762- }
1763-
1764- return 0 , 0 , days , value , nil
1765- }
1766-
1767- // extractDayHour extracts day and hour from a string and its format is `DD HH`.
1768- func extractDayHour (format string ) (int64 , int64 , int64 , float64 , error ) {
1769- fields := strings .Split (format , " " )
1770- if len (fields ) != 2 {
1771- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1659+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17721660 }
17731661
1774- days , err := strconv .ParseInt (fields [0 ], 10 , 64 )
1662+ hours , err := strconv .ParseFloat (fields [HourIndex ] , 64 )
17751663 if err != nil {
1776- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1664+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17771665 }
1778-
1779- hours , err := strconv .ParseFloat (fields [1 ], 64 )
1666+ minutes , err := strconv .ParseFloat (fields [MinuteIndex ], 64 )
17801667 if err != nil {
1781- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1668+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17821669 }
1783-
1784- return 0 , 0 , days , hours * float64 (gotime .Hour ), nil
1785- }
1786-
1787- // extractYearMonth extracts year and month from a string and its format is `YYYY-MM`.
1788- func extractYearMonth (format string ) (int64 , int64 , int64 , float64 , error ) {
1789- fields := strings .Split (format , "-" )
1790- if len (fields ) != 2 {
1791- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1792- }
1793-
1794- years , err := strconv .ParseInt (fields [0 ], 10 , 64 )
1670+ seconds , err := strconv .ParseFloat (fields [SecondIndex ], 64 )
17951671 if err != nil {
1796- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1672+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
17971673 }
1798-
1799- months , err := strconv .ParseInt (fields [1 ], 10 , 64 )
1674+ microseconds , err := strconv .ParseFloat (alignFrac (fields [MicrosecondIndex ], MaxFsp ), 64 )
18001675 if err != nil {
1801- return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1676+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
18021677 }
1678+ durations := hours * float64 (gotime .Hour ) + minutes * float64 (gotime .Minute ) +
1679+ seconds * float64 (gotime .Second ) + microseconds * float64 (gotime .Microsecond )
18031680
1804- return years , months , 0 , 0 , nil
1681+ return years , months , days , durations , nil
18051682}
18061683
18071684// ExtractTimeValue extracts time value from time unit and format.
@@ -1810,27 +1687,27 @@ func ExtractTimeValue(unit string, format string) (int64, int64, int64, float64,
18101687 case "MICROSECOND" , "SECOND" , "MINUTE" , "HOUR" , "DAY" , "WEEK" , "MONTH" , "QUARTER" , "YEAR" :
18111688 return extractSingleTimeValue (unit , format )
18121689 case "SECOND_MICROSECOND" :
1813- return extractSecondMicrosecond (format )
1690+ return extractTimeValue (format , MicrosecondIndex , SecondMicrosecondMaxCnt )
18141691 case "MINUTE_MICROSECOND" :
1815- return extractMinuteMicrosecond (format )
1692+ return extractTimeValue (format , MicrosecondIndex , MinuteMicrosecondMaxCnt )
18161693 case "MINUTE_SECOND" :
1817- return extractMinuteSecond (format )
1694+ return extractTimeValue (format , SecondIndex , MinuteSecondMaxCnt )
18181695 case "HOUR_MICROSECOND" :
1819- return extractHourMicrosecond (format )
1696+ return extractTimeValue (format , MicrosecondIndex , HourMicrosecondMaxCnt )
18201697 case "HOUR_SECOND" :
1821- return extractHourSecond (format )
1698+ return extractTimeValue (format , SecondIndex , HourSecondMaxCnt )
18221699 case "HOUR_MINUTE" :
1823- return extractHourMinute (format )
1700+ return extractTimeValue (format , MinuteIndex , HourMinuteMaxCnt )
18241701 case "DAY_MICROSECOND" :
1825- return extractDayMicrosecond (format )
1702+ return extractTimeValue (format , MicrosecondIndex , DayMicrosecondMaxCnt )
18261703 case "DAY_SECOND" :
1827- return extractDaySecond (format )
1704+ return extractTimeValue (format , SecondIndex , DaySecondMaxCnt )
18281705 case "DAY_MINUTE" :
1829- return extractDayMinute (format )
1706+ return extractTimeValue (format , MinuteIndex , DayMinuteMaxCnt )
18301707 case "DAY_HOUR" :
1831- return extractDayHour (format )
1708+ return extractTimeValue (format , HourIndex , DayHourMaxCnt )
18321709 case "YEAR_MONTH" :
1833- return extractYearMonth (format )
1710+ return extractTimeValue (format , MonthIndex , YearMonthMaxCnt )
18341711 default :
18351712 return 0 , 0 , 0 , 0 , errors .Errorf ("invalid singel timeunit - %s" , unit )
18361713 }
@@ -2391,6 +2268,9 @@ var twoDigitRegex = regexp.MustCompile("^[1-9][0-9]?")
23912268// oneToSixDigitRegex: it was just for [0, 999999]
23922269var oneToSixDigitRegex = regexp .MustCompile ("^[0-9]{0,6}" )
23932270
2271+ // numericRegex: it was for any numeric characters
2272+ var numericRegex = regexp .MustCompile ("[0-9]+" )
2273+
23942274// parseTwoNumeric is used for pattens 0..31 0..24 0..60 and so on.
23952275// It returns the parsed int, and remain data after parse.
23962276func parseTwoNumeric (input string ) (int , string ) {
0 commit comments