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 }