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