@@ -1602,41 +1602,81 @@ func ExtractDurationNum(d *Duration, unit string) (int64, error) {
1602
1602
}
1603
1603
}
1604
1604
1605
- func extractSingleTimeValue (unit string , format string ) (int64 , int64 , int64 , float64 , error ) {
1606
- fv , err := strconv .ParseFloat (format , 64 )
1605
+ func parseSingleTimeValue (unit string , format string ) (int64 , int64 , int64 , int64 , error ) {
1606
+ // Format is a preformatted number, it format should be A[.[B]].
1607
+ decimalPointPos := strings .IndexRune (format , '.' )
1608
+ if decimalPointPos == - 1 {
1609
+ decimalPointPos = len (format )
1610
+ }
1611
+ sign := int64 (1 )
1612
+ if len (format ) > 0 && format [0 ] == '-' {
1613
+ sign = int64 (- 1 )
1614
+ }
1615
+ iv , err := strconv .ParseInt (format [0 :decimalPointPos ], 10 , 64 )
1607
1616
if err != nil {
1608
1617
return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1609
1618
}
1610
- iv := int64 ( math . Round ( fv ))
1619
+ riv := iv // Rounded integer value
1611
1620
1621
+ dv := int64 (0 )
1622
+ lf := len (format ) - 1
1623
+ // Has fraction part
1624
+ if decimalPointPos < lf {
1625
+ if lf - decimalPointPos >= 6 {
1626
+ // MySQL rounds down to 1e-6.
1627
+ if dv , err = strconv .ParseInt (format [decimalPointPos + 1 :decimalPointPos + 7 ], 10 , 64 ); err != nil {
1628
+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1629
+ }
1630
+ } else {
1631
+ if dv , err = strconv .ParseInt (format [decimalPointPos + 1 :]+ "000000" [:6 - (lf - decimalPointPos )], 10 , 64 ); err != nil {
1632
+ return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (format )
1633
+ }
1634
+ }
1635
+ if dv >= 500000 { // Round up, and we should keep 6 digits for microsecond, so dv should in [000000, 999999].
1636
+ riv += sign
1637
+ }
1638
+ if unit != "SECOND" {
1639
+ err = ErrTruncatedWrongValue .GenWithStackByArgs (format )
1640
+ }
1641
+ }
1642
+ const gotimeDay = 24 * gotime .Hour
1612
1643
switch strings .ToUpper (unit ) {
1613
1644
case "MICROSECOND" :
1614
- return 0 , 0 , 0 , fv * float64 (gotime .Microsecond ), nil
1645
+ dayCount := riv / int64 (gotimeDay / gotime .Microsecond )
1646
+ riv %= int64 (gotimeDay / gotime .Microsecond )
1647
+ return 0 , 0 , dayCount , riv * int64 (gotime .Microsecond ), err
1615
1648
case "SECOND" :
1616
- return 0 , 0 , 0 , fv * float64 (gotime .Second ), nil
1649
+ dayCount := iv / int64 (gotimeDay / gotime .Second )
1650
+ iv %= int64 (gotimeDay / gotime .Second )
1651
+ return 0 , 0 , dayCount , iv * int64 (gotime .Second ) + dv * int64 (gotime .Microsecond ), err
1617
1652
case "MINUTE" :
1618
- return 0 , 0 , 0 , float64 (iv * int64 (gotime .Minute )), nil
1653
+ dayCount := riv / int64 (gotimeDay / gotime .Minute )
1654
+ riv %= int64 (gotimeDay / gotime .Minute )
1655
+ return 0 , 0 , dayCount , riv * int64 (gotime .Minute ), err
1619
1656
case "HOUR" :
1620
- return 0 , 0 , 0 , float64 (iv * int64 (gotime .Hour )), nil
1657
+ dayCount := riv / 24
1658
+ riv %= 24
1659
+ return 0 , 0 , dayCount , riv * int64 (gotime .Hour ), err
1621
1660
case "DAY" :
1622
- return 0 , 0 , iv , 0 , nil
1661
+ return 0 , 0 , riv , 0 , err
1623
1662
case "WEEK" :
1624
- return 0 , 0 , 7 * iv , 0 , nil
1663
+ return 0 , 0 , 7 * riv , 0 , err
1625
1664
case "MONTH" :
1626
- return 0 , iv , 0 , 0 , nil
1665
+ return 0 , riv , 0 , 0 , err
1627
1666
case "QUARTER" :
1628
- return 0 , 3 * iv , 0 , 0 , nil
1667
+ return 0 , 3 * riv , 0 , 0 , err
1629
1668
case "YEAR" :
1630
- return iv , 0 , 0 , 0 , nil
1669
+ return riv , 0 , 0 , 0 , err
1631
1670
}
1632
1671
1633
1672
return 0 , 0 , 0 , 0 , errors .Errorf ("invalid singel timeunit - %s" , unit )
1634
1673
}
1635
1674
1636
- // extractTimeValue extracts years, months, days, microseconds from a string
1675
+ // parseTimeValue gets years, months, days, nanoseconds from a string
1676
+ // nanosecond will not exceed length of single day
1637
1677
// MySQL permits any punctuation delimiter in the expr format.
1638
1678
// See https://dev.mysql.com/doc/refman/8.0/en/expressions.html#temporal-intervals
1639
- func extractTimeValue (format string , index , cnt int ) (int64 , int64 , int64 , float64 , error ) {
1679
+ func parseTimeValue (format string , index , cnt int ) (int64 , int64 , int64 , int64 , error ) {
1640
1680
neg := false
1641
1681
originalFmt := format
1642
1682
format = strings .TrimSpace (format )
@@ -1674,55 +1714,57 @@ func extractTimeValue(format string, index, cnt int) (int64, int64, int64, float
1674
1714
return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
1675
1715
}
1676
1716
1677
- hours , err := strconv .ParseFloat (fields [HourIndex ], 64 )
1717
+ hours , err := strconv .ParseInt (fields [HourIndex ], 10 , 64 )
1678
1718
if err != nil {
1679
1719
return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
1680
1720
}
1681
- minutes , err := strconv .ParseFloat (fields [MinuteIndex ], 64 )
1721
+ minutes , err := strconv .ParseInt (fields [MinuteIndex ], 10 , 64 )
1682
1722
if err != nil {
1683
1723
return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
1684
1724
}
1685
- seconds , err := strconv .ParseFloat (fields [SecondIndex ], 64 )
1725
+ seconds , err := strconv .ParseInt (fields [SecondIndex ], 10 , 64 )
1686
1726
if err != nil {
1687
1727
return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
1688
1728
}
1689
- microseconds , err := strconv .ParseFloat (alignFrac (fields [MicrosecondIndex ], MaxFsp ), 64 )
1729
+ microseconds , err := strconv .ParseInt (alignFrac (fields [MicrosecondIndex ], MaxFsp ), 10 , 64 )
1690
1730
if err != nil {
1691
1731
return 0 , 0 , 0 , 0 , ErrIncorrectDatetimeValue .GenWithStackByArgs (originalFmt )
1692
1732
}
1693
- durations : = hours * float64 ( gotime . Hour ) + minutes * float64 ( gotime . Minute ) +
1694
- seconds * float64 ( gotime . Second ) + microseconds * float64 ( gotime . Microsecond )
1695
-
1696
- return years , months , days , durations , nil
1733
+ seconds = hours * 3600 + minutes * 60 + seconds
1734
+ days += seconds / ( 3600 * 24 )
1735
+ seconds %= 3600 * 24
1736
+ return years , months , days , seconds * int64 ( gotime . Second ) + microseconds * int64 ( gotime . Microsecond ) , nil
1697
1737
}
1698
1738
1699
- // ExtractTimeValue extracts time value from time unit and format.
1700
- func ExtractTimeValue (unit string , format string ) (int64 , int64 , int64 , float64 , error ) {
1739
+ // ParseDurationValue parses time value from time unit and format.
1740
+ // Returns y years m months d days + n nanoseconds
1741
+ // Nanoseconds will no longer than one day.
1742
+ func ParseDurationValue (unit string , format string ) (y int64 , m int64 , d int64 , n int64 , _ error ) {
1701
1743
switch strings .ToUpper (unit ) {
1702
1744
case "MICROSECOND" , "SECOND" , "MINUTE" , "HOUR" , "DAY" , "WEEK" , "MONTH" , "QUARTER" , "YEAR" :
1703
- return extractSingleTimeValue (unit , format )
1745
+ return parseSingleTimeValue (unit , format )
1704
1746
case "SECOND_MICROSECOND" :
1705
- return extractTimeValue (format , MicrosecondIndex , SecondMicrosecondMaxCnt )
1747
+ return parseTimeValue (format , MicrosecondIndex , SecondMicrosecondMaxCnt )
1706
1748
case "MINUTE_MICROSECOND" :
1707
- return extractTimeValue (format , MicrosecondIndex , MinuteMicrosecondMaxCnt )
1749
+ return parseTimeValue (format , MicrosecondIndex , MinuteMicrosecondMaxCnt )
1708
1750
case "MINUTE_SECOND" :
1709
- return extractTimeValue (format , SecondIndex , MinuteSecondMaxCnt )
1751
+ return parseTimeValue (format , SecondIndex , MinuteSecondMaxCnt )
1710
1752
case "HOUR_MICROSECOND" :
1711
- return extractTimeValue (format , MicrosecondIndex , HourMicrosecondMaxCnt )
1753
+ return parseTimeValue (format , MicrosecondIndex , HourMicrosecondMaxCnt )
1712
1754
case "HOUR_SECOND" :
1713
- return extractTimeValue (format , SecondIndex , HourSecondMaxCnt )
1755
+ return parseTimeValue (format , SecondIndex , HourSecondMaxCnt )
1714
1756
case "HOUR_MINUTE" :
1715
- return extractTimeValue (format , MinuteIndex , HourMinuteMaxCnt )
1757
+ return parseTimeValue (format , MinuteIndex , HourMinuteMaxCnt )
1716
1758
case "DAY_MICROSECOND" :
1717
- return extractTimeValue (format , MicrosecondIndex , DayMicrosecondMaxCnt )
1759
+ return parseTimeValue (format , MicrosecondIndex , DayMicrosecondMaxCnt )
1718
1760
case "DAY_SECOND" :
1719
- return extractTimeValue (format , SecondIndex , DaySecondMaxCnt )
1761
+ return parseTimeValue (format , SecondIndex , DaySecondMaxCnt )
1720
1762
case "DAY_MINUTE" :
1721
- return extractTimeValue (format , MinuteIndex , DayMinuteMaxCnt )
1763
+ return parseTimeValue (format , MinuteIndex , DayMinuteMaxCnt )
1722
1764
case "DAY_HOUR" :
1723
- return extractTimeValue (format , HourIndex , DayHourMaxCnt )
1765
+ return parseTimeValue (format , HourIndex , DayHourMaxCnt )
1724
1766
case "YEAR_MONTH" :
1725
- return extractTimeValue (format , MonthIndex , YearMonthMaxCnt )
1767
+ return parseTimeValue (format , MonthIndex , YearMonthMaxCnt )
1726
1768
default :
1727
1769
return 0 , 0 , 0 , 0 , errors .Errorf ("invalid singel timeunit - %s" , unit )
1728
1770
}
0 commit comments