github.com/matrixorigin/matrixone@v0.7.0/pkg/sql/plan/function/builtin/binary/generalTime.go (about) 1 // Copyright 2021 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package binary 16 17 import ( 18 "fmt" 19 "strings" 20 "unicode" 21 ) 22 23 // GeneralTime is the internal struct type for Time. 24 type GeneralTime struct { 25 year uint16 26 month uint8 27 day uint8 28 hour uint8 29 minute uint8 30 second uint8 31 microsecond uint32 32 } 33 34 func NewGeneralTime() *GeneralTime { 35 return &GeneralTime{} 36 } 37 38 func FromDate(year int, month int, day int, hour int, minute int, second int, microsecond int) GeneralTime { 39 return GeneralTime{ 40 year: uint16(year), 41 month: uint8(month), 42 day: uint8(day), 43 hour: uint8(hour), 44 minute: uint8(minute), 45 second: uint8(second), 46 microsecond: uint32(microsecond), 47 } 48 } 49 50 // Reset GeneralTime to initialization state 51 func (t GeneralTime) ResetTime() { 52 t.year = 0 53 t.month = 0 54 t.day = 0 55 t.hour = 0 56 t.minute = 0 57 t.second = 0 58 t.microsecond = 0 59 return 60 } 61 62 // String implements fmt.Stringer. 63 func (t GeneralTime) String() string { 64 return fmt.Sprintf("{%d %d %d %d %d %d %d}", t.getYear(), t.getMonth(), t.getDay(), t.getHour(), t.getMinute(), t.getSecond(), t.getMicrosecond()) 65 } 66 67 func (t GeneralTime) getYear() uint16 { 68 return t.year 69 } 70 71 func (t *GeneralTime) setYear(year uint16) { 72 t.year = year 73 } 74 75 func (t GeneralTime) getMonth() uint8 { 76 return t.month 77 } 78 79 func (t *GeneralTime) setMonth(month uint8) { 80 t.month = month 81 } 82 83 func (t GeneralTime) getDay() uint8 { 84 return t.day 85 } 86 87 func (t *GeneralTime) setDay(day uint8) { 88 t.day = day 89 } 90 91 func (t GeneralTime) getHour() uint8 { 92 return t.hour 93 } 94 95 func (t *GeneralTime) setHour(hour uint8) { 96 t.hour = hour 97 } 98 99 func (t GeneralTime) getMinute() uint8 { 100 return t.minute 101 } 102 103 func (t *GeneralTime) setMinute(minute uint8) { 104 t.minute = minute 105 } 106 107 // Minute returns the minute value. 108 func (t GeneralTime) Minute() int { 109 return int(t.getMinute()) 110 } 111 112 func (t GeneralTime) getSecond() uint8 { 113 return t.second 114 } 115 116 func (t *GeneralTime) setSecond(second uint8) { 117 t.second = second 118 } 119 120 func (t GeneralTime) getMicrosecond() uint32 { 121 return t.microsecond 122 } 123 124 func (t *GeneralTime) setMicrosecond(microsecond uint32) { 125 t.microsecond = microsecond 126 } 127 128 // The month represents one month of the year (January=1,...). 129 type Month int 130 131 const ( 132 January Month = 1 + iota 133 February 134 March 135 April 136 May 137 June 138 July 139 August 140 September 141 October 142 November 143 December 144 ) 145 146 var monthAbbrev = map[string]Month{ 147 "jan": January, 148 "feb": February, 149 "mar": March, 150 "apr": April, 151 "may": May, 152 "jun": June, 153 "jul": July, 154 "aug": August, 155 "sep": September, 156 "oct": October, 157 "nov": November, 158 "dec": December, 159 } 160 161 type dateFormatParser func(t *GeneralTime, date string, ctx map[string]int) (remain string, succ bool) 162 163 var dateFormatParserTable = map[string]dateFormatParser{ 164 "%b": abbreviatedMonth, // Abbreviated month name (Jan..Dec) 165 "%c": monthNumeric, // Month, numeric (0..12) 166 "%d": dayOfMonthNumeric, // Day of the month, numeric (0..31) 167 "%e": dayOfMonthNumeric, // Day of the month, numeric (0..31) 168 "%f": microSeconds, // Microseconds (000000..999999) 169 "%h": hour12Numeric, // Hour (01..12) 170 "%H": hour24Numeric, // Hour (00..23) 171 "%I": hour12Numeric, // Hour (01..12) 172 "%i": minutesNumeric, // Minutes, numeric (00..59) 173 "%j": dayOfYearNumeric, // Day of year (001..366) 174 "%k": hour24Numeric, // Hour (0..23) 175 "%l": hour12Numeric, // Hour (1..12) 176 "%M": fullNameMonth, // Month name (January..December) 177 "%m": monthNumeric, // Month, numeric (00..12) 178 "%p": isAMOrPM, // AM or PM 179 "%r": time12Hour, // Time, 12-hour (hh:mm:ss followed by AM or PM) 180 "%s": secondsNumeric, // Seconds (00..59) 181 "%S": secondsNumeric, // Seconds (00..59) 182 "%T": time24Hour, // Time, 24-hour (hh:mm:ss) 183 "%Y": yearNumericFourDigits, // Year, numeric, four digits 184 "%#": skipAllNums, // Skip all numbers 185 "%.": skipAllPunct, // Skip all punctation characters 186 "%@": skipAllAlpha, // Skip all alpha characters 187 "%y": yearNumericTwoDigits, // Year, numeric (two digits) 188 // "%a": abbreviatedWeekday, // Abbreviated weekday name (Sun..Sat) 189 // "%D": dayOfMonthWithSuffix, // Day of the month with English suffix (0th, 1st, 2nd, 3rd) 190 // "%U": weekMode0, // Week (00..53), where Sunday is the first day of the week; WEEK() mode 0 191 // "%u": weekMode1, // Week (00..53), where Monday is the first day of the week; WEEK() mode 1 192 // "%V": weekMode2, // Week (01..53), where Sunday is the first day of the week; WEEK() mode 2; used with %X 193 // "%v": weekMode3, // Week (01..53), where Monday is the first day of the week; WEEK() mode 3; used with %x 194 // "%W": weekdayName, // Weekday name (Sunday..Saturday) 195 // "%w": dayOfWeek, // Day of the week (0=Sunday..6=Saturday) 196 // "%X": yearOfWeek, // Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V 197 // "%x": yearOfWeek, // Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v 198 } 199 200 func matchDateWithToken(t *GeneralTime, date string, token string, ctx map[string]int) (remain string, succ bool) { 201 if parse, ok := dateFormatParserTable[token]; ok { 202 return parse(t, date, ctx) 203 } 204 205 if strings.HasPrefix(date, token) { 206 return date[len(token):], true 207 } 208 return date, false 209 } 210 211 // Try to parse digits with number of `limit` starting from `input` 212 // Return <number, n chars to step forward> if success. 213 // Return <_, 0> if fail. 214 func parseNDigits(input string, limit int) (number int, step int) { 215 if limit <= 0 { 216 return 0, 0 217 } 218 219 var num uint64 = 0 220 step = 0 221 for ; step < len(input) && step < limit && '0' <= input[step] && input[step] <= '9'; step++ { 222 num = num*10 + uint64(input[step]-'0') 223 } 224 return int(num), step 225 } 226 227 // Seconds (00..59) 228 func secondsNumeric(t *GeneralTime, input string, _ map[string]int) (string, bool) { 229 v, step := parseNDigits(input, 2) 230 if step <= 0 || v >= 60 { 231 return input, false 232 } 233 t.setSecond(uint8(v)) 234 return input[step:], true 235 } 236 237 // Minutes, numeric (00..59) 238 func minutesNumeric(t *GeneralTime, input string, _ map[string]int) (string, bool) { 239 v, step := parseNDigits(input, 2) 240 if step <= 0 || v >= 60 { 241 return input, false 242 } 243 t.setMinute(uint8(v)) 244 return input[step:], true 245 } 246 247 type parseState int32 248 249 const ( 250 parseStateNormal parseState = 1 251 parseStateFail parseState = 2 252 parseStateEndOfLine parseState = 3 253 ) 254 255 func parseSep(input string) (string, parseState) { 256 input = trimWhiteSpace(input) 257 if len(input) == 0 { 258 return input, parseStateEndOfLine 259 } 260 if input[0] != ':' { 261 return input, parseStateFail 262 } 263 if input = trimWhiteSpace(input[1:]); len(input) == 0 { 264 return input, parseStateEndOfLine 265 } 266 return input, parseStateNormal 267 } 268 269 // Time, 12-hour (hh:mm:ss followed by AM or PM) 270 func time12Hour(t *GeneralTime, input string, _ map[string]int) (string, bool) { 271 tryParse := func(input string) (string, parseState) { 272 // hh:mm:ss AM 273 /// Note that we should update `t` as soon as possible, or we 274 /// can not get correct result for incomplete input like "12:13" 275 /// that is shorter than "hh:mm:ss" 276 hour, step := parseNDigits(input, 2) // 1..12 277 if step <= 0 || hour > 12 || hour == 0 { 278 return input, parseStateFail 279 } 280 // Handle special case: 12:34:56 AM -> 00:34:56 281 // For PM, we will add 12 it later 282 if hour == 12 { 283 hour = 0 284 } 285 t.setHour(uint8(hour)) 286 287 // ':' 288 var state parseState 289 if input, state = parseSep(input[step:]); state != parseStateNormal { 290 return input, state 291 } 292 293 minute, step := parseNDigits(input, 2) // 0..59 294 if step <= 0 || minute > 59 { 295 return input, parseStateFail 296 } 297 t.setMinute(uint8(minute)) 298 299 // ':' 300 if input, state = parseSep(input[step:]); state != parseStateNormal { 301 return input, state 302 } 303 304 second, step := parseNDigits(input, 2) // 0..59 305 if step <= 0 || second > 59 { 306 return input, parseStateFail 307 } 308 t.setSecond(uint8(second)) 309 310 input = trimWhiteSpace(input[step:]) 311 if len(input) == 0 { 312 // No "AM"/"PM" suffix, it is ok 313 return input, parseStateEndOfLine 314 } else if len(input) < 2 { 315 // some broken char, fail 316 return input, parseStateFail 317 } 318 319 switch { 320 case hasCaseInsensitivePrefix(input, "AM"): 321 t.setHour(uint8(hour)) 322 case hasCaseInsensitivePrefix(input, "PM"): 323 t.setHour(uint8(hour + 12)) 324 default: 325 return input, parseStateFail 326 } 327 328 return input[2:], parseStateNormal 329 } 330 331 remain, state := tryParse(input) 332 if state == parseStateFail { 333 return input, false 334 } 335 return remain, true 336 } 337 338 // Time, 24-hour (hh:mm:ss) 339 func time24Hour(t *GeneralTime, input string, _ map[string]int) (string, bool) { 340 tryParse := func(input string) (string, parseState) { 341 // hh:mm:ss 342 /// Note that we should update `t` as soon as possible, or we 343 /// can not get correct result for incomplete input like "12:13" 344 /// that is shorter than "hh:mm:ss" 345 hour, step := parseNDigits(input, 2) // 0..23 346 if step <= 0 || hour > 23 { 347 return input, parseStateFail 348 } 349 t.setHour(uint8(hour)) 350 351 // ':' 352 var state parseState 353 if input, state = parseSep(input[step:]); state != parseStateNormal { 354 return input, state 355 } 356 357 minute, step := parseNDigits(input, 2) // 0..59 358 if step <= 0 || minute > 59 { 359 return input, parseStateFail 360 } 361 t.setMinute(uint8(minute)) 362 363 // ':' 364 if input, state = parseSep(input[step:]); state != parseStateNormal { 365 return input, state 366 } 367 368 second, step := parseNDigits(input, 2) // 0..59 369 if step <= 0 || second > 59 { 370 return input, parseStateFail 371 } 372 t.setSecond(uint8(second)) 373 return input[step:], parseStateNormal 374 } 375 376 remain, state := tryParse(input) 377 if state == parseStateFail { 378 return input, false 379 } 380 return remain, true 381 } 382 383 const ( 384 timeOfAM = 1 + iota 385 timeOfPM 386 ) 387 388 // judege AM or PM 389 func isAMOrPM(_ *GeneralTime, input string, ctx map[string]int) (string, bool) { 390 if len(input) < 2 { 391 return input, false 392 } 393 394 s := strings.ToLower(input[:2]) 395 switch s { 396 case "am": 397 ctx["%p"] = timeOfAM 398 case "pm": 399 ctx["%p"] = timeOfPM 400 default: 401 return input, false 402 } 403 return input[2:], true 404 } 405 406 // Day of the month, numeric (0..31) 407 func dayOfMonthNumeric(t *GeneralTime, input string, _ map[string]int) (string, bool) { 408 v, step := parseNDigits(input, 2) // 0..31 409 if step <= 0 || v > 31 { 410 return input, false 411 } 412 t.setDay(uint8(v)) 413 return input[step:], true 414 } 415 416 // Hour (00..23) 417 func hour24Numeric(t *GeneralTime, input string, ctx map[string]int) (string, bool) { 418 v, step := parseNDigits(input, 2) // 0..23 419 if step <= 0 || v > 23 { 420 return input, false 421 } 422 t.setHour(uint8(v)) 423 ctx["%H"] = v 424 return input[step:], true 425 } 426 427 // Hour result (01..12) 428 func hour12Numeric(t *GeneralTime, input string, ctx map[string]int) (string, bool) { 429 v, step := parseNDigits(input, 2) // 1..12 430 if step <= 0 || v > 12 || v == 0 { 431 return input, false 432 } 433 t.setHour(uint8(v)) 434 ctx["%h"] = v 435 return input[step:], true 436 } 437 438 // Microseconds (000000..999999) 439 func microSeconds(t *GeneralTime, input string, _ map[string]int) (string, bool) { 440 v, step := parseNDigits(input, 6) 441 if step <= 0 { 442 t.setMicrosecond(0) 443 return input, true 444 } 445 for i := step; i < 6; i++ { 446 v *= 10 447 } 448 t.setMicrosecond(uint32(v)) 449 return input[step:], true 450 } 451 452 // Year, numeric, four digits 453 func yearNumericFourDigits(t *GeneralTime, input string, ctx map[string]int) (string, bool) { 454 return yearNumericNDigits(t, input, ctx, 4) 455 } 456 457 // Year, numeric (two digits) 458 func yearNumericTwoDigits(t *GeneralTime, input string, ctx map[string]int) (string, bool) { 459 return yearNumericNDigits(t, input, ctx, 2) 460 } 461 462 func yearNumericNDigits(t *GeneralTime, input string, _ map[string]int, n int) (string, bool) { 463 year, step := parseNDigits(input, n) 464 if step <= 0 { 465 return input, false 466 } else if step <= 2 { 467 year = adjustYear(year) 468 } 469 t.setYear(uint16(year)) 470 return input[step:], true 471 } 472 473 // adjustYear adjusts year according to y. 474 // link to: https://dev.mysql.com/doc/refman/8.0/en/two-digit-years.html 475 func adjustYear(y int) int { 476 if y >= 0 && y <= 69 { 477 y = 2000 + y 478 } else if y >= 70 && y <= 99 { 479 y = 1900 + y 480 } 481 return y 482 } 483 484 // Day of year (001..366) 485 func dayOfYearNumeric(_ *GeneralTime, input string, ctx map[string]int) (string, bool) { 486 // MySQL declares that "%j" should be "Day of year (001..366)". But actually, 487 // it accepts a number that is up to three digits, which range is [1, 999]. 488 v, step := parseNDigits(input, 3) 489 if step <= 0 || v == 0 { 490 return input, false 491 } 492 ctx["%j"] = v 493 return input[step:], true 494 } 495 496 // Abbreviated month name (Jan..Dec) 497 func abbreviatedMonth(t *GeneralTime, input string, _ map[string]int) (string, bool) { 498 if len(input) >= 3 { 499 monthName := strings.ToLower(input[:3]) 500 if month, ok := monthAbbrev[monthName]; ok { 501 t.setMonth(uint8(month)) 502 return input[len(monthName):], true 503 } 504 } 505 return input, false 506 } 507 508 func hasCaseInsensitivePrefix(input, prefix string) bool { 509 if len(input) < len(prefix) { 510 return false 511 } 512 return strings.EqualFold(input[:len(prefix)], prefix) 513 } 514 515 // Month name (January..December) 516 func fullNameMonth(t *GeneralTime, input string, _ map[string]int) (string, bool) { 517 for i, month := range MonthNames { 518 if hasCaseInsensitivePrefix(input, month) { 519 t.setMonth(uint8(i + 1)) 520 return input[len(month):], true 521 } 522 } 523 return input, false 524 } 525 526 // Month, numeric (0..12) 527 func monthNumeric(t *GeneralTime, input string, _ map[string]int) (string, bool) { 528 v, step := parseNDigits(input, 2) // 1..12 529 if step <= 0 || v > 12 { 530 return input, false 531 } 532 t.setMonth(uint8(v)) 533 return input[step:], true 534 } 535 536 // DateFSP gets fsp from date string. 537 func DateFSP(date string) (fsp int) { 538 i := strings.LastIndex(date, ".") 539 if i != -1 { 540 fsp = len(date) - i - 1 541 } 542 return 543 } 544 545 // Skip all numbers 546 func skipAllNums(_ *GeneralTime, input string, _ map[string]int) (string, bool) { 547 retIdx := 0 548 for i, ch := range input { 549 if unicode.IsNumber(ch) { 550 retIdx = i + 1 551 } else { 552 break 553 } 554 } 555 return input[retIdx:], true 556 } 557 558 // Skip all punctation characters 559 func skipAllPunct(_ *GeneralTime, input string, _ map[string]int) (string, bool) { 560 retIdx := 0 561 for i, ch := range input { 562 if unicode.IsPunct(ch) { 563 retIdx = i + 1 564 } else { 565 break 566 } 567 } 568 return input[retIdx:], true 569 } 570 571 // Skip all alpha characters 572 func skipAllAlpha(_ *GeneralTime, input string, _ map[string]int) (string, bool) { 573 retIdx := 0 574 for i, ch := range input { 575 if unicode.IsLetter(ch) { 576 retIdx = i + 1 577 } else { 578 break 579 } 580 } 581 return input[retIdx:], true 582 }