i want to get either a string or a number which:
1. always sorts in time order even if the system reboots
2. no 2 calls by any user or process can ever get the same result
3. is portable across across Windows and all POSIX compliant systems
obviously this depends on the system having capability. if a system only has a one-second clock event, the OS would need to compensate such as by appending a call counter. does Python provide such a thing? if this can be done by some amount of user space C code that is portable enough (system test code is OK) then i would expect there to be a way to do it in Python.
i have been using int(time.time()*3906250) on recent Linux. that number (10**9/2**8) shifts the significant bits in floating point time that Linux provides to be whole numbers. i do not have Windows to test on.
A few years ago I wrote a
code snippet to generate unique words based on computer time, this could perhaps give you some ideas.
#!/usr/bin/env python
# -*-coding: utf8-*-
# Title: newword.py
# Author: Gribouillis for the python forum at www.daniweb.com
# Created: 2015 March 21
# License: Public Domain
# Use this code freely.
# History:
# 0.3.1 bugfix
# 0.3.0 The 3 first letters of words now indicate the day the word
# was created. Words can now have more than 11 characters
# (currently 3 + 11 = 14) which should allow a better time
# granularity if a faster machine is used. Generated words
# will be ordered until year 2354
# 0.2.2 bugfix
# 0.2.1 sort letters to generate ordered words
# 0.2.0 bugfix for python 3
# 0.1.0 first version
'''This module defines a function to generate unique words
'''
from __future__ import (absolute_import, division,
print_function, unicode_literals)
__version__ = '0.3.1'
class new_word(object):
from struct import pack, unpack
from string import ascii_letters
ascii_letters = str('').join(sorted(ascii_letters))
import time
from datetime import datetime
origin = datetime(year=1970, month=1, day=1)
day_length = 24 * 3600.0
if hasattr(time, 'perf_counter'):
perf_counter = time.perf_counter
else:
import timeit
perf_counter = timeit.default_timer
def int_from_float(self, x):
u, v = self.unpack(">LL", self.pack(">d", x))
return (u << 32) | v
def __init__(self):
self.day_init()
self.result = ''
def day_init(self):
now = self.datetime.now()
today = self.datetime(year=now.year, month=now.month, day=now.day)
days = (today - self.origin).days
self.prefix = self.days_prefix(days)
self.offset = (now - today).total_seconds()
self.zero = self.perf_counter()
def days_prefix(self, n):
L = [None, None, None]
for i in range(3):
n, r = divmod(n, 52)
L[2-i] = self.ascii_letters[r]
return ''.join(L)
def new_int(self):
while True:
c = self.perf_counter()
offset = self.offset + (c - self.zero)
if offset > self.day_length:
self.day_init()
elif offset > self.offset:
self.zero = c
self.offset = offset
return self.int_from_float(offset)
def gen_letter(self):
ivalue = self.new_int()
for i in range(11):
ivalue, r = divmod(ivalue, 52)
yield self.ascii_letters[r]
def new_word(self, nchar=14):
"""new_word(nchar=14) --> a new word containing only ascii letters
The word is a unique str instance, based on the machine time. Unlikely
collisions can occur if the computer's clock is reset to a time in
the past.
new in version 0.3.0:
argument nchar indicates maximum number of characters (up to 14)
Example:
>>> new_word()
'GLAgSFyTODBfRd'
Remark:
The output can be reverse-engineered to discover when the
word was generated, according to the generating computer's clock.
"""
while True:
result = (self.prefix + str('').join(self.gen_letter())[::-1])[:nchar]
if result > self.result:
self.result = result
return result
new_word = new_word().new_word
if __name__ == '__main__':
L = []
for i in range(5):
L. append(new_word())
for w in L:
print(w)
""" my output -->
GLAgSGaBhFOkxy
GLAgSGaBhFqvOe
GLAgSGaBhGGbKu
GLAgSGaBhGSVMm
GLAgSGaBhGcKLK
"""
UUID is not sorted in time order...
The time attribute looks sortable
import uuid
for _ in range(10):
print(uuid.uuid1().time)
Output:
138134349496775452
138134349496825456
138134349496825457
138134349496825458
138134349496825459
138134349496825460
138134349496835458
138134349496835459
138134349496835460
138134349496835461
I would look at time.monotonic() and some related functions. They are designed to monotonically increase even if NTP is used to adjust the time. I don't know how they behave after a system reboot if time is adjusted before the operating system boots.
@
Yoriz I didn't know that. Can you find this time() member again once the UUID instance has been converted to an string ? Does it represent the moment the uuid was created?
>>> import uuid
>>> u = uuid.uuid1()
>>> u
UUID('a6374978-c086-11ea-9c52-0c54a50d680a')
>>> s = str(u)
>>> s
'a6374978-c086-11ea-9c52-0c54a50d680a'
Yes current time is used to create a timestamp in uuid1
https://docs.python.org/3.7/library/uuid...uuid.uuid1 Wrote:uuid.uuid1(node=None, clock_seq=None)
Generate a UUID from a host ID, sequence number, and the current time. If node is not given, getnode() is used to obtain the hardware address. If clock_seq is given, it is used as the sequence number; otherwise a random 14-bit sequence number is chosen.
The following is from inside the def of uuid1
import time
nanoseconds = int(time.time() * 1e9)
# 0x01b21dd213814000 is the number of 100-ns intervals between the
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
timestamp = int(nanoseconds/100) + 0x01b21dd213814000
I would just keep a reference to the generated UUID then you can access the string or the time or any other method/attribute.
Yoriz Wrote:I would just keep a reference to the generated UUID then you can access the string or the time or any other method/attribute.
But you may want to use the UUID for a more persistent purpose. For example you could use it to create a file name or a database entry. If you do so, can you compare old uuid1's based on this time value ?
You can turn the UUID string back into a UUID
import uuid
import datetime
my_uuid = uuid.uuid1()
print(datetime.datetime(1582, 10, 15) +
datetime.timedelta(microseconds=my_uuid.time//10))
ustring = str(my_uuid)
print(f'{type(ustring)} {ustring}')
back_to_uuid = uuid.UUID(ustring)
print(f'{type(my_uuid)} {my_uuid}')
print(f'{type(back_to_uuid)} {back_to_uuid}')
print(my_uuid == back_to_uuid)
print(datetime.datetime(1582, 10, 15) +
datetime.timedelta(microseconds=back_to_uuid.time//10))
Output:
2020-07-07 20:07:14.264177
<class 'str'> 7320546e-c08d-11ea-89fb-0c9d92c73acb
<class 'uuid.UUID'> 7320546e-c08d-11ea-89fb-0c9d92c73acb
<class 'uuid.UUID'> 7320546e-c08d-11ea-89fb-0c9d92c73acb
True
2020-07-07 20:07:14.264177