github.com/matrixorigin/matrixone@v1.2.0/pkg/container/types/timestamp.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  // timestamp data type:
    16  // Question 1: When should I use Datetime, and when should I use Timestamp?
    17  // 		Well, during insertion, the Datetime value will be stored as is(we have some bugs in here now),
    18  //		but for timestamp, the timestamp value passed in will be converted to a UTC timestamp, that is,
    19  //		the value passed in subtract by the server's local Time Zone
    20  // 		so, during retrieval, if the server's time zone is the same as the time zone when the timestamp value got inserted,
    21  //		the timestamp valued retrieved is the same value as the inserted, but if these two timezones are different, you
    22  // 		will get different timestamp value.
    23  //      for example:     		insertion timezone	insertion value					retrieval timezone  retrieval value
    24  // 								UTC+8 				2022-05-01 11:11:11				UTC+9				2022-05-01 12:11:11
    25  //
    26  // So, if your application is geo-distributed cross different timezones, using TIMESTAMP could save you trouble
    27  // you may otherwise encounter by using DATETIME
    28  //
    29  // Internal representation:
    30  // timestamp values are represented using a 64bit integer, which stores the microsecs since January 1, year 1, local time zone, in Gregorian calendar
    31  // the default fractional seconds scale(fsp) for TIMESTAMP is 6, as SQL standard requires.
    32  
    33  package types
    34  
    35  import (
    36  	"fmt"
    37  	"strconv"
    38  	"time"
    39  	"unsafe"
    40  
    41  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    42  )
    43  
    44  var (
    45  	FillString = []string{"", "0", "00", "000", "0000", "00000", "000000", "0000000"}
    46  )
    47  
    48  //const microSecondsDigits = 6
    49  
    50  var TimestampMinValue Timestamp
    51  var TimestampMaxValue Timestamp
    52  
    53  // the range for TIMESTAMP values is '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.999999'.
    54  func init() {
    55  	TimestampMinValue = FromClockUTC(1970, 1, 1, 0, 0, 1, 0)
    56  	TimestampMaxValue = FromClockUTC(9999, 12, 31, 23, 59, 59, 999999)
    57  }
    58  
    59  func (ts Timestamp) String() string {
    60  	dt := Datetime(int64(ts))
    61  	y, m, d, _ := dt.ToDate().Calendar(true)
    62  	hour, minute, sec := dt.Clock()
    63  	msec := int64(ts) % MicroSecsPerSec
    64  	return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d UTC", y, m, d, hour, minute, sec, msec)
    65  }
    66  
    67  // String2 stringify timestamp, including its fractional seconds precision part(fsp)
    68  func (ts Timestamp) String2(loc *time.Location, scale int32) string {
    69  	t := time.UnixMicro(int64(ts) - unixEpochMicroSecs).In(loc)
    70  	y, m, d := t.Date()
    71  	hour, minute, sec := t.Clock()
    72  	if scale > 0 {
    73  		msec := t.Nanosecond() / 1000
    74  		msecInstr := fmt.Sprintf("%06d\n", msec)
    75  		msecInstr = msecInstr[:scale]
    76  
    77  		return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d"+"."+msecInstr, y, m, d, hour, minute, sec)
    78  	}
    79  
    80  	return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", y, m, d, hour, minute, sec)
    81  }
    82  
    83  func (ts Timestamp) Unix() int64 {
    84  	return (int64(ts) - unixEpochMicroSecs) / MicroSecsPerSec
    85  }
    86  
    87  func (ts Timestamp) UnixToFloat() float64 {
    88  	return float64(int64(ts)-unixEpochMicroSecs) / MicroSecsPerSec
    89  }
    90  
    91  func (ts Timestamp) UnixToDecimal64() (Decimal64, error) {
    92  	return Decimal64(int64(ts) - unixEpochMicroSecs), nil
    93  }
    94  
    95  func (ts Timestamp) UnixToDecimal128() (Decimal128, error) {
    96  	return Decimal128{uint64(int64(ts) - unixEpochMicroSecs), 0}, nil
    97  }
    98  
    99  // this scaleTable stores the corresponding microseconds value for a scale
   100  var scaleTable = [...]uint32{1000000, 100000, 10000, 1000, 100, 10, 1}
   101  
   102  var OneSecInMicroSeconds = uint32(1000000)
   103  
   104  func getMsec(msecStr string, scale int32) (uint32, uint32, error) {
   105  	msecs := uint32(0)
   106  	carry := uint32(0)
   107  	msecCarry := uint32(0)
   108  	if len(msecStr) > int(scale) {
   109  		if msecStr[scale] >= '5' && msecStr[scale] <= '9' {
   110  			msecCarry = 1
   111  		} else if msecStr[scale] >= '0' && msecStr[scale] <= '4' {
   112  			msecCarry = 0
   113  		} else {
   114  			return 0, 0, moerr.NewInvalidArgNoCtx("get ms", msecStr)
   115  		}
   116  		msecStr = msecStr[:scale]
   117  	} else if len(msecStr) < int(scale) {
   118  		lengthMsecStr := len(msecStr)
   119  		padZeros := int(scale) - lengthMsecStr
   120  		msecStr = msecStr + FillString[padZeros]
   121  	}
   122  	if len(msecStr) == 0 { // this means the scale is 0
   123  		return 0, msecCarry, nil
   124  	}
   125  	m, err := strconv.ParseUint(msecStr, 10, 32)
   126  	if err != nil {
   127  		return 0, 0, moerr.NewInvalidArgNoCtx("get ms", msecStr)
   128  	}
   129  	msecs = (uint32(m) + msecCarry) * scaleTable[scale]
   130  	if msecs == OneSecInMicroSeconds {
   131  		carry = 1
   132  		msecs = 0
   133  	}
   134  	return msecs, carry, nil
   135  }
   136  
   137  // ParseTimestamp will parse a string to be a Timestamp
   138  // Support Format:
   139  // 1. all the Date value
   140  // 2. yyyy-mm-dd hh:mm:ss(.msec)
   141  // 3. yyyymmddhhmmss(.msec)
   142  func ParseTimestamp(loc *time.Location, s string, scale int32) (Timestamp, error) {
   143  	dt, err := ParseDatetime(s, scale)
   144  	if err != nil {
   145  		return -1, moerr.NewInvalidArgNoCtx("parse timestamp", s)
   146  	}
   147  
   148  	result := dt.ToTimestamp(loc)
   149  	//for issue5305, do not do this check
   150  	//according to mysql, timestamp function actually return a datetime value
   151  	/*
   152  		if result < TimestampMinValue {
   153  			return -1, moerr.NewInvalidArgNoCtx("parse timestamp", s)
   154  		}
   155  	*/
   156  
   157  	return result, nil
   158  }
   159  
   160  type unsafeLoc struct {
   161  	name string
   162  	zone []struct {
   163  		name   string
   164  		offset int
   165  		isDST  bool
   166  	}
   167  	tx []struct {
   168  		when         int64
   169  		index        uint8
   170  		isstd, isutc bool
   171  	}
   172  	extend string
   173  }
   174  
   175  func TimestampToDatetime(loc *time.Location, xs []Timestamp, rs []Datetime) ([]Datetime, error) {
   176  	xsInInt64 := *(*[]int64)(unsafe.Pointer(&xs))
   177  	rsInInt64 := *(*[]int64)(unsafe.Pointer(&rs))
   178  	locPtr := (*unsafeLoc)(unsafe.Pointer(loc))
   179  	if len(locPtr.zone) == 1 {
   180  		offset := int64(locPtr.zone[0].offset) * MicroSecsPerSec
   181  		for i, x := range xsInInt64 {
   182  			rsInInt64[i] = x + offset
   183  		}
   184  	} else {
   185  		for i, x := range xsInInt64 {
   186  			t := time.UnixMicro(x - unixEpochMicroSecs).In(loc)
   187  			_, offset := t.Zone()
   188  			rsInInt64[i] = x + int64(offset)*MicroSecsPerSec
   189  		}
   190  	}
   191  	return rs, nil
   192  }
   193  
   194  func (ts Timestamp) ToDatetime(loc *time.Location) Datetime {
   195  	t := time.UnixMicro(int64(ts) - unixEpochMicroSecs).In(loc)
   196  	_, offset := t.Zone()
   197  	return Datetime(ts) + Datetime(offset)*MicroSecsPerSec
   198  }
   199  
   200  // FromClockUTC gets the utc time value in Timestamp
   201  func FromClockUTC(year int32, month, day, hour, minute, sec uint8, msec uint32) Timestamp {
   202  	days := DateFromCalendar(year, month, day)
   203  	secs := int64(days)*SecsPerDay + int64(hour)*SecsPerHour + int64(minute)*SecsPerMinute + int64(sec)
   204  	return Timestamp(secs*MicroSecsPerSec + int64(msec))
   205  }
   206  
   207  // FromClockZone gets the local time value in Timestamp
   208  func FromClockZone(loc *time.Location, year int32, month, day, hour, minute, sec uint8, msec uint32) Timestamp {
   209  	t := time.Date(int(year), time.Month(month), int(day), int(hour), int(minute), int(sec), int(msec*1000), loc)
   210  	return Timestamp(t.UnixMicro() + unixEpochMicroSecs)
   211  }
   212  
   213  func CurrentTimestamp() Timestamp {
   214  	return Timestamp(time.Now().UnixMicro() + unixEpochMicroSecs)
   215  }
   216  
   217  func ValidTimestamp(timestamp Timestamp) bool {
   218  	return timestamp > TimestampMinValue
   219  }
   220  
   221  func UnixToTimestamp(ts int64) Timestamp {
   222  	return Timestamp(ts*MicroSecsPerSec + unixEpochMicroSecs)
   223  }
   224  
   225  func UnixMicroToTimestamp(ts int64) Timestamp {
   226  	return Timestamp(ts + unixEpochMicroSecs)
   227  }
   228  func UnixNanoToTimestamp(ts int64) Timestamp {
   229  	return Timestamp(ts/nanoSecsPerMicroSec + unixEpochMicroSecs)
   230  }