diff --git a/pydicts/casts.py b/pydicts/casts.py index 6e75610..19d8fa9 100644 --- a/pydicts/casts.py +++ b/pydicts/casts.py @@ -321,7 +321,7 @@ def str2date(iso, format="YYYY-MM-DD"): raise exceptions.CastException(_("I can't convert this format '{}'. I only support this {}").format(format, allowed)) def str2dtnaive(s, format): - allowed=["%Y%m%d%H%M","%Y-%m-%d %H:%M:%S","%d/%m/%Y %H:%M","%d %m %H:%M %Y","%Y-%m-%d %H:%M:%S.","%H:%M:%S", '%b %d %H:%M:%S'] + allowed=["%Y%m%d%H%M","%Y-%m-%d %H:%M:%S","%d/%m/%Y %H:%M","%d %m %H:%M %Y","%Y-%m-%d %H:%M:%S.","%H:%M:%S", '%b %d %H:%M:%S', "JsIso"] if format in allowed: if format=="%Y%m%d%H%M": dat=datetime.strptime( s, format ) @@ -346,6 +346,10 @@ def str2dtnaive(s, format): if format=='%b %d %H:%M:%S': #Apr 26 07:50:44. Year is missing so I set to current s=f"{date.today().year} {s}" return datetime.strptime(s, '%Y %b %d %H:%M:%S') + if format=="JsIso": #2021-08-21T06:27:38.294 + s=s.replace("T"," ") + dtnaive=str2dtnaive(s,"%Y-%m-%d %H:%M:%S.") + return dtnaive else: raise exceptions.CastException(_("I can't convert this format '{}'. I only support this {}").format(format, allowed)) diff --git a/pydicts/myjsonencoder.py b/pydicts/myjsonencoder.py index 9608e36..646638e 100644 --- a/pydicts/myjsonencoder.py +++ b/pydicts/myjsonencoder.py @@ -2,6 +2,7 @@ from decimal import Decimal from json import JSONEncoder, dumps, loads from base64 import b64encode, b64decode +from pydicts import casts # Forma en que debe parsearse los Decimals class DecimalsWay: @@ -94,107 +95,133 @@ def MyJSONEncoderDecimalsAsString_dumps(r, indent=4): def MyJSONEncoderDecimalsAsFloat_dumps(r, indent=4): return dumps(r, cls=MyJSONEncoderDecimalsAsFloat, indent=indent) -def get_date(s): - try: - return date.fromisoformat(s) - except: - return None - -def get_datetime(s): - try: - return datetime.fromisoformat(s) - except: - return None - -# Parse the ISO8601 duration string as hours, minutes, seconds -def get_timedelta(str): -# try: -## https://stackoverflow.com/questions/36976138/is-there-an-easy-way-to-convert-iso-8601-duration-to-timedelta -## Parse the ISO8601 duration as years,months,weeks,days, hours,minutes,seconds -## Returns: milliseconds -## Examples: "PT1H30M15.460S", "P5DT4M", "P2WT3H" - def get_isosplit(str, split): - if split in str: - n, str = str.split(split, 1) - else: - n = '0' - return n.replace(',', '.'), str # to handle like "P0,5Y" - - str = str.split('P', 1)[-1] # Remove prefix - s_yr, str = get_isosplit(str, 'Y') # Step through letter dividers - s_mo, str = get_isosplit(str, 'M') - s_wk, str = get_isosplit(str, 'W') - s_dy, str = get_isosplit(str, 'D') - _, str = get_isosplit(str, 'T') - s_hr, str = get_isosplit(str, 'H') - s_mi, str = get_isosplit(str, 'M') - s_sc, str = get_isosplit(str, 'S') - n_yr = float(s_yr) * 365 # approx days for year, month, week - n_mo = float(s_mo) * 30.4 - n_wk = float(s_wk) * 7 - dt = datetime.timedelta(days=n_yr+n_mo+n_wk+float(s_dy), hours=float(s_hr), minutes=float(s_mi), seconds=float(s_sc)) - print(dt) - return int(dt.total_seconds()*1000) ## int(dt.total_seconds()) | dt -# except: -# return None - -def get_time(s): - try: - if not ":" in s: - return None - return time.fromisoformat(s) - except: - return None - -def get_bytes(s): - try: - return b64decode(s) - except: - return None - -def get_Decimal(s): - try: - return eval(s) - except: - return None def MyJSONEncoder_loads(s): + def hooks_MyJSONEncoder(iter_value): + return hooks(iter_value, DecimalsWay.Decimal) + ############################## return loads(s, object_hook=hooks_MyJSONEncoder) def MyJSONEncoderDecimalsAsFloat_loads(s): + + def hooks_MyJSONEncoderAsFloat(iter_value): + return hooks(iter_value, DecimalsWay.Float) + #################################### return loads(s, object_hook=hooks_MyJSONEncoderAsFloat) def MyJSONEncoderDecimalsAsString_loads(s): + def hooks_MyJSONEncoderAsString(iter_value): + return hooks(iter_value, DecimalsWay.String) + ###################################### return loads(s, object_hook=hooks_MyJSONEncoderAsString) -def guess_cast(o, decimal_way): - if decimal_way==DecimalsWay.Decimal: - r=get_Decimal(o) - if r is not None: - return r - - r=get_date(o) - if r is not None: - return r - - r=get_datetime(o) - if r is not None: - return r - - r=get_time(o) - if r is not None: - return r - - r=get_bytes(o) - if r is not None: - return r - - return o def hooks(iter_value, decimals_way): """ Iterates a dict or list to cast decimals and dtaware in json.loads using objeck_hook """ + + def get_date(s): + try: + return date.fromisoformat(s) + except: + return None + + def get_dtaware(s): + try: + print(s, casts.str2dtaware(s,"JsUtcIso")) + return casts.str2dtaware(s,"JsUtcIso") + except: + return None + + def get_dtnaive(s): + try: + print(s, casts.str2dtnaive(s,"JsIso")) + return casts.str2dtnaive(s,"JsIso") + except: + return None + + # Parse the ISO8601 duration string as hours, minutes, seconds + def get_timedelta(str): + # try: + ## https://stackoverflow.com/questions/36976138/is-there-an-easy-way-to-convert-iso-8601-duration-to-timedelta + ## Parse the ISO8601 duration as years,months,weeks,days, hours,minutes,seconds + ## Returns: milliseconds + ## Examples: "PT1H30M15.460S", "P5DT4M", "P2WT3H" + def get_isosplit(str, split): + if split in str: + n, str = str.split(split, 1) + else: + n = '0' + return n.replace(',', '.'), str # to handle like "P0,5Y" + + str = str.split('P', 1)[-1] # Remove prefix + s_yr, str = get_isosplit(str, 'Y') # Step through letter dividers + s_mo, str = get_isosplit(str, 'M') + s_wk, str = get_isosplit(str, 'W') + s_dy, str = get_isosplit(str, 'D') + _, str = get_isosplit(str, 'T') + s_hr, str = get_isosplit(str, 'H') + s_mi, str = get_isosplit(str, 'M') + s_sc, str = get_isosplit(str, 'S') + n_yr = float(s_yr) * 365 # approx days for year, month, week + n_mo = float(s_mo) * 30.4 + n_wk = float(s_wk) * 7 + dt = datetime.timedelta(days=n_yr+n_mo+n_wk+float(s_dy), hours=float(s_hr), minutes=float(s_mi), seconds=float(s_sc)) + print(dt) + return int(dt.total_seconds()*1000) ## int(dt.total_seconds()) | dt + # except: + # return None + + def get_time(s): + try: + if not ":" in s: + return None + return time.fromisoformat(s) + except: + return None + + def get_bytes(s): + try: + return b64decode(s) + except: + return None + + def get_Decimal(s): + try: + return eval(s) + except: + return None + + + def guess_cast(o, decimal_way): + if decimal_way==DecimalsWay.Decimal: + r=get_Decimal(o) + if r is not None: + return r + + r=get_date(o) + if r is not None: + return r + + r=get_dtnaive(o) + if r is not None: + return r + + r=get_dtaware(o) + if r is not None: + return r + + r=get_time(o) + if r is not None: + return r + + r=get_bytes(o) + if r is not None: + return r + + return o + ######################################################## if isinstance(iter_value, dict): for k, v in iter_value.items(): if isinstance(v, dict): @@ -209,12 +236,5 @@ def hooks(iter_value, decimals_way): i=hooks(i, decimals_way) return iter_value -def hooks_MyJSONEncoder(iter_value): - return hooks(iter_value, DecimalsWay.Decimal) - -def hooks_MyJSONEncoderAsFloat(iter_value): - return hooks(iter_value, DecimalsWay.Float) -def hooks_MyJSONEncoderAsString(iter_value): - return hooks(iter_value, DecimalsWay.String) diff --git a/pydicts/tests/test_casts.py b/pydicts/tests/test_casts.py index 79e95e3..f54e5d4 100644 --- a/pydicts/tests/test_casts.py +++ b/pydicts/tests/test_casts.py @@ -196,7 +196,8 @@ def test_str2dtnaive(): # assert casts.str2dtnaive("2023-11-26 17:05:05", "%Y-%m-%d %H:%M:%S")==datetime(2023, 11, 26, 17, 5, 5) # assert casts.str2dtnaive("20231126 1705", "%Y%m%d %H%M")==datetime(2023, 11, 26, 17, 5, 5) assert casts.str2dtnaive("202311261705", "%Y%m%d%H%M")==datetime(2023, 11, 26, 17, 5) -# assert casts.str2dtnaive("2023-11-26T17:05:05Z", "JsUtcIso")==datetime(2023, 11, 26, 17, 5, 5) + assert casts.str2dtnaive("2023-11-26T17:05:05.123456", "JsIso")==datetime(2023, 11, 26, 17, 5, 5, 123456) + assert casts.str2dtnaive("2023-11-26T17:05:05", "JsIso")==datetime(2023, 11, 26, 17, 5, 5) def test_str2dtaware(): diff --git a/pydicts/tests/test_myjsonencoder.py b/pydicts/tests/test_myjsonencoder.py index 189bc26..8d2bd49 100644 --- a/pydicts/tests/test_myjsonencoder.py +++ b/pydicts/tests/test_myjsonencoder.py @@ -1,5 +1,6 @@ from decimal import Decimal -from datetime import timezone, date, datetime, time, timedelta +from datetime import date, datetime, time, timedelta +from zoneinfo import ZoneInfo from pydicts import myjsonencoder d={} d["None"]=None @@ -9,7 +10,7 @@ d["Datetime"]=datetime.now() d["Timedelta"]=timedelta(hours=4, days=2, minutes=12, seconds=12) d["Time"]=time(12, 12, 12, 123456) -d["Datetime aware"]=d["Datetime"].replace(tzinfo=timezone.utc) +d["Datetime aware"]=d["Datetime"].replace(tzinfo=ZoneInfo("UTC")) d["Bytes"]=b"Byte array" d["Decimal"]=Decimal("12.12123414")