{"id":13815081,"url":"https://github.com/luismedel/tsid-python","last_synced_at":"2025-10-12T01:45:06.372Z","repository":{"id":152202260,"uuid":"625050176","full_name":"luismedel/tsid-python","owner":"luismedel","description":"Time-Sorted Unique Identifiers (TSID) for Python","archived":false,"fork":false,"pushed_at":"2024-07-14T17:07:55.000Z","size":57,"stargazers_count":37,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-26T07:43:45.750Z","etag":null,"topics":["id","snowflake","snowflake-id","snowflake-twitter","tsid","twitter-snowflake","uuid","uuidv7"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luismedel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-07T23:40:12.000Z","updated_at":"2025-08-29T03:36:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"ac1a6cfc-4dfb-4576-873e-205b8d626cb5","html_url":"https://github.com/luismedel/tsid-python","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/luismedel/tsid-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luismedel%2Ftsid-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luismedel%2Ftsid-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luismedel%2Ftsid-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luismedel%2Ftsid-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luismedel","download_url":"https://codeload.github.com/luismedel/tsid-python/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luismedel%2Ftsid-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279009795,"owners_count":26084648,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["id","snowflake","snowflake-id","snowflake-twitter","tsid","twitter-snowflake","uuid","uuidv7"],"created_at":"2024-08-04T04:02:55.650Z","updated_at":"2025-10-12T01:45:06.322Z","avatar_url":"https://github.com/luismedel.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# tsid-python\n\nA Python library for generating Time-Sorted Unique Identifiers (TSID) as defined in \u003chttps://github.com/f4b6a3/tsid-creator\u003e.\n\nThis library is a port of the original Java code by [Fabio Lima](https://github.com/fabiolimace).\n\n## Installation\n\n```bash\npip install tsidpy\n```\n\n## What is a TSID?\n\nThe term TSID stands for (roughly) Time-Sorted ID. A TSID is a value that is formed by its creation time along with a random value.\n\nIt brings together ideas from [Twitter's Snowflake](https://github.com/twitter-archive/snowflake/tree/snowflake-2010) and [ULID Spec](https://github.com/ulid/spec).\n\nIn summary:\n\n- Sorted by generation time.\n- Can be stored as a 64-bit integer.\n- Can be stored as a 13-char len string.\n- String format is encoded to [Crockford's base32](https://www.crockford.com/base32.html).\n- String format is URL safe, case insensitive and has no hyphens.\n- Shorter than other unique identifiers, like [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier), [ULID](https://github.com/ulid/spec) and [KSUID](https://github.com/segmentio/ksuid).\n\n## TSID Structure\n\nA TSID has 2 components:\n\n1. A time component (42 bits), consisting in the elapsed milliseconds since `2020-01-01 00:00:00 UTC` (this epoch can be configured)\n2. A _random_ component (22 bits), containing 2 sub-parts:\n\n    - A node identifier (can use 0 to 20 bits)\n    - A counter (can use 2 to 22 bits)\n\n    \u003e Note: The counter length depends on the node identifier length.\n    \u003e\n    \u003e For example, if we use 10 bits for the node representation:\n    \u003e\n    \u003e - The counter is limited to 12 bits.\n    \u003e - The maximum node value is `2^10-1 = 1023`\n    \u003e - The maximum counter value is `2^12-1 = 4095`, so the maximum TSIDs that can be generated _per millisecond_ is `4096`.\n\nThis is the default TSID structure:\n\n```text\n                                            adjustable\n                                           \u003c----------\u003e\n|------------------------------------------|----------|------------|\n       time (msecs since 2020-01-01)           node      counter\n                42 bits                       10 bits    12 bits\n\n- time:    2^42 = ~69 to ~139 years with adjustable epoch (see notes below)\n- node:    up to 2^20 values with adjustable bits.\n- counter: 2^2..2^22 with adjustable bits and randomized values every millisecond.\n```\n\n\u003e Notes:\n\u003e\n\u003e - The time component can be used for ~69 years if stored in a `SIGNED 64-bit` integer field (41 usable bits) or ~139 years if stored in a `UNSIGNED 64-bit` integer field (42 usable bits).\n\u003e - By default, new TSID generators use 10 bits for the node identifier and 12 bits to the counter. It's possible to adjust the node identifier length to a value between 0 and 20.\n\u003e - The time component can be 1 ms or more ahead of the system time when necessary to maintain monotonicity and generation speed.\n\n### Node identifier\n\nThe simplest way to avoid collisions is to make sure that each generator has an exclusive node ID.\n\nThe node ID can be passed to the `TSIDGenerator` constructor. If no node ID is passed, the generator will use a random value.\n\n### Recommended readings\n\n- [The best UUID type for a database Primary Key](https://vladmihalcea.com/uuid-database-primary-key/)\n- [The primary key dilemma: ID vs UUID and some practical solutions](https://fillumina.wordpress.com/2023/02/06/the-primary-key-dilemma-id-vs-uuid-and-some-practical-solutions/)\n- [Primary keys in the DB - what to use? ID vs UUID or is there something else?](https://www.linkedin.com/pulse/primary-keys-db-what-use-id-vs-uuid-something-else-lucas-persson)\n\nRelated with the [original library](https://github.com/f4b6a3/tsid-creator):\n\n- [FAQ wiki page](https://github.com/f4b6a3/tsid-creator/wiki)\n- [Javadocs](https://javadoc.io/doc/com.github.f4b6a3/tsid-creator)\n- [How to not use TSID factories](https://fillumina.wordpress.com/2023/01/19/how-to-not-use-tsid-factories/)\n- [The best way to generate a TSID entity identifier with JPA and Hibernate](https://vladmihalcea.com/tsid-identifier-jpa-hibernate/)\n\n## Basic usage\n\nCreate a TSID:\n\n```python\nfrom tsidpy import TSID\n\ntsid: TSID = TSID.create()\n```\n\nCreate a TSID as an `int`:\n\n```python\n\u003e\u003e\u003e TSID.create().number\n432511671823499267\n```\n\nCreate a TSID as a `str`:\n\n```python\n\u003e\u003e\u003e str(TSID.create())\n'0C04Q2BR40003'\n```\n\nCreate a TSID as an hexadecimal `str`:\n\n```python\n\u003e\u003e\u003e TSID.create().to_string('x')\n'06009712f0400003'\n```\n\n\u003e Note: TSID generators are [thread-safe](https://en.wikipedia.org/wiki/Thread_safety).\n\n### TSID as int\n\nThe `TSID::number` property simply unwraps the internal `int` value of a TSID.\n\n```python\n\u003e\u003e\u003e from tsidpy import TSID\n\u003e\u003e\u003e TSID.create(432511671823499267).number\n432511671823499267\n```\n\nSequence of TSIDs:\n\n```text\n38352658567418867\n38352658567418868\n38352658567418869\n38352658567418870\n38352658567418871\n38352658567418872\n38352658567418873\n38352658567418874\n38352658573940759 \u003c millisecond changed\n38352658573940760\n38352658573940761\n38352658573940762\n38352658573940763\n38352658573940764\n38352658573940765\n38352658573940766\n         ^      ^ look\n                                   \n|--------|------|\n   time   random\n```\n\n### TSID as str\n\nThe `TSID::to_string()` method encodes a TSID as a [Crockford's base 32](https://www.crockford.com/base32.html) string. The returned string is 13 characters long.\n\n```python\n\u003e\u003e\u003e from tsidpy import TSID\n\u003e\u003e\u003e tsid: str = TSID.create().to_string()\n'0C04Q2BR40004'\n```\n\nOr, alternatively:\n\n```python\n\u003e\u003e\u003e tsid: str = str(TSID.create())\n'0C04Q2BR40004'\n```\n\nSequence of TSID strings:\n\n```text\n01226N0640J7K\n01226N0640J7M\n01226N0640J7N\n01226N0640J7P\n01226N0640J7Q\n01226N0640J7R\n01226N0640J7S\n01226N0640J7T\n01226N0693HDA \u003c millisecond changed\n01226N0693HDB\n01226N0693HDC\n01226N0693HDD\n01226N0693HDE\n01226N0693HDF\n01226N0693HDG\n01226N0693HDH\n        ^   ^ look\n                                   \n|-------|---|\n   time random\n```\n\nThe string format can be useful for languages that store numbers in [IEEE 754 double-precision binary floating-point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format), such as [Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number).\n\n### More Examples\n\nCreate a TSID using the default generator:\n\n```python\nfrom tsidpy import TSID\n\ntsid: TSID = TSID.create()\n```\n\n---\n\nCreate a TSID from a canonical string (13 chars):\n\n```python\nfrom tsidpy import TSID\n\ntsid: TSID = TSID.from_string('0123456789ABC')\n```\n\n---\n\nConvert a TSID into a canonical string in lower case:\n\n```python\n\u003e\u003e\u003e tsid.to_string('s')\n'0123456789abc'\n```\n\n---\n\nGet the creation `timestamp` of a TSID:\n\n```python\n\u003e\u003e\u003e tsid.timestamp\n1680948418241.0  # datetime.datetime(2023, 4, 8, 12, 6, 58, 241000)\n```\n\n---\n\nEncode a TSID to base-62:\n\n```python\n\u003e\u003e\u003e tsid.to_string('z')\n'0T5jFDIkmmy'\n```\n\n---\n\nA `TSIDGenerator` that creates TSIDs similar to [Twitter Snowflakes](https://github.com/twitter-archive/snowflake):\n\n- Twitter snowflakes use 10 bits for node id: 5 bits for datacenter ID (max 31) and 5 bits for worker ID (max 31)\n- Epoch starts on `2010-11-04T01:42:54.657Z`\n- Counter uses 12 bits and starts at `0` (max: 4095 values per millisecond)\n\n```python\nfrom tsidpy import TSID, TSIDGenerator\n\ndatacenter: int = 1\nworker: int = 1\nnode: int = datacenter \u003c\u003c 5 | worker\nepoch: datetime = datetime.fromisoformat('2010-11-04T01:42:54.657Z')\n\ntwitter_generator: TSIDGenerator = TSIDGenerator(node=node, node_bits=10,\n                                                 epoch=epoch.timestamp() * 1000,\n                                                 random_fn=lambda n: 0)\n\n# use the generator\ntsid: TSID = twitter_generator.create()\n```\n\n---\n\nA `TSIDGenerator` that creates TSIDs similar to [Discord Snowflakes](https://discord.com/developers/docs/reference#snowflakes):\n\n- Discord snowflakes use 10 bits for node id: 5 bits for worker ID (max 31) and 5 bits for process ID (max 31)\n- Epoch starts on `2015-01-01T00:00:00.000Z`\n- Counter uses 12 bits and starts at a random value.\n\n```python\nfrom tsidpy import TSID, TSIDGenerator\n\nworker: int = 1\nprocess: int = 1\nnode: int = worker \u003c\u003c 5 | process\nepoch: datetime = datetime.fromisoformat(\"2015-01-01T00:00:00.000Z\")\n\ndiscord_generator: TSIDGenerator = TSIDGenerator(node=node, node_bits=10,\n                                                 epoch=epoch.timestamp() * 1000)\n\n# use the generator\ntsid: TSID = discord_generator.create()\n```\n\n---\n\nMake `TSID.create()` to use the previous Discord generator:\n\n```python\n\nTSID.set_default_generator(discord_generator)\n\n# at this point, you can use the default TSID.create()\ntsid: TSID = TSID.create()\n\n# or the generator\ntsid: TSID = discord_generator.create()\n```\n\n---\n\n### A note about node id and node bits\n\nWhen creating a `TSIDGenerator`, remember you can't use a node id greater than `2^node_bits - 1`. For example, if you need to use a node id greater than 7, you need to use more than 3 bits for the node id:\n\n```python\nfrom tsidpy import TSIDGenerator\n\ngen0 = TSIDGenerator(node=0, node_bis=3)  # ok\ngen1 = TSIDGenerator(node=1, node_bis=3)  # ok\n...\ngen7 = TSIDGenerator(node=7, node_bis=3)  # ok\n\n# error: can't represent 8 with 3 bits\ngen8 = TSIDGenerator(node=8, node_bis=3)\n```\n\n## Other ports, forks and OSS\n\nPorts, forks, implementations and other OSS\n------------------------------------------------------\n\nPorts, forks and implementations:\n\n| Language | Name |\n| -------- | ---- |\n| Go       | [vishal-bihani/go-tsid](https://github.com/vishal-bihani/go-tsid) |\n| Java     | [vladmihalcea/hypersistence-tsid](https://github.com/vladmihalcea/hypersistence-tsid) |\n| Java     | [vincentdaogithub/tsid](https://github.com/vincentdaogithub/tsid) |\n| .NET     | [kgkoutis/TSID.Creator.NET](https://github.com/kgkoutis/TSID.Creator.NET) |\n| PHP      | [odan/tsid](https://github.com/odan/tsid) |\n| Python   | [luismedel/tsid-python](https://github.com/luismedel/tsid-python) |\n| Rust     | [jakudlaty/tsid](https://github.com/jakudlaty/tsid)\n| TypeScript  | [yubintw/tsid-ts](https://github.com/yubinTW/tsid-ts) |\n\nOther OSS:\n\n| Language | Name |\n| -------- | ---- |\n| Java     | [fillumina/id-encryptor](https://github.com/fillumina/id-encryptor) |\n| .NET     | [ullmark/hashids.net](https://github.com/ullmark/hashids.net) |\n\n## License\n\nThis library is Open Source software released under the [MIT license](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluismedel%2Ftsid-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluismedel%2Ftsid-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluismedel%2Ftsid-python/lists"}