Switch datetime handling, var handling to corelibs libraries
Use external corelib libraries for datetime handling and var handling enum base.
This commit is contained in:
10
README.md
10
README.md
@@ -4,6 +4,7 @@
|
||||
> This is pre-production, location of methods and names of paths can change
|
||||
>
|
||||
> This will be split up into modules per file and this will be just a collection holder
|
||||
> See [Deprecated](#deprecated) below
|
||||
|
||||
This is a pip package that can be installed into any project and covers the following parts
|
||||
|
||||
@@ -42,6 +43,15 @@ This is a pip package that can be installed into any project and covers the foll
|
||||
- csv_handling/csv_interface: The CSV DictWriter interface is just in a very basic way implemented
|
||||
- script_handling/script_helpers: No idea if there is need for this, tests are written but not finished
|
||||
|
||||
## Deprecated
|
||||
|
||||
All content in this module will move to stand alone libraries, as of now the following entries have moved and will throw deprecated warnings if used
|
||||
|
||||
- var_handling.enum_base: corelibs-enum-base
|
||||
- var_handling.var_helpers: corelibs-var
|
||||
- datetime_handling: corelibs-datetime
|
||||
- string_handling.text_colors: corelibs-text-colors
|
||||
|
||||
## UV setup
|
||||
|
||||
uv must be [installed](https://docs.astral.sh/uv/getting-started/installation/)
|
||||
|
||||
@@ -6,12 +6,20 @@ description = "Collection of utils for Python scripts"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"corelibs-datetime>=1.0.1",
|
||||
"corelibs-enum-base>=1.0.0",
|
||||
"corelibs-var>=1.0.0",
|
||||
"cryptography>=46.0.3",
|
||||
"jmespath>=1.0.1",
|
||||
"jsonpath-ng>=1.7.0",
|
||||
"psutil>=7.0.0",
|
||||
"requests>=2.32.4",
|
||||
]
|
||||
|
||||
# MARK: build system
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
# set this to disable publish to pypi (pip)
|
||||
# classifiers = ["Private :: Do Not Upload"]
|
||||
|
||||
@@ -21,10 +29,10 @@ name = "opj-pypi"
|
||||
url = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/"
|
||||
publish-url = "https://git.egplusww.jp/api/packages/PyPI/pypi"
|
||||
|
||||
# MARK: build system
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
[tool.uv.sources]
|
||||
corelibs-enum-base = { index = "opj-pypi" }
|
||||
corelibs-datetime = { index = "opj-pypi" }
|
||||
corelibs-var = { index = "opj-pypi" }
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
|
||||
@@ -2,26 +2,13 @@
|
||||
Various string based date/time helpers
|
||||
"""
|
||||
|
||||
import time as time_t
|
||||
from datetime import datetime, time
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
from typing import Callable
|
||||
|
||||
DAYS_OF_WEEK_LONG_TO_SHORT: dict[str, str] = {
|
||||
'Monday': 'Mon',
|
||||
'Tuesday': 'Tue',
|
||||
'Wednesay': 'Wed',
|
||||
'Thursday': 'Thu',
|
||||
'Friday': 'Fri',
|
||||
'Saturday': 'Sat',
|
||||
'Sunday': 'Sun',
|
||||
}
|
||||
DAYS_OF_WEEK_ISO: dict[int, str] = {
|
||||
1: 'Mon', 2: 'Tue', 3: 'Wed', 4: 'Thu', 5: 'Fri', 6: 'Sat', 7: 'Sun'
|
||||
}
|
||||
DAYS_OF_WEEK_ISO_REVERSED: dict[str, int] = {value: key for key, value in DAYS_OF_WEEK_ISO.items()}
|
||||
from warnings import deprecated
|
||||
from zoneinfo import ZoneInfo
|
||||
from corelibs_datetime import datetime_helpers
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.create_time instead")
|
||||
def create_time(timestamp: float, timestamp_format: str = "%Y-%m-%d %H:%M:%S") -> str:
|
||||
"""
|
||||
just takes a timestamp and prints out humand readable format
|
||||
@@ -35,21 +22,17 @@ def create_time(timestamp: float, timestamp_format: str = "%Y-%m-%d %H:%M:%S") -
|
||||
Returns:
|
||||
str -- _description_
|
||||
"""
|
||||
return time_t.strftime(timestamp_format, time_t.localtime(timestamp))
|
||||
return datetime_helpers.create_time(timestamp, timestamp_format)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.get_system_timezone instead")
|
||||
def get_system_timezone():
|
||||
"""Get system timezone using datetime's automatic detection"""
|
||||
# Get current time with system timezone
|
||||
local_time = datetime.now().astimezone()
|
||||
|
||||
# Extract timezone info
|
||||
system_tz = local_time.tzinfo
|
||||
timezone_name = str(system_tz)
|
||||
|
||||
return system_tz, timezone_name
|
||||
return datetime_helpers.get_system_timezone()
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.parse_timezone_data instead")
|
||||
def parse_timezone_data(timezone_tz: str = '') -> ZoneInfo:
|
||||
"""
|
||||
parses a string to get the ZoneInfo
|
||||
@@ -62,40 +45,10 @@ def parse_timezone_data(timezone_tz: str = '') -> ZoneInfo:
|
||||
Returns:
|
||||
ZoneInfo -- _description_
|
||||
"""
|
||||
try:
|
||||
return ZoneInfo(timezone_tz)
|
||||
except (ZoneInfoNotFoundError, ValueError, TypeError):
|
||||
# use default
|
||||
time_tz, time_tz_str = get_system_timezone()
|
||||
if time_tz is None:
|
||||
return ZoneInfo('UTC')
|
||||
# TODO build proper TZ lookup
|
||||
tz_mapping = {
|
||||
'JST': 'Asia/Tokyo',
|
||||
'KST': 'Asia/Seoul',
|
||||
'IST': 'Asia/Kolkata',
|
||||
'CST': 'Asia/Shanghai', # Default to China for CST
|
||||
'AEST': 'Australia/Sydney',
|
||||
'AWST': 'Australia/Perth',
|
||||
'EST': 'America/New_York',
|
||||
'EDT': 'America/New_York',
|
||||
'CDT': 'America/Chicago',
|
||||
'MST': 'America/Denver',
|
||||
'MDT': 'America/Denver',
|
||||
'PST': 'America/Los_Angeles',
|
||||
'PDT': 'America/Los_Angeles',
|
||||
'GMT': 'UTC',
|
||||
'UTC': 'UTC',
|
||||
'CET': 'Europe/Berlin',
|
||||
'CEST': 'Europe/Berlin',
|
||||
'BST': 'Europe/London',
|
||||
}
|
||||
try:
|
||||
return ZoneInfo(tz_mapping[time_tz_str])
|
||||
except (ZoneInfoNotFoundError, IndexError) as e:
|
||||
raise ValueError(f"No mapping for {time_tz_str}: {e}") from e
|
||||
return datetime_helpers.parse_timezone_data(timezone_tz)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.get_datetime_iso8601 instead")
|
||||
def get_datetime_iso8601(timezone_tz: str | ZoneInfo = '', sep: str = 'T', timespec: str = 'microseconds') -> str:
|
||||
"""
|
||||
set a datetime in the iso8601 format with microseconds
|
||||
@@ -103,12 +56,13 @@ def get_datetime_iso8601(timezone_tz: str | ZoneInfo = '', sep: str = 'T', times
|
||||
Returns:
|
||||
str -- _description_
|
||||
"""
|
||||
# parse if this is a string
|
||||
if isinstance(timezone_tz, str):
|
||||
timezone_tz = parse_timezone_data(timezone_tz)
|
||||
return datetime.now(timezone_tz).isoformat(sep=sep, timespec=timespec)
|
||||
try:
|
||||
return datetime_helpers.get_datetime_iso8601(timezone_tz, sep, timespec)
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Deprecated ValueError, change to KeyError: {e}") from e
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.validate_date instead")
|
||||
def validate_date(date: str, not_before: datetime | None = None, not_after: datetime | None = None) -> bool:
|
||||
"""
|
||||
check if Y-m-d or Y/m/d are parsable and valid
|
||||
@@ -119,20 +73,10 @@ def validate_date(date: str, not_before: datetime | None = None, not_after: date
|
||||
Returns:
|
||||
bool -- _description_
|
||||
"""
|
||||
formats = ['%Y-%m-%d', '%Y/%m/%d']
|
||||
for __format in formats:
|
||||
try:
|
||||
__date = datetime.strptime(date, __format).date()
|
||||
if not_before is not None and __date < not_before.date():
|
||||
return False
|
||||
if not_after is not None and __date > not_after.date():
|
||||
return False
|
||||
return True
|
||||
except ValueError:
|
||||
continue
|
||||
return False
|
||||
return datetime_helpers.validate_date(date, not_before, not_after)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.parse_flexible_date instead")
|
||||
def parse_flexible_date(
|
||||
date_str: str,
|
||||
timezone_tz: str | ZoneInfo | None = None,
|
||||
@@ -154,49 +98,14 @@ def parse_flexible_date(
|
||||
Returns:
|
||||
datetime | None -- _description_
|
||||
"""
|
||||
|
||||
date_str = date_str.strip()
|
||||
|
||||
# Try different parsing methods
|
||||
parsers: list[Callable[[str], datetime]] = [
|
||||
# ISO 8601 format, also with missing "T"
|
||||
lambda x: datetime.fromisoformat(x), # pylint: disable=W0108
|
||||
lambda x: datetime.fromisoformat(x.replace(' ', 'T')), # pylint: disable=W0108
|
||||
# Simple date format
|
||||
lambda x: datetime.strptime(x, "%Y-%m-%d"),
|
||||
# datetime without T
|
||||
lambda x: datetime.strptime(x, "%Y-%m-%d %H:%M:%S"),
|
||||
lambda x: datetime.strptime(x, "%Y-%m-%d %H:%M:%S.%f"),
|
||||
# Alternative ISO formats (fallback)
|
||||
lambda x: datetime.strptime(x, "%Y-%m-%dT%H:%M:%S"),
|
||||
lambda x: datetime.strptime(x, "%Y-%m-%dT%H:%M:%S.%f"),
|
||||
]
|
||||
|
||||
if timezone_tz is not None:
|
||||
if isinstance(timezone_tz, str):
|
||||
timezone_tz = parse_timezone_data(timezone_tz)
|
||||
|
||||
date_new = None
|
||||
for parser in parsers:
|
||||
try:
|
||||
date_new = parser(date_str)
|
||||
break
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if date_new is not None:
|
||||
if timezone_tz is not None:
|
||||
# shift time zone (default), this will change the date
|
||||
# if the date has no +HH:MM it will take the local time zone as base
|
||||
if shift_time_zone:
|
||||
return date_new.astimezone(timezone_tz)
|
||||
# just add the time zone
|
||||
return date_new.replace(tzinfo=timezone_tz)
|
||||
return date_new
|
||||
|
||||
return None
|
||||
return datetime_helpers.parse_flexible_date(
|
||||
date_str,
|
||||
timezone_tz,
|
||||
shift_time_zone
|
||||
)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.compare_dates instead")
|
||||
def compare_dates(date1_str: str, date2_str: str) -> None | bool:
|
||||
"""
|
||||
compare two dates, if the first one is newer than the second one return True
|
||||
@@ -210,23 +119,10 @@ def compare_dates(date1_str: str, date2_str: str) -> None | bool:
|
||||
Returns:
|
||||
None | bool -- _description_
|
||||
"""
|
||||
|
||||
try:
|
||||
# Parse both dates
|
||||
date1 = parse_flexible_date(date1_str)
|
||||
date2 = parse_flexible_date(date2_str)
|
||||
|
||||
# Check if parsing was successful
|
||||
if date1 is None or date2 is None:
|
||||
return None
|
||||
|
||||
# Compare dates
|
||||
return date1.date() > date2.date()
|
||||
|
||||
except ValueError:
|
||||
return None
|
||||
return datetime_helpers.compare_dates(date1_str, date2_str)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.find_newest_datetime_in_list instead")
|
||||
def find_newest_datetime_in_list(date_list: list[str]) -> None | str:
|
||||
"""
|
||||
Find the newest date from a list of ISO 8601 formatted date strings.
|
||||
@@ -238,31 +134,10 @@ def find_newest_datetime_in_list(date_list: list[str]) -> None | str:
|
||||
Returns:
|
||||
str: The date string with the newest/latest date, or None if list is empty or all dates are invalid
|
||||
"""
|
||||
if not date_list:
|
||||
return None
|
||||
|
||||
valid_dates: list[tuple[str, datetime]] = []
|
||||
|
||||
for date_str in date_list:
|
||||
try:
|
||||
# Parse the date string and store both original string and parsed datetime
|
||||
parsed_date = parse_flexible_date(date_str)
|
||||
if parsed_date is None:
|
||||
continue
|
||||
valid_dates.append((date_str, parsed_date))
|
||||
except ValueError:
|
||||
# Skip invalid date strings
|
||||
continue
|
||||
|
||||
if not valid_dates:
|
||||
return None
|
||||
|
||||
# Find the date string with the maximum datetime value
|
||||
newest_date_str: str = max(valid_dates, key=lambda x: x[1])[0]
|
||||
|
||||
return newest_date_str
|
||||
return datetime_helpers.find_newest_datetime_in_list(date_list)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.parse_day_of_week_range instead")
|
||||
def parse_day_of_week_range(dow_days: str) -> list[tuple[int, str]]:
|
||||
"""
|
||||
Parse a day of week list/range string and return a list of tuples with day index and name.
|
||||
@@ -279,59 +154,13 @@ def parse_day_of_week_range(dow_days: str) -> list[tuple[int, str]]:
|
||||
"""
|
||||
# we have Sun twice because it can be 0 or 7
|
||||
# Mon is 1 and Sun is 7, which is ISO standard
|
||||
dow_day = dow_days.split(",")
|
||||
dow_day = [day.strip() for day in dow_day if day.strip()]
|
||||
__out_dow_days: list[tuple[int, str]] = []
|
||||
for __dow_day in dow_day:
|
||||
# if we have a "-" in there fill
|
||||
if "-" in __dow_day:
|
||||
__dow_range = __dow_day.split("-")
|
||||
__dow_range = [day.strip().capitalize() for day in __dow_range if day.strip()]
|
||||
try:
|
||||
start_day = DAYS_OF_WEEK_ISO_REVERSED[__dow_range[0]]
|
||||
end_day = DAYS_OF_WEEK_ISO_REVERSED[__dow_range[1]]
|
||||
except KeyError:
|
||||
# try long time
|
||||
try:
|
||||
start_day = DAYS_OF_WEEK_ISO_REVERSED[DAYS_OF_WEEK_LONG_TO_SHORT[__dow_range[0]]]
|
||||
end_day = DAYS_OF_WEEK_ISO_REVERSED[DAYS_OF_WEEK_LONG_TO_SHORT[__dow_range[1]]]
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Invalid day of week entry found: {__dow_day}: {e}") from e
|
||||
# Check if this spans across the weekend (e.g., Fri-Mon)
|
||||
if start_day > end_day:
|
||||
# Handle weekend-spanning range: start_day to 7, then 1 to end_day
|
||||
__out_dow_days.extend(
|
||||
[
|
||||
(i, DAYS_OF_WEEK_ISO[i])
|
||||
for i in range(start_day, 8) # start_day to Sunday (7)
|
||||
]
|
||||
)
|
||||
__out_dow_days.extend(
|
||||
[
|
||||
(i, DAYS_OF_WEEK_ISO[i])
|
||||
for i in range(1, end_day + 1) # Monday (1) to end_day
|
||||
]
|
||||
)
|
||||
else:
|
||||
# Normal range: start_day to end_day
|
||||
__out_dow_days.extend(
|
||||
[
|
||||
(i, DAYS_OF_WEEK_ISO[i])
|
||||
for i in range(start_day, end_day + 1)
|
||||
]
|
||||
)
|
||||
else:
|
||||
try:
|
||||
__out_dow_days.append((DAYS_OF_WEEK_ISO_REVERSED[__dow_day], __dow_day))
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Invalid day of week entry found: {__dow_day}: {e}") from e
|
||||
# if there are duplicates, alert
|
||||
if len(__out_dow_days) != len(set(__out_dow_days)):
|
||||
raise ValueError(f"Duplicate day of week entries found: {__out_dow_days}")
|
||||
|
||||
return __out_dow_days
|
||||
try:
|
||||
return datetime_helpers.parse_day_of_week_range(dow_days)
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Deprecated ValueError, change to KeyError: {e}") from e
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.parse_time_range instead")
|
||||
def parse_time_range(time_str: str, time_format: str = "%H:%M") -> tuple[time, time]:
|
||||
"""
|
||||
Parse a time range string in the format "HH:MM-HH:MM" and return a tuple of two time objects.
|
||||
@@ -347,22 +176,13 @@ def parse_time_range(time_str: str, time_format: str = "%H:%M") -> tuple[time, t
|
||||
Returns:
|
||||
tuple[time, time] -- start time, end time: leading zeros formattd
|
||||
"""
|
||||
__time_str = time_str.strip()
|
||||
# split by "-"
|
||||
__time_split = __time_str.split("-")
|
||||
if len(__time_split) != 2:
|
||||
raise ValueError(f"Invalid time block: {__time_str}")
|
||||
try:
|
||||
__time_start = datetime.strptime(__time_split[0], time_format).time()
|
||||
__time_end = datetime.strptime(__time_split[1], time_format).time()
|
||||
except ValueError as e:
|
||||
raise ValueError(f"Invalid time block format [{__time_str}]: {e}") from e
|
||||
if __time_start >= __time_end:
|
||||
raise ValueError(f"Invalid time block set, start time after end time or equal: {__time_str}")
|
||||
|
||||
return __time_start, __time_end
|
||||
return datetime_helpers.parse_time_range(time_str, time_format)
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Deprecated ValueError, change to KeyError: {e}") from e
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.times_overlap_or_connect instead")
|
||||
def times_overlap_or_connect(time1: tuple[time, time], time2: tuple[time, time], allow_touching: bool = False) -> bool:
|
||||
"""
|
||||
Check if two time ranges overlap or connect
|
||||
@@ -375,16 +195,10 @@ def times_overlap_or_connect(time1: tuple[time, time], time2: tuple[time, time],
|
||||
Returns:
|
||||
bool: True if ranges overlap or connect (based on allow_touching)
|
||||
"""
|
||||
start1, end1 = time1
|
||||
start2, end2 = time2
|
||||
|
||||
if allow_touching:
|
||||
# Only check for actual overlap (touching is OK)
|
||||
return start1 < end2 and start2 < end1
|
||||
# Check for overlap OR touching
|
||||
return start1 <= end2 and start2 <= end1
|
||||
return datetime_helpers.times_overlap_or_connect(time1, time2, allow_touching)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.is_time_in_range instead")
|
||||
def is_time_in_range(current_time: str, start_time: str, end_time: str) -> bool:
|
||||
"""
|
||||
Check if current_time is within start_time and end_time (inclusive)
|
||||
@@ -399,18 +213,10 @@ def is_time_in_range(current_time: str, start_time: str, end_time: str) -> bool:
|
||||
bool -- _description_
|
||||
"""
|
||||
# Convert string times to time objects
|
||||
current = datetime.strptime(current_time, "%H:%M:%S").time()
|
||||
start = datetime.strptime(start_time, "%H:%M:%S").time()
|
||||
end = datetime.strptime(end_time, "%H:%M:%S").time()
|
||||
|
||||
# Handle case where range crosses midnight (e.g., 22:00 to 06:00)
|
||||
if start <= end:
|
||||
# Normal case: start time is before end time
|
||||
return start <= current <= end
|
||||
# Crosses midnight: e.g., 22:00 to 06:00
|
||||
return current >= start or current <= end
|
||||
return datetime_helpers.is_time_in_range(current_time, start_time, end_time)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.datetime_helpers.reorder_weekdays_from_today instead")
|
||||
def reorder_weekdays_from_today(base_day: str) -> dict[int, str]:
|
||||
"""
|
||||
Reorder the days of the week starting from the specified base_day.
|
||||
@@ -422,18 +228,8 @@ def reorder_weekdays_from_today(base_day: str) -> dict[int, str]:
|
||||
dict[int, str] -- A dictionary mapping day numbers to day names.
|
||||
"""
|
||||
try:
|
||||
today_num = DAYS_OF_WEEK_ISO_REVERSED[base_day]
|
||||
except KeyError:
|
||||
try:
|
||||
today_num = DAYS_OF_WEEK_ISO_REVERSED[DAYS_OF_WEEK_LONG_TO_SHORT[base_day]]
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Invalid day name provided: {base_day}: {e}") from e
|
||||
# Convert to list of tuples
|
||||
items = list(DAYS_OF_WEEK_ISO.items())
|
||||
# Reorder: from today onwards + from beginning to yesterday
|
||||
reordered_items = items[today_num - 1:] + items[:today_num - 1]
|
||||
|
||||
# Convert back to dictionary
|
||||
return dict(reordered_items)
|
||||
return datetime_helpers.reorder_weekdays_from_today(base_day)
|
||||
except KeyError as e:
|
||||
raise ValueError(f"Deprecated ValueError, change to KeyError: {e}") from e
|
||||
|
||||
# __END__
|
||||
|
||||
@@ -2,19 +2,22 @@
|
||||
Convert timestamp strings with time units into seconds and vice versa.
|
||||
"""
|
||||
|
||||
from math import floor
|
||||
import re
|
||||
from corelibs.var_handling.var_helpers import is_float
|
||||
from warnings import deprecated
|
||||
from corelibs_datetime import timestamp_convert
|
||||
from corelibs_datetime.timestamp_convert import TimeParseError as NewTimeParseError, TimeUnitError as NewTimeUnitError
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.timestamp_convert.TimeParseError instead")
|
||||
class TimeParseError(Exception):
|
||||
"""Custom exception for time parsing errors."""
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.timestamp_convert.TimeUnitError instead")
|
||||
class TimeUnitError(Exception):
|
||||
"""Custom exception for time parsing errors."""
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.timestamp_convert.convert_to_seconds instead")
|
||||
def convert_to_seconds(time_string: str | int | float) -> int:
|
||||
"""
|
||||
Conver a string with time units into a seconds string
|
||||
@@ -35,67 +38,15 @@ def convert_to_seconds(time_string: str | int | float) -> int:
|
||||
|
||||
# skip out if this is a number of any type
|
||||
# numbers will br made float, rounded and then converted to int
|
||||
if is_float(time_string):
|
||||
return int(round(float(time_string)))
|
||||
time_string = str(time_string)
|
||||
|
||||
# Check if the time string is negative
|
||||
negative = time_string.startswith('-')
|
||||
if negative:
|
||||
time_string = time_string[1:] # Remove the negative sign for processing
|
||||
|
||||
# Define time unit conversion factors
|
||||
unit_factors: dict[str, int] = {
|
||||
'Y': 31536000, # 365 days * 86400 seconds/day
|
||||
'M': 2592000 * 12, # 1 year in seconds (assuming 365 days per year)
|
||||
'd': 86400, # 1 day in seconds
|
||||
'h': 3600, # 1 hour in seconds
|
||||
'm': 60, # minutes to seconds
|
||||
's': 1 # 1 second in seconds
|
||||
}
|
||||
long_unit_names: dict[str, str] = {
|
||||
'year': 'Y',
|
||||
'years': 'Y',
|
||||
'month': 'M',
|
||||
'months': 'M',
|
||||
'day': 'd',
|
||||
'days': 'd',
|
||||
'hour': 'h',
|
||||
'hours': 'h',
|
||||
'minute': 'm',
|
||||
'minutes': 'm',
|
||||
'min': 'm',
|
||||
'second': 's',
|
||||
'seconds': 's',
|
||||
'sec': 's',
|
||||
}
|
||||
|
||||
total_seconds = 0
|
||||
|
||||
seen_units: list[str] = [] # Track units that have been encountered
|
||||
|
||||
# Use regex to match number and time unit pairs
|
||||
for match in re.finditer(r'(\d+)\s*([a-zA-Z]+)', time_string):
|
||||
value, unit = int(match.group(1)), match.group(2)
|
||||
|
||||
# full name check, fallback to original name
|
||||
unit = long_unit_names.get(unit.lower(), unit)
|
||||
|
||||
# Check for duplicate units
|
||||
if unit in seen_units:
|
||||
raise TimeParseError(f"Unit '{unit}' appears more than once.")
|
||||
# Check invalid unit
|
||||
if unit not in unit_factors:
|
||||
raise TimeUnitError(f"Unit '{unit}' is not a valid unit name.")
|
||||
# Add to total seconds based on the units
|
||||
if unit in unit_factors:
|
||||
total_seconds += value * unit_factors[unit]
|
||||
|
||||
seen_units.append(unit)
|
||||
|
||||
return -total_seconds if negative else total_seconds
|
||||
try:
|
||||
return timestamp_convert.convert_to_seconds(time_string)
|
||||
except NewTimeParseError as e:
|
||||
raise TimeParseError(f"Deprecated, use corelibs_datetime.timestamp_convert.TimeParseError: {e}") from e
|
||||
except NewTimeUnitError as e:
|
||||
raise TimeUnitError(f"Deprecated, use corelibs_datetime.timestamp_convert.TimeUnitError: {e}") from e
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.timestamp_convert.seconds_to_string instead")
|
||||
def seconds_to_string(seconds: str | int | float, show_microseconds: bool = False) -> str:
|
||||
"""
|
||||
Convert seconds to compact human readable format (e.g., "1d 2h 3m 4.567s")
|
||||
@@ -111,46 +62,10 @@ def seconds_to_string(seconds: str | int | float, show_microseconds: bool = Fals
|
||||
Returns:
|
||||
str: Compact human readable time format
|
||||
"""
|
||||
# if not int or float, return as is
|
||||
if not isinstance(seconds, (int, float)):
|
||||
return seconds
|
||||
# Handle negative values
|
||||
negative = seconds < 0
|
||||
seconds = abs(seconds)
|
||||
|
||||
whole_seconds = int(seconds)
|
||||
fractional = seconds - whole_seconds
|
||||
|
||||
days = whole_seconds // 86400
|
||||
hours = (whole_seconds % 86400) // 3600
|
||||
minutes = (whole_seconds % 3600) // 60
|
||||
secs = whole_seconds % 60
|
||||
|
||||
parts: list[str] = []
|
||||
if days > 0:
|
||||
parts.append(f"{days}d")
|
||||
if hours > 0:
|
||||
parts.append(f"{hours}h")
|
||||
if minutes > 0:
|
||||
parts.append(f"{minutes}m")
|
||||
|
||||
# Handle seconds with fractional part
|
||||
if fractional > 0:
|
||||
if show_microseconds:
|
||||
total_seconds = secs + fractional
|
||||
formatted = f"{total_seconds:.6f}".rstrip('0').rstrip('.')
|
||||
parts.append(f"{formatted}s")
|
||||
else:
|
||||
total_seconds = secs + fractional
|
||||
formatted = f"{total_seconds:.3f}".rstrip('0').rstrip('.')
|
||||
parts.append(f"{formatted}s")
|
||||
elif secs > 0 or not parts:
|
||||
parts.append(f"{secs}s")
|
||||
|
||||
result = " ".join(parts)
|
||||
return f"-{result}" if negative else result
|
||||
return timestamp_convert.seconds_to_string(seconds, show_microseconds)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_datetime.timestamp_convert.convert_timestamp instead")
|
||||
def convert_timestamp(timestamp: float | int | str, show_microseconds: bool = True) -> str:
|
||||
"""
|
||||
format timestamp into human readable format. This function will add 0 values between set values
|
||||
@@ -168,33 +83,6 @@ def convert_timestamp(timestamp: float | int | str, show_microseconds: bool = Tr
|
||||
Returns:
|
||||
str -- _description_
|
||||
"""
|
||||
if not isinstance(timestamp, (int, float)):
|
||||
return timestamp
|
||||
# cut of the ms, but first round them up to four
|
||||
__timestamp_ms_split = str(round(timestamp, 4)).split(".")
|
||||
timestamp = int(__timestamp_ms_split[0])
|
||||
negative = timestamp < 0
|
||||
timestamp = abs(timestamp)
|
||||
try:
|
||||
ms = int(__timestamp_ms_split[1])
|
||||
except IndexError:
|
||||
ms = 0
|
||||
timegroups = (86400, 3600, 60, 1)
|
||||
output: list[int] = []
|
||||
for i in timegroups:
|
||||
output.append(int(floor(timestamp / i)))
|
||||
timestamp = timestamp % i
|
||||
# output has days|hours|min|sec ms
|
||||
time_string = ""
|
||||
if output[0]:
|
||||
time_string = f"{output[0]}d "
|
||||
if output[0] or output[1]:
|
||||
time_string += f"{output[1]}h "
|
||||
if output[0] or output[1] or output[2]:
|
||||
time_string += f"{output[2]}m "
|
||||
time_string += f"{output[3]}s"
|
||||
if show_microseconds:
|
||||
time_string += f" {ms}ms" if ms else " 0ms"
|
||||
return f"-{time_string}" if negative else time_string
|
||||
return timestamp_convert.convert_timestamp(timestamp, show_microseconds)
|
||||
|
||||
# __END__
|
||||
|
||||
@@ -2,31 +2,20 @@
|
||||
Current timestamp strings and time zones
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||
from warnings import deprecated
|
||||
from zoneinfo import ZoneInfo
|
||||
from corelibs_datetime import timestamp_strings
|
||||
|
||||
|
||||
class TimestampStrings:
|
||||
class TimestampStrings(timestamp_strings.TimestampStrings):
|
||||
"""
|
||||
set default time stamps
|
||||
"""
|
||||
|
||||
TIME_ZONE: str = 'Asia/Tokyo'
|
||||
|
||||
@deprecated("Use corelibs_datetime.timestamp_strings.TimestampStrings instead")
|
||||
def __init__(self, time_zone: str | ZoneInfo | None = None):
|
||||
self.timestamp_now = datetime.now()
|
||||
# set time zone as string
|
||||
time_zone = time_zone if time_zone is not None else self.TIME_ZONE
|
||||
self.time_zone = str(time_zone) if not isinstance(time_zone, str) else time_zone
|
||||
# set ZoneInfo type
|
||||
try:
|
||||
self.time_zone_zi = ZoneInfo(self.time_zone)
|
||||
except ZoneInfoNotFoundError as e:
|
||||
raise ValueError(f'Zone could not be loaded [{self.time_zone}]: {e}') from e
|
||||
self.timestamp_now_tz = datetime.now(self.time_zone_zi)
|
||||
self.today = self.timestamp_now.strftime('%Y-%m-%d')
|
||||
self.timestamp = self.timestamp_now.strftime("%Y-%m-%d %H:%M:%S")
|
||||
self.timestamp_tz = self.timestamp_now_tz.strftime("%Y-%m-%d %H:%M:%S %Z")
|
||||
self.timestamp_file = self.timestamp_now.strftime("%Y-%m-%d_%H%M%S")
|
||||
super().__init__(time_zone)
|
||||
|
||||
# __END__
|
||||
|
||||
@@ -3,7 +3,9 @@ Enum base classes
|
||||
"""
|
||||
|
||||
from enum import Enum
|
||||
from warnings import deprecated
|
||||
from typing import Any
|
||||
# from corelibs_enum_base.enum_base import EnumBase as CorelibsEnumBase
|
||||
|
||||
|
||||
class EnumBase(Enum):
|
||||
@@ -14,6 +16,7 @@ class EnumBase(Enum):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
||||
def lookup_key(cls, enum_key: str):
|
||||
"""Lookup from key side (must be string)"""
|
||||
# if there is a ":", then this is legacy, replace with ___
|
||||
@@ -27,6 +30,7 @@ class EnumBase(Enum):
|
||||
raise ValueError(f"Invalid key: {enum_key}") from e
|
||||
|
||||
@classmethod
|
||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
||||
def lookup_value(cls, enum_value: Any):
|
||||
"""Lookup through value side"""
|
||||
try:
|
||||
@@ -35,6 +39,7 @@ class EnumBase(Enum):
|
||||
raise ValueError(f"Invalid value: {enum_value}") from e
|
||||
|
||||
@classmethod
|
||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
||||
def from_any(cls, enum_any: Any):
|
||||
"""
|
||||
This only works in the following order
|
||||
@@ -62,14 +67,17 @@ class EnumBase(Enum):
|
||||
raise ValueError(f"Could not find as key or value: {enum_any}") from e
|
||||
return cls.lookup_value(enum_any)
|
||||
|
||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
||||
def to_value(self) -> Any:
|
||||
"""Convert to value"""
|
||||
return self.value
|
||||
|
||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
||||
def to_lower_case(self) -> str:
|
||||
"""return lower case"""
|
||||
return self.name.lower()
|
||||
|
||||
@deprecated("Use corelibs_enum_base.EnumBase instead")
|
||||
def __str__(self) -> str:
|
||||
"""return [Enum].NAME like it was called with .name"""
|
||||
return self.name
|
||||
|
||||
@@ -3,8 +3,11 @@ variable convert, check, etc helepr
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
from warnings import deprecated
|
||||
import corelibs_var.var_helpers
|
||||
|
||||
|
||||
@deprecated("Use corelibs_var.var_helpers.is_int instead")
|
||||
def is_int(string: Any) -> bool:
|
||||
"""
|
||||
check if a value is int
|
||||
@@ -15,15 +18,10 @@ def is_int(string: Any) -> bool:
|
||||
Returns:
|
||||
bool -- _description_
|
||||
"""
|
||||
try:
|
||||
int(string)
|
||||
return True
|
||||
except TypeError:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return corelibs_var.var_helpers.is_int(string)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_var.var_helpers.is_float instead")
|
||||
def is_float(string: Any) -> bool:
|
||||
"""
|
||||
check if a value is float
|
||||
@@ -34,15 +32,10 @@ def is_float(string: Any) -> bool:
|
||||
Returns:
|
||||
bool -- _description_
|
||||
"""
|
||||
try:
|
||||
float(string)
|
||||
return True
|
||||
except TypeError:
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
return corelibs_var.var_helpers.is_float(string)
|
||||
|
||||
|
||||
@deprecated("Use corelibs_var.var_helpers.str_to_bool instead")
|
||||
def str_to_bool(string: str):
|
||||
"""
|
||||
convert string to bool
|
||||
@@ -56,10 +49,6 @@ def str_to_bool(string: str):
|
||||
Returns:
|
||||
_type_ -- _description_
|
||||
"""
|
||||
if string == "True" or string == "true":
|
||||
return True
|
||||
if string == "False" or string == "false":
|
||||
return False
|
||||
raise ValueError(f"Invalid boolean string: {string}")
|
||||
return corelibs_var.var_helpers.str_to_bool(string)
|
||||
|
||||
# __END__
|
||||
|
||||
36
uv.lock
generated
36
uv.lock
generated
@@ -111,6 +111,9 @@ name = "corelibs"
|
||||
version = "0.36.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "corelibs-datetime" },
|
||||
{ name = "corelibs-enum-base" },
|
||||
{ name = "corelibs-var" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "jmespath" },
|
||||
{ name = "jsonpath-ng" },
|
||||
@@ -127,6 +130,9 @@ dev = [
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "corelibs-datetime", specifier = ">=1.0.1", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" },
|
||||
{ name = "corelibs-enum-base", specifier = ">=1.0.0", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" },
|
||||
{ name = "corelibs-var", specifier = ">=1.0.0", index = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" },
|
||||
{ name = "cryptography", specifier = ">=46.0.3" },
|
||||
{ name = "jmespath", specifier = ">=1.0.1" },
|
||||
{ name = "jsonpath-ng", specifier = ">=1.7.0" },
|
||||
@@ -141,6 +147,36 @@ dev = [
|
||||
{ name = "pytest-cov", specifier = ">=6.2.1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "corelibs-datetime"
|
||||
version = "1.0.1"
|
||||
source = { registry = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }
|
||||
dependencies = [
|
||||
{ name = "corelibs-var" },
|
||||
]
|
||||
sdist = { url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-datetime/1.0.1/corelibs_datetime-1.0.1.tar.gz", hash = "sha256:ff58c6f824f35b87b1a5c153f65fdd82b65e42bb5a649d46d9115dc5fa61042f" }
|
||||
wheels = [
|
||||
{ url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-datetime/1.0.1/corelibs_datetime-1.0.1-py3-none-any.whl", hash = "sha256:f1a4d431f9f913dd39976a119ff8a2db34e966c61b1775c26b0da72a8bdb5ec1" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "corelibs-enum-base"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }
|
||||
sdist = { url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-enum-base/1.0.0/corelibs_enum_base-1.0.0.tar.gz", hash = "sha256:c696a297d88f674d40e5d190f396909b5f663a995ac735e545ceb5bb4907121d" }
|
||||
wheels = [
|
||||
{ url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-enum-base/1.0.0/corelibs_enum_base-1.0.0-py3-none-any.whl", hash = "sha256:c305d4063c69021aaf9ef75fbcce961039dae3c3de7820febeac7082c998a1f8" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "corelibs-var"
|
||||
version = "1.0.0"
|
||||
source = { registry = "https://git.egplusww.jp/api/packages/PyPI/pypi/simple/" }
|
||||
sdist = { url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-var/1.0.0/corelibs_var-1.0.0.tar.gz", hash = "sha256:b85d6fd3802a1b687290666e4b1dbb47cf9723aa72bf73eb004e9e4936776364" }
|
||||
wheels = [
|
||||
{ url = "https://git.egplusww.jp/api/packages/PyPI/pypi/files/corelibs-var/1.0.0/corelibs_var-1.0.0-py3-none-any.whl", hash = "sha256:a3546785bf9c94eec08b5c500b69b971e83e11d92bc0e4d3cbd9411a561fdbc2" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.12.0"
|
||||
|
||||
Reference in New Issue
Block a user