Python Forum
system-wide unique incrementing string or number
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
system-wide unique incrementing string or number
#1
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.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#2
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
"""
Reply
#3
Maybe you could use UUID
Reply
#4
UUID is not sorted in time order...
Reply
#5
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
Reply
#6
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.
Gribouillis likes this post
Reply
#7
@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'
Reply
#8
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.
Reply
#9
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 ?
Reply
#10
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
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  convert a string to a number Naheed 7 4,442 Apr-26-2021, 08:08 AM
Last Post: Naheed
  find unique string Skaperen 1 1,739 Nov-06-2019, 06:42 AM
Last Post: perfringo
  Forum-wide Competition: Rock Paper Scissors [Meta-thread] nilamo 10 10,169 Oct-13-2016, 10:41 PM
Last Post: ichabod801

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020