exonware-xwsystem 0.1.0.1__py3-none-any.whl → 0.1.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exonware/__init__.py +2 -1
- exonware/conf.py +2 -2
- exonware/xwsystem/__init__.py +115 -43
- exonware/xwsystem/base.py +30 -0
- exonware/xwsystem/caching/__init__.py +39 -13
- exonware/xwsystem/caching/base.py +24 -6
- exonware/xwsystem/caching/bloom_cache.py +2 -2
- exonware/xwsystem/caching/cache_manager.py +2 -1
- exonware/xwsystem/caching/conditional.py +2 -2
- exonware/xwsystem/caching/contracts.py +85 -139
- exonware/xwsystem/caching/decorators.py +6 -19
- exonware/xwsystem/caching/defs.py +2 -1
- exonware/xwsystem/caching/disk_cache.py +2 -1
- exonware/xwsystem/caching/distributed.py +2 -1
- exonware/xwsystem/caching/errors.py +2 -1
- exonware/xwsystem/caching/events.py +110 -27
- exonware/xwsystem/caching/eviction_strategies.py +2 -2
- exonware/xwsystem/caching/external_caching_python.py +701 -0
- exonware/xwsystem/caching/facade.py +253 -0
- exonware/xwsystem/caching/factory.py +300 -0
- exonware/xwsystem/caching/fluent.py +14 -12
- exonware/xwsystem/caching/integrity.py +21 -6
- exonware/xwsystem/caching/lfu_cache.py +2 -1
- exonware/xwsystem/caching/lfu_optimized.py +18 -6
- exonware/xwsystem/caching/lru_cache.py +7 -4
- exonware/xwsystem/caching/memory_bounded.py +2 -2
- exonware/xwsystem/caching/metrics_exporter.py +2 -2
- exonware/xwsystem/caching/observable_cache.py +2 -2
- exonware/xwsystem/caching/pluggable_cache.py +2 -2
- exonware/xwsystem/caching/rate_limiter.py +2 -2
- exonware/xwsystem/caching/read_through.py +2 -2
- exonware/xwsystem/caching/secure_cache.py +81 -28
- exonware/xwsystem/caching/serializable.py +9 -7
- exonware/xwsystem/caching/stats.py +2 -2
- exonware/xwsystem/caching/tagging.py +2 -2
- exonware/xwsystem/caching/ttl_cache.py +4 -3
- exonware/xwsystem/caching/two_tier_cache.py +6 -3
- exonware/xwsystem/caching/utils.py +30 -12
- exonware/xwsystem/caching/validation.py +2 -2
- exonware/xwsystem/caching/warming.py +6 -3
- exonware/xwsystem/caching/write_behind.py +15 -6
- exonware/xwsystem/config/__init__.py +11 -17
- exonware/xwsystem/config/base.py +5 -5
- exonware/xwsystem/config/contracts.py +93 -153
- exonware/xwsystem/config/defaults.py +3 -2
- exonware/xwsystem/config/defs.py +3 -2
- exonware/xwsystem/config/errors.py +2 -5
- exonware/xwsystem/config/logging.py +12 -8
- exonware/xwsystem/config/logging_setup.py +3 -2
- exonware/xwsystem/config/performance.py +1 -46
- exonware/xwsystem/config/performance_modes.py +9 -8
- exonware/xwsystem/config/version_manager.py +1 -0
- exonware/xwsystem/config.py +27 -0
- exonware/xwsystem/console/__init__.py +53 -0
- exonware/xwsystem/console/base.py +133 -0
- exonware/xwsystem/console/cli/__init__.py +61 -0
- exonware/xwsystem/{cli → console/cli}/args.py +27 -24
- exonware/xwsystem/{cli → console/cli}/base.py +18 -87
- exonware/xwsystem/{cli → console/cli}/colors.py +15 -13
- exonware/xwsystem/console/cli/console.py +98 -0
- exonware/xwsystem/{cli → console/cli}/contracts.py +51 -69
- exonware/xwsystem/console/cli/defs.py +87 -0
- exonware/xwsystem/console/cli/encoding.py +69 -0
- exonware/xwsystem/{cli → console/cli}/errors.py +8 -3
- exonware/xwsystem/console/cli/event_logger.py +166 -0
- exonware/xwsystem/{cli → console/cli}/progress.py +25 -21
- exonware/xwsystem/{cli → console/cli}/prompts.py +3 -2
- exonware/xwsystem/{cli → console/cli}/tables.py +27 -24
- exonware/xwsystem/console/contracts.py +113 -0
- exonware/xwsystem/console/defs.py +154 -0
- exonware/xwsystem/console/errors.py +34 -0
- exonware/xwsystem/console/event_logger.py +385 -0
- exonware/xwsystem/console/writer.py +132 -0
- exonware/xwsystem/contracts.py +28 -0
- exonware/xwsystem/data_structures/__init__.py +23 -0
- exonware/xwsystem/data_structures/trie.py +34 -0
- exonware/xwsystem/data_structures/union_find.py +144 -0
- exonware/xwsystem/defs.py +17 -0
- exonware/xwsystem/errors.py +23 -0
- exonware/xwsystem/facade.py +62 -0
- exonware/xwsystem/http_client/__init__.py +22 -1
- exonware/xwsystem/http_client/advanced_client.py +8 -5
- exonware/xwsystem/http_client/base.py +3 -2
- exonware/xwsystem/http_client/client.py +7 -4
- exonware/xwsystem/http_client/contracts.py +42 -56
- exonware/xwsystem/http_client/defs.py +2 -1
- exonware/xwsystem/http_client/errors.py +2 -1
- exonware/xwsystem/http_client/facade.py +156 -0
- exonware/xwsystem/io/__init__.py +22 -3
- exonware/xwsystem/io/archive/__init__.py +8 -2
- exonware/xwsystem/io/archive/archive.py +1 -1
- exonware/xwsystem/io/archive/archive_files.py +4 -7
- exonware/xwsystem/io/archive/archivers.py +120 -10
- exonware/xwsystem/io/archive/base.py +4 -5
- exonware/xwsystem/io/archive/codec_integration.py +1 -2
- exonware/xwsystem/io/archive/compression.py +1 -2
- exonware/xwsystem/io/archive/facade.py +263 -0
- exonware/xwsystem/io/archive/formats/__init__.py +2 -3
- exonware/xwsystem/io/archive/formats/brotli_format.py +20 -7
- exonware/xwsystem/io/archive/formats/lz4_format.py +20 -7
- exonware/xwsystem/io/archive/formats/rar.py +11 -5
- exonware/xwsystem/io/archive/formats/sevenzip.py +12 -6
- exonware/xwsystem/io/archive/formats/squashfs_format.py +1 -2
- exonware/xwsystem/io/archive/formats/tar.py +52 -7
- exonware/xwsystem/io/archive/formats/wim_format.py +11 -5
- exonware/xwsystem/io/archive/formats/zip.py +1 -2
- exonware/xwsystem/io/archive/formats/zpaq_format.py +1 -2
- exonware/xwsystem/io/archive/formats/zstandard.py +20 -7
- exonware/xwsystem/io/base.py +119 -115
- exonware/xwsystem/io/codec/__init__.py +4 -2
- exonware/xwsystem/io/codec/base.py +19 -13
- exonware/xwsystem/io/codec/contracts.py +59 -2
- exonware/xwsystem/io/codec/registry.py +67 -21
- exonware/xwsystem/io/common/__init__.py +1 -1
- exonware/xwsystem/io/common/atomic.py +29 -16
- exonware/xwsystem/io/common/base.py +11 -10
- exonware/xwsystem/io/common/lock.py +6 -5
- exonware/xwsystem/io/common/path_manager.py +2 -1
- exonware/xwsystem/io/common/watcher.py +1 -2
- exonware/xwsystem/io/contracts.py +301 -433
- exonware/xwsystem/io/contracts_1.py +1180 -0
- exonware/xwsystem/io/data_operations.py +19 -20
- exonware/xwsystem/io/defs.py +4 -3
- exonware/xwsystem/io/errors.py +3 -2
- exonware/xwsystem/io/facade.py +87 -61
- exonware/xwsystem/io/file/__init__.py +1 -1
- exonware/xwsystem/io/file/base.py +8 -9
- exonware/xwsystem/io/file/conversion.py +2 -3
- exonware/xwsystem/io/file/file.py +61 -18
- exonware/xwsystem/io/file/paged_source.py +8 -8
- exonware/xwsystem/io/file/paging/__init__.py +1 -2
- exonware/xwsystem/io/file/paging/byte_paging.py +4 -5
- exonware/xwsystem/io/file/paging/line_paging.py +2 -3
- exonware/xwsystem/io/file/paging/record_paging.py +2 -3
- exonware/xwsystem/io/file/paging/registry.py +1 -2
- exonware/xwsystem/io/file/source.py +13 -17
- exonware/xwsystem/io/filesystem/__init__.py +1 -1
- exonware/xwsystem/io/filesystem/base.py +1 -2
- exonware/xwsystem/io/filesystem/local.py +3 -4
- exonware/xwsystem/io/folder/__init__.py +1 -1
- exonware/xwsystem/io/folder/base.py +1 -2
- exonware/xwsystem/io/folder/folder.py +16 -7
- exonware/xwsystem/io/indexing/__init__.py +14 -0
- exonware/xwsystem/io/indexing/facade.py +443 -0
- exonware/xwsystem/io/path_parser.py +98 -0
- exonware/xwsystem/io/serialization/__init__.py +21 -3
- exonware/xwsystem/io/serialization/auto_serializer.py +146 -20
- exonware/xwsystem/io/serialization/base.py +84 -34
- exonware/xwsystem/io/serialization/contracts.py +50 -73
- exonware/xwsystem/io/serialization/defs.py +2 -1
- exonware/xwsystem/io/serialization/errors.py +2 -1
- exonware/xwsystem/io/serialization/flyweight.py +154 -7
- exonware/xwsystem/io/serialization/format_detector.py +15 -14
- exonware/xwsystem/io/serialization/formats/__init__.py +8 -5
- exonware/xwsystem/io/serialization/formats/binary/bson.py +15 -6
- exonware/xwsystem/io/serialization/formats/binary/cbor.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/marshal.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/msgpack.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/pickle.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/plistlib.py +5 -5
- exonware/xwsystem/io/serialization/formats/database/dbm.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/shelve.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/sqlite3.py +7 -7
- exonware/xwsystem/io/serialization/formats/tabular/__init__.py +27 -0
- exonware/xwsystem/io/serialization/formats/tabular/base.py +89 -0
- exonware/xwsystem/io/serialization/formats/tabular/csv.py +319 -0
- exonware/xwsystem/io/serialization/formats/tabular/df.py +249 -0
- exonware/xwsystem/io/serialization/formats/tabular/excel.py +291 -0
- exonware/xwsystem/io/serialization/formats/tabular/googlesheets.py +374 -0
- exonware/xwsystem/io/serialization/formats/text/__init__.py +1 -1
- exonware/xwsystem/io/serialization/formats/text/append_only_log.py +5 -7
- exonware/xwsystem/io/serialization/formats/text/configparser.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/csv.py +7 -5
- exonware/xwsystem/io/serialization/formats/text/formdata.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/json.py +27 -18
- exonware/xwsystem/io/serialization/formats/text/json5.py +8 -4
- exonware/xwsystem/io/serialization/formats/text/jsonlines.py +18 -14
- exonware/xwsystem/io/serialization/formats/text/multipart.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/toml.py +8 -6
- exonware/xwsystem/io/serialization/formats/text/xml.py +25 -20
- exonware/xwsystem/io/serialization/formats/text/yaml.py +8 -6
- exonware/xwsystem/io/serialization/parsers/__init__.py +3 -2
- exonware/xwsystem/io/serialization/parsers/base.py +6 -5
- exonware/xwsystem/io/serialization/parsers/hybrid_parser.py +7 -6
- exonware/xwsystem/io/serialization/parsers/msgspec_parser.py +10 -7
- exonware/xwsystem/io/serialization/parsers/orjson_direct_parser.py +7 -6
- exonware/xwsystem/io/serialization/parsers/orjson_parser.py +11 -8
- exonware/xwsystem/io/serialization/parsers/pysimdjson_parser.py +13 -9
- exonware/xwsystem/io/serialization/parsers/rapidjson_parser.py +10 -7
- exonware/xwsystem/io/serialization/parsers/registry.py +11 -10
- exonware/xwsystem/io/serialization/parsers/standard.py +7 -6
- exonware/xwsystem/io/serialization/parsers/ujson_parser.py +10 -7
- exonware/xwsystem/io/serialization/registry.py +4 -4
- exonware/xwsystem/io/serialization/serializer.py +168 -79
- exonware/xwsystem/io/serialization/universal_options.py +367 -0
- exonware/xwsystem/io/serialization/utils/__init__.py +1 -2
- exonware/xwsystem/io/serialization/utils/path_ops.py +5 -6
- exonware/xwsystem/io/source_reader.py +223 -0
- exonware/xwsystem/io/stream/__init__.py +1 -1
- exonware/xwsystem/io/stream/async_operations.py +61 -14
- exonware/xwsystem/io/stream/base.py +1 -2
- exonware/xwsystem/io/stream/codec_io.py +6 -7
- exonware/xwsystem/ipc/__init__.py +1 -0
- exonware/xwsystem/ipc/async_fabric.py +4 -4
- exonware/xwsystem/ipc/base.py +6 -5
- exonware/xwsystem/ipc/contracts.py +41 -66
- exonware/xwsystem/ipc/defs.py +2 -1
- exonware/xwsystem/ipc/errors.py +2 -1
- exonware/xwsystem/ipc/message_queue.py +5 -2
- exonware/xwsystem/ipc/pipes.py +70 -34
- exonware/xwsystem/ipc/process_manager.py +7 -5
- exonware/xwsystem/ipc/process_pool.py +6 -5
- exonware/xwsystem/ipc/shared_memory.py +64 -11
- exonware/xwsystem/monitoring/__init__.py +7 -0
- exonware/xwsystem/monitoring/base.py +11 -8
- exonware/xwsystem/monitoring/contracts.py +86 -144
- exonware/xwsystem/monitoring/defs.py +2 -1
- exonware/xwsystem/monitoring/error_recovery.py +16 -3
- exonware/xwsystem/monitoring/errors.py +2 -1
- exonware/xwsystem/monitoring/facade.py +183 -0
- exonware/xwsystem/monitoring/memory_monitor.py +1 -0
- exonware/xwsystem/monitoring/metrics.py +1 -0
- exonware/xwsystem/monitoring/performance_manager_generic.py +7 -7
- exonware/xwsystem/monitoring/performance_monitor.py +1 -0
- exonware/xwsystem/monitoring/performance_validator.py +1 -0
- exonware/xwsystem/monitoring/system_monitor.py +6 -5
- exonware/xwsystem/monitoring/tracing.py +18 -16
- exonware/xwsystem/monitoring/tracker.py +2 -1
- exonware/xwsystem/operations/__init__.py +5 -50
- exonware/xwsystem/operations/base.py +3 -44
- exonware/xwsystem/operations/contracts.py +25 -15
- exonware/xwsystem/operations/defs.py +1 -1
- exonware/xwsystem/operations/diff.py +5 -4
- exonware/xwsystem/operations/errors.py +1 -1
- exonware/xwsystem/operations/merge.py +6 -4
- exonware/xwsystem/operations/patch.py +5 -4
- exonware/xwsystem/patterns/__init__.py +1 -0
- exonware/xwsystem/patterns/base.py +2 -1
- exonware/xwsystem/patterns/context_manager.py +2 -1
- exonware/xwsystem/patterns/contracts.py +215 -256
- exonware/xwsystem/patterns/defs.py +2 -1
- exonware/xwsystem/patterns/dynamic_facade.py +1 -0
- exonware/xwsystem/patterns/errors.py +2 -4
- exonware/xwsystem/patterns/handler_factory.py +2 -3
- exonware/xwsystem/patterns/import_registry.py +1 -0
- exonware/xwsystem/patterns/object_pool.py +1 -0
- exonware/xwsystem/patterns/registry.py +4 -43
- exonware/xwsystem/plugins/__init__.py +2 -1
- exonware/xwsystem/plugins/base.py +6 -5
- exonware/xwsystem/plugins/contracts.py +94 -158
- exonware/xwsystem/plugins/defs.py +2 -1
- exonware/xwsystem/plugins/errors.py +2 -1
- exonware/xwsystem/py.typed +3 -0
- exonware/xwsystem/query/__init__.py +36 -0
- exonware/xwsystem/query/contracts.py +56 -0
- exonware/xwsystem/query/errors.py +22 -0
- exonware/xwsystem/query/registry.py +128 -0
- exonware/xwsystem/runtime/__init__.py +2 -1
- exonware/xwsystem/runtime/base.py +4 -3
- exonware/xwsystem/runtime/contracts.py +39 -60
- exonware/xwsystem/runtime/defs.py +2 -1
- exonware/xwsystem/runtime/env.py +11 -9
- exonware/xwsystem/runtime/errors.py +2 -1
- exonware/xwsystem/runtime/reflection.py +3 -2
- exonware/xwsystem/security/__init__.py +68 -11
- exonware/xwsystem/security/audit.py +167 -0
- exonware/xwsystem/security/base.py +121 -24
- exonware/xwsystem/security/contracts.py +91 -146
- exonware/xwsystem/security/crypto.py +17 -16
- exonware/xwsystem/security/defs.py +2 -1
- exonware/xwsystem/security/errors.py +2 -1
- exonware/xwsystem/security/facade.py +321 -0
- exonware/xwsystem/security/file_security.py +330 -0
- exonware/xwsystem/security/hazmat.py +11 -8
- exonware/xwsystem/security/monitor.py +372 -0
- exonware/xwsystem/security/path_validator.py +140 -18
- exonware/xwsystem/security/policy.py +357 -0
- exonware/xwsystem/security/resource_limits.py +1 -0
- exonware/xwsystem/security/validator.py +455 -0
- exonware/xwsystem/shared/__init__.py +14 -1
- exonware/xwsystem/shared/base.py +285 -2
- exonware/xwsystem/shared/contracts.py +415 -126
- exonware/xwsystem/shared/defs.py +2 -1
- exonware/xwsystem/shared/errors.py +2 -2
- exonware/xwsystem/shared/xwobject.py +316 -0
- exonware/xwsystem/structures/__init__.py +1 -0
- exonware/xwsystem/structures/base.py +3 -2
- exonware/xwsystem/structures/circular_detector.py +15 -14
- exonware/xwsystem/structures/contracts.py +53 -76
- exonware/xwsystem/structures/defs.py +2 -1
- exonware/xwsystem/structures/errors.py +2 -1
- exonware/xwsystem/structures/tree_walker.py +2 -1
- exonware/xwsystem/threading/__init__.py +21 -4
- exonware/xwsystem/threading/async_primitives.py +6 -5
- exonware/xwsystem/threading/base.py +3 -2
- exonware/xwsystem/threading/contracts.py +87 -143
- exonware/xwsystem/threading/defs.py +2 -1
- exonware/xwsystem/threading/errors.py +2 -1
- exonware/xwsystem/threading/facade.py +175 -0
- exonware/xwsystem/threading/locks.py +1 -0
- exonware/xwsystem/threading/safe_factory.py +1 -0
- exonware/xwsystem/utils/__init__.py +40 -0
- exonware/xwsystem/utils/base.py +22 -21
- exonware/xwsystem/utils/contracts.py +50 -73
- exonware/xwsystem/utils/dt/__init__.py +19 -3
- exonware/xwsystem/utils/dt/base.py +5 -4
- exonware/xwsystem/utils/dt/contracts.py +22 -29
- exonware/xwsystem/utils/dt/defs.py +2 -1
- exonware/xwsystem/utils/dt/errors.py +2 -5
- exonware/xwsystem/utils/dt/formatting.py +88 -2
- exonware/xwsystem/utils/dt/humanize.py +10 -9
- exonware/xwsystem/utils/dt/parsing.py +56 -5
- exonware/xwsystem/utils/dt/timezone_utils.py +2 -24
- exonware/xwsystem/utils/errors.py +2 -4
- exonware/xwsystem/utils/paths.py +1 -0
- exonware/xwsystem/utils/string.py +49 -0
- exonware/xwsystem/utils/test_runner.py +185 -0
- exonware/xwsystem/utils/utils_contracts.py +2 -1
- exonware/xwsystem/utils/web.py +110 -0
- exonware/xwsystem/validation/__init__.py +25 -1
- exonware/xwsystem/validation/base.py +6 -5
- exonware/xwsystem/validation/contracts.py +29 -41
- exonware/xwsystem/validation/data_validator.py +1 -0
- exonware/xwsystem/validation/declarative.py +11 -8
- exonware/xwsystem/validation/defs.py +2 -1
- exonware/xwsystem/validation/errors.py +2 -1
- exonware/xwsystem/validation/facade.py +198 -0
- exonware/xwsystem/validation/fluent_validator.py +22 -19
- exonware/xwsystem/validation/schema_discovery.py +210 -0
- exonware/xwsystem/validation/type_safety.py +2 -1
- exonware/xwsystem/version.py +2 -2
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.4.dist-info}/METADATA +71 -4
- exonware_xwsystem-0.1.0.4.dist-info/RECORD +337 -0
- exonware/xwsystem/cli/__init__.py +0 -43
- exonware/xwsystem/cli/console.py +0 -113
- exonware/xwsystem/cli/defs.py +0 -134
- exonware/xwsystem/conf.py +0 -44
- exonware/xwsystem/security/auth.py +0 -484
- exonware_xwsystem-0.1.0.1.dist-info/RECORD +0 -284
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.4.dist-info}/WHEEL +0 -0
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/base.py
|
|
1
2
|
#exonware/xwsystem/datetime/base.py
|
|
2
3
|
"""
|
|
3
4
|
Company: eXonware.com
|
|
4
5
|
Author: Eng. Muhammad AlShehri
|
|
5
6
|
Email: connect@exonware.com
|
|
6
|
-
Version: 0.1.0.
|
|
7
|
+
Version: 0.1.0.4
|
|
7
8
|
Generation Date: September 04, 2025
|
|
8
9
|
|
|
9
10
|
DateTime module base classes - abstract classes for date/time functionality.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from abc import ABC, abstractmethod
|
|
13
|
-
from typing import Any, Optional
|
|
14
|
+
from typing import Any, Optional
|
|
14
15
|
from datetime import datetime, date, time, timedelta
|
|
15
16
|
from .contracts import DateTimeFormat, TimezoneType, HumanizeStyle
|
|
16
17
|
|
|
@@ -39,7 +40,7 @@ class ADateTimeBase(ABC):
|
|
|
39
40
|
pass
|
|
40
41
|
|
|
41
42
|
@abstractmethod
|
|
42
|
-
def from_timestamp(self, timestamp:
|
|
43
|
+
def from_timestamp(self, timestamp: int | float, timezone: Optional[str] = None) -> datetime:
|
|
43
44
|
"""Create datetime from timestamp."""
|
|
44
45
|
pass
|
|
45
46
|
|
|
@@ -230,7 +231,7 @@ class BaseDateTime(ADateTimeBase):
|
|
|
230
231
|
"""Get current UTC datetime."""
|
|
231
232
|
return datetime.utcnow()
|
|
232
233
|
|
|
233
|
-
def from_timestamp(self, timestamp:
|
|
234
|
+
def from_timestamp(self, timestamp: int | float, timezone: Optional[str] = None) -> datetime:
|
|
234
235
|
"""Create datetime from timestamp."""
|
|
235
236
|
dt = datetime.fromtimestamp(timestamp)
|
|
236
237
|
if timezone:
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/contracts.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.1.0.
|
|
6
|
+
Version: 0.1.0.4
|
|
6
7
|
Generation Date: September 04, 2025
|
|
7
8
|
|
|
8
9
|
DateTime module contracts - interfaces and enums for date/time functionality.
|
|
9
10
|
"""
|
|
10
11
|
|
|
11
|
-
from
|
|
12
|
-
from typing import Any, Optional, Union
|
|
12
|
+
from typing import Any, Optional, Protocol, runtime_checkable
|
|
13
13
|
from datetime import datetime, date, time, timezone
|
|
14
14
|
|
|
15
15
|
# Import enums from types module
|
|
@@ -23,72 +23,65 @@ from .defs import (
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
@runtime_checkable
|
|
27
|
+
class IDateTimeFormatter(Protocol):
|
|
27
28
|
"""Interface for date/time formatting."""
|
|
28
29
|
|
|
29
|
-
@abstractmethod
|
|
30
30
|
def format_datetime(self, dt: datetime, format_type: TimeFormat) -> str:
|
|
31
31
|
"""Format datetime object."""
|
|
32
|
-
|
|
32
|
+
...
|
|
33
33
|
|
|
34
|
-
@abstractmethod
|
|
35
34
|
def format_date(self, d: date, format_type: DateFormat) -> str:
|
|
36
35
|
"""Format date object."""
|
|
37
|
-
|
|
36
|
+
...
|
|
38
37
|
|
|
39
|
-
@abstractmethod
|
|
40
38
|
def format_time(self, t: time, format_type: TimeFormat) -> str:
|
|
41
39
|
"""Format time object."""
|
|
42
|
-
|
|
40
|
+
...
|
|
43
41
|
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
@runtime_checkable
|
|
44
|
+
class IDateTimeParser(Protocol):
|
|
46
45
|
"""Interface for date/time parsing."""
|
|
47
46
|
|
|
48
|
-
@abstractmethod
|
|
49
47
|
def parse_datetime(self, date_string: str, format_type: Optional[TimeFormat] = None) -> datetime:
|
|
50
48
|
"""Parse datetime string."""
|
|
51
|
-
|
|
49
|
+
...
|
|
52
50
|
|
|
53
|
-
@abstractmethod
|
|
54
51
|
def parse_date(self, date_string: str, format_type: Optional[DateFormat] = None) -> date:
|
|
55
52
|
"""Parse date string."""
|
|
56
|
-
|
|
53
|
+
...
|
|
57
54
|
|
|
58
|
-
@abstractmethod
|
|
59
55
|
def parse_time(self, time_string: str, format_type: Optional[TimeFormat] = None) -> time:
|
|
60
56
|
"""Parse time string."""
|
|
61
|
-
|
|
57
|
+
...
|
|
62
58
|
|
|
63
59
|
|
|
64
|
-
|
|
60
|
+
@runtime_checkable
|
|
61
|
+
class IDateTimeHumanizer(Protocol):
|
|
65
62
|
"""Interface for humanizing time differences."""
|
|
66
63
|
|
|
67
|
-
@abstractmethod
|
|
68
64
|
def humanize(self, dt: datetime, reference: Optional[datetime] = None) -> str:
|
|
69
65
|
"""Humanize datetime relative to reference."""
|
|
70
|
-
|
|
66
|
+
...
|
|
71
67
|
|
|
72
|
-
@abstractmethod
|
|
73
68
|
def natural_time(self, dt: datetime, reference: Optional[datetime] = None) -> str:
|
|
74
69
|
"""Get natural time representation."""
|
|
75
|
-
|
|
70
|
+
...
|
|
76
71
|
|
|
77
72
|
|
|
78
|
-
|
|
73
|
+
@runtime_checkable
|
|
74
|
+
class ITimezoneUtils(Protocol):
|
|
79
75
|
"""Interface for timezone utilities."""
|
|
80
76
|
|
|
81
|
-
@abstractmethod
|
|
82
77
|
def get_timezone(self, tz_name: str) -> timezone:
|
|
83
78
|
"""Get timezone object."""
|
|
84
|
-
|
|
79
|
+
...
|
|
85
80
|
|
|
86
|
-
@abstractmethod
|
|
87
81
|
def convert_timezone(self, dt: datetime, target_tz: timezone) -> datetime:
|
|
88
82
|
"""Convert datetime to target timezone."""
|
|
89
|
-
|
|
83
|
+
...
|
|
90
84
|
|
|
91
|
-
@abstractmethod
|
|
92
85
|
def get_local_timezone(self) -> timezone:
|
|
93
86
|
"""Get local timezone."""
|
|
94
|
-
|
|
87
|
+
...
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/defs.py
|
|
2
3
|
#exonware/xwsystem/datetime/types.py
|
|
3
4
|
"""
|
|
4
5
|
Company: eXonware.com
|
|
5
6
|
Author: Eng. Muhammad AlShehri
|
|
6
7
|
Email: connect@exonware.com
|
|
7
|
-
Version: 0.1.0.
|
|
8
|
+
Version: 0.1.0.4
|
|
8
9
|
Generation Date: 07-Sep-2025
|
|
9
10
|
|
|
10
11
|
DateTime types and enums for XWSystem.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/errors.py
|
|
1
2
|
#exonware/xwsystem/datetime/errors.py
|
|
2
3
|
"""
|
|
3
4
|
Company: eXonware.com
|
|
4
5
|
Author: Eng. Muhammad AlShehri
|
|
5
6
|
Email: connect@exonware.com
|
|
6
|
-
Version: 0.1.0.
|
|
7
|
+
Version: 0.1.0.4
|
|
7
8
|
Generation Date: September 04, 2025
|
|
8
9
|
|
|
9
10
|
DateTime module errors - exception classes for date/time functionality.
|
|
@@ -69,7 +70,3 @@ class DateTimeUnderflowError(DateTimeError):
|
|
|
69
70
|
"""Raised when datetime value underflows."""
|
|
70
71
|
pass
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
# Aliases for backward compatibility
|
|
74
|
-
FormatError = DateTimeFormatError
|
|
75
|
-
ParseError = DateTimeParseError
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/formatting.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.1.0.
|
|
6
|
+
Version: 0.1.0.4
|
|
6
7
|
Generation Date: September 04, 2025
|
|
7
8
|
|
|
8
9
|
DateTime formatting utilities for XSystem.
|
|
@@ -134,4 +135,89 @@ def format_relative(dt: datetime) -> str:
|
|
|
134
135
|
minutes = diff.seconds // 60
|
|
135
136
|
return f"{minutes} minutes ago"
|
|
136
137
|
else:
|
|
137
|
-
return "just now"
|
|
138
|
+
return "just now"
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def get_datetime() -> str:
|
|
142
|
+
"""
|
|
143
|
+
Get current datetime as formatted string.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Current datetime in "YYYY-MM-DD HH:MM:SS" format
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
>>> dt_str = get_datetime()
|
|
150
|
+
>>> len(dt_str) == 19
|
|
151
|
+
True
|
|
152
|
+
"""
|
|
153
|
+
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def get_date() -> str:
|
|
157
|
+
"""
|
|
158
|
+
Get current date as formatted string.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Current date in "YYYY-MM-DD" format
|
|
162
|
+
|
|
163
|
+
Examples:
|
|
164
|
+
>>> date_str = get_date()
|
|
165
|
+
>>> len(date_str) == 10
|
|
166
|
+
True
|
|
167
|
+
"""
|
|
168
|
+
return datetime.now().strftime("%Y-%m-%d")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def get_date_from_to_month(year_month: str) -> tuple[str, str]:
|
|
172
|
+
"""
|
|
173
|
+
Get start and end dates for a month.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
year_month: Format "YYYY-MM"
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Tuple of (start_date, end_date) in "YYYY-MM-DD" format
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
>>> start, end = get_date_from_to_month("2025-01")
|
|
183
|
+
>>> start
|
|
184
|
+
'2025-01-01'
|
|
185
|
+
>>> end
|
|
186
|
+
'2025-01-31'
|
|
187
|
+
"""
|
|
188
|
+
import calendar
|
|
189
|
+
|
|
190
|
+
year, month = map(int, year_month.split('-'))
|
|
191
|
+
|
|
192
|
+
# Start of the month is day 1
|
|
193
|
+
start_of_month = f"{year}-{month:02d}-01"
|
|
194
|
+
|
|
195
|
+
# Get the last day of the month
|
|
196
|
+
_, last_day = calendar.monthrange(year, month)
|
|
197
|
+
end_of_month = f"{year}-{month:02d}-{last_day:02d}"
|
|
198
|
+
|
|
199
|
+
return start_of_month, end_of_month
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def calculate_duration_days(date_from: datetime, date_to: datetime) -> float:
|
|
203
|
+
"""
|
|
204
|
+
Calculate duration between two dates in days.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
date_from: Start date
|
|
208
|
+
date_to: End date
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Duration in days (rounded to 2 decimal places)
|
|
212
|
+
|
|
213
|
+
Examples:
|
|
214
|
+
>>> from datetime import datetime, timedelta
|
|
215
|
+
>>> start = datetime(2025, 1, 1)
|
|
216
|
+
>>> end = datetime(2025, 1, 5)
|
|
217
|
+
>>> calculate_duration_days(end, start)
|
|
218
|
+
4.0
|
|
219
|
+
"""
|
|
220
|
+
delta = date_from - date_to
|
|
221
|
+
seconds = delta.total_seconds()
|
|
222
|
+
days = seconds / 86400 # 60 * 60 * 24
|
|
223
|
+
return round(days, 2)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/humanize.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.1.0.
|
|
6
|
+
Version: 0.1.0.4
|
|
6
7
|
Generation Date: September 04, 2025
|
|
7
8
|
|
|
8
9
|
Human-friendly datetime formatting and parsing utilities.
|
|
@@ -10,7 +11,7 @@ Human-friendly datetime formatting and parsing utilities.
|
|
|
10
11
|
|
|
11
12
|
import re
|
|
12
13
|
from datetime import datetime, timedelta
|
|
13
|
-
from typing import Optional
|
|
14
|
+
from typing import Optional
|
|
14
15
|
|
|
15
16
|
from exonware.xwsystem.config.logging_setup import get_logger
|
|
16
17
|
|
|
@@ -85,7 +86,7 @@ def humanize_timedelta(td: timedelta, precision: int = 2, max_units: int = 2) ->
|
|
|
85
86
|
return f"{'-' if negative else ''}{result}"
|
|
86
87
|
|
|
87
88
|
|
|
88
|
-
def humanize_timestamp(timestamp:
|
|
89
|
+
def humanize_timestamp(timestamp: datetime | float,
|
|
89
90
|
reference: Optional[datetime] = None,
|
|
90
91
|
precision: int = 2) -> str:
|
|
91
92
|
"""
|
|
@@ -133,7 +134,7 @@ def humanize_timestamp(timestamp: Union[datetime, float],
|
|
|
133
134
|
return f"{prefix}{human_diff}{suffix}"
|
|
134
135
|
|
|
135
136
|
|
|
136
|
-
def time_ago(timestamp:
|
|
137
|
+
def time_ago(timestamp: datetime | float, precision: int = 2) -> str:
|
|
137
138
|
"""
|
|
138
139
|
Get time ago string from timestamp.
|
|
139
140
|
|
|
@@ -147,7 +148,7 @@ def time_ago(timestamp: Union[datetime, float], precision: int = 2) -> str:
|
|
|
147
148
|
return humanize_timestamp(timestamp, precision=precision)
|
|
148
149
|
|
|
149
150
|
|
|
150
|
-
def time_until(timestamp:
|
|
151
|
+
def time_until(timestamp: datetime | float, precision: int = 2) -> str:
|
|
151
152
|
"""
|
|
152
153
|
Get time until string from timestamp.
|
|
153
154
|
|
|
@@ -180,7 +181,7 @@ def format_relative_time(dt: datetime, relative_to: Optional[datetime] = None, p
|
|
|
180
181
|
return humanize_timestamp(dt, reference=relative_to, precision=precision)
|
|
181
182
|
|
|
182
183
|
|
|
183
|
-
def duration_to_human(seconds:
|
|
184
|
+
def duration_to_human(seconds: int | float,
|
|
184
185
|
precision: int = 2,
|
|
185
186
|
max_units: int = 2) -> str:
|
|
186
187
|
"""
|
|
@@ -316,7 +317,7 @@ def smart_time_format(dt: datetime, reference: Optional[datetime] = None) -> str
|
|
|
316
317
|
return dt.strftime("%b %d, %Y")
|
|
317
318
|
|
|
318
319
|
|
|
319
|
-
def approximate_duration(seconds:
|
|
320
|
+
def approximate_duration(seconds: int | float) -> str:
|
|
320
321
|
"""
|
|
321
322
|
Get approximate duration in natural language.
|
|
322
323
|
|
|
@@ -409,7 +410,7 @@ class DateTimeHumanizer:
|
|
|
409
410
|
"""Convert datetime to human-readable string."""
|
|
410
411
|
return smart_time_format(dt, relative_to)
|
|
411
412
|
|
|
412
|
-
def humanize_date(self, date_obj:
|
|
413
|
+
def humanize_date(self, date_obj: datetime | str, relative_to: Optional[datetime] = None) -> str:
|
|
413
414
|
"""Convert date to human-readable string."""
|
|
414
415
|
if isinstance(date_obj, datetime):
|
|
415
416
|
return smart_time_format(date_obj, relative_to)
|
|
@@ -421,7 +422,7 @@ class DateTimeHumanizer:
|
|
|
421
422
|
return date_obj
|
|
422
423
|
return str(date_obj)
|
|
423
424
|
|
|
424
|
-
def humanize_time(self, time_obj:
|
|
425
|
+
def humanize_time(self, time_obj: datetime | str, relative_to: Optional[datetime] = None) -> str:
|
|
425
426
|
"""Convert time to human-readable string."""
|
|
426
427
|
if isinstance(time_obj, datetime):
|
|
427
428
|
return smart_time_format(time_obj, relative_to)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/parsing.py
|
|
1
2
|
"""
|
|
2
3
|
Datetime Parsing Utilities
|
|
3
4
|
==========================
|
|
@@ -7,13 +8,13 @@ Production-grade datetime parsing for XSystem.
|
|
|
7
8
|
Company: eXonware.com
|
|
8
9
|
Author: Eng. Muhammad AlShehri
|
|
9
10
|
Email: connect@exonware.com
|
|
10
|
-
Version: 0.1.0.
|
|
11
|
+
Version: 0.1.0.4
|
|
11
12
|
Generated: 2025-01-27
|
|
12
13
|
"""
|
|
13
14
|
|
|
14
15
|
import re
|
|
15
16
|
from datetime import datetime, date, time, timezone, timedelta
|
|
16
|
-
from typing import Optional
|
|
17
|
+
from typing import Optional
|
|
17
18
|
import logging
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger(__name__)
|
|
@@ -144,13 +145,32 @@ def parse_iso8601(text: str) -> Optional[datetime]:
|
|
|
144
145
|
return parse_datetime(text)
|
|
145
146
|
|
|
146
147
|
|
|
147
|
-
def parse_timestamp(timestamp:
|
|
148
|
-
"""
|
|
148
|
+
def parse_timestamp(timestamp: int | float | str) -> Optional[datetime]:
|
|
149
|
+
"""
|
|
150
|
+
Parse Unix timestamp to datetime.
|
|
151
|
+
|
|
152
|
+
Automatically detects if timestamp is in seconds or milliseconds.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
timestamp: Unix timestamp (seconds or milliseconds)
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Parsed datetime or None
|
|
159
|
+
|
|
160
|
+
Examples:
|
|
161
|
+
>>> dt = parse_timestamp(1609459200) # 2021-01-01 00:00:00 UTC
|
|
162
|
+
>>> dt is not None
|
|
163
|
+
True
|
|
164
|
+
>>> dt = parse_timestamp(1609459200000) # milliseconds
|
|
165
|
+
>>> dt is not None
|
|
166
|
+
True
|
|
167
|
+
"""
|
|
149
168
|
try:
|
|
150
169
|
if isinstance(timestamp, str):
|
|
151
170
|
timestamp = float(timestamp)
|
|
152
171
|
|
|
153
172
|
# Handle both seconds and milliseconds
|
|
173
|
+
# Timestamps > 1e10 are likely milliseconds (year 2286+ in seconds)
|
|
154
174
|
if timestamp > 1e10: # Likely milliseconds
|
|
155
175
|
timestamp = timestamp / 1000
|
|
156
176
|
|
|
@@ -161,6 +181,37 @@ def parse_timestamp(timestamp: Union[int, float, str]) -> Optional[datetime]:
|
|
|
161
181
|
return None
|
|
162
182
|
|
|
163
183
|
|
|
184
|
+
def parse_timestamp_milliseconds(timestamp: int | float | str) -> Optional[datetime]:
|
|
185
|
+
"""
|
|
186
|
+
Parse timestamp that may be in milliseconds.
|
|
187
|
+
|
|
188
|
+
Explicitly handles millisecond timestamps (common in JavaScript/Java).
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
timestamp: Unix timestamp (assumed to be in milliseconds)
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Parsed datetime or None
|
|
195
|
+
|
|
196
|
+
Examples:
|
|
197
|
+
>>> dt = parse_timestamp_milliseconds(1609459200000) # milliseconds
|
|
198
|
+
>>> dt is not None
|
|
199
|
+
True
|
|
200
|
+
"""
|
|
201
|
+
try:
|
|
202
|
+
if isinstance(timestamp, str):
|
|
203
|
+
timestamp = float(timestamp)
|
|
204
|
+
|
|
205
|
+
# Treat as milliseconds
|
|
206
|
+
timestamp_seconds = timestamp / 1000.0
|
|
207
|
+
|
|
208
|
+
return datetime.fromtimestamp(timestamp_seconds, tz=timezone.utc)
|
|
209
|
+
|
|
210
|
+
except (ValueError, OSError, OverflowError) as e:
|
|
211
|
+
logger.debug(f"Failed to parse millisecond timestamp '{timestamp}': {e}")
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
|
|
164
215
|
class DateTimeParser:
|
|
165
216
|
"""Advanced datetime parser with multiple format support."""
|
|
166
217
|
|
|
@@ -196,7 +247,7 @@ class DateTimeParser:
|
|
|
196
247
|
"""Parse ISO 8601 datetime string."""
|
|
197
248
|
return parse_iso8601(text)
|
|
198
249
|
|
|
199
|
-
def parse_timestamp(self, timestamp:
|
|
250
|
+
def parse_timestamp(self, timestamp: int | float | str) -> Optional[datetime]:
|
|
200
251
|
"""Parse Unix timestamp to datetime."""
|
|
201
252
|
return parse_timestamp(timestamp)
|
|
202
253
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/dt/timezone_utils.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.1.0.
|
|
6
|
+
Version: 0.1.0.4
|
|
6
7
|
Generation Date: September 04, 2025
|
|
7
8
|
|
|
8
9
|
Timezone utilities - Placeholder.
|
|
@@ -99,26 +100,3 @@ class TimezoneUtils:
|
|
|
99
100
|
return datetime.now()
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
def convert_timezone(dt, tz):
|
|
103
|
-
"""Convert timezone - backward compatibility."""
|
|
104
|
-
return TimezoneUtils.convert_timezone(dt, tz)
|
|
105
|
-
|
|
106
|
-
def get_timezone_info(tz):
|
|
107
|
-
"""Get timezone info - backward compatibility."""
|
|
108
|
-
return TimezoneUtils.get_timezone_info(tz)
|
|
109
|
-
|
|
110
|
-
def list_timezones():
|
|
111
|
-
"""List timezones - backward compatibility."""
|
|
112
|
-
return TimezoneUtils.list_timezones()
|
|
113
|
-
|
|
114
|
-
def get_local_timezone():
|
|
115
|
-
"""Get local timezone - backward compatibility."""
|
|
116
|
-
return TimezoneUtils.get_local_timezone()
|
|
117
|
-
|
|
118
|
-
def utc_now():
|
|
119
|
-
"""Get UTC now - backward compatibility."""
|
|
120
|
-
return TimezoneUtils.utc_now()
|
|
121
|
-
|
|
122
|
-
def local_now():
|
|
123
|
-
"""Get local now - backward compatibility."""
|
|
124
|
-
return TimezoneUtils.local_now()
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/errors.py
|
|
1
2
|
#exonware/xwsystem/utils/errors.py
|
|
2
3
|
"""
|
|
3
4
|
Company: eXonware.com
|
|
4
5
|
Author: Eng. Muhammad AlShehri
|
|
5
6
|
Email: connect@exonware.com
|
|
6
|
-
Version: 0.1.0.
|
|
7
|
+
Version: 0.1.0.4
|
|
7
8
|
Generation Date: September 04, 2025
|
|
8
9
|
|
|
9
10
|
Utils module errors - exception classes for utility functionality.
|
|
@@ -124,6 +125,3 @@ class ResourceExhaustionError(ResourceManagerError):
|
|
|
124
125
|
"""Raised when resources are exhausted."""
|
|
125
126
|
pass
|
|
126
127
|
|
|
127
|
-
|
|
128
|
-
# Aliases for backward compatibility
|
|
129
|
-
PathError = PathUtilsError
|
exonware/xwsystem/utils/paths.py
CHANGED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
#exonware/xwsystem/src/exonware/xwsystem/utils/string.py
|
|
3
|
+
"""
|
|
4
|
+
Company: eXonware.com
|
|
5
|
+
Author: Eng. Muhammad AlShehri
|
|
6
|
+
Email: connect@exonware.com
|
|
7
|
+
Version: 0.1.0.4
|
|
8
|
+
Generation Date: January 2025
|
|
9
|
+
|
|
10
|
+
String utility functions for XSystem.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def find_nth_occurrence(text: str, key: str, n: int) -> int:
|
|
17
|
+
"""
|
|
18
|
+
Find the nth occurrence of a substring in text.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
text: Text to search
|
|
22
|
+
key: Substring to find
|
|
23
|
+
n: Occurrence number (1-based)
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Index of nth occurrence, or -1 if not found
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
>>> find_nth_occurrence("hello world hello", "hello", 1)
|
|
30
|
+
0
|
|
31
|
+
>>> find_nth_occurrence("hello world hello", "hello", 2)
|
|
32
|
+
12
|
|
33
|
+
>>> find_nth_occurrence("hello world hello", "hello", 3)
|
|
34
|
+
-1
|
|
35
|
+
"""
|
|
36
|
+
if not text or not key or n < 1:
|
|
37
|
+
return -1
|
|
38
|
+
|
|
39
|
+
start = text.find(key)
|
|
40
|
+
while start >= 0 and n > 1:
|
|
41
|
+
start = text.find(key, start + len(key))
|
|
42
|
+
n -= 1
|
|
43
|
+
|
|
44
|
+
return start
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
'find_nth_occurrence',
|
|
49
|
+
]
|