github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/time.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package types 15 16 import ( 17 "bytes" 18 "fmt" 19 "math" 20 "regexp" 21 "strconv" 22 "strings" 23 gotime "time" 24 "unicode" 25 26 "github.com/pingcap/errors" 27 28 "github.com/XiaoMi/Gaea/log" 29 "github.com/XiaoMi/Gaea/mysql" 30 "github.com/XiaoMi/Gaea/parser/stmtctx" 31 "github.com/XiaoMi/Gaea/parser/terror" 32 ) 33 34 // Portable of some common call errors. 35 var ( 36 ErrInvalidTimeFormat = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid time format: '%v'") 37 ErrInvalidYearFormat = errors.New("invalid year format") 38 ErrInvalidYear = errors.New("invalid year") 39 ErrZeroDate = errors.New("datetime zero in date") 40 ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'") 41 ErrTruncatedWrongValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue]) 42 ) 43 44 // Time format without fractional seconds precision. 45 const ( 46 DateFormat = "2006-01-02" 47 TimeFormat = "2006-01-02 15:04:05" 48 // TimeFSPFormat is time format with fractional seconds precision. 49 TimeFSPFormat = "2006-01-02 15:04:05.000000" 50 ) 51 52 const ( 53 // MinYear is the minimum for mysql year type. 54 MinYear int16 = 1901 55 // MaxYear is the maximum for mysql year type. 56 MaxYear int16 = 2155 57 // MaxDuration is the maximum for duration. 58 MaxDuration int64 = 838*10000 + 59*100 + 59 59 // MinTime is the minimum for mysql time type. 60 MinTime = -gotime.Duration(838*3600+59*60+59) * gotime.Second 61 // MaxTime is the maximum for mysql time type. 62 MaxTime = gotime.Duration(838*3600+59*60+59) * gotime.Second 63 // ZeroDatetimeStr is the string representation of a zero datetime. 64 ZeroDatetimeStr = "0000-00-00 00:00:00" 65 // ZeroDateStr is the string representation of a zero date. 66 ZeroDateStr = "0000-00-00" 67 68 // TimeMaxHour is the max hour for mysql time type. 69 TimeMaxHour = 838 70 // TimeMaxMinute is the max minute for mysql time type. 71 TimeMaxMinute = 59 72 // TimeMaxSecond is the max second for mysql time type. 73 TimeMaxSecond = 59 74 // TimeMaxValue is the maximum value for mysql time type. 75 TimeMaxValue = TimeMaxHour*10000 + TimeMaxMinute*100 + TimeMaxSecond 76 // TimeMaxValueSeconds is the maximum second value for mysql time type. 77 TimeMaxValueSeconds = TimeMaxHour*3600 + TimeMaxMinute*60 + TimeMaxSecond 78 ) 79 80 // Zero values for different types. 81 var ( 82 // ZeroDuration is the zero value for Duration type. 83 ZeroDuration = Duration{Duration: gotime.Duration(0), Fsp: DefaultFsp} 84 85 // ZeroTime is the zero value for TimeInternal type. 86 ZeroTime = MysqlTime{} 87 88 // ZeroDatetime is the zero value for datetime Time. 89 ZeroDatetime = Time{ 90 Time: ZeroTime, 91 Type: mysql.TypeDatetime, 92 Fsp: DefaultFsp, 93 } 94 95 // ZeroTimestamp is the zero value for timestamp Time. 96 ZeroTimestamp = Time{ 97 Time: ZeroTime, 98 Type: mysql.TypeTimestamp, 99 Fsp: DefaultFsp, 100 } 101 102 // ZeroDate is the zero value for date Time. 103 ZeroDate = Time{ 104 Time: ZeroTime, 105 Type: mysql.TypeDate, 106 Fsp: DefaultFsp, 107 } 108 ) 109 110 var ( 111 // MinDatetime is the minimum for mysql datetime type. 112 MinDatetime = FromDate(1000, 1, 1, 0, 0, 0, 0) 113 // MaxDatetime is the maximum for mysql datetime type. 114 MaxDatetime = FromDate(9999, 12, 31, 23, 59, 59, 999999) 115 116 // BoundTimezone is the timezone for min and max timestamp. 117 BoundTimezone = gotime.UTC 118 // MinTimestamp is the minimum for mysql timestamp type. 119 MinTimestamp = Time{ 120 Time: FromDate(1970, 1, 1, 0, 0, 1, 0), 121 Type: mysql.TypeTimestamp, 122 Fsp: DefaultFsp, 123 } 124 // MaxTimestamp is the maximum for mysql timestamp type. 125 MaxTimestamp = Time{ 126 Time: FromDate(2038, 1, 19, 3, 14, 7, 999999), 127 Type: mysql.TypeTimestamp, 128 Fsp: DefaultFsp, 129 } 130 131 // WeekdayNames lists names of weekdays, which are used in builtin time function `dayname`. 132 WeekdayNames = []string{ 133 "Monday", 134 "Tuesday", 135 "Wednesday", 136 "Thursday", 137 "Friday", 138 "Saturday", 139 "Sunday", 140 } 141 142 // MonthNames lists names of months, which are used in builtin time function `monthname`. 143 MonthNames = []string{ 144 "January", "February", 145 "March", "April", 146 "May", "June", 147 "July", "August", 148 "September", "October", 149 "November", "December", 150 } 151 ) 152 153 // FromGoTime translates time.Time to mysql time internal representation. 154 func FromGoTime(t gotime.Time) MysqlTime { 155 year, month, day := t.Date() 156 hour, minute, second := t.Clock() 157 // Nanosecond plus 500 then divided 1000 means rounding to microseconds. 158 microsecond := (t.Nanosecond() + 500) / 1000 159 return FromDate(year, int(month), day, hour, minute, second, microsecond) 160 } 161 162 // FromDate makes a internal time representation from the given date. 163 func FromDate(year int, month int, day int, hour int, minute int, second int, microsecond int) MysqlTime { 164 return MysqlTime{ 165 uint16(year), 166 uint8(month), 167 uint8(day), 168 hour, 169 uint8(minute), 170 uint8(second), 171 uint32(microsecond), 172 } 173 } 174 175 // Clock returns the hour, minute, and second within the day specified by t. 176 func (t Time) Clock() (hour int, minute int, second int) { 177 return t.Time.Hour(), t.Time.Minute(), t.Time.Second() 178 } 179 180 // Time is the struct for handling datetime, timestamp and date. 181 // TODO: check if need a NewTime function to set Fsp default value? 182 type Time struct { 183 Time MysqlTime 184 Type uint8 185 // Fsp is short for Fractional Seconds Precision. 186 // See http://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html 187 Fsp int 188 } 189 190 // MaxMySQLTime returns Time with maximum mysql time type. 191 func MaxMySQLTime(fsp int) Time { 192 return Time{Time: FromDate(0, 0, 0, TimeMaxHour, TimeMaxMinute, TimeMaxSecond, 0), Type: mysql.TypeDuration, Fsp: fsp} 193 } 194 195 // CurrentTime returns current time with type tp. 196 func CurrentTime(tp uint8) Time { 197 return Time{Time: FromGoTime(gotime.Now()), Type: tp, Fsp: 0} 198 } 199 200 // ConvertTimeZone converts the time value from one timezone to another. 201 // The input time should be a valid timestamp. 202 func (t *Time) ConvertTimeZone(from, to *gotime.Location) error { 203 if !t.IsZero() { 204 raw, err := t.Time.GoTime(from) 205 if err != nil { 206 return errors.Trace(err) 207 } 208 converted := raw.In(to) 209 t.Time = FromGoTime(converted) 210 } 211 return nil 212 } 213 214 func (t Time) String() string { 215 if t.Type == mysql.TypeDate { 216 // We control the format, so no error would occur. 217 str, err := t.DateFormat("%Y-%m-%d") 218 terror.Log(errors.Trace(err)) 219 return str 220 } 221 222 str, err := t.DateFormat("%Y-%m-%d %H:%i:%s") 223 terror.Log(errors.Trace(err)) 224 if t.Fsp > 0 { 225 tmp := fmt.Sprintf(".%06d", t.Time.Microsecond()) 226 str = str + tmp[:1+t.Fsp] 227 } 228 229 return str 230 } 231 232 // IsZero returns a boolean indicating whether the time is equal to ZeroTime. 233 func (t Time) IsZero() bool { 234 return compareTime(t.Time, ZeroTime) == 0 235 } 236 237 // InvalidZero returns a boolean indicating whether the month or day is zero. 238 func (t Time) InvalidZero() bool { 239 return t.Time.Month() == 0 || t.Time.Day() == 0 240 } 241 242 const numberFormat = "%Y%m%d%H%i%s" 243 const dateFormat = "%Y%m%d" 244 245 // ToNumber returns a formatted number. 246 // e.g, 247 // 2012-12-12 -> 20121212 248 // 2012-12-12T10:10:10 -> 20121212101010 249 // 2012-12-12T10:10:10.123456 -> 20121212101010.123456 250 func (t Time) ToNumber() *MyDecimal { 251 if t.IsZero() { 252 return &MyDecimal{} 253 } 254 255 // Fix issue #1046 256 // Prevents from converting 2012-12-12 to 20121212000000 257 var tfStr string 258 if t.Type == mysql.TypeDate { 259 tfStr = dateFormat 260 } else { 261 tfStr = numberFormat 262 } 263 264 s, err := t.DateFormat(tfStr) 265 if err != nil { 266 log.Warn("Fatal: never happen because we've control the format!") 267 } 268 269 if t.Fsp > 0 { 270 s1 := fmt.Sprintf("%s.%06d", s, t.Time.Microsecond()) 271 s = s1[:len(s)+t.Fsp+1] 272 } 273 274 // We skip checking error here because time formatted string can be parsed certainly. 275 dec := new(MyDecimal) 276 err = dec.FromString([]byte(s)) 277 terror.Log(errors.Trace(err)) 278 return dec 279 } 280 281 // Convert converts t with type tp. 282 func (t Time) Convert(sc *stmtctx.StatementContext, tp uint8) (Time, error) { 283 if t.Type == tp || t.IsZero() { 284 return Time{Time: t.Time, Type: tp, Fsp: t.Fsp}, nil 285 } 286 287 t1 := Time{Time: t.Time, Type: tp, Fsp: t.Fsp} 288 err := t1.check(sc) 289 return t1, errors.Trace(err) 290 } 291 292 // ConvertToDuration converts mysql datetime, timestamp and date to mysql time type. 293 // e.g, 294 // 2012-12-12T10:10:10 -> 10:10:10 295 // 2012-12-12 -> 0 296 func (t Time) ConvertToDuration() (Duration, error) { 297 if t.IsZero() { 298 return ZeroDuration, nil 299 } 300 301 hour, minute, second := t.Clock() 302 frac := t.Time.Microsecond() * 1000 303 304 d := gotime.Duration(hour*3600+minute*60+second)*gotime.Second + gotime.Duration(frac) 305 // TODO: check convert validation 306 return Duration{Duration: d, Fsp: t.Fsp}, nil 307 } 308 309 // Compare returns an integer comparing the time instant t to o. 310 // If t is after o, return 1, equal o, return 0, before o, return -1. 311 func (t Time) Compare(o Time) int { 312 return compareTime(t.Time, o.Time) 313 } 314 315 // compareTime compare two MysqlTime. 316 // return: 317 // 0: if a == b 318 // 1: if a > b 319 // -1: if a < b 320 func compareTime(a, b MysqlTime) int { 321 ta := datetimeToUint64(a) 322 tb := datetimeToUint64(b) 323 324 switch { 325 case ta < tb: 326 return -1 327 case ta > tb: 328 return 1 329 } 330 331 switch { 332 case a.Microsecond() < b.Microsecond(): 333 return -1 334 case a.Microsecond() > b.Microsecond(): 335 return 1 336 } 337 338 return 0 339 } 340 341 // CompareString is like Compare, 342 // but parses string to Time then compares. 343 func (t Time) CompareString(sc *stmtctx.StatementContext, str string) (int, error) { 344 // use MaxFsp to parse the string 345 o, err := ParseTime(sc, str, t.Type, MaxFsp) 346 if err != nil { 347 return 0, errors.Trace(err) 348 } 349 350 return t.Compare(o), nil 351 } 352 353 // roundTime rounds the time value according to digits count specified by fsp. 354 func roundTime(t gotime.Time, fsp int) gotime.Time { 355 d := gotime.Duration(math.Pow10(9 - fsp)) 356 return t.Round(d) 357 } 358 359 // RoundFrac rounds the fraction part of a time-type value according to `fsp`. 360 func (t Time) RoundFrac(sc *stmtctx.StatementContext, fsp int) (Time, error) { 361 if t.Type == mysql.TypeDate || t.IsZero() { 362 // date type has no fsp 363 return t, nil 364 } 365 366 fsp, err := CheckFsp(fsp) 367 if err != nil { 368 return t, errors.Trace(err) 369 } 370 371 if fsp == t.Fsp { 372 // have same fsp 373 return t, nil 374 } 375 376 var nt MysqlTime 377 if t1, err := t.Time.GoTime(sc.TimeZone); err == nil { 378 t1 = roundTime(t1, fsp) 379 nt = FromGoTime(t1) 380 } else { 381 // Take the hh:mm:ss part out to avoid handle month or day = 0. 382 hour, minute, second, microsecond := t.Time.Hour(), t.Time.Minute(), t.Time.Second(), t.Time.Microsecond() 383 t1 := gotime.Date(1, 1, 1, hour, minute, second, microsecond*1000, gotime.Local) 384 t2 := roundTime(t1, fsp) 385 hour, minute, second = t2.Clock() 386 microsecond = t2.Nanosecond() / 1000 387 388 // TODO: when hh:mm:ss overflow one day after rounding, it should be add to yy:mm:dd part, 389 // but mm:dd may contain 0, it makes the code complex, so we ignore it here. 390 if t2.Day()-1 > 0 { 391 return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(t.String())) 392 } 393 nt = FromDate(t.Time.Year(), t.Time.Month(), t.Time.Day(), hour, minute, second, microsecond) 394 } 395 396 return Time{Time: nt, Type: t.Type, Fsp: fsp}, nil 397 } 398 399 // GetFsp gets the fsp of a string. 400 func GetFsp(s string) (fsp int) { 401 fsp = len(s) - strings.LastIndex(s, ".") - 1 402 if fsp == len(s) { 403 fsp = 0 404 } else if fsp > 6 { 405 fsp = 6 406 } 407 return 408 } 409 410 // RoundFrac rounds fractional seconds precision with new fsp and returns a new one. 411 // We will use the “round half up” rule, e.g, >= 0.5 -> 1, < 0.5 -> 0, 412 // so 2011:11:11 10:10:10.888888 round 0 -> 2011:11:11 10:10:11 413 // and 2011:11:11 10:10:10.111111 round 0 -> 2011:11:11 10:10:10 414 func RoundFrac(t gotime.Time, fsp int) (gotime.Time, error) { 415 _, err := CheckFsp(fsp) 416 if err != nil { 417 return t, errors.Trace(err) 418 } 419 return t.Round(gotime.Duration(math.Pow10(9-fsp)) * gotime.Nanosecond), nil 420 } 421 422 // ToPackedUint encodes Time to a packed uint64 value. 423 // 424 // 1 bit 0 425 // 17 bits year*13+month (year 0-9999, month 0-12) 426 // 5 bits day (0-31) 427 // 5 bits hour (0-23) 428 // 6 bits minute (0-59) 429 // 6 bits second (0-59) 430 // 24 bits microseconds (0-999999) 431 // 432 // Total: 64 bits = 8 bytes 433 // 434 // 0YYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff 435 // 436 func (t Time) ToPackedUint() (uint64, error) { 437 tm := t.Time 438 if t.IsZero() { 439 return 0, nil 440 } 441 year, month, day := tm.Year(), tm.Month(), tm.Day() 442 hour, minute, sec := tm.Hour(), tm.Minute(), tm.Second() 443 ymd := uint64(((year*13 + month) << 5) | day) 444 hms := uint64(hour<<12 | minute<<6 | sec) 445 micro := uint64(tm.Microsecond()) 446 return ((ymd<<17 | hms) << 24) | micro, nil 447 } 448 449 // FromPackedUint decodes Time from a packed uint64 value. 450 func (t *Time) FromPackedUint(packed uint64) error { 451 if packed == 0 { 452 t.Time = ZeroTime 453 return nil 454 } 455 ymdhms := packed >> 24 456 ymd := ymdhms >> 17 457 day := int(ymd & (1<<5 - 1)) 458 ym := ymd >> 5 459 month := int(ym % 13) 460 year := int(ym / 13) 461 462 hms := ymdhms & (1<<17 - 1) 463 second := int(hms & (1<<6 - 1)) 464 minute := int((hms >> 6) & (1<<6 - 1)) 465 hour := int(hms >> 12) 466 microsec := int(packed % (1 << 24)) 467 468 t.Time = FromDate(year, month, day, hour, minute, second, microsec) 469 470 return nil 471 } 472 473 // check whether t matches valid Time format. 474 // If allowZeroInDate is false, it returns ErrZeroDate when month or day is zero. 475 // FIXME: See https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_in_date 476 func (t *Time) check(sc *stmtctx.StatementContext) error { 477 allowZeroInDate := false 478 allowInvalidDate := false 479 // We should avoid passing sc as nil here as far as possible. 480 if sc != nil { 481 allowZeroInDate = sc.IgnoreZeroInDate 482 allowInvalidDate = sc.AllowInvalidDate 483 } 484 var err error 485 switch t.Type { 486 case mysql.TypeTimestamp: 487 err = checkTimestampType(sc, t.Time) 488 case mysql.TypeDatetime: 489 err = checkDatetimeType(t.Time, allowZeroInDate, allowInvalidDate) 490 case mysql.TypeDate: 491 err = checkDateType(t.Time, allowZeroInDate, allowInvalidDate) 492 } 493 return errors.Trace(err) 494 } 495 496 // Check if 't' is valid 497 func (t *Time) Check(sc *stmtctx.StatementContext) error { 498 return t.check(sc) 499 } 500 501 // Sub subtracts t1 from t, returns a duration value. 502 // Note that sub should not be done on different time types. 503 func (t *Time) Sub(sc *stmtctx.StatementContext, t1 *Time) Duration { 504 var duration gotime.Duration 505 if t.Type == mysql.TypeTimestamp && t1.Type == mysql.TypeTimestamp { 506 a, err := t.Time.GoTime(sc.TimeZone) 507 terror.Log(errors.Trace(err)) 508 b, err := t1.Time.GoTime(sc.TimeZone) 509 terror.Log(errors.Trace(err)) 510 duration = a.Sub(b) 511 } else { 512 seconds, microseconds, neg := calcTimeDiff(t.Time, t1.Time, 1) 513 duration = gotime.Duration(seconds*1e9 + microseconds*1e3) 514 if neg { 515 duration = -duration 516 } 517 } 518 519 fsp := t.Fsp 520 if fsp < t1.Fsp { 521 fsp = t1.Fsp 522 } 523 return Duration{ 524 Duration: duration, 525 Fsp: fsp, 526 } 527 } 528 529 // Add adds d to t, returns the result time value. 530 func (t *Time) Add(sc *stmtctx.StatementContext, d Duration) (Time, error) { 531 sign, hh, mm, ss, micro := splitDuration(d.Duration) 532 seconds, microseconds, _ := calcTimeDiff(t.Time, FromDate(0, 0, 0, hh, mm, ss, micro), -sign) 533 days := seconds / secondsIn24Hour 534 year, month, day := getDateFromDaynr(uint(days)) 535 var tm MysqlTime 536 tm.year, tm.month, tm.day = uint16(year), uint8(month), uint8(day) 537 calcTimeFromSec(&tm, seconds%secondsIn24Hour, microseconds) 538 if t.Type == mysql.TypeDate { 539 tm.hour = 0 540 tm.minute = 0 541 tm.second = 0 542 tm.microsecond = 0 543 } 544 fsp := t.Fsp 545 if d.Fsp > fsp { 546 fsp = d.Fsp 547 } 548 ret := Time{ 549 Time: tm, 550 Type: t.Type, 551 Fsp: fsp, 552 } 553 return ret, ret.Check(sc) 554 } 555 556 // TimestampDiff returns t2 - t1 where t1 and t2 are date or datetime expressions. 557 // The unit for the result (an integer) is given by the unit argument. 558 // The legal values for unit are "YEAR" "QUARTER" "MONTH" "DAY" "HOUR" "SECOND" and so on. 559 func TimestampDiff(unit string, t1 Time, t2 Time) int64 { 560 return timestampDiff(unit, t1.Time, t2.Time) 561 } 562 563 // ParseDateFormat parses a formatted date string and returns separated components. 564 func ParseDateFormat(format string) []string { 565 format = strings.TrimSpace(format) 566 567 start := 0 568 var seps = make([]string, 0) 569 for i := 0; i < len(format); i++ { 570 // Date format must start and end with number. 571 if i == 0 || i == len(format)-1 { 572 if !unicode.IsNumber(rune(format[i])) { 573 return nil 574 } 575 576 continue 577 } 578 579 // Separator is a single none-number char. 580 if !unicode.IsNumber(rune(format[i])) { 581 if !unicode.IsNumber(rune(format[i-1])) { 582 return nil 583 } 584 585 seps = append(seps, format[start:i]) 586 start = i + 1 587 } 588 589 } 590 591 seps = append(seps, format[start:]) 592 return seps 593 } 594 595 // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html. 596 // The only delimiter recognized between a date and time part and a fractional seconds part is the decimal point. 597 func splitDateTime(format string) (seps []string, fracStr string) { 598 if i := strings.LastIndex(format, "."); i > 0 { 599 fracStr = strings.TrimSpace(format[i+1:]) 600 format = format[:i] 601 } 602 603 seps = ParseDateFormat(format) 604 return 605 } 606 607 // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html. 608 func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bool) (Time, error) { 609 // Try to split str with delimiter. 610 // TODO: only punctuation can be the delimiter for date parts or time parts. 611 // But only space and T can be the delimiter between the date and time part. 612 var ( 613 year, month, day, hour, minute, second int 614 fracStr string 615 hhmmss bool 616 err error 617 ) 618 619 seps, fracStr := splitDateTime(str) 620 var truncatedOrIncorrect bool 621 switch len(seps) { 622 case 1: 623 l := len(seps[0]) 624 switch l { 625 case 14: // No delimiter. 626 // YYYYMMDDHHMMSS 627 _, err = fmt.Sscanf(seps[0], "%4d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second) 628 hhmmss = true 629 case 12: // YYMMDDHHMMSS 630 _, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute, &second) 631 year = adjustYear(year) 632 hhmmss = true 633 case 11: // YYMMDDHHMMS 634 _, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute, &second) 635 year = adjustYear(year) 636 hhmmss = true 637 case 10: // YYMMDDHHMM 638 _, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%2d", &year, &month, &day, &hour, &minute) 639 year = adjustYear(year) 640 case 9: // YYMMDDHHM 641 _, err = fmt.Sscanf(seps[0], "%2d%2d%2d%2d%1d", &year, &month, &day, &hour, &minute) 642 year = adjustYear(year) 643 case 8: // YYYYMMDD 644 _, err = fmt.Sscanf(seps[0], "%4d%2d%2d", &year, &month, &day) 645 case 6, 5: 646 // YYMMDD && YYMMD 647 _, err = fmt.Sscanf(seps[0], "%2d%2d%2d", &year, &month, &day) 648 year = adjustYear(year) 649 default: 650 return ZeroDatetime, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(str)) 651 } 652 if l == 5 || l == 6 || l == 8 { 653 // YYMMDD or YYYYMMDD 654 // We must handle float => string => datetime, the difference is that fractional 655 // part of float type is discarded directly, while fractional part of string type 656 // is parsed to HH:MM:SS. 657 if isFloat { 658 // 20170118.123423 => 2017-01-18 00:00:00 659 } else { 660 // '20170118.123423' => 2017-01-18 12:34:23.234 661 switch len(fracStr) { 662 case 0: 663 case 1, 2: 664 _, err = fmt.Sscanf(fracStr, "%2d ", &hour) 665 case 3, 4: 666 _, err = fmt.Sscanf(fracStr, "%2d%2d ", &hour, &minute) 667 default: 668 _, err = fmt.Sscanf(fracStr, "%2d%2d%2d ", &hour, &minute, &second) 669 } 670 truncatedOrIncorrect = err != nil 671 } 672 } 673 if l == 9 || l == 10 { 674 if len(fracStr) == 0 { 675 second = 0 676 } else { 677 _, err = fmt.Sscanf(fracStr, "%2d ", &second) 678 } 679 truncatedOrIncorrect = err != nil 680 } 681 if truncatedOrIncorrect && sc != nil { 682 sc.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs("datetime", str)) 683 err = nil 684 } 685 case 3: 686 // YYYY-MM-DD 687 err = scanTimeArgs(seps, &year, &month, &day) 688 case 4: 689 // YYYY-MM-DD HH 690 err = scanTimeArgs(seps, &year, &month, &day, &hour) 691 case 5: 692 // YYYY-MM-DD HH-MM 693 err = scanTimeArgs(seps, &year, &month, &day, &hour, &minute) 694 case 6: 695 // We don't have fractional seconds part. 696 // YYYY-MM-DD HH-MM-SS 697 err = scanTimeArgs(seps, &year, &month, &day, &hour, &minute, &second) 698 hhmmss = true 699 default: 700 return ZeroDatetime, errors.Trace(ErrIncorrectDatetimeValue.GenWithStackByArgs(str)) 701 } 702 if err != nil { 703 return ZeroDatetime, errors.Trace(err) 704 } 705 706 // If str is sepereated by delimiters, the first one is year, and if the year is 2 digit, 707 // we should adjust it. 708 // TODO: adjust year is very complex, now we only consider the simplest way. 709 if len(seps[0]) == 2 { 710 if year == 0 && month == 0 && day == 0 && hour == 0 && minute == 0 && second == 0 && fracStr == "" { 711 // Skip a special case "00-00-00". 712 } else { 713 year = adjustYear(year) 714 } 715 } 716 717 var microsecond int 718 var overflow bool 719 if hhmmss { 720 // If input string is "20170118.999", without hhmmss, fsp is meanless. 721 microsecond, overflow, err = ParseFrac(fracStr, fsp) 722 if err != nil { 723 return ZeroDatetime, errors.Trace(err) 724 } 725 } 726 727 tmp := FromDate(year, month, day, hour, minute, second, microsecond) 728 if overflow { 729 // Convert to Go time and add 1 second, to handle input like 2017-01-05 08:40:59.575601 730 t1, err := tmp.GoTime(gotime.Local) 731 if err != nil { 732 return ZeroDatetime, errors.Trace(err) 733 } 734 tmp = FromGoTime(t1.Add(gotime.Second)) 735 } 736 737 nt := Time{ 738 Time: tmp, 739 Type: mysql.TypeDatetime, 740 Fsp: fsp} 741 742 return nt, nil 743 } 744 745 func scanTimeArgs(seps []string, args ...*int) error { 746 if len(seps) != len(args) { 747 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(seps)) 748 } 749 750 var err error 751 for i, s := range seps { 752 *args[i], err = strconv.Atoi(s) 753 if err != nil { 754 return errors.Trace(err) 755 } 756 } 757 return nil 758 } 759 760 // ParseYear parses a formatted string and returns a year number. 761 func ParseYear(str string) (int16, error) { 762 v, err := strconv.ParseInt(str, 10, 16) 763 if err != nil { 764 return 0, errors.Trace(err) 765 } 766 y := int16(v) 767 768 if len(str) == 4 { 769 // Nothing to do. 770 } else if len(str) == 2 || len(str) == 1 { 771 y = int16(adjustYear(int(y))) 772 } else { 773 return 0, errors.Trace(ErrInvalidYearFormat) 774 } 775 776 if y < MinYear || y > MaxYear { 777 return 0, errors.Trace(ErrInvalidYearFormat) 778 } 779 780 return y, nil 781 } 782 783 // adjustYear adjusts year according to y. 784 // See https://dev.mysql.com/doc/refman/5.7/en/two-digit-years.html 785 func adjustYear(y int) int { 786 if y >= 0 && y <= 69 { 787 y = 2000 + y 788 } else if y >= 70 && y <= 99 { 789 y = 1900 + y 790 } 791 return y 792 } 793 794 // AdjustYear is used for adjusting year and checking its validation. 795 func AdjustYear(y int64, shouldAdjust bool) (int64, error) { 796 if y == 0 && !shouldAdjust { 797 return y, nil 798 } 799 y = int64(adjustYear(int(y))) 800 if y < int64(MinYear) || y > int64(MaxYear) { 801 return 0, errors.Trace(ErrInvalidYear) 802 } 803 804 return y, nil 805 } 806 807 // Duration is the type for MySQL TIME type. 808 type Duration struct { 809 gotime.Duration 810 // Fsp is short for Fractional Seconds Precision. 811 // See http://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html 812 Fsp int 813 } 814 815 //Add adds d to d, returns a duration value. 816 func (d Duration) Add(v Duration) (Duration, error) { 817 if &v == nil { 818 return d, nil 819 } 820 dsum, err := AddInt64(int64(d.Duration), int64(v.Duration)) 821 if err != nil { 822 return Duration{}, errors.Trace(err) 823 } 824 if d.Fsp >= v.Fsp { 825 return Duration{Duration: gotime.Duration(dsum), Fsp: d.Fsp}, nil 826 } 827 return Duration{Duration: gotime.Duration(dsum), Fsp: v.Fsp}, nil 828 } 829 830 // Sub subtracts d to d, returns a duration value. 831 func (d Duration) Sub(v Duration) (Duration, error) { 832 if &v == nil { 833 return d, nil 834 } 835 dsum, err := SubInt64(int64(d.Duration), int64(v.Duration)) 836 if err != nil { 837 return Duration{}, errors.Trace(err) 838 } 839 if d.Fsp >= v.Fsp { 840 return Duration{Duration: gotime.Duration(dsum), Fsp: d.Fsp}, nil 841 } 842 return Duration{Duration: gotime.Duration(dsum), Fsp: v.Fsp}, nil 843 } 844 845 // String returns the time formatted using default TimeFormat and fsp. 846 func (d Duration) String() string { 847 var buf bytes.Buffer 848 849 sign, hours, minutes, seconds, fraction := splitDuration(d.Duration) 850 if sign < 0 { 851 buf.WriteByte('-') 852 } 853 854 fmt.Fprintf(&buf, "%02d:%02d:%02d", hours, minutes, seconds) 855 if d.Fsp > 0 { 856 buf.WriteString(".") 857 buf.WriteString(d.formatFrac(fraction)) 858 } 859 860 p := buf.String() 861 862 return p 863 } 864 865 func (d Duration) formatFrac(frac int) string { 866 s := fmt.Sprintf("%06d", frac) 867 return s[0:d.Fsp] 868 } 869 870 // ToNumber changes duration to number format. 871 // e.g, 872 // 10:10:10 -> 101010 873 func (d Duration) ToNumber() *MyDecimal { 874 sign, hours, minutes, seconds, fraction := splitDuration(d.Duration) 875 var ( 876 s string 877 signStr string 878 ) 879 880 if sign < 0 { 881 signStr = "-" 882 } 883 884 if d.Fsp == 0 { 885 s = fmt.Sprintf("%s%02d%02d%02d", signStr, hours, minutes, seconds) 886 } else { 887 s = fmt.Sprintf("%s%02d%02d%02d.%s", signStr, hours, minutes, seconds, d.formatFrac(fraction)) 888 } 889 890 // We skip checking error here because time formatted string can be parsed certainly. 891 dec := new(MyDecimal) 892 err := dec.FromString([]byte(s)) 893 terror.Log(errors.Trace(err)) 894 return dec 895 } 896 897 // ConvertToTime converts duration to Time. 898 // Tp is TypeDatetime, TypeTimestamp and TypeDate. 899 func (d Duration) ConvertToTime(sc *stmtctx.StatementContext, tp uint8) (Time, error) { 900 year, month, day := gotime.Now().In(sc.TimeZone).Date() 901 sign, hour, minute, second, frac := splitDuration(d.Duration) 902 datePart := FromDate(year, int(month), day, 0, 0, 0, 0) 903 timePart := FromDate(0, 0, 0, hour, minute, second, frac) 904 mixDateAndTime(&datePart, &timePart, sign < 0) 905 906 t := Time{ 907 Time: datePart, 908 Type: mysql.TypeDatetime, 909 Fsp: d.Fsp, 910 } 911 return t.Convert(sc, tp) 912 } 913 914 // RoundFrac rounds fractional seconds precision with new fsp and returns a new one. 915 // We will use the “round half up” rule, e.g, >= 0.5 -> 1, < 0.5 -> 0, 916 // so 10:10:10.999999 round 0 -> 10:10:11 917 // and 10:10:10.000000 round 0 -> 10:10:10 918 func (d Duration) RoundFrac(fsp int) (Duration, error) { 919 fsp, err := CheckFsp(fsp) 920 if err != nil { 921 return d, errors.Trace(err) 922 } 923 924 if fsp == d.Fsp { 925 return d, nil 926 } 927 928 n := gotime.Date(0, 0, 0, 0, 0, 0, 0, gotime.Local) 929 nd := n.Add(d.Duration).Round(gotime.Duration(math.Pow10(9-fsp)) * gotime.Nanosecond).Sub(n) 930 return Duration{Duration: nd, Fsp: fsp}, nil 931 } 932 933 // Compare returns an integer comparing the Duration instant t to o. 934 // If d is after o, return 1, equal o, return 0, before o, return -1. 935 func (d Duration) Compare(o Duration) int { 936 if d.Duration > o.Duration { 937 return 1 938 } else if d.Duration == o.Duration { 939 return 0 940 } else { 941 return -1 942 } 943 } 944 945 // CompareString is like Compare, 946 // but parses str to Duration then compares. 947 func (d Duration) CompareString(sc *stmtctx.StatementContext, str string) (int, error) { 948 // use MaxFsp to parse the string 949 o, err := ParseDuration(sc, str, MaxFsp) 950 if err != nil { 951 return 0, err 952 } 953 954 return d.Compare(o), nil 955 } 956 957 // Hour returns current hour. 958 // e.g, hour("11:11:11") -> 11 959 func (d Duration) Hour() int { 960 _, hour, _, _, _ := splitDuration(d.Duration) 961 return hour 962 } 963 964 // Minute returns current minute. 965 // e.g, hour("11:11:11") -> 11 966 func (d Duration) Minute() int { 967 _, _, minute, _, _ := splitDuration(d.Duration) 968 return minute 969 } 970 971 // Second returns current second. 972 // e.g, hour("11:11:11") -> 11 973 func (d Duration) Second() int { 974 _, _, _, second, _ := splitDuration(d.Duration) 975 return second 976 } 977 978 // MicroSecond returns current microsecond. 979 // e.g, hour("11:11:11.11") -> 110000 980 func (d Duration) MicroSecond() int { 981 _, _, _, _, frac := splitDuration(d.Duration) 982 return frac 983 } 984 985 // ParseDuration parses the time form a formatted string with a fractional seconds part, 986 // returns the duration type Time value. 987 // See http://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html 988 func ParseDuration(sc *stmtctx.StatementContext, str string, fsp int) (Duration, error) { 989 var ( 990 day, hour, minute, second int 991 err error 992 sign = 0 993 dayExists = false 994 origStr = str 995 ) 996 997 fsp, err = CheckFsp(fsp) 998 if err != nil { 999 return ZeroDuration, errors.Trace(err) 1000 } 1001 1002 if len(str) == 0 { 1003 return ZeroDuration, nil 1004 } else if str[0] == '-' { 1005 str = str[1:] 1006 sign = -1 1007 } 1008 1009 // Time format may has day. 1010 if n := strings.IndexByte(str, ' '); n >= 0 { 1011 if day, err = strconv.Atoi(str[:n]); err == nil { 1012 dayExists = true 1013 } 1014 str = str[n+1:] 1015 } 1016 1017 var ( 1018 integeralPart = str 1019 fracPart int 1020 overflow bool 1021 ) 1022 if n := strings.IndexByte(str, '.'); n >= 0 { 1023 // It has fractional precision parts. 1024 fracStr := str[n+1:] 1025 fracPart, overflow, err = ParseFrac(fracStr, fsp) 1026 if err != nil { 1027 return ZeroDuration, errors.Trace(err) 1028 } 1029 integeralPart = str[0:n] 1030 } 1031 1032 // It tries to split integeralPart with delimiter, time delimiter must be : 1033 seps := strings.Split(integeralPart, ":") 1034 1035 switch len(seps) { 1036 case 1: 1037 if dayExists { 1038 hour, err = strconv.Atoi(seps[0]) 1039 } else { 1040 // No delimiter. 1041 switch len(integeralPart) { 1042 case 7: // HHHMMSS 1043 _, err = fmt.Sscanf(integeralPart, "%3d%2d%2d", &hour, &minute, &second) 1044 case 6: // HHMMSS 1045 _, err = fmt.Sscanf(integeralPart, "%2d%2d%2d", &hour, &minute, &second) 1046 case 5: // HMMSS 1047 _, err = fmt.Sscanf(integeralPart, "%1d%2d%2d", &hour, &minute, &second) 1048 case 4: // MMSS 1049 _, err = fmt.Sscanf(integeralPart, "%2d%2d", &minute, &second) 1050 case 3: // MSS 1051 _, err = fmt.Sscanf(integeralPart, "%1d%2d", &minute, &second) 1052 case 2: // SS 1053 _, err = fmt.Sscanf(integeralPart, "%2d", &second) 1054 case 1: // 0S 1055 _, err = fmt.Sscanf(integeralPart, "%1d", &second) 1056 default: // Maybe contains date. 1057 t, err1 := ParseDatetime(sc, str) 1058 if err1 != nil { 1059 return ZeroDuration, ErrTruncatedWrongVal.GenWithStackByArgs("time", origStr) 1060 } 1061 var dur Duration 1062 dur, err1 = t.ConvertToDuration() 1063 if err1 != nil { 1064 return ZeroDuration, errors.Trace(err) 1065 } 1066 return dur.RoundFrac(fsp) 1067 } 1068 } 1069 case 2: 1070 // HH:MM 1071 _, err = fmt.Sscanf(integeralPart, "%2d:%2d", &hour, &minute) 1072 case 3: 1073 // Time format maybe HH:MM:SS or HHH:MM:SS. 1074 // See https://dev.mysql.com/doc/refman/5.7/en/time.html 1075 if len(seps[0]) == 3 { 1076 _, err = fmt.Sscanf(integeralPart, "%3d:%2d:%2d", &hour, &minute, &second) 1077 } else { 1078 _, err = fmt.Sscanf(integeralPart, "%2d:%2d:%2d", &hour, &minute, &second) 1079 } 1080 default: 1081 return ZeroDuration, ErrTruncatedWrongVal.GenWithStackByArgs("time", origStr) 1082 } 1083 1084 if err != nil { 1085 return ZeroDuration, errors.Trace(err) 1086 } 1087 1088 if overflow { 1089 second++ 1090 fracPart = 0 1091 } 1092 // Invalid TIME values are converted to '00:00:00'. 1093 // See https://dev.mysql.com/doc/refman/5.7/en/time.html 1094 if minute >= 60 || second > 60 || (!overflow && second == 60) { 1095 return ZeroDuration, ErrTruncatedWrongVal.GenWithStackByArgs("time", origStr) 1096 } 1097 d := gotime.Duration(day*24*3600+hour*3600+minute*60+second)*gotime.Second + gotime.Duration(fracPart)*gotime.Microsecond 1098 if sign == -1 { 1099 d = -d 1100 } 1101 1102 d, err = TruncateOverflowMySQLTime(d) 1103 return Duration{Duration: d, Fsp: fsp}, errors.Trace(err) 1104 } 1105 1106 // TruncateOverflowMySQLTime truncates d when it overflows, and return ErrTruncatedWrongVal. 1107 func TruncateOverflowMySQLTime(d gotime.Duration) (gotime.Duration, error) { 1108 if d > MaxTime { 1109 return MaxTime, ErrTruncatedWrongVal.GenWithStackByArgs("time", d.String()) 1110 } else if d < MinTime { 1111 return MinTime, ErrTruncatedWrongVal.GenWithStackByArgs("time", d.String()) 1112 } 1113 1114 return d, nil 1115 } 1116 1117 func splitDuration(t gotime.Duration) (int, int, int, int, int) { 1118 sign := 1 1119 if t < 0 { 1120 t = -t 1121 sign = -1 1122 } 1123 1124 hours := t / gotime.Hour 1125 t -= hours * gotime.Hour 1126 minutes := t / gotime.Minute 1127 t -= minutes * gotime.Minute 1128 seconds := t / gotime.Second 1129 t -= seconds * gotime.Second 1130 fraction := t / gotime.Microsecond 1131 1132 return sign, int(hours), int(minutes), int(seconds), int(fraction) 1133 } 1134 1135 var maxDaysInMonth = []int{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} 1136 1137 func getTime(sc *stmtctx.StatementContext, num int64, tp byte) (Time, error) { 1138 s1 := num / 1000000 1139 s2 := num - s1*1000000 1140 1141 year := int(s1 / 10000) 1142 s1 %= 10000 1143 month := int(s1 / 100) 1144 day := int(s1 % 100) 1145 1146 hour := int(s2 / 10000) 1147 s2 %= 10000 1148 minute := int(s2 / 100) 1149 second := int(s2 % 100) 1150 1151 t := Time{ 1152 Time: FromDate(year, month, day, hour, minute, second, 0), 1153 Type: tp, 1154 Fsp: DefaultFsp, 1155 } 1156 err := t.check(sc) 1157 return t, errors.Trace(err) 1158 } 1159 1160 // parseDateTimeFromNum parses date time from num. 1161 // See number_to_datetime function. 1162 // https://github.com/mysql/mysql-server/blob/5.7/sql-common/my_time.c 1163 func parseDateTimeFromNum(sc *stmtctx.StatementContext, num int64) (Time, error) { 1164 t := ZeroDate 1165 // Check zero. 1166 if num == 0 { 1167 return t, nil 1168 } 1169 1170 // Check datetime type. 1171 if num >= 10000101000000 { 1172 t.Type = mysql.TypeDatetime 1173 return getTime(sc, num, t.Type) 1174 } 1175 1176 // Check MMDD. 1177 if num < 101 { 1178 return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(num)) 1179 } 1180 1181 // Adjust year 1182 // YYMMDD, year: 2000-2069 1183 if num <= (70-1)*10000+1231 { 1184 num = (num + 20000000) * 1000000 1185 return getTime(sc, num, t.Type) 1186 } 1187 1188 // Check YYMMDD. 1189 if num < 70*10000+101 { 1190 return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(num)) 1191 } 1192 1193 // Adjust year 1194 // YYMMDD, year: 1970-1999 1195 if num <= 991231 { 1196 num = (num + 19000000) * 1000000 1197 return getTime(sc, num, t.Type) 1198 } 1199 1200 // Check YYYYMMDD. 1201 if num < 10000101 { 1202 return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(num)) 1203 } 1204 1205 // Adjust hour/min/second. 1206 if num <= 99991231 { 1207 num = num * 1000000 1208 return getTime(sc, num, t.Type) 1209 } 1210 1211 // Check MMDDHHMMSS. 1212 if num < 101000000 { 1213 return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(num)) 1214 } 1215 1216 // Set TypeDatetime type. 1217 t.Type = mysql.TypeDatetime 1218 1219 // Adjust year 1220 // YYMMDDHHMMSS, 2000-2069 1221 if num <= 69*10000000000+1231235959 { 1222 num = num + 20000000000000 1223 return getTime(sc, num, t.Type) 1224 } 1225 1226 // Check YYYYMMDDHHMMSS. 1227 if num < 70*10000000000+101000000 { 1228 return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(num)) 1229 } 1230 1231 // Adjust year 1232 // YYMMDDHHMMSS, 1970-1999 1233 if num <= 991231235959 { 1234 num = num + 19000000000000 1235 return getTime(sc, num, t.Type) 1236 } 1237 1238 return getTime(sc, num, t.Type) 1239 } 1240 1241 // ParseTime parses a formatted string with type tp and specific fsp. 1242 // Type is TypeDatetime, TypeTimestamp and TypeDate. 1243 // Fsp is in range [0, 6]. 1244 // MySQL supports many valid datetime format, but still has some limitation. 1245 // If delimiter exists, the date part and time part is separated by a space or T, 1246 // other punctuation character can be used as the delimiter between date parts or time parts. 1247 // If no delimiter, the format must be YYYYMMDDHHMMSS or YYMMDDHHMMSS 1248 // If we have fractional seconds part, we must use decimal points as the delimiter. 1249 // The valid datetime range is from '1000-01-01 00:00:00.000000' to '9999-12-31 23:59:59.999999'. 1250 // The valid timestamp range is from '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.999999'. 1251 // The valid date range is from '1000-01-01' to '9999-12-31' 1252 func ParseTime(sc *stmtctx.StatementContext, str string, tp byte, fsp int) (Time, error) { 1253 return parseTime(sc, str, tp, fsp, false) 1254 } 1255 1256 // ParseTimeFromFloatString is similar to ParseTime, except that it's used to parse a float converted string. 1257 func ParseTimeFromFloatString(sc *stmtctx.StatementContext, str string, tp byte, fsp int) (Time, error) { 1258 return parseTime(sc, str, tp, fsp, true) 1259 } 1260 1261 func parseTime(sc *stmtctx.StatementContext, str string, tp byte, fsp int, isFloat bool) (Time, error) { 1262 fsp, err := CheckFsp(fsp) 1263 if err != nil { 1264 return Time{Time: ZeroTime, Type: tp}, errors.Trace(err) 1265 } 1266 1267 t, err := parseDatetime(sc, str, fsp, isFloat) 1268 if err != nil { 1269 return Time{Time: ZeroTime, Type: tp}, errors.Trace(err) 1270 } 1271 1272 t.Type = tp 1273 if err = t.check(sc); err != nil { 1274 return Time{Time: ZeroTime, Type: tp}, errors.Trace(err) 1275 } 1276 return t, nil 1277 } 1278 1279 // ParseDatetime is a helper function wrapping ParseTime with datetime type and default fsp. 1280 func ParseDatetime(sc *stmtctx.StatementContext, str string) (Time, error) { 1281 return ParseTime(sc, str, mysql.TypeDatetime, GetFsp(str)) 1282 } 1283 1284 // ParseTimestamp is a helper function wrapping ParseTime with timestamp type and default fsp. 1285 func ParseTimestamp(sc *stmtctx.StatementContext, str string) (Time, error) { 1286 return ParseTime(sc, str, mysql.TypeTimestamp, GetFsp(str)) 1287 } 1288 1289 // ParseDate is a helper function wrapping ParseTime with date type. 1290 func ParseDate(sc *stmtctx.StatementContext, str string) (Time, error) { 1291 // date has no fractional seconds precision 1292 return ParseTime(sc, str, mysql.TypeDate, MinFsp) 1293 } 1294 1295 // ParseTimeFromNum parses a formatted int64, 1296 // returns the value which type is tp. 1297 func ParseTimeFromNum(sc *stmtctx.StatementContext, num int64, tp byte, fsp int) (Time, error) { 1298 fsp, err := CheckFsp(fsp) 1299 if err != nil { 1300 return Time{Time: ZeroTime, Type: tp}, errors.Trace(err) 1301 } 1302 1303 t, err := parseDateTimeFromNum(sc, num) 1304 if err != nil { 1305 return Time{Time: ZeroTime, Type: tp}, errors.Trace(err) 1306 } 1307 1308 t.Type = tp 1309 t.Fsp = fsp 1310 if err := t.check(sc); err != nil { 1311 return Time{Time: ZeroTime, Type: tp}, errors.Trace(err) 1312 } 1313 return t, nil 1314 } 1315 1316 // ParseDatetimeFromNum is a helper function wrapping ParseTimeFromNum with datetime type and default fsp. 1317 func ParseDatetimeFromNum(sc *stmtctx.StatementContext, num int64) (Time, error) { 1318 return ParseTimeFromNum(sc, num, mysql.TypeDatetime, DefaultFsp) 1319 } 1320 1321 // ParseTimestampFromNum is a helper function wrapping ParseTimeFromNum with timestamp type and default fsp. 1322 func ParseTimestampFromNum(sc *stmtctx.StatementContext, num int64) (Time, error) { 1323 return ParseTimeFromNum(sc, num, mysql.TypeTimestamp, DefaultFsp) 1324 } 1325 1326 // ParseDateFromNum is a helper function wrapping ParseTimeFromNum with date type. 1327 func ParseDateFromNum(sc *stmtctx.StatementContext, num int64) (Time, error) { 1328 // date has no fractional seconds precision 1329 return ParseTimeFromNum(sc, num, mysql.TypeDate, MinFsp) 1330 } 1331 1332 // TimeFromDays Converts a day number to a date. 1333 func TimeFromDays(num int64) Time { 1334 if num < 0 { 1335 return Time{ 1336 Time: FromDate(0, 0, 0, 0, 0, 0, 0), 1337 Type: mysql.TypeDate, 1338 Fsp: 0, 1339 } 1340 } 1341 year, month, day := getDateFromDaynr(uint(num)) 1342 1343 return Time{ 1344 Time: FromDate(int(year), int(month), int(day), 0, 0, 0, 0), 1345 Type: mysql.TypeDate, 1346 Fsp: 0, 1347 } 1348 } 1349 1350 func checkDateType(t MysqlTime, allowZeroInDate, allowInvalidDate bool) error { 1351 year, month, day := t.Year(), t.Month(), t.Day() 1352 if year == 0 && month == 0 && day == 0 { 1353 return nil 1354 } 1355 1356 if !allowZeroInDate && (month == 0 || day == 0) { 1357 return ErrIncorrectDatetimeValue.GenWithStackByArgs(fmt.Sprintf("%04d-%02d-%02d", year, month, day)) 1358 } 1359 1360 if err := checkDateRange(t); err != nil { 1361 return errors.Trace(err) 1362 } 1363 1364 if err := checkMonthDay(year, month, day, allowInvalidDate); err != nil { 1365 return errors.Trace(err) 1366 } 1367 1368 return nil 1369 } 1370 1371 func checkDateRange(t MysqlTime) error { 1372 // Oddly enough, MySQL document says date range should larger than '1000-01-01', 1373 // but we can insert '0001-01-01' actually. 1374 if t.Year() < 0 || t.Month() < 0 || t.Day() < 0 { 1375 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(t)) 1376 } 1377 if compareTime(t, MaxDatetime) > 0 { 1378 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(t)) 1379 } 1380 return nil 1381 } 1382 1383 func checkMonthDay(year, month, day int, allowInvalidDate bool) error { 1384 if month < 0 || month > 12 { 1385 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(month)) 1386 } 1387 1388 maxDay := 31 1389 if !allowInvalidDate { 1390 if month > 0 { 1391 maxDay = maxDaysInMonth[month-1] 1392 } 1393 if month == 2 && year%4 != 0 { 1394 maxDay = 28 1395 } 1396 } 1397 1398 if day < 0 || day > maxDay { 1399 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(day)) 1400 } 1401 return nil 1402 } 1403 1404 func checkTimestampType(sc *stmtctx.StatementContext, t MysqlTime) error { 1405 if compareTime(t, ZeroTime) == 0 { 1406 return nil 1407 } 1408 1409 if sc == nil { 1410 return errors.New("statementContext is required during checkTimestampType") 1411 } 1412 1413 var checkTime MysqlTime 1414 if sc.TimeZone != BoundTimezone { 1415 convertTime := Time{Time: t, Type: mysql.TypeTimestamp} 1416 err := convertTime.ConvertTimeZone(sc.TimeZone, BoundTimezone) 1417 if err != nil { 1418 return err 1419 } 1420 checkTime = convertTime.Time 1421 } else { 1422 checkTime = t 1423 } 1424 if compareTime(checkTime, MaxTimestamp.Time) > 0 || compareTime(checkTime, MinTimestamp.Time) < 0 { 1425 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(t)) 1426 } 1427 1428 if _, err := t.GoTime(gotime.Local); err != nil { 1429 return errors.Trace(err) 1430 } 1431 1432 return nil 1433 } 1434 1435 func checkDatetimeType(t MysqlTime, allowZeroInDate, allowInvalidDate bool) error { 1436 if err := checkDateType(t, allowZeroInDate, allowInvalidDate); err != nil { 1437 return errors.Trace(err) 1438 } 1439 1440 hour, minute, second := t.Hour(), t.Minute(), t.Second() 1441 if hour < 0 || hour >= 24 { 1442 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(hour)) 1443 } 1444 if minute < 0 || minute >= 60 { 1445 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(minute)) 1446 } 1447 if second < 0 || second >= 60 { 1448 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(second)) 1449 } 1450 1451 return nil 1452 } 1453 1454 // ExtractDatetimeNum extracts time value number from datetime unit and format. 1455 func ExtractDatetimeNum(t *Time, unit string) (int64, error) { 1456 switch strings.ToUpper(unit) { 1457 case "DAY": 1458 return int64(t.Time.Day()), nil 1459 case "WEEK": 1460 week := t.Time.Week(0) 1461 return int64(week), nil 1462 case "MONTH": 1463 // TODO: Consider time_zone variable. 1464 t1, err := t.Time.GoTime(gotime.Local) 1465 if err != nil { 1466 return 0, errors.Trace(err) 1467 } 1468 return int64(t1.Month()), nil 1469 case "QUARTER": 1470 m := int64(t.Time.Month()) 1471 // 1 - 3 -> 1 1472 // 4 - 6 -> 2 1473 // 7 - 9 -> 3 1474 // 10 - 12 -> 4 1475 return (m + 2) / 3, nil 1476 case "YEAR": 1477 return int64(t.Time.Year()), nil 1478 case "DAY_MICROSECOND": 1479 h, m, s := t.Clock() 1480 d := t.Time.Day() 1481 return int64(d*1000000+h*10000+m*100+s)*1000000 + int64(t.Time.Microsecond()), nil 1482 case "DAY_SECOND": 1483 h, m, s := t.Clock() 1484 d := t.Time.Day() 1485 return int64(d)*1000000 + int64(h)*10000 + int64(m)*100 + int64(s), nil 1486 case "DAY_MINUTE": 1487 h, m, _ := t.Clock() 1488 d := t.Time.Day() 1489 return int64(d)*10000 + int64(h)*100 + int64(m), nil 1490 case "DAY_HOUR": 1491 h, _, _ := t.Clock() 1492 d := t.Time.Day() 1493 return int64(d)*100 + int64(h), nil 1494 case "YEAR_MONTH": 1495 y, m := t.Time.Year(), t.Time.Month() 1496 return int64(y)*100 + int64(m), nil 1497 default: 1498 return 0, errors.Errorf("invalid unit %s", unit) 1499 } 1500 } 1501 1502 // ExtractDurationNum extracts duration value number from duration unit and format. 1503 func ExtractDurationNum(d *Duration, unit string) (int64, error) { 1504 switch strings.ToUpper(unit) { 1505 case "MICROSECOND": 1506 return int64(d.MicroSecond()), nil 1507 case "SECOND": 1508 return int64(d.Second()), nil 1509 case "MINUTE": 1510 return int64(d.Minute()), nil 1511 case "HOUR": 1512 return int64(d.Hour()), nil 1513 case "SECOND_MICROSECOND": 1514 return int64(d.Second())*1000000 + int64(d.MicroSecond()), nil 1515 case "MINUTE_MICROSECOND": 1516 return int64(d.Minute())*100000000 + int64(d.Second())*1000000 + int64(d.MicroSecond()), nil 1517 case "MINUTE_SECOND": 1518 return int64(d.Minute()*100 + d.Second()), nil 1519 case "HOUR_MICROSECOND": 1520 return int64(d.Hour())*10000000000 + int64(d.Minute())*100000000 + int64(d.Second())*1000000 + int64(d.MicroSecond()), nil 1521 case "HOUR_SECOND": 1522 return int64(d.Hour())*10000 + int64(d.Minute())*100 + int64(d.Second()), nil 1523 case "HOUR_MINUTE": 1524 return int64(d.Hour())*100 + int64(d.Minute()), nil 1525 default: 1526 return 0, errors.Errorf("invalid unit %s", unit) 1527 } 1528 } 1529 1530 func extractSingleTimeValue(unit string, format string) (int64, int64, int64, float64, error) { 1531 fv, err := strconv.ParseFloat(format, 64) 1532 if err != nil { 1533 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1534 } 1535 iv := int64(math.Round(fv)) 1536 1537 switch strings.ToUpper(unit) { 1538 case "MICROSECOND": 1539 return 0, 0, 0, fv * float64(gotime.Microsecond), nil 1540 case "SECOND": 1541 return 0, 0, 0, fv * float64(gotime.Second), nil 1542 case "MINUTE": 1543 return 0, 0, 0, float64(iv * int64(gotime.Minute)), nil 1544 case "HOUR": 1545 return 0, 0, 0, float64(iv * int64(gotime.Hour)), nil 1546 case "DAY": 1547 return 0, 0, iv, 0, nil 1548 case "WEEK": 1549 return 0, 0, 7 * iv, 0, nil 1550 case "MONTH": 1551 return 0, iv, 0, 0, nil 1552 case "QUARTER": 1553 return 0, 3 * iv, 0, 0, nil 1554 case "YEAR": 1555 return iv, 0, 0, 0, nil 1556 } 1557 1558 return 0, 0, 0, 0, errors.Errorf("invalid singel timeunit - %s", unit) 1559 } 1560 1561 // extractSecondMicrosecond extracts second and microsecond from a string and its format is `SS.FFFFFF`. 1562 func extractSecondMicrosecond(format string) (int64, int64, int64, float64, error) { 1563 fields := strings.Split(format, ".") 1564 if len(fields) != 2 { 1565 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1566 } 1567 1568 seconds, err := strconv.ParseFloat(fields[0], 64) 1569 if err != nil { 1570 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1571 } 1572 1573 microseconds, err := strconv.ParseFloat(alignFrac(fields[1], MaxFsp), 64) 1574 if err != nil { 1575 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1576 } 1577 1578 return 0, 0, 0, seconds*float64(gotime.Second) + microseconds*float64(gotime.Microsecond), nil 1579 } 1580 1581 // extractMinuteMicrosecond extracts minutes and microsecond from a string and its format is `MM:SS.FFFFFF`. 1582 func extractMinuteMicrosecond(format string) (int64, int64, int64, float64, error) { 1583 fields := strings.Split(format, ":") 1584 if len(fields) != 2 { 1585 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1586 } 1587 1588 minutes, err := strconv.ParseFloat(fields[0], 64) 1589 if err != nil { 1590 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1591 } 1592 1593 _, _, _, value, err := extractSecondMicrosecond(fields[1]) 1594 if err != nil { 1595 return 0, 0, 0, 0, errors.Trace(err) 1596 } 1597 1598 return 0, 0, 0, minutes*float64(gotime.Minute) + value, nil 1599 } 1600 1601 // extractMinuteSecond extracts minutes and second from a string and its format is `MM:SS`. 1602 func extractMinuteSecond(format string) (int64, int64, int64, float64, error) { 1603 fields := strings.Split(format, ":") 1604 if len(fields) != 2 { 1605 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1606 } 1607 1608 minutes, err := strconv.ParseFloat(fields[0], 64) 1609 if err != nil { 1610 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1611 } 1612 1613 seconds, err := strconv.ParseFloat(fields[1], 64) 1614 if err != nil { 1615 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1616 } 1617 1618 return 0, 0, 0, minutes*float64(gotime.Minute) + seconds*float64(gotime.Second), nil 1619 } 1620 1621 // extractHourMicrosecond extracts hour and microsecond from a string and its format is `HH:MM:SS.FFFFFF`. 1622 func extractHourMicrosecond(format string) (int64, int64, int64, float64, error) { 1623 fields := strings.Split(format, ":") 1624 if len(fields) != 3 { 1625 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1626 } 1627 1628 hours, err := strconv.ParseFloat(fields[0], 64) 1629 if err != nil { 1630 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1631 } 1632 1633 minutes, err := strconv.ParseFloat(fields[1], 64) 1634 if err != nil { 1635 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1636 } 1637 1638 _, _, _, value, err := extractSecondMicrosecond(fields[2]) 1639 if err != nil { 1640 return 0, 0, 0, 0, errors.Trace(err) 1641 } 1642 1643 return 0, 0, 0, hours*float64(gotime.Hour) + minutes*float64(gotime.Minute) + value, nil 1644 } 1645 1646 // extractHourSecond extracts hour and second from a string and its format is `HH:MM:SS`. 1647 func extractHourSecond(format string) (int64, int64, int64, float64, error) { 1648 fields := strings.Split(format, ":") 1649 if len(fields) != 3 { 1650 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1651 } 1652 1653 hours, err := strconv.ParseFloat(fields[0], 64) 1654 if err != nil { 1655 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1656 } 1657 1658 minutes, err := strconv.ParseFloat(fields[1], 64) 1659 if err != nil { 1660 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1661 } 1662 1663 seconds, err := strconv.ParseFloat(fields[2], 64) 1664 if err != nil { 1665 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1666 } 1667 1668 return 0, 0, 0, hours*float64(gotime.Hour) + minutes*float64(gotime.Minute) + seconds*float64(gotime.Second), nil 1669 } 1670 1671 // extractHourMinute extracts hour and minute from a string and its format is `HH:MM`. 1672 func extractHourMinute(format string) (int64, int64, int64, float64, error) { 1673 fields := strings.Split(format, ":") 1674 if len(fields) != 2 { 1675 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1676 } 1677 1678 hours, err := strconv.ParseFloat(fields[0], 64) 1679 if err != nil { 1680 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1681 } 1682 1683 minutes, err := strconv.ParseFloat(fields[1], 64) 1684 if err != nil { 1685 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1686 } 1687 1688 return 0, 0, 0, hours*float64(gotime.Hour) + minutes*float64(gotime.Minute), nil 1689 } 1690 1691 // extractDayMicrosecond extracts day and microsecond from a string and its format is `DD HH:MM:SS.FFFFFF`. 1692 func extractDayMicrosecond(format string) (int64, int64, int64, float64, error) { 1693 fields := strings.Split(format, " ") 1694 if len(fields) != 2 { 1695 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1696 } 1697 1698 days, err := strconv.ParseInt(fields[0], 10, 64) 1699 if err != nil { 1700 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1701 } 1702 1703 _, _, _, value, err := extractHourMicrosecond(fields[1]) 1704 if err != nil { 1705 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1706 } 1707 1708 return 0, 0, days, value, nil 1709 } 1710 1711 // extractDaySecond extracts day and hour from a string and its format is `DD HH:MM:SS`. 1712 func extractDaySecond(format string) (int64, int64, int64, float64, error) { 1713 fields := strings.Split(format, " ") 1714 if len(fields) != 2 { 1715 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1716 } 1717 1718 days, err := strconv.ParseInt(fields[0], 10, 64) 1719 if err != nil { 1720 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1721 } 1722 1723 _, _, _, value, err := extractHourSecond(fields[1]) 1724 if err != nil { 1725 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1726 } 1727 1728 return 0, 0, days, value, nil 1729 } 1730 1731 // extractDayMinute extracts day and minute from a string and its format is `DD HH:MM`. 1732 func extractDayMinute(format string) (int64, int64, int64, float64, error) { 1733 fields := strings.Split(format, " ") 1734 if len(fields) != 2 { 1735 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1736 } 1737 1738 days, err := strconv.ParseInt(fields[0], 10, 64) 1739 if err != nil { 1740 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1741 } 1742 1743 _, _, _, value, err := extractHourMinute(fields[1]) 1744 if err != nil { 1745 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1746 } 1747 1748 return 0, 0, days, value, nil 1749 } 1750 1751 // extractDayHour extracts day and hour from a string and its format is `DD HH`. 1752 func extractDayHour(format string) (int64, int64, int64, float64, error) { 1753 fields := strings.Split(format, " ") 1754 if len(fields) != 2 { 1755 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1756 } 1757 1758 days, err := strconv.ParseInt(fields[0], 10, 64) 1759 if err != nil { 1760 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1761 } 1762 1763 hours, err := strconv.ParseFloat(fields[1], 64) 1764 if err != nil { 1765 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1766 } 1767 1768 return 0, 0, days, hours * float64(gotime.Hour), nil 1769 } 1770 1771 // extractYearMonth extracts year and month from a string and its format is `YYYY-MM`. 1772 func extractYearMonth(format string) (int64, int64, int64, float64, error) { 1773 fields := strings.Split(format, "-") 1774 if len(fields) != 2 { 1775 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1776 } 1777 1778 years, err := strconv.ParseInt(fields[0], 10, 64) 1779 if err != nil { 1780 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1781 } 1782 1783 months, err := strconv.ParseInt(fields[1], 10, 64) 1784 if err != nil { 1785 return 0, 0, 0, 0, ErrIncorrectDatetimeValue.GenWithStackByArgs(format) 1786 } 1787 1788 return years, months, 0, 0, nil 1789 } 1790 1791 // ExtractTimeValue extracts time value from time unit and format. 1792 func ExtractTimeValue(unit string, format string) (int64, int64, int64, float64, error) { 1793 switch strings.ToUpper(unit) { 1794 case "MICROSECOND", "SECOND", "MINUTE", "HOUR", "DAY", "WEEK", "MONTH", "QUARTER", "YEAR": 1795 return extractSingleTimeValue(unit, format) 1796 case "SECOND_MICROSECOND": 1797 return extractSecondMicrosecond(format) 1798 case "MINUTE_MICROSECOND": 1799 return extractMinuteMicrosecond(format) 1800 case "MINUTE_SECOND": 1801 return extractMinuteSecond(format) 1802 case "HOUR_MICROSECOND": 1803 return extractHourMicrosecond(format) 1804 case "HOUR_SECOND": 1805 return extractHourSecond(format) 1806 case "HOUR_MINUTE": 1807 return extractHourMinute(format) 1808 case "DAY_MICROSECOND": 1809 return extractDayMicrosecond(format) 1810 case "DAY_SECOND": 1811 return extractDaySecond(format) 1812 case "DAY_MINUTE": 1813 return extractDayMinute(format) 1814 case "DAY_HOUR": 1815 return extractDayHour(format) 1816 case "YEAR_MONTH": 1817 return extractYearMonth(format) 1818 default: 1819 return 0, 0, 0, 0, errors.Errorf("invalid singel timeunit - %s", unit) 1820 } 1821 } 1822 1823 // IsClockUnit returns true when unit is interval unit with hour, minute or second. 1824 func IsClockUnit(unit string) bool { 1825 switch strings.ToUpper(unit) { 1826 case "MICROSECOND", "SECOND", "MINUTE", "HOUR", 1827 "SECOND_MICROSECOND", "MINUTE_MICROSECOND", "MINUTE_SECOND", 1828 "HOUR_MICROSECOND", "HOUR_SECOND", "HOUR_MINUTE", 1829 "DAY_MICROSECOND", "DAY_SECOND", "DAY_MINUTE", "DAY_HOUR": 1830 return true 1831 default: 1832 return false 1833 } 1834 } 1835 1836 // IsDateFormat returns true when the specified time format could contain only date. 1837 func IsDateFormat(format string) bool { 1838 format = strings.TrimSpace(format) 1839 seps := ParseDateFormat(format) 1840 length := len(format) 1841 switch len(seps) { 1842 case 1: 1843 if (length == 8) || (length == 6) { 1844 return true 1845 } 1846 case 3: 1847 return true 1848 } 1849 return false 1850 } 1851 1852 // ParseTimeFromInt64 parses mysql time value from int64. 1853 func ParseTimeFromInt64(sc *stmtctx.StatementContext, num int64) (Time, error) { 1854 return parseDateTimeFromNum(sc, num) 1855 } 1856 1857 // DateFormat returns a textual representation of the time value formatted 1858 // according to layout. 1859 // See http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format 1860 func (t Time) DateFormat(layout string) (string, error) { 1861 var buf bytes.Buffer 1862 inPatternMatch := false 1863 for _, b := range layout { 1864 if inPatternMatch { 1865 if err := t.convertDateFormat(b, &buf); err != nil { 1866 return "", errors.Trace(err) 1867 } 1868 inPatternMatch = false 1869 continue 1870 } 1871 1872 // It's not in pattern match now. 1873 if b == '%' { 1874 inPatternMatch = true 1875 } else { 1876 buf.WriteRune(b) 1877 } 1878 } 1879 return buf.String(), nil 1880 } 1881 1882 var abbrevWeekdayName = []string{ 1883 "Sun", "Mon", "Tue", 1884 "Wed", "Thu", "Fri", "Sat", 1885 } 1886 1887 func (t Time) convertDateFormat(b rune, buf *bytes.Buffer) error { 1888 switch b { 1889 case 'b': 1890 m := t.Time.Month() 1891 if m == 0 || m > 12 { 1892 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(m)) 1893 } 1894 buf.WriteString(MonthNames[m-1][:3]) 1895 case 'M': 1896 m := t.Time.Month() 1897 if m == 0 || m > 12 { 1898 return errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(m)) 1899 } 1900 buf.WriteString(MonthNames[m-1]) 1901 case 'm': 1902 fmt.Fprintf(buf, "%02d", t.Time.Month()) 1903 case 'c': 1904 fmt.Fprintf(buf, "%d", t.Time.Month()) 1905 case 'D': 1906 fmt.Fprintf(buf, "%d%s", t.Time.Day(), abbrDayOfMonth(t.Time.Day())) 1907 case 'd': 1908 fmt.Fprintf(buf, "%02d", t.Time.Day()) 1909 case 'e': 1910 fmt.Fprintf(buf, "%d", t.Time.Day()) 1911 case 'j': 1912 fmt.Fprintf(buf, "%03d", t.Time.YearDay()) 1913 case 'H': 1914 fmt.Fprintf(buf, "%02d", t.Time.Hour()) 1915 case 'k': 1916 fmt.Fprintf(buf, "%d", t.Time.Hour()) 1917 case 'h', 'I': 1918 t := t.Time.Hour() 1919 if t == 0 || t == 12 { 1920 fmt.Fprintf(buf, "%02d", 12) 1921 } else { 1922 fmt.Fprintf(buf, "%02d", t%12) 1923 } 1924 case 'l': 1925 t := t.Time.Hour() 1926 if t == 0 || t == 12 { 1927 fmt.Fprintf(buf, "%d", 12) 1928 } else { 1929 fmt.Fprintf(buf, "%d", t%12) 1930 } 1931 case 'i': 1932 fmt.Fprintf(buf, "%02d", t.Time.Minute()) 1933 case 'p': 1934 hour := t.Time.Hour() 1935 if hour/12%2 == 0 { 1936 buf.WriteString("AM") 1937 } else { 1938 buf.WriteString("PM") 1939 } 1940 case 'r': 1941 h := t.Time.Hour() 1942 switch { 1943 case h == 0: 1944 fmt.Fprintf(buf, "%02d:%02d:%02d AM", 12, t.Time.Minute(), t.Time.Second()) 1945 case h == 12: 1946 fmt.Fprintf(buf, "%02d:%02d:%02d PM", 12, t.Time.Minute(), t.Time.Second()) 1947 case h < 12: 1948 fmt.Fprintf(buf, "%02d:%02d:%02d AM", h, t.Time.Minute(), t.Time.Second()) 1949 default: 1950 fmt.Fprintf(buf, "%02d:%02d:%02d PM", h-12, t.Time.Minute(), t.Time.Second()) 1951 } 1952 case 'T': 1953 fmt.Fprintf(buf, "%02d:%02d:%02d", t.Time.Hour(), t.Time.Minute(), t.Time.Second()) 1954 case 'S', 's': 1955 fmt.Fprintf(buf, "%02d", t.Time.Second()) 1956 case 'f': 1957 fmt.Fprintf(buf, "%06d", t.Time.Microsecond()) 1958 case 'U': 1959 w := t.Time.Week(0) 1960 fmt.Fprintf(buf, "%02d", w) 1961 case 'u': 1962 w := t.Time.Week(1) 1963 fmt.Fprintf(buf, "%02d", w) 1964 case 'V': 1965 w := t.Time.Week(2) 1966 fmt.Fprintf(buf, "%02d", w) 1967 case 'v': 1968 _, w := t.Time.YearWeek(3) 1969 fmt.Fprintf(buf, "%02d", w) 1970 case 'a': 1971 weekday := t.Time.Weekday() 1972 buf.WriteString(abbrevWeekdayName[weekday]) 1973 case 'W': 1974 buf.WriteString(t.Time.Weekday().String()) 1975 case 'w': 1976 fmt.Fprintf(buf, "%d", t.Time.Weekday()) 1977 case 'X': 1978 year, _ := t.Time.YearWeek(2) 1979 if year < 0 { 1980 fmt.Fprintf(buf, "%v", uint64(math.MaxUint32)) 1981 } else { 1982 fmt.Fprintf(buf, "%04d", year) 1983 } 1984 case 'x': 1985 year, _ := t.Time.YearWeek(3) 1986 if year < 0 { 1987 fmt.Fprintf(buf, "%v", uint64(math.MaxUint32)) 1988 } else { 1989 fmt.Fprintf(buf, "%04d", year) 1990 } 1991 case 'Y': 1992 fmt.Fprintf(buf, "%04d", t.Time.Year()) 1993 case 'y': 1994 str := fmt.Sprintf("%04d", t.Time.Year()) 1995 buf.WriteString(str[2:]) 1996 default: 1997 buf.WriteRune(b) 1998 } 1999 2000 return nil 2001 } 2002 2003 func abbrDayOfMonth(day int) string { 2004 var str string 2005 switch day { 2006 case 1, 21, 31: 2007 str = "st" 2008 case 2, 22: 2009 str = "nd" 2010 case 3, 23: 2011 str = "rd" 2012 default: 2013 str = "th" 2014 } 2015 return str 2016 } 2017 2018 // StrToDate converts date string according to format. 2019 // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format 2020 func (t *Time) StrToDate(sc *stmtctx.StatementContext, date, format string) bool { 2021 ctx := make(map[string]int) 2022 var tm MysqlTime 2023 if !strToDate(&tm, date, format, ctx) { 2024 t.Time = ZeroTime 2025 t.Type = mysql.TypeDatetime 2026 t.Fsp = 0 2027 return false 2028 } 2029 if err := mysqlTimeFix(&tm, ctx); err != nil { 2030 return false 2031 } 2032 2033 t.Time = tm 2034 t.Type = mysql.TypeDatetime 2035 return t.check(sc) == nil 2036 } 2037 2038 // mysqlTimeFix fixes the MysqlTime use the values in the context. 2039 func mysqlTimeFix(t *MysqlTime, ctx map[string]int) error { 2040 // Key of the ctx is the format char, such as `%j` `%p` and so on. 2041 if yearOfDay, ok := ctx["%j"]; ok { 2042 // TODO: Implement the function that converts day of year to yy:mm:dd. 2043 _ = yearOfDay 2044 } 2045 if valueAMorPm, ok := ctx["%p"]; ok { 2046 if t.hour == 0 { 2047 return ErrInvalidTimeFormat.GenWithStackByArgs(t) 2048 } 2049 if t.hour == 12 { 2050 // 12 is a special hour. 2051 switch valueAMorPm { 2052 case constForAM: 2053 t.hour = 0 2054 case constForPM: 2055 t.hour = 12 2056 } 2057 return nil 2058 } 2059 if valueAMorPm == constForPM { 2060 t.hour += 12 2061 } 2062 } 2063 return nil 2064 } 2065 2066 // strToDate converts date string according to format, returns true on success, 2067 // the value will be stored in argument t or ctx. 2068 func strToDate(t *MysqlTime, date string, format string, ctx map[string]int) bool { 2069 date = skipWhiteSpace(date) 2070 format = skipWhiteSpace(format) 2071 2072 token, formatRemain, succ := getFormatToken(format) 2073 if !succ { 2074 return false 2075 } 2076 2077 if token == "" { 2078 // Extra characters at the end of date are ignored. 2079 return true 2080 } 2081 2082 dateRemain, succ := matchDateWithToken(t, date, token, ctx) 2083 if !succ { 2084 return false 2085 } 2086 2087 return strToDate(t, dateRemain, formatRemain, ctx) 2088 } 2089 2090 // getFormatToken takes one format control token from the string. 2091 // format "%d %H %m" will get token "%d" and the remain is " %H %m". 2092 func getFormatToken(format string) (token string, remain string, succ bool) { 2093 if len(format) == 0 { 2094 return "", "", true 2095 } 2096 2097 // Just one character. 2098 if len(format) == 1 { 2099 if format[0] == '%' { 2100 return "", "", false 2101 } 2102 return format, "", true 2103 } 2104 2105 // More than one character. 2106 if format[0] == '%' { 2107 return format[:2], format[2:], true 2108 } 2109 2110 return format[:1], format[1:], true 2111 } 2112 2113 func skipWhiteSpace(input string) string { 2114 for i, c := range input { 2115 if !unicode.IsSpace(c) { 2116 return input[i:] 2117 } 2118 } 2119 return "" 2120 } 2121 2122 var weekdayAbbrev = map[string]gotime.Weekday{ 2123 "Sun": gotime.Sunday, 2124 "Mon": gotime.Monday, 2125 "Tue": gotime.Tuesday, 2126 "Wed": gotime.Wednesday, 2127 "Thu": gotime.Tuesday, 2128 "Fri": gotime.Friday, 2129 "Sat": gotime.Saturday, 2130 } 2131 2132 var monthAbbrev = map[string]gotime.Month{ 2133 "Jan": gotime.January, 2134 "Feb": gotime.February, 2135 "Mar": gotime.March, 2136 "Apr": gotime.April, 2137 "May": gotime.May, 2138 "Jun": gotime.June, 2139 "Jul": gotime.July, 2140 "Aug": gotime.August, 2141 "Sep": gotime.September, 2142 "Oct": gotime.October, 2143 "Nov": gotime.November, 2144 "Dec": gotime.December, 2145 } 2146 2147 type dateFormatParser func(t *MysqlTime, date string, ctx map[string]int) (remain string, succ bool) 2148 2149 var dateFormatParserTable = map[string]dateFormatParser{ 2150 "%b": abbreviatedMonth, // Abbreviated month name (Jan..Dec) 2151 "%c": monthNumeric, // Month, numeric (0..12) 2152 "%d": dayOfMonthNumeric, // Day of the month, numeric (0..31) 2153 "%e": dayOfMonthNumeric, // Day of the month, numeric (0..31) 2154 "%f": microSeconds, // Microseconds (000000..999999) 2155 "%h": hour24TwoDigits, // Hour (01..12) 2156 "%H": hour24TwoDigits, // Hour (01..12) 2157 "%I": hour24TwoDigits, // Hour (01..12) 2158 "%i": minutesNumeric, // Minutes, numeric (00..59) 2159 "%j": dayOfYearThreeDigits, // Day of year (001..366) 2160 "%k": hour24Numeric, // Hour (0..23) 2161 "%l": hour12Numeric, // Hour (1..12) 2162 "%M": fullNameMonth, // Month name (January..December) 2163 "%m": monthNumeric, // Month, numeric (00..12) 2164 "%p": isAMOrPM, // AM or PM 2165 "%r": time12Hour, // Time, 12-hour (hh:mm:ss followed by AM or PM) 2166 "%s": secondsNumeric, // Seconds (00..59) 2167 "%S": secondsNumeric, // Seconds (00..59) 2168 "%T": time24Hour, // Time, 24-hour (hh:mm:ss) 2169 "%Y": yearNumericFourDigits, // Year, numeric, four digits 2170 // Deprecated since MySQL 5.7.5 2171 "%y": yearNumericTwoDigits, // Year, numeric (two digits) 2172 // TODO: Add the following... 2173 // "%a": abbreviatedWeekday, // Abbreviated weekday name (Sun..Sat) 2174 // "%D": dayOfMonthWithSuffix, // Day of the month with English suffix (0th, 1st, 2nd, 3rd) 2175 // "%U": weekMode0, // Week (00..53), where Sunday is the first day of the week; WEEK() mode 0 2176 // "%u": weekMode1, // Week (00..53), where Monday is the first day of the week; WEEK() mode 1 2177 // "%V": weekMode2, // Week (01..53), where Sunday is the first day of the week; WEEK() mode 2; used with %X 2178 // "%v": weekMode3, // Week (01..53), where Monday is the first day of the week; WEEK() mode 3; used with %x 2179 // "%W": weekdayName, // Weekday name (Sunday..Saturday) 2180 // "%w": dayOfWeek, // Day of the week (0=Sunday..6=Saturday) 2181 // "%X": yearOfWeek, // Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V 2182 // "%x": yearOfWeek, // Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v 2183 } 2184 2185 // GetFormatType checks the type(Duration, Date or Datetime) of a format string. 2186 func GetFormatType(format string) (isDuration, isDate bool) { 2187 durationTokens := map[string]struct{}{ 2188 "%h": {}, 2189 "%H": {}, 2190 "%i": {}, 2191 "%I": {}, 2192 "%s": {}, 2193 "%S": {}, 2194 "%k": {}, 2195 "%l": {}, 2196 } 2197 dateTokens := map[string]struct{}{ 2198 "%y": {}, 2199 "%Y": {}, 2200 "%m": {}, 2201 "%M": {}, 2202 "%c": {}, 2203 "%b": {}, 2204 "%D": {}, 2205 "%d": {}, 2206 "%e": {}, 2207 } 2208 2209 format = skipWhiteSpace(format) 2210 var token string 2211 var succ bool 2212 for { 2213 token, format, succ = getFormatToken(format) 2214 if len(token) == 0 { 2215 break 2216 } 2217 if !succ { 2218 isDuration, isDate = false, false 2219 break 2220 } 2221 if _, ok := durationTokens[token]; ok { 2222 isDuration = true 2223 } else if _, ok := dateTokens[token]; ok { 2224 isDate = true 2225 } 2226 if isDuration && isDate { 2227 break 2228 } 2229 } 2230 return 2231 } 2232 2233 func matchDateWithToken(t *MysqlTime, date string, token string, ctx map[string]int) (remain string, succ bool) { 2234 if parse, ok := dateFormatParserTable[token]; ok { 2235 return parse(t, date, ctx) 2236 } 2237 2238 if strings.HasPrefix(date, token) { 2239 return date[len(token):], true 2240 } 2241 return date, false 2242 } 2243 2244 func parseDigits(input string, count int) (int, bool) { 2245 if count <= 0 || len(input) < count { 2246 return 0, false 2247 } 2248 2249 v, err := strconv.ParseUint(input[:count], 10, 64) 2250 if err != nil { 2251 return int(v), false 2252 } 2253 return int(v), true 2254 } 2255 2256 func hour24TwoDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2257 v, succ := parseDigits(input, 2) 2258 if !succ || v >= 24 { 2259 return input, false 2260 } 2261 t.hour = v 2262 return input[2:], true 2263 } 2264 2265 func secondsNumeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2266 v, succ := parseDigits(input, 2) 2267 if !succ || v >= 60 { 2268 return input, false 2269 } 2270 t.second = uint8(v) 2271 return input[2:], true 2272 } 2273 2274 func minutesNumeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2275 v, succ := parseDigits(input, 2) 2276 if !succ || v >= 60 { 2277 return input, false 2278 } 2279 t.minute = uint8(v) 2280 return input[2:], true 2281 } 2282 2283 const time12HourLen = len("hh:mm:ssAM") 2284 2285 func time12Hour(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2286 // hh:mm:ss AM 2287 if len(input) < time12HourLen { 2288 return input, false 2289 } 2290 hour, succ := parseDigits(input, 2) 2291 if !succ || hour > 12 || hour == 0 || input[2] != ':' { 2292 return input, false 2293 } 2294 2295 minute, succ := parseDigits(input[3:], 2) 2296 if !succ || minute > 59 || input[5] != ':' { 2297 return input, false 2298 } 2299 2300 second, succ := parseDigits(input[6:], 2) 2301 if !succ || second > 59 { 2302 return input, false 2303 } 2304 2305 remain := skipWhiteSpace(input[8:]) 2306 switch { 2307 case strings.HasPrefix(remain, "AM"): 2308 t.hour = hour 2309 case strings.HasPrefix(remain, "PM"): 2310 t.hour = hour + 12 2311 default: 2312 return input, false 2313 } 2314 2315 t.minute = uint8(minute) 2316 t.second = uint8(second) 2317 return remain, true 2318 } 2319 2320 const time24HourLen = len("hh:mm:ss") 2321 2322 func time24Hour(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2323 // hh:mm:ss 2324 if len(input) < time24HourLen { 2325 return input, false 2326 } 2327 2328 hour, succ := parseDigits(input, 2) 2329 if !succ || hour > 23 || input[2] != ':' { 2330 return input, false 2331 } 2332 2333 minute, succ := parseDigits(input[3:], 2) 2334 if !succ || minute > 59 || input[5] != ':' { 2335 return input, false 2336 } 2337 2338 second, succ := parseDigits(input[6:], 2) 2339 if !succ || second > 59 { 2340 return input, false 2341 } 2342 2343 t.hour = hour 2344 t.minute = uint8(minute) 2345 t.second = uint8(second) 2346 return input[8:], true 2347 } 2348 2349 const ( 2350 constForAM = 1 + iota 2351 constForPM 2352 ) 2353 2354 func isAMOrPM(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2355 if strings.HasPrefix(input, "AM") { 2356 ctx["%p"] = constForAM 2357 } else if strings.HasPrefix(input, "PM") { 2358 ctx["%p"] = constForPM 2359 } else { 2360 return input, false 2361 } 2362 return input[2:], true 2363 } 2364 2365 // digitRegex: it was used to scan a variable-length monthly day or month in the string. Ex: "01" or "1" or "30" 2366 var oneOrTwoDigitRegex = regexp.MustCompile("^[0-9]{1,2}") 2367 2368 // twoDigitRegex: it was just for two digit number string. Ex: "01" or "12" 2369 var twoDigitRegex = regexp.MustCompile("^[1-9][0-9]?") 2370 2371 // parseTwoNumeric is used for pattens 0..31 0..24 0..60 and so on. 2372 // It returns the parsed int, and remain data after parse. 2373 func parseTwoNumeric(input string) (int, string) { 2374 if len(input) > 1 && input[0] == '0' { 2375 return 0, input[1:] 2376 } 2377 matched := twoDigitRegex.FindAllString(input, -1) 2378 if len(matched) == 0 { 2379 return 0, input 2380 } 2381 2382 str := matched[0] 2383 v, err := strconv.ParseInt(str, 10, 64) 2384 if err != nil { 2385 return 0, input 2386 } 2387 return int(v), input[len(str):] 2388 } 2389 2390 func dayOfMonthNumeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2391 result := oneOrTwoDigitRegex.FindString(input) // 0..31 2392 length := len(result) 2393 2394 v, ok := parseDigits(input, length) 2395 2396 if !ok || v > 31 { 2397 return input, false 2398 } 2399 t.day = uint8(v) 2400 return input[length:], true 2401 } 2402 2403 func hour24Numeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2404 result := oneOrTwoDigitRegex.FindString(input) // 0..23 2405 length := len(result) 2406 2407 v, ok := parseDigits(input, length) 2408 2409 if !ok || v > 23 { 2410 return input, false 2411 } 2412 t.hour = v 2413 return input[length:], true 2414 } 2415 2416 func hour12Numeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2417 result := oneOrTwoDigitRegex.FindString(input) // 1..12 2418 length := len(result) 2419 2420 v, ok := parseDigits(input, length) 2421 2422 if !ok || v > 12 || v == 0 { 2423 return input, false 2424 } 2425 t.hour = v 2426 return input[length:], true 2427 } 2428 2429 func microSeconds(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2430 if len(input) < 6 { 2431 return input, false 2432 } 2433 v, err := strconv.ParseUint(input[:6], 10, 64) 2434 if err != nil { 2435 return input, false 2436 } 2437 t.microsecond = uint32(v) 2438 return input[6:], true 2439 } 2440 2441 func yearNumericFourDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2442 return yearNumericNDigits(t, input, ctx, 4) 2443 } 2444 2445 func yearNumericTwoDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2446 return yearNumericNDigits(t, input, ctx, 2) 2447 } 2448 2449 func yearNumericNDigits(t *MysqlTime, input string, ctx map[string]int, n int) (string, bool) { 2450 effectiveCount, effectiveValue := 0, 0 2451 for effectiveCount+1 <= n { 2452 value, succeed := parseDigits(input, effectiveCount+1) 2453 if !succeed { 2454 break 2455 } 2456 effectiveCount++ 2457 effectiveValue = value 2458 } 2459 if effectiveCount == 0 { 2460 return input, false 2461 } 2462 if effectiveCount <= 2 { 2463 effectiveValue = adjustYear(effectiveValue) 2464 } 2465 t.year = uint16(effectiveValue) 2466 return input[effectiveCount:], true 2467 } 2468 2469 func dayOfYearThreeDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2470 v, succ := parseDigits(input, 3) 2471 if !succ || v == 0 || v > 366 { 2472 return input, false 2473 } 2474 ctx["%j"] = v 2475 return input[3:], true 2476 } 2477 2478 func abbreviatedWeekday(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2479 if len(input) >= 3 { 2480 dayName := input[:3] 2481 if _, ok := weekdayAbbrev[dayName]; ok { 2482 // TODO: We need refact mysql time to support this. 2483 return input, false 2484 } 2485 } 2486 return input, false 2487 } 2488 2489 func abbreviatedMonth(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2490 if len(input) >= 3 { 2491 monthName := input[:3] 2492 if month, ok := monthAbbrev[monthName]; ok { 2493 t.month = uint8(month) 2494 return input[len(monthName):], true 2495 } 2496 } 2497 return input, false 2498 } 2499 2500 func fullNameMonth(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2501 for i, month := range MonthNames { 2502 if strings.HasPrefix(input, month) { 2503 t.month = uint8(i + 1) 2504 return input[len(month):], true 2505 } 2506 } 2507 return input, false 2508 } 2509 2510 func monthNumeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2511 result := oneOrTwoDigitRegex.FindString(input) // 1..12 2512 length := len(result) 2513 2514 v, ok := parseDigits(input, length) 2515 2516 if !ok || v > 12 { 2517 return input, false 2518 } 2519 t.month = uint8(v) 2520 return input[length:], true 2521 } 2522 2523 // dayOfMonthWithSuffix returns different suffix according t being which day. i.e. 0 return th. 1 return st. 2524 func dayOfMonthWithSuffix(t *MysqlTime, input string, ctx map[string]int) (string, bool) { 2525 month, remain := parseOrdinalNumbers(input) 2526 if month >= 0 { 2527 t.month = uint8(month) 2528 return remain, true 2529 } 2530 return input, false 2531 } 2532 2533 func parseOrdinalNumbers(input string) (value int, remain string) { 2534 for i, c := range input { 2535 if !unicode.IsDigit(c) { 2536 v, err := strconv.ParseUint(input[:i], 10, 64) 2537 if err != nil { 2538 return -1, input 2539 } 2540 value = int(v) 2541 break 2542 } 2543 } 2544 switch { 2545 case strings.HasPrefix(remain, "st"): 2546 if value == 1 { 2547 remain = remain[2:] 2548 return 2549 } 2550 case strings.HasPrefix(remain, "nd"): 2551 if value == 2 { 2552 remain = remain[2:] 2553 return 2554 } 2555 case strings.HasPrefix(remain, "th"): 2556 remain = remain[2:] 2557 return 2558 } 2559 return -1, input 2560 } 2561 2562 // DateFSP gets fsp from date string. 2563 func DateFSP(date string) (fsp int) { 2564 i := strings.LastIndex(date, ".") 2565 if i != -1 { 2566 fsp = len(date) - i - 1 2567 } 2568 return 2569 }