timebased.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package timebased
  2. import (
  3. "crypto/rand"
  4. "encoding/binary"
  5. "net"
  6. "sync"
  7. "time"
  8. "github.com/golang-plus/uuid/internal"
  9. "github.com/golang-plus/errors"
  10. )
  11. const (
  12. // Intervals bewteen 1/1/1970 and 15/10/1582 (Julain days of 1 Jan 1970 - Julain days of 15 Oct 1582) * 100-Nanoseconds Per Day
  13. intervals = (2440587 - 2299160) * 86400 * 10000000
  14. )
  15. var (
  16. lastGenerated time.Time // last generated time
  17. clockSequence uint16 // clock sequence for same tick
  18. nodeID []byte // node id (MAC Address)
  19. locker sync.Mutex // global lock
  20. )
  21. // NewUUID returns a new time-based uuid.
  22. func NewUUID() ([]byte, error) {
  23. // Get and release a global lock
  24. locker.Lock()
  25. defer locker.Unlock()
  26. uuid := make([]byte, 16)
  27. // get timestamp
  28. now := time.Now().UTC()
  29. timestamp := uint64(now.UnixNano()/100) + intervals // get timestamp
  30. if !now.After(lastGenerated) {
  31. clockSequence++ // last generated time known, then just increment clock sequence
  32. } else {
  33. b := make([]byte, 2)
  34. _, err := rand.Read(b)
  35. if err != nil {
  36. return nil, errors.Wrap(err, "could not generate clock sequence")
  37. }
  38. clockSequence = uint16(int(b[0])<<8 | int(b[1])) // set to a random value (network byte order)
  39. }
  40. lastGenerated = now // remember the last generated time
  41. timeLow := uint32(timestamp & 0xffffffff)
  42. timeMiddle := uint16((timestamp >> 32) & 0xffff)
  43. timeHigh := uint16((timestamp >> 48) & 0xfff)
  44. // network byte order(BigEndian)
  45. binary.BigEndian.PutUint32(uuid[0:], timeLow)
  46. binary.BigEndian.PutUint16(uuid[4:], timeMiddle)
  47. binary.BigEndian.PutUint16(uuid[6:], timeHigh)
  48. binary.BigEndian.PutUint16(uuid[8:], clockSequence)
  49. // get node id(mac address)
  50. if nodeID == nil {
  51. interfaces, err := net.Interfaces()
  52. if err != nil {
  53. return nil, errors.Wrap(err, "could not get network interfaces")
  54. }
  55. for _, i := range interfaces {
  56. if len(i.HardwareAddr) >= 6 {
  57. nodeID = make([]byte, 6)
  58. copy(nodeID, i.HardwareAddr)
  59. break
  60. }
  61. }
  62. if nodeID == nil {
  63. nodeID = make([]byte, 6)
  64. _, err := rand.Read(nodeID)
  65. if err != nil {
  66. return nil, errors.Wrap(err, "could not generate node id")
  67. }
  68. }
  69. }
  70. copy(uuid[10:], nodeID)
  71. // set version(v1)
  72. internal.SetVersion(uuid, internal.VersionTimeBased)
  73. // set layout(RFC4122)
  74. internal.SetLayout(uuid, internal.LayoutRFC4122)
  75. return uuid, nil
  76. }