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