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  }