func (t Time) ToPackedUint() (uint64, error) {
	tm := t.Time
	if t.IsZero() {
		return 0, nil
	}
	year, month, day := tm.Year(), tm.Month(), tm.Day()
	hour, minute, sec := tm.Hour(), tm.Minute(), tm.Second()
	ymd := uint64(((year*13 + month) << 5) | day)
	hms := uint64(hour<<12 | minute<<6 | sec)
	micro := uint64(tm.Microsecond())
	return ((ymd<<17 | hms) << 24) | micro, nil
}
ToPackedUint encodes Time to a packed uint64 value.

1 bit  0
17 bits year*13+month   (year 0-9999, month 0-12)
5 bits day             (0-31)
5 bits hour            (0-23)
6 bits minute          (0-59)
6 bits second          (0-59)
24 bits microseconds    (0-999999)

Total: 64 bits = 8 bytes

0YYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
package types
import (
	"bytes"
	"fmt"
	"math"
	"regexp"
	"strconv"
	"strings"
	gotime "time"
	"unicode"
	"github.com/pingcap/errors"
	"github.com/pingcap/parser/mysql"
	"github.com/pingcap/parser/terror"
	"github.com/pingcap/tidb/sessionctx/stmtctx"
	log "github.com/sirupsen/logrus"
)
var (
	ErrInvalidTimeFormat      = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "invalid time format: '%v'")
	ErrInvalidYearFormat      = errors.New("invalid year format")
	ErrInvalidYear            = errors.New("invalid year")
	ErrZeroDate               = errors.New("datetime zero in date")
	ErrIncorrectDatetimeValue = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, "Incorrect datetime value: '%s'")
	ErrTruncatedWrongValue    = terror.ClassTypes.New(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrTruncatedWrongValue])
)
const (
	DateFormat = "2006-01-02"
	TimeFormat = "2006-01-02 15:04:05"
	TimeFSPFormat = "2006-01-02 15:04:05.000000"
)
const (
	MinYear int16 = 1901
	MaxYear int16 = 2155
	MaxDuration int64 = 838*10000 + 59*100 + 59
	MinTime = -gotime.Duration(838*3600+59*60+59) * gotime.Second
	MaxTime = gotime.Duration(838*3600+59*60+59) * gotime.Second
	ZeroDatetimeStr = "0000-00-00 00:00:00"
	ZeroDateStr = "0000-00-00"
	TimeMaxHour = 838
	TimeMaxMinute = 59
	TimeMaxSecond = 59
	TimeMaxValue = TimeMaxHour*10000 + TimeMaxMinute*100 + TimeMaxSecond
	TimeMaxValueSeconds = TimeMaxHour*3600 + TimeMaxMinute*60 + TimeMaxSecond
)
var (
	ZeroDuration = Duration{Duration: gotime.Duration(0), Fsp: DefaultFsp}
	ZeroTime = MysqlTime{}
	ZeroDatetime = Time{
		Time: ZeroTime,
		Type: mysql.TypeDatetime,
		Fsp:  DefaultFsp,
	}
	ZeroTimestamp = Time{
		Time: ZeroTime,
		Type: mysql.TypeTimestamp,
		Fsp:  DefaultFsp,
	}
	ZeroDate = Time{
		Time: ZeroTime,
		Type: mysql.TypeDate,
		Fsp:  DefaultFsp,
	}
)
var (
	MinDatetime = FromDate(1000, 1, 1, 0, 0, 0, 0)
	MaxDatetime = FromDate(9999, 12, 31, 23, 59, 59, 999999)
	BoundTimezone = gotime.UTC
	MinTimestamp = Time{
		Time: FromDate(1970, 1, 1, 0, 0, 1, 0),
		Type: mysql.TypeTimestamp,
		Fsp:  DefaultFsp,
	}
	MaxTimestamp = Time{
		Time: FromDate(2038, 1, 19, 3, 14, 7, 999999),
		Type: mysql.TypeTimestamp,
		Fsp:  DefaultFsp,
	}
	WeekdayNames = []string{
		"Monday",
		"Tuesday",
		"Wednesday",
		"Thursday",
		"Friday",
		"Saturday",
		"Sunday",
	}
	MonthNames = []string{
		"January", "February",
		"March", "April",
		"May", "June",
		"July", "August",
		"September", "October",
		"November", "December",
	}
)
func FromGoTime(t gotime.Time) MysqlTime {
	year, month, day := t.Date()
	hour, minute, second := t.Clock()
	microsecond := (t.Nanosecond() + 500) / 1000
	return FromDate(year, int(month), day, hour, minute, second, microsecond)
}
func FromDate(year int, month int, day int, hour int, minute int, second int, microsecond int) MysqlTime {
	return MysqlTime{
		uint16(year),
		uint8(month),
		uint8(day),
		hour,
		uint8(minute),
		uint8(second),
		uint32(microsecond),
	}
}
func (t Time) Clock() (hour int, minute int, second int) {
	return t.Time.Hour(), t.Time.Minute(), t.Time.Second()
}
type Time struct {
	Time MysqlTime
	Type uint8
	Fsp int
}
func MaxMySQLTime(fsp int) Time {
	return Time{Time: FromDate(0, 0, 0, TimeMaxHour, TimeMaxMinute, TimeMaxSecond, 0), Type: mysql.TypeDuration, Fsp: fsp}
}
func CurrentTime(tp uint8) Time {
	return Time{Time: FromGoTime(gotime.Now()), Type: tp, Fsp: 0}
}
func (t *Time) ConvertTimeZone(from, to *gotime.Location) error {
	if !t.IsZero() {
		raw, err := t.Time.GoTime(from)
		if err != nil {
			return errors.Trace(err)
		}
		converted := raw.In(to)
		t.Time = FromGoTime(converted)
	}
	return nil
}
func (t Time) String() string {
	if t.Type == mysql.TypeDate {
		str, err := t.DateFormat("%Y-%m-%d")
		terror.Log(errors.Trace(err))
		return str
	}
	str, err := t.DateFormat("%Y-%m-%d %H:%i:%s")
	terror.Log(errors.Trace(err))
	if t.Fsp > 0 {
		tmp := fmt.Sprintf(".%06d", t.Time.Microsecond())
		str = str + tmp[:1+t.Fsp]
	}
	return str
}
func (t Time) IsZero() bool {
	return compareTime(t.Time, ZeroTime) == 0
}
func (t Time) InvalidZero() bool {
	return t.Time.Month() == 0 || t.Time.Day() == 0
}
const numberFormat = "%Y%m%d%H%i%s"
const dateFormat = "%Y%m%d"
func (t Time) ToNumber() *MyDecimal {
	if t.IsZero() {
		return &MyDecimal{}
	}
	var tfStr string
	if t.Type == mysql.TypeDate {
		tfStr = dateFormat
	} else {
		tfStr = numberFormat
	}
	s, err := t.DateFormat(tfStr)
	if err != nil {
		log.Error("Fatal: never happen because we've control the format!")
	}
	if t.Fsp > 0 {
		s1 := fmt.Sprintf("%s.%06d", s, t.Time.Microsecond())
		s = s1[:len(s)+t.Fsp+1]
	}
	dec := new(MyDecimal)
	err = dec.FromString([]byte(s))
	terror.Log(errors.Trace(err))
	return dec
}
func (t Time) Convert(sc *stmtctx.StatementContext, tp uint8) (Time, error) {
	if t.Type == tp || t.IsZero() {
		return Time{Time: t.Time, Type: tp, Fsp: t.Fsp}, nil
	}
	t1 := Time{Time: t.Time, Type: tp, Fsp: t.Fsp}
	err := t1.check(sc)
	return t1, errors.Trace(err)
}
func (t Time) ConvertToDuration() (Duration, error) {
	if t.IsZero() {
		return ZeroDuration, nil
	}
	hour, minute, second := t.Clock()
	frac := t.Time.Microsecond() * 1000
	d := gotime.Duration(hour*3600+minute*60+second)*gotime.Second + gotime.Duration(frac)
	return Duration{Duration: d, Fsp: t.Fsp}, nil
}
func (t Time) Compare(o Time) int {
	return compareTime(t.Time, o.Time)
}
func compareTime(a, b MysqlTime) int {
	ta := datetimeToUint64(a)
	tb := datetimeToUint64(b)
	switch {
	case ta < tb:
		return -1
	case ta > tb:
		return 1
	}
	switch {
	case a.Microsecond() < b.Microsecond():
		return -1
	case a.Microsecond() > b.Microsecond():
		return 1
	}
	return 0
}
func (t Time) CompareString(sc *stmtctx.StatementContext, str string) (int, error) {
	o, err := ParseTime(sc, str, t.Type, MaxFsp)
	if err != nil {
		return 0, errors.Trace(err)
	}
	return t.Compare(o), nil
}
func roundTime(t gotime.Time, fsp int) gotime.Time {
	d := gotime.Duration(math.Pow10(9 - fsp))
	return t.Round(d)
}
func (t Time) RoundFrac(sc *stmtctx.StatementContext, fsp int) (Time, error) {
	if t.Type == mysql.TypeDate || t.IsZero() {
		return t, nil
	}
	fsp, err := CheckFsp(fsp)
	if err != nil {
		return t, errors.Trace(err)
	}
	if fsp == t.Fsp {
		return t, nil
	}
	var nt MysqlTime
	if t1, err := t.Time.GoTime(sc.TimeZone); err == nil {
		t1 = roundTime(t1, fsp)
		nt = FromGoTime(t1)
	} else {
		hour, minute, second, microsecond := t.Time.Hour(), t.Time.Minute(), t.Time.Second(), t.Time.Microsecond()
		t1 := gotime.Date(1, 1, 1, hour, minute, second, microsecond*1000, gotime.Local)
		t2 := roundTime(t1, fsp)
		hour, minute, second = t2.Clock()
		microsecond = t2.Nanosecond() / 1000
		if t2.Day()-1 > 0 {
			return t, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(t.String()))
		}
		nt = FromDate(t.Time.Year(), t.Time.Month(), t.Time.Day(), hour, minute, second, microsecond)
	}
	return Time{Time: nt, Type: t.Type, Fsp: fsp}, nil
}
func GetFsp(s string) (fsp int) {
	fsp = len(s) - strings.LastIndex(s, ".") - 1
	if fsp == len(s) {
		fsp = 0
	} else if fsp > 6 {
		fsp = 6
	}
	return
}
func RoundFrac(t gotime.Time, fsp int) (gotime.Time, error) {
	_, err := CheckFsp(fsp)
	if err != nil {
		return t, errors.Trace(err)
	}
	return t.Round(gotime.Duration(math.Pow10(9-fsp)) * gotime.Nanosecond), nil
}