github.com/matrixorigin/matrixone@v0.7.0/pkg/container/types/date.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 types 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 "time" 22 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 ) 25 26 const ( 27 daysPer400Years = 365*400 + 97 28 daysPer100Years = 365*100 + 24 29 daysPer4Years = 365*4 + 1 30 ) 31 32 type Weekday uint8 33 34 const ( 35 Sunday Weekday = iota 36 Monday 37 Tuesday 38 Wednesday 39 Thursday 40 Friday 41 Saturday 42 ) 43 44 // String returns the English name of the day ("Sunday", "Monday", ...). 45 func (d Weekday) String() string { 46 if Sunday <= d && d <= Saturday { 47 return longDayNames[d] 48 } 49 return "%Weekday(" + strconv.FormatUint(uint64(d), 10) + ")" 50 } 51 52 var unixEpochSecs = int64(DatetimeFromClock(1970, 1, 1, 0, 0, 0, 0)) 53 var unixEpochDays = int32(DateFromCalendar(1970, 1, 1)) 54 55 var ( 56 leapYearMonthDays = []uint8{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} 57 flatYearMonthDays = []uint8{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} 58 ) 59 60 const ( 61 MaxDateYear = 9999 62 MinDateYear = 1 63 MaxMonthInYear = 12 64 MinMonthInYear = 1 65 ) 66 67 type TimeType int32 68 69 const ( 70 DateType = 0 71 DateTimeType = 1 72 TimeStampType = 2 73 ) 74 75 const ( 76 Start = iota 77 YearState 78 MonthState 79 DayState 80 HourState 81 MinuteState 82 SecondState 83 MsState 84 End 85 ) 86 87 func IsNumber(s *string, idx int) bool { 88 if (*s)[idx] >= '0' && (*s)[idx] <= '9' { 89 return true 90 } 91 return false 92 } 93 94 func IsAllNumber(s *string) bool { 95 for i := 0; i < len(*s); i++ { 96 if !IsNumber(s, i) { 97 return false 98 } 99 } 100 return true 101 } 102 103 func ToNumber(s string) int64 { 104 var result int64 105 for i := 0; i < len(s); i++ { 106 result = 10*result + int64((s[i] - '0')) 107 } 108 return result 109 } 110 111 // rewrite ParseDateCast, don't use regexp, that's too slow 112 // the format we need to support: 113 // 1.yyyy-mm-dd hh:mm:ss.ms 114 // 2.yyyy-mm-dd 115 // 3.yyyymmdd 116 func ParseDateCast(s string) (Date, error) { 117 var y, m, d, hh, mm, ss string 118 s = strings.TrimSpace(s) 119 if len(s) < 7 && IsAllNumber(&s) { 120 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 121 } 122 // a special we need to process 123 // we need to support 2220919,so we need to add a '0' to extend 124 if len(s) == 7 && IsAllNumber(&s) { 125 s = string('0') + s 126 } 127 // for the third type, process here 128 if len(s) == 8 && IsAllNumber(&s) { 129 y = s[:4] 130 m = s[4:6] 131 d = s[6:8] 132 } else { 133 // state is used to flag the state of the DAG, we process 1,2 below 134 var state = Start 135 for i := 0; i < len(s); i++ { 136 switch state { 137 case Start: 138 if !IsNumber(&s, i) { 139 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 140 } 141 state = YearState 142 y = y + string(s[i]) 143 if len(y) >= 5 { 144 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 145 } 146 case YearState: 147 if IsNumber(&s, i) { 148 y = y + string(s[i]) 149 } else if s[i] == '-' { 150 state = MonthState 151 if y == "" { 152 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 153 } 154 } else { 155 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 156 } 157 case MonthState: 158 if IsNumber(&s, i) { 159 m = m + string(s[i]) 160 if len(m) >= 3 { 161 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 162 } 163 } else if s[i] == '-' { 164 // Can't go into DayState, because the Month is empty 165 if m == "" { 166 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 167 } else { 168 state = DayState 169 } 170 } 171 case DayState: 172 if IsNumber(&s, i) { 173 d = d + string(s[i]) 174 if len(d) >= 3 { 175 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 176 } 177 } else { 178 if s[i] == ' ' { 179 if d == "" { 180 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 181 } 182 state = HourState 183 } else { 184 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 185 } 186 } 187 if i == len(s)-1 { 188 state = End 189 } 190 case HourState: 191 // we need to support '2022-09-01 23:11:12.3' 192 // not only '2022-09-01 23:11:12.3' 193 if s[i] == ' ' { 194 continue 195 } else { 196 if IsNumber(&s, i) { 197 hh += string(s[i]) 198 if len(hh) >= 3 { 199 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 200 } 201 } else { 202 if s[i] == ':' { 203 if hh == "" { 204 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 205 } 206 state = MinuteState 207 } else { 208 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 209 } 210 } 211 } 212 case MinuteState: 213 if IsNumber(&s, i) { 214 mm = mm + string(s[i]) 215 if len(mm) >= 3 { 216 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 217 } 218 } else if s[i] == ':' { 219 // Can't go into SecondState, because the Minute is empty 220 if mm == "" { 221 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 222 } else { 223 state = SecondState 224 } 225 } 226 case SecondState: 227 if IsNumber(&s, i) { 228 ss = ss + string(s[i]) 229 if len(d) >= 3 { 230 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 231 } 232 } else { 233 if s[i] == '.' { 234 if d == "" { 235 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 236 } 237 state = MsState 238 } else { 239 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 240 } 241 } 242 if i == len(s)-1 { 243 state = End 244 } 245 case MsState: 246 temp_s := string(s[i:]) 247 if IsAllNumber(&temp_s) { 248 state = End 249 // break out loop 250 i = len(s) 251 } else { 252 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 253 } 254 } 255 } 256 if state != End { 257 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 258 } 259 } 260 year := ToNumber(y) 261 month := ToNumber(m) 262 day := ToNumber(d) 263 if ValidDate(int32(year), uint8(month), uint8(day)) { 264 return DateFromCalendar(int32(year), uint8(month), uint8(day)), nil 265 } 266 return -1, moerr.NewInvalidArgNoCtx("parsedate", s) 267 } 268 269 // date[0001-01-01 to 9999-12-31] 270 func ValidDate(year int32, month, day uint8) bool { 271 if year >= MinDateYear && year <= MaxDateYear { 272 if MinMonthInYear <= month && month <= MaxMonthInYear { 273 if day > 0 { 274 if isLeap(year) { 275 return day <= leapYearMonthDays[month-1] 276 } else { 277 return day <= flatYearMonthDays[month-1] 278 } 279 } 280 } 281 } 282 return false 283 } 284 285 func (d Date) String() string { 286 y, m, day, _ := d.Calendar(true) 287 return fmt.Sprintf("%04d-%02d-%02d", y, m, day) 288 } 289 290 // Today Holds number of days since January 1, year 1 in Gregorian calendar 291 func Today(loc *time.Location) Date { 292 return Now(loc).ToDate() 293 } 294 295 const dayInfoTableMinYear = 1924 296 const dayInfoTableMaxYear = 2099 297 const dayInfoTableYears = dayInfoTableMaxYear - dayInfoTableMinYear + 1 298 const dayInfoTableSize = dayInfoTableYears*365 + (dayInfoTableMaxYear-dayInfoTableMinYear)/4 + 1 299 const dayNumOfTableEpoch = 702360 // the day number of "1924-01-01" 300 301 type dayInfo struct { 302 year uint16 303 //month uint8 304 //week uint8 305 } 306 307 var dayInfoTable [dayInfoTableSize]dayInfo 308 309 // this init function takes a bit of build time 310 func init() { 311 yearNow := uint16(1924) 312 i := int32(0) 313 for yearIndex := 0; yearIndex < dayInfoTableYears; yearIndex++ { 314 if yearIndex%4 == 0 { // this is a leap year 315 for j := 0; j < 366; j++ { 316 dayInfoTable[i].year = yearNow 317 i++ 318 } 319 } else { 320 for j := 0; j < 365; j++ { 321 dayInfoTable[i].year = yearNow 322 i++ 323 } 324 } 325 yearNow++ 326 } 327 } 328 329 // Year takes a date and returns an uint16 number as the year of this date 330 func (d Date) Year() uint16 { 331 dayNum := int32(d) 332 insideDayInfoTable := dayNum >= dayNumOfTableEpoch && dayNum < dayNumOfTableEpoch+dayInfoTableSize 333 if insideDayInfoTable { 334 return dayInfoTable[dayNum-dayNumOfTableEpoch].year 335 } 336 // Account for 400 year cycles. 337 n := d / daysPer400Years 338 y := 400 * n 339 d -= daysPer400Years * n 340 341 // Cut off 100-year cycles. 342 // The last cycle has one extra leap year, so on the last day 343 // of that year, day / daysPer100Years will be 4 instead of 3. 344 // Cut it back down to 3 by subtracting n>>2. 345 n = d / daysPer100Years 346 n -= n >> 2 347 y += 100 * n 348 d -= daysPer100Years * n 349 350 // Cut off 4-year cycles. 351 // The last cycle has a missing leap year, which does not 352 // affect the computation. 353 n = d / daysPer4Years 354 y += 4 * n 355 d -= daysPer4Years * n 356 357 // Cut off years within a 4-year cycle. 358 // The last year is a leap year, so on the last day of that year, 359 // day / 365 will be 4 instead of 3. Cut it back down to 3 360 // by subtracting n>>2. 361 n = d / 365 362 n -= n >> 2 363 y += n 364 365 year := uint16(y) + 1 366 367 return year 368 } 369 370 func (d Date) YearMonth() uint32 { 371 year, month, _, _ := d.Calendar(true) 372 yearStr := fmt.Sprintf("%04d", year) 373 monthStr := fmt.Sprintf("%02d", month) 374 result, _ := strconv.ParseUint(yearStr+monthStr, 10, 32) 375 return uint32(result) 376 } 377 378 func (d Date) YearMonthStr() string { 379 year, month, _, _ := d.Calendar(true) 380 yearStr := fmt.Sprintf("%04d", year) 381 monthStr := fmt.Sprintf("%02d", month) 382 return yearStr + monthStr 383 } 384 385 var monthToQuarter = map[uint8]uint32{ 386 1: 1, 387 2: 1, 388 3: 1, 389 4: 2, 390 5: 2, 391 6: 2, 392 7: 3, 393 8: 3, 394 9: 3, 395 10: 4, 396 11: 4, 397 12: 4, 398 } 399 400 func (d Date) Quarter() uint32 { 401 _, month, _, _ := d.Calendar(true) 402 return monthToQuarter[month] 403 } 404 405 func (d Date) Calendar(full bool) (year int32, month, day uint8, yday uint16) { 406 // Account for 400 year cycles. 407 n := d / daysPer400Years 408 y := 400 * n 409 d -= daysPer400Years * n 410 411 // Cut off 100-year cycles. 412 // The last cycle has one extra leap year, so on the last day 413 // of that year, day / daysPer100Years will be 4 instead of 3. 414 // Cut it back down to 3 by subtracting n>>2. 415 n = d / daysPer100Years 416 n -= n >> 2 417 y += 100 * n 418 d -= daysPer100Years * n 419 420 // Cut off 4-year cycles. 421 // The last cycle has a missing leap year, which does not 422 // affect the computation. 423 n = d / daysPer4Years 424 y += 4 * n 425 d -= daysPer4Years * n 426 427 // Cut off years within a 4-year cycle. 428 // The last year is a leap year, so on the last day of that year, 429 // day / 365 will be 4 instead of 3. Cut it back down to 3 430 // by subtracting n>>2. 431 n = d / 365 432 n -= n >> 2 433 y += n 434 d -= 365 * n 435 436 year = int32(y) + 1 437 yday = uint16(d + 1) 438 439 if !full { 440 return 441 } 442 443 if isLeap(year) { 444 // Leap year 445 switch { 446 case d > 31+29-1: 447 // After leap day; pretend it wasn't there. 448 d-- 449 case d == 31+29-1: 450 // Leap day. 451 month = 2 452 day = 29 453 return 454 } 455 } 456 457 // Estimate month on assumption that every month has 31 days. 458 // The estimate may be too low by at most one month, so adjust. 459 month = uint8(d / 31) 460 end := daysBefore[month+1] 461 var begin uint16 462 if uint16(d) >= end { 463 month++ 464 begin = end 465 } else { 466 begin = daysBefore[month] 467 } 468 469 month++ // because January is 1 470 day = uint8(uint16(d) - begin + 1) 471 return year, month, day, yday 472 } 473 474 // daysBefore[m] counts the number of days in a non-leap year 475 // before month m begins. There is an entry for m=12, counting 476 // the number of days before January of next year (365). 477 478 var daysBefore = [...]uint16{ 479 0, 480 31, 481 31 + 28, 482 31 + 28 + 31, 483 31 + 28 + 31 + 30, 484 31 + 28 + 31 + 30 + 31, 485 31 + 28 + 31 + 30 + 31 + 30, 486 31 + 28 + 31 + 30 + 31 + 30 + 31, 487 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, 488 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, 489 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, 490 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, 491 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, 492 } 493 494 func DateFromCalendar(year int32, month, day uint8) Date { 495 // Compute days since the absolute epoch. 496 d := daysSinceEpoch(year - 1) 497 498 // Add in days before this month. 499 d += int32(daysBefore[month-1]) 500 if isLeap(year) && month >= 3 { 501 d++ // February 29 502 } 503 504 // Add in days before today. 505 d += int32(day - 1) 506 507 return Date(d) 508 } 509 510 func daysSinceEpoch(year int32) int32 { 511 // Add in days from 400-year cycles. 512 n := year / 400 513 year -= 400 * n 514 d := daysPer400Years * n 515 516 // Add in 100-year cycles. 517 n = year / 100 518 year -= 100 * n 519 d += daysPer100Years * n 520 521 // Add in 4-year cycles. 522 n = year / 4 523 year -= 4 * n 524 d += daysPer4Years * n 525 526 // Add in non-leap years. 527 n = year 528 d += 365 * n 529 530 return d 531 } 532 533 // DayOfWeek return the day of the week of the date 534 func (d Date) DayOfWeek() Weekday { 535 // January 1, year 1 in Gregorian calendar, was a Monday. 536 return Weekday((d + 1) % 7) 537 } 538 539 // DayOfYear return day of year (001..366) 540 func (d Date) DayOfYear() uint16 { 541 _, _, _, yday := d.Calendar(false) 542 return yday 543 } 544 545 func (d Date) WeekOfYear() (year int32, week uint8) { 546 // According to the rule that the first calendar week of a calendar year is 547 // the week including the first Thursday of that year, and that the last one is 548 // the week immediately preceding the first calendar week of the next calendar year. 549 // See https://www.iso.org/obp/ui#iso:std:iso:8601:-1:ed-1:v1:en:term:3.1.1.23 for details. 550 551 // weeks start with Monday 552 // Monday Tuesday Wednesday Thursday Friday Saturday Sunday 553 // 1 2 3 4 5 6 7 554 // +3 +2 +1 0 -1 -2 -3 555 // the offset to Thursday 556 delta := 4 - int32(d.DayOfWeek()) 557 // handle Sunday 558 if delta == 4 { 559 delta = -3 560 } 561 // find the Thursday of the calendar week 562 d = Date(int32(d) + delta) 563 year, _, _, yday := d.Calendar(false) 564 return year, uint8((yday-1)/7 + 1) 565 } 566 567 func (d Date) WeekOfYear2() uint8 { 568 // According to the rule that the first calendar week of a calendar year is 569 // the week including the first Thursday of that year, and that the last one is 570 // the week immediately preceding the first calendar week of the next calendar year. 571 // See https://www.iso.org/obp/ui#iso:std:iso:8601:-1:ed-1:v1:en:term:3.1.1.23 for details. 572 573 // weeks start with Monday 574 // Monday Tuesday Wednesday Thursday Friday Saturday Sunday 575 // 1 2 3 4 5 6 7 576 // +3 +2 +1 0 -1 -2 -3 577 // the offset to Thursday 578 delta := 4 - int32(d.DayOfWeek()) 579 // handle Sunday 580 if delta == 4 { 581 delta = -3 582 } 583 // find the Thursday of the calendar week 584 d = Date(int32(d) + delta) 585 _, _, _, yday := d.Calendar(false) 586 return uint8((yday-1)/7 + 1) 587 } 588 589 type weekBehaviour uint 590 591 const ( 592 // WeekMondayFirst: set Monday as first day of week; otherwise Sunday is first day of week 593 WeekMondayFirst weekBehaviour = 1 594 595 // WeekYear: If set, Week is in range 1-53, otherwise Week is in range 0-53. 596 // Week 0 is returned for the the last week of the previous year (for 597 // a date at start of january) In this case one can get 53 for the 598 // first week of next year. This flag ensures that the week is 599 // relevant for the given year. Note that this flag is only 600 // releveant if WEEK_JANUARY is not set. 601 WeekYear = 2 602 603 //WeekFirstWeekday: If not set, Weeks are numbered according to ISO 8601:1988. 604 // If set, the week that contains the first 'first-day-of-week' is week 1. 605 // ISO 8601:1988 means that if the week containing January 1 has 606 // four or more days in the new year, then it is week 1; 607 // Otherwise it is the last week of the previous year, and the next week is week 1. 608 WeekFirstWeekday = 4 609 ) 610 611 func (v weekBehaviour) bitAnd(flag weekBehaviour) bool { 612 return (v & flag) != 0 613 } 614 615 func weekMode(mode int) weekBehaviour { 616 weekFormat := weekBehaviour(mode & 7) 617 if (weekFormat & WeekMondayFirst) == 0 { 618 weekFormat ^= WeekFirstWeekday 619 } 620 return weekFormat 621 } 622 623 // Week (00..53), where Sunday is the first day of the week; WEEK() mode 0 624 // Week (00..53), where Monday is the first day of the week; WEEK() mode 1 625 func (d Date) Week(mode int) int { 626 if d.Month() == 0 || d.Day() == 0 { 627 return 0 628 } 629 _, week := calcWeek(d, weekMode(mode)) 630 return week 631 } 632 633 // YearWeek returns year and week. 634 func (d Date) YearWeek(mode int) (year int, week int) { 635 behavior := weekMode(mode) | WeekYear 636 return calcWeek(d, behavior) 637 } 638 639 // calcWeek calculates week and year for the date. 640 func calcWeek(d Date, wb weekBehaviour) (year int, week int) { 641 var days int 642 ty, tm, td := int(d.Year()), int(d.Month()), int(d.Day()) 643 daynr := calcDaynr(ty, tm, td) 644 firstDaynr := calcDaynr(ty, 1, 1) 645 mondayFirst := wb.bitAnd(WeekMondayFirst) 646 weekYear := wb.bitAnd(WeekYear) 647 firstWeekday := wb.bitAnd(WeekFirstWeekday) 648 649 weekday := calcWeekday(firstDaynr, !mondayFirst) 650 651 year = ty 652 653 if tm == 1 && td <= 7-weekday { 654 if !weekYear && 655 ((firstWeekday && weekday != 0) || (!firstWeekday && weekday >= 4)) { 656 week = 0 657 return 658 } 659 weekYear = true 660 year-- 661 days = calcDaysInYear(year) 662 firstDaynr -= days 663 weekday = (weekday + 53*7 - days) % 7 664 } 665 666 if (firstWeekday && weekday != 0) || 667 (!firstWeekday && weekday >= 4) { 668 days = daynr - (firstDaynr + 7 - weekday) 669 } else { 670 days = daynr - (firstDaynr - weekday) 671 } 672 673 if weekYear && days >= 52*7 { 674 weekday = (weekday + calcDaysInYear(year)) % 7 675 if (!firstWeekday && weekday < 4) || 676 (firstWeekday && weekday == 0) { 677 year++ 678 week = 1 679 return 680 } 681 } 682 week = days/7 + 1 683 return 684 } 685 686 // calcWeekday calculates weekday from daynr, returns 0 for Monday, 1 for Tuesday 687 func calcWeekday(daynr int, sundayFirstDayOfWeek bool) int { 688 daynr += 5 689 if sundayFirstDayOfWeek { 690 daynr++ 691 } 692 return daynr % 7 693 } 694 695 // Calculate nr of day since year 0 in new date-system (from 1615). 696 func calcDaynr(year, month, day int) int { 697 if year == 0 && month == 0 { 698 return 0 699 } 700 701 delsum := 365*year + 31*(month-1) + day 702 if month <= 2 { 703 year-- 704 } else { 705 delsum -= (month*4 + 23) / 10 706 } 707 temp := ((year/100 + 1) * 3) / 4 708 return delsum + year/4 - temp 709 } 710 711 // calcDaysInYear calculates days in one year, it works with 0 <= year <= 99. 712 func calcDaysInYear(year int) int { 713 if (year&3) == 0 && (year%100 != 0 || (year%400 == 0 && (year != 0))) { 714 return 366 715 } 716 return 365 717 } 718 719 func isLeap(year int32) bool { 720 return year%4 == 0 && (year%100 != 0 || year%400 == 0) 721 } 722 723 func (d Date) ToDatetime() Datetime { 724 return Datetime(int64(d) * secsPerDay * microSecsPerSec) 725 } 726 727 func (d Date) ToTime() Time { 728 return Time(0) 729 } 730 731 func (d Date) ToTimestamp(loc *time.Location) Timestamp { 732 year, mon, day, _ := d.Calendar(true) 733 t := time.Date(int(year), time.Month(mon), int(day), 0, 0, 0, 0, loc) 734 return Timestamp(t.UnixMicro() + unixEpochSecs) 735 } 736 737 func (d Date) Month() uint8 { 738 _, month, _, _ := d.Calendar(true) 739 return month 740 } 741 742 func LastDay(year int32, month uint8) uint8 { 743 if isLeap(year) { 744 return leapYearMonthDays[month-1] 745 } 746 return flatYearMonthDays[month-1] 747 } 748 749 func (d Date) Day() uint8 { 750 _, _, day, _ := d.Calendar(true) 751 return day 752 } 753 754 func (d Date) DaysSinceUnixEpoch() int32 { 755 return int32(d) - unixEpochDays 756 } 757 758 func GetUnixEpochSecs() int64 { 759 return unixEpochSecs 760 }