github.com/matrixorigin/matrixone@v1.2.0/pkg/container/types/datetime.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 "math" 20 "strconv" 21 "strings" 22 "time" 23 24 "github.com/matrixorigin/matrixone/pkg/common/moerr" 25 ) 26 27 const ( 28 SecsPerMinute = 60 29 SecsPerHour = 60 * SecsPerMinute 30 SecsPerDay = 24 * SecsPerHour 31 SecsPerWeek = 7 * SecsPerDay 32 NanoSecsPerSec = 1000000000 // 10^9 33 MicroSecsPerSec = 1000000 // 10^6 34 MillisecsPerSec = 1000 // 10^3 35 nanoSecsPerMicroSec = 1000 36 microSecsPerDay = SecsPerDay * MicroSecsPerSec 37 MaxDatetimeYear = 9999 38 MinDatetimeYear = 1 39 40 minHourInDay, maxHourInDay = 0, 23 41 minMinuteInHour, maxMinuteInHour = 0, 59 42 minSecondInMinute, maxSecondInMinute = 0, 59 43 ) 44 45 var ( 46 scaleVal = []Datetime{1000000, 100000, 10000, 1000, 100, 10, 1} 47 ) 48 49 const ( 50 // ZeroDatetime is the zero value for date Time '0000-01-01 00:00:00'. 51 ZeroDatetime = Datetime(0) 52 ) 53 54 // The Datetime type holds number of microseconds since January 1, year 1 in Gregorian calendar 55 56 func (dt Datetime) String() string { 57 y, m, d, _ := dt.ToDate().Calendar(true) 58 hour, minute, sec := dt.Clock() 59 return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, hour, minute, sec) 60 } 61 62 func (dt Datetime) String2(scale int32) string { 63 y, m, d, _ := dt.ToDate().Calendar(true) 64 hour, minute, sec := dt.Clock() 65 if scale > 0 { 66 msec := int64(dt) % MicroSecsPerSec 67 msecInstr := fmt.Sprintf("%06d\n", msec) 68 msecInstr = msecInstr[:scale] 69 70 return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d"+"."+msecInstr, y, m, d, hour, minute, sec) 71 } 72 return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, hour, minute, sec) 73 } 74 75 // ParseDatetime will parse a string to be a Datetime 76 // Support Format: 77 // 1. all the Date value 78 // 2. yyyy-mm-dd hh:mm:ss(.msec) 79 // now support mm/dd/hh/mm/ss can be single number 80 // 3. yyyymmddhhmmss(.msec) 81 // during parsing, the Datetime value will be rounded(away from zero) to the predefined scale, for example: 82 // Datetime(3) input string parsing result 83 // 84 // "1999-09-09 11:11:11.1234" "1999-09-09 11:11:11.123" 85 // "1999-09-09 11:11:11.1235" "1999-09-09 11:11:11.124" 86 // "1999-09-09 11:11:11.9994" "1999-09-09 11:11:11.999" 87 // "1999-09-09 11:11:11.9995" "1999-09-09 11:11:12.000" 88 // "1999-09-09 11:11" "1999-09-09 11:11:00.000" 89 // "1999-09-09 11:11:" "1999-09-09 11:11:00.000" 90 func ParseDatetime(s string, scale int32) (Datetime, error) { 91 s = strings.TrimSpace(s) 92 if len(s) < 14 { 93 if d, err := ParseDateCast(s); err == nil { 94 return d.ToDatetime(), nil 95 } 96 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 97 } 98 var year int32 99 var month, day, hour, minute, second uint8 100 var msec uint32 = 0 101 var carry uint32 = 0 102 var err error 103 104 if s[4] == '-' || s[4] == '/' { 105 var num int64 106 var unum uint64 107 strArr := strings.Split(s, " ") 108 if len(strArr) != 2 { 109 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 110 } 111 // solve year/month/day 112 front := strings.Split(strArr[0], s[4:5]) 113 if len(front) != 3 { 114 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 115 } 116 num, err = strconv.ParseInt(front[0], 10, 32) 117 if err != nil { 118 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 119 } 120 year = int32(num) 121 unum, err = strconv.ParseUint(front[1], 10, 8) 122 if err != nil { 123 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 124 } 125 month = uint8(unum) 126 unum, err = strconv.ParseUint(front[2], 10, 8) 127 if err != nil { 128 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 129 } 130 day = uint8(unum) 131 132 if !ValidDate(year, month, day) { 133 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 134 } 135 136 middleAndBack := strings.Split(strArr[1], ".") 137 // solve hour/minute/second 138 middle := strings.Split(middleAndBack[0], ":") 139 if len(middle) != 3 { 140 if len(middle) == 2 { 141 middle = append(middle, "00") 142 } else { 143 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 144 } 145 } 146 if len(middle) == 3 && middle[2] == "" { 147 middle[2] = "00" 148 } 149 unum, err = strconv.ParseUint(middle[0], 10, 8) 150 if err != nil { 151 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 152 } 153 hour = uint8(unum) 154 unum, err = strconv.ParseUint(middle[1], 10, 8) 155 if err != nil { 156 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 157 } 158 minute = uint8(unum) 159 unum, err = strconv.ParseUint(middle[2], 10, 8) 160 if err != nil { 161 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 162 } 163 second = uint8(unum) 164 if !ValidTimeInDay(hour, minute, second) { 165 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 166 } 167 // solve microsecond 168 if len(middleAndBack) == 2 { 169 msec, carry, err = getMsec(middleAndBack[1], scale) 170 if err != nil { 171 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 172 } 173 } else if len(middleAndBack) > 2 { 174 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 175 } 176 } else { 177 year = int32(s[0]-'0')*1000 + int32(s[1]-'0')*100 + int32(s[2]-'0')*10 + int32(s[3]-'0') 178 month = (s[4]-'0')*10 + (s[5] - '0') 179 day = (s[6]-'0')*10 + (s[7] - '0') 180 hour = (s[8]-'0')*10 + (s[9] - '0') 181 minute = (s[10]-'0')*10 + (s[11] - '0') 182 second = (s[12]-'0')*10 + (s[13] - '0') 183 if len(s) > 14 { 184 if len(s) > 15 && s[14] == '.' { 185 msecStr := s[15:] 186 msec, carry, err = getMsec(msecStr, scale) 187 if err != nil { 188 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 189 } 190 } else { 191 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 192 } 193 } 194 } 195 if !ValidDate(year, month, day) { 196 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 197 } 198 result := DatetimeFromClock(year, month, day, hour, minute, second+uint8(carry), msec) 199 y, m, d, _ := result.ToDate().Calendar(true) 200 if !ValidDate(y, m, d) { 201 return -1, moerr.NewInvalidInputNoCtx("invalid datetime value %s", s) 202 } 203 return result, nil 204 } 205 206 // validTimeInDay return true if hour, minute and second can be a time during a day 207 func ValidTimeInDay(h, m, s uint8) bool { 208 if h < minHourInDay || h > maxHourInDay { 209 return false 210 } 211 if m < minMinuteInHour || m > maxMinuteInHour { 212 return false 213 } 214 if s < minSecondInMinute || s > maxSecondInMinute { 215 return false 216 } 217 return true 218 } 219 220 func (dt Datetime) UnixTimestamp(loc *time.Location) int64 { 221 return dt.ConvertToGoTime(loc).Unix() 222 } 223 224 func DatetimeFromUnix(loc *time.Location, ts int64) Datetime { 225 t := time.Unix(ts, 0).In(loc) 226 _, offset := t.Zone() 227 return Datetime((ts+int64(offset))*MicroSecsPerSec + unixEpochMicroSecs) 228 } 229 230 func DatetimeFromUnixWithNsec(loc *time.Location, sec int64, nsec int64) Datetime { 231 t := time.Unix(sec, nsec).In(loc) 232 _, offset := t.Zone() 233 msec := math.Round(float64(nsec) / 1000) 234 return Datetime((sec+int64(offset))*MicroSecsPerSec + int64(msec) + unixEpochMicroSecs) 235 } 236 237 func Now(loc *time.Location) Datetime { 238 now := time.Now().In(loc) 239 _, offset := now.Zone() 240 return Datetime(now.UnixMicro() + int64(offset)*MicroSecsPerSec + unixEpochMicroSecs) 241 } 242 243 func UTC() Datetime { 244 return Datetime(time.Now().UnixMicro() + unixEpochMicroSecs) 245 } 246 247 func (dt Datetime) ToDate() Date { 248 return Date((dt.sec()) / SecsPerDay) 249 } 250 251 // We need to truncate the part after scale position when cast 252 // between different scale. 253 func (dt Datetime) ToTime(scale int32) Time { 254 if scale == 6 { 255 return Time(dt % microSecsPerDay) 256 } 257 258 // truncate the date part 259 ms := dt % microSecsPerDay 260 261 base := ms / scaleVal[scale] 262 if ms%scaleVal[scale]/scaleVal[scale+1] >= 5 { // check carry 263 base += 1 264 } 265 266 return Time(base * scaleVal[scale]) 267 } 268 269 func (dt Datetime) Clock() (hour, minute, sec int8) { 270 t := (dt.sec()) % SecsPerDay 271 hour = int8(t / SecsPerHour) 272 minute = int8(t % SecsPerHour / SecsPerMinute) 273 sec = int8(t % SecsPerMinute) 274 return 275 } 276 277 func (dt Datetime) Sec() int8 { 278 _, _, sec := dt.Clock() 279 return sec 280 } 281 282 func (dt Datetime) Minute() int8 { 283 _, minute, _ := dt.Clock() 284 return minute 285 } 286 287 func (dt Datetime) Hour() int8 { 288 hour, _, _ := dt.Clock() 289 return hour 290 } 291 292 func DatetimeFromClock(year int32, month, day, hour, minute, sec uint8, msec uint32) Datetime { 293 days := DateFromCalendar(year, month, day) 294 secs := int64(days)*SecsPerDay + int64(hour)*SecsPerHour + int64(minute)*SecsPerMinute + int64(sec) 295 return Datetime(secs*MicroSecsPerSec + int64(msec)) 296 } 297 298 func (dt Datetime) ConvertToGoTime(loc *time.Location) time.Time { 299 year, mon, day, _ := dt.ToDate().Calendar(true) 300 hour, minute, sec := dt.Clock() 301 nsec := dt.MicroSec() * 1000 302 return time.Date(int(year), time.Month(mon), int(day), int(hour), int(minute), int(sec), int(nsec), loc) 303 } 304 305 func (dt Datetime) AddDateTime(addMonth, addYear int64, timeType TimeType) (Datetime, bool) { 306 // corner case: mysql: date_add('2022-01-31',interval 1 month) -> 2022-02-28 307 // only in the month year year-month 308 oldDate := dt.ToDate() 309 y, m, d, _ := oldDate.Calendar(true) 310 year := int64(y) + addYear + addMonth/12 311 month := int64(m) + addMonth%12 312 if month <= 0 { 313 year-- 314 month += 12 315 } 316 if month > 12 { 317 year++ 318 month -= 12 319 } 320 321 y = int32(year) 322 m = uint8(month) 323 324 lastDay := LastDay(y, m) 325 if lastDay < d { 326 d = lastDay 327 } 328 329 switch timeType { 330 case DateType: 331 if !ValidDate(y, m, d) { 332 return 0, false 333 } 334 case DateTimeType, TimeStampType: 335 if !ValidDatetime(y, m, d) { 336 return 0, false 337 } 338 } 339 newDate := DateFromCalendar(y, m, d) 340 return dt + Datetime(newDate-oldDate)*SecsPerDay*MicroSecsPerSec, true 341 } 342 343 // AddInterval now date or datetime use the function to add/sub date, 344 // we need a bool arg to tell isDate/isDatetime 345 // date/datetime have different regions, so we don't use same valid function 346 // return type bool means the if the date/datetime is valid 347 func (dt Datetime) AddInterval(nums int64, its IntervalType, timeType TimeType) (Datetime, bool) { 348 var addMonth, addYear int64 349 switch its { 350 case Second: 351 nums *= MicroSecsPerSec 352 case Minute: 353 nums *= MicroSecsPerSec * SecsPerMinute 354 case Hour: 355 nums *= MicroSecsPerSec * SecsPerHour 356 case Day: 357 nums *= MicroSecsPerSec * SecsPerDay 358 case Week: 359 nums *= MicroSecsPerSec * SecsPerWeek 360 case Month: 361 addMonth = nums 362 return dt.AddDateTime(addMonth, addYear, timeType) 363 case Quarter: 364 addMonth = 3 * nums 365 return dt.AddDateTime(addMonth, addYear, timeType) 366 case Year: 367 addYear = nums 368 return dt.AddDateTime(addMonth, addYear, timeType) 369 } 370 371 newDate := dt + Datetime(nums) 372 y, m, d, _ := newDate.ToDate().Calendar(true) 373 if !ValidDatetime(y, m, d) { 374 return 0, false 375 } 376 return newDate, true 377 } 378 379 func (dt Datetime) DateTimeDiffWithUnit(its string, secondDt Datetime) (int64, error) { 380 switch its { 381 case "microsecond": 382 return int64(dt - secondDt), nil 383 case "second": 384 return (dt - secondDt).sec(), nil 385 case "minute": 386 return int64(dt-secondDt) / (MicroSecsPerSec * SecsPerMinute), nil 387 case "hour": 388 return int64(dt-secondDt) / (MicroSecsPerSec * SecsPerHour), nil 389 case "day": 390 return int64(dt-secondDt) / (MicroSecsPerSec * SecsPerDay), nil 391 case "week": 392 return int64(dt-secondDt) / (MicroSecsPerSec * SecsPerWeek), nil 393 case "month": 394 return dt.ConvertToMonth(secondDt), nil 395 case "quarter": 396 return dt.ConvertToMonth(secondDt) / 3, nil 397 case "year": 398 return dt.ConvertToMonth(secondDt) / 12, nil 399 } 400 return 0, moerr.NewInvalidInputNoCtx("invalid time_stamp_unit input") 401 } 402 403 func (dt Datetime) DatetimeMinusWithSecond(secondDt Datetime) int64 { 404 return int64((dt - secondDt) / MicroSecsPerSec) 405 } 406 407 func (dt Datetime) ConvertToMonth(secondDt Datetime) int64 { 408 409 dayDiff := int64(dt.ToDate().Day()) - int64(secondDt.ToDate().Day()) 410 monthDiff := (int64(dt.ToDate().Year())-int64(secondDt.ToDate().Year()))*12 + int64(dt.ToDate().Month()) - int64(secondDt.ToDate().Month()) 411 412 if dayDiff >= 0 { 413 return monthDiff 414 } else { 415 return monthDiff - 1 416 } 417 } 418 419 func (dt Datetime) MicroSec() int64 { 420 return int64(dt) % MicroSecsPerSec 421 } 422 423 func (dt Datetime) sec() int64 { 424 return int64(dt) / MicroSecsPerSec 425 } 426 427 func (dt Datetime) Year() uint16 { 428 return dt.ToDate().Year() 429 } 430 431 func (dt Datetime) Month() uint8 { 432 return dt.ToDate().Month() 433 } 434 435 func (dt Datetime) Day() uint8 { 436 return dt.ToDate().Day() 437 } 438 439 func (dt Datetime) WeekOfYear() (int32, uint8) { 440 return dt.ToDate().WeekOfYear() 441 } 442 443 func (dt Datetime) DayOfYear() uint16 { 444 return dt.ToDate().DayOfYear() 445 } 446 447 func (dt Datetime) DayOfWeek() Weekday { 448 return dt.ToDate().DayOfWeek() 449 } 450 451 func (dt Datetime) DayOfWeek2() Weekday { 452 return dt.ToDate().DayOfWeek2() 453 } 454 455 func (dt Datetime) Week(mode int) int { 456 return dt.ToDate().Week(mode) 457 } 458 459 // YearWeek returns year and week. 460 func (dt Datetime) YearWeek(mode int) (year int, week int) { 461 return dt.ToDate().YearWeek(mode) 462 } 463 464 func (dt Datetime) ToTimestamp(loc *time.Location) Timestamp { 465 return Timestamp(dt.ConvertToGoTime(loc).UnixMicro() + unixEpochMicroSecs) 466 } 467 468 func (dt Datetime) SecondMicrosecondStr() string { 469 result := fmt.Sprintf("%02d", dt.Sec()) + "." + fmt.Sprintf("%06d", dt.MicroSec()) 470 return result 471 } 472 473 func (dt Datetime) MinuteMicrosecondStr() string { 474 result := fmt.Sprintf("%02d", dt.Minute()) + ":" + fmt.Sprintf("%02d", dt.Sec()) + "." + fmt.Sprintf("%06d", dt.MicroSec()) 475 return result 476 } 477 478 func (dt Datetime) MinuteSecondStr() string { 479 result := fmt.Sprintf("%02d", dt.Minute()) + ":" + fmt.Sprintf("%02d", dt.Sec()) 480 return result 481 } 482 483 func (dt Datetime) HourMicrosecondStr() string { 484 result := fmt.Sprintf("%2d", dt.Hour()) + ":" + fmt.Sprintf("%02d", dt.Minute()) + ":" + fmt.Sprintf("%02d", dt.Sec()) + "." + fmt.Sprintf("%06d", dt.MicroSec()) 485 return result 486 } 487 488 func (dt Datetime) HourSecondStr() string { 489 result := fmt.Sprintf("%2d", dt.Hour()) + ":" + fmt.Sprintf("%02d", dt.Minute()) + ":" + fmt.Sprintf("%02d", dt.Sec()) 490 return result 491 } 492 493 func (dt Datetime) HourMinuteStr() string { 494 result := fmt.Sprintf("%2d", dt.Hour()) + ":" + fmt.Sprintf("%02d", dt.Minute()) 495 return result 496 } 497 498 func (dt Datetime) DayMicrosecondStr() string { 499 result := fmt.Sprintf("%02d", dt.Day()) + " " + dt.HourMicrosecondStr() 500 return result 501 } 502 503 func (dt Datetime) DaySecondStr() string { 504 result := fmt.Sprintf("%02d", dt.Day()) + " " + dt.HourSecondStr() 505 return result 506 } 507 508 func (dt Datetime) DayMinuteStr() string { 509 result := fmt.Sprintf("%02d", dt.Day()) + " " + dt.HourMinuteStr() 510 return result 511 } 512 513 func (dt Datetime) DayHourStr() string { 514 result := fmt.Sprintf("%02d", dt.Day()) + " " + fmt.Sprintf("%02d", dt.Hour()) 515 return result 516 } 517 518 func (dt Datetime) YearMonthStr() string { 519 result := fmt.Sprintf("%04d", dt.Year()) + " " + fmt.Sprintf("%02d", dt.Month()) 520 return result 521 } 522 523 // date[0001-01-01 00:00:00 to 9999-12-31 23:59:59] 524 func ValidDatetime(year int32, month, day uint8) bool { 525 if year >= MinDatetimeYear && year <= MaxDatetimeYear { 526 if MinMonthInYear <= month && month <= MaxMonthInYear { 527 if day > 0 { 528 if isLeap(year) { 529 return day <= leapYearMonthDays[month-1] 530 } else { 531 return day <= flatYearMonthDays[month-1] 532 } 533 } 534 } 535 } 536 return false 537 } 538 539 func (dt Datetime) SecsSinceUnixEpoch() int64 { 540 return (int64(dt) - unixEpochMicroSecs) / MicroSecsPerSec 541 } 542 543 func (dt Datetime) ToDecimal64() Decimal64 { 544 return Decimal64(int64(dt) - unixEpochMicroSecs) 545 } 546 547 func (dt Datetime) ToDecimal128() Decimal128 { 548 return Decimal128{uint64(int64(dt) - unixEpochMicroSecs), 0} 549 }