dately 2.1.4__tar.gz → 2.2.0b2__tar.gz

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.
Files changed (55) hide show
  1. {dately-2.1.4 → dately-2.2.0b2}/PKG-INFO +4 -6
  2. dately-2.2.0b2/dately/mold/include/clean_str.h +7 -0
  3. dately-2.2.0b2/dately/mold/include/iso8601T.h +12 -0
  4. dately-2.2.0b2/dately/mold/include/iso8601Z.h +6 -0
  5. dately-2.2.0b2/dately/mold/include/root_dir_search.h +14 -0
  6. dately-2.2.0b2/dately/mold/include/time_zones.h +21 -0
  7. dately-2.2.0b2/dately/mold/pyx/Compiled.pyx +186 -0
  8. dately-2.2.0b2/dately/mold/pyx/UniversalDateFormatter.pyx +189 -0
  9. dately-2.2.0b2/dately/mold/pyx/clean_str.pyx +10 -0
  10. dately-2.2.0b2/dately/mold/pyx/iso8601T.pyx +5 -0
  11. dately-2.2.0b2/dately/mold/pyx/iso8601Z.pyx +12 -0
  12. dately-2.2.0b2/dately/mold/pyx/time_zones.pyx +41 -0
  13. dately-2.2.0b2/dately/mold/pyx/whichformat.pyx +331 -0
  14. dately-2.2.0b2/dately/mold/src/Compiled.c +13474 -0
  15. dately-2.2.0b2/dately/mold/src/UniversalDateFormatter.c +15740 -0
  16. dately-2.2.0b2/dately/mold/src/clean_str.c +6572 -0
  17. dately-2.2.0b2/dately/mold/src/clean_str_impl.c +35 -0
  18. dately-2.2.0b2/dately/mold/src/iso8601T.c +6178 -0
  19. dately-2.2.0b2/dately/mold/src/iso8601T_impl.c +160 -0
  20. dately-2.2.0b2/dately/mold/src/iso8601Z.c +6275 -0
  21. dately-2.2.0b2/dately/mold/src/iso8601Z_impl.c +19 -0
  22. dately-2.2.0b2/dately/mold/src/root_dir_search_impl.c +53 -0
  23. dately-2.2.0b2/dately/mold/src/time_zones.c +6234 -0
  24. dately-2.2.0b2/dately/mold/src/time_zones_impl.c +2166 -0
  25. dately-2.2.0b2/dately/mold/src/whichformat.c +18038 -0
  26. {dately-2.1.4 → dately-2.2.0b2}/dately/mskutils.py +5 -5
  27. dately-2.2.0b2/dately/sources/__init__.py +2 -0
  28. dately-2.2.0b2/dately/sources/iana_zones.json +1 -0
  29. dately-2.2.0b2/dately/sources/timezone_data.json +4178 -0
  30. {dately-2.1.4 → dately-2.2.0b2}/dately/timezone.py +188 -304
  31. {dately-2.1.4 → dately-2.2.0b2}/dately/utils.py +3 -0
  32. {dately-2.1.4 → dately-2.2.0b2}/dately.egg-info/PKG-INFO +4 -6
  33. dately-2.2.0b2/dately.egg-info/SOURCES.txt +51 -0
  34. dately-2.2.0b2/setup.py +149 -0
  35. dately-2.1.4/dately.egg-info/SOURCES.txt +0 -24
  36. dately-2.1.4/setup.py +0 -53
  37. {dately-2.1.4 → dately-2.2.0b2}/README.md +0 -0
  38. {dately-2.1.4 → dately-2.2.0b2}/dately/__init__.py +0 -0
  39. {dately-2.1.4 → dately-2.2.0b2}/dately/core.py +0 -0
  40. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/__init__.py +0 -0
  41. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/Compiled.cp38-win_amd64.pyd +0 -0
  42. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/__init__.py +0 -0
  43. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/cdatetime/UniversalDateFormatter.cp38-win_amd64.pyd +0 -0
  44. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/cdatetime/__init__.py +0 -0
  45. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/cdatetime/iso8601T.cp38-win_amd64.pyd +0 -0
  46. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/cdatetime/iso8601Z.cp38-win_amd64.pyd +0 -0
  47. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/cdatetime/whichformat.cp38-win_amd64.pyd +0 -0
  48. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/clean_str.cp38-win_amd64.pyd +0 -0
  49. {dately-2.1.4 → dately-2.2.0b2}/dately/mold/pyd/time_zones.cp38-win_amd64.pyd +0 -0
  50. {dately-2.1.4 → dately-2.2.0b2}/dately/sysutils.py +0 -0
  51. {dately-2.1.4 → dately-2.2.0b2}/dately/timeutils.py +0 -0
  52. {dately-2.1.4 → dately-2.2.0b2}/dately.egg-info/dependency_links.txt +0 -0
  53. {dately-2.1.4 → dately-2.2.0b2}/dately.egg-info/requires.txt +0 -0
  54. {dately-2.1.4 → dately-2.2.0b2}/dately.egg-info/top_level.txt +0 -0
  55. {dately-2.1.4 → dately-2.2.0b2}/setup.cfg +0 -0
@@ -1,20 +1,18 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dately
3
- Version: 2.1.4
3
+ Version: 2.2.0b2
4
4
  Summary: A comprehensive Python library for advanced date and time manipulation.
5
- Home-page: https://github.com/cedricmoorejr/dately
5
+ Home-page: https://github.com/cedricmoorejr/dately/tree/v2.2.0-b.2
6
6
  Author: Cedric Moore Jr.
7
7
  Author-email: cedricmoorejunior5@gmail.com
8
8
  License: MIT
9
- Project-URL: Source Code, https://github.com/cedricmoorejr/dately/tree/main/dately
9
+ Project-URL: Source Code, https://github.com/cedricmoorejr/dately/releases/tag/v2.2.0-beta.2
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: License :: OSI Approved :: MIT License
12
12
  Classifier: Operating System :: Microsoft :: Windows
13
- Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Development Status :: 4 - Beta
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: Natural Language :: English
16
- Classifier: License :: OSI Approved :: MIT License
17
- Classifier: Programming Language :: Python :: 3
18
16
  Classifier: Programming Language :: Python :: 3.8
19
17
  Classifier: Programming Language :: Python :: 3.9
20
18
  Classifier: Programming Language :: Python :: 3.10
@@ -0,0 +1,7 @@
1
+ // clean_str.h
2
+ #ifndef CLEAN_STR_H
3
+ #define CLEAN_STR_H
4
+
5
+ void clean_string(const char* input, char* output);
6
+
7
+ #endif // CLEAN_STR_H
@@ -0,0 +1,12 @@
1
+ #ifndef ISO8601T_H
2
+ #define ISO8601T_H
3
+
4
+ int is_leap_year(int year);
5
+ int validate_date(int year, int month, int day);
6
+ int validate_time(int hour, int minute, int second);
7
+ int is_digit(char c);
8
+ int check_basic_format(const char* date_string);
9
+ int check_extended_format(const char* date_string);
10
+ int find_date_match(const char* date_string);
11
+
12
+ #endif // ISO8601T_H
@@ -0,0 +1,6 @@
1
+ #ifndef ISO8601Z_H
2
+ #define ISO8601Z_H
3
+
4
+ void replace_zulu_suffix_with_utc(char *datetime_string);
5
+
6
+ #endif // ISO8601Z_H
@@ -0,0 +1,14 @@
1
+ #ifndef ROOR_DIR_SEARCH_H
2
+ #define ROOR_DIR_SEARCH_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ char* find_directory(const char *start_path, const char *dir_name);
9
+
10
+ #ifdef __cplusplus
11
+ }
12
+ #endif
13
+
14
+ #endif // ROOR_DIR_SEARCH_H
@@ -0,0 +1,21 @@
1
+ // time_zones.h
2
+ #ifndef TIME_ZONES_H
3
+ #define TIME_ZONES_H
4
+
5
+ typedef struct {
6
+ char full_name[50];
7
+ char region[50];
8
+ char offset[11];
9
+ char type[10];
10
+ char dst[7];
11
+ } TimeZoneInfo;
12
+
13
+ typedef struct {
14
+ char code[7];
15
+ TimeZoneInfo info;
16
+ } TimeZone;
17
+
18
+ extern TimeZone time_zones[];
19
+ extern int time_zones_count;
20
+
21
+ #endif // TIME_ZONES_H
@@ -0,0 +1,186 @@
1
+ # Compiled.pyx
2
+ import re
3
+ import os
4
+ import sys
5
+ from libc.string cimport strdup
6
+ from libc.stdlib cimport free # Import free to release allocated memory
7
+ from cython cimport unicode, boundscheck, wraparound
8
+ from cpython cimport array
9
+
10
+ cdef extern from "root_dir_search.h":
11
+ char* find_directory(const char *start_path, const char *dir_name)
12
+
13
+ def get_directory_path(directory_name):
14
+ script_dir = os.path.dirname(os.path.abspath(__file__))
15
+ directory_path_c = find_directory(script_dir.encode('utf-8'), directory_name.encode('utf-8'))
16
+ if directory_path_c:
17
+ directory_path = directory_path_c.decode('utf-8')
18
+ free(directory_path_c) # Free the allocated memory
19
+ return directory_path
20
+ else:
21
+ raise EnvironmentError(f"{directory_name} directory not found.")
22
+
23
+ # Find the dately path
24
+ dately_path = get_directory_path("dately")
25
+ if dately_path:
26
+ sys.path.append(dately_path)
27
+ else:
28
+ raise EnvironmentError("dately directory not found.")
29
+
30
+ from mold.pyd.time_zones import time_zones_dict
31
+
32
+
33
+
34
+
35
+ cdef dict __datetime_named_group_patterns__ = {
36
+ '%a': r'(?P<weekday>Mon|Tue|Wed|Thu|Fri|Sat|Sun)',
37
+ '%A': r'(?P<weekday>Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)',
38
+ '%w': r'(?P<weekday>\d)',
39
+ '%u': r'\d',
40
+ '%b': r'(?P<month>[A-Za-z]{3})',
41
+ '%B': r'(?P<month>[A-Za-z]+)',
42
+ '%m': r'(?P<month>\d{1,2})',
43
+ '%-m': r'(?P<month>\d{1,2})',
44
+ '%d': r'(?P<day>\d{1,2})',
45
+ '%-d': r'(?P<day>\d{1,2})',
46
+ '%j': r'(?P<day>\d{1,3})',
47
+ '%-H': r'(?P<hour24>(?:[0-9]|1[0-9]|2[0-3]|\d{1,2}))',
48
+ '%H': r'(?P<hour24>(?:\d{1,2}|0?[0-9]|1[0-9]|2[0-3]))',
49
+ '%I': r'(?P<hour12>(?:\d{1,2}|0?[1-9]|1[0-2]))',
50
+ '%-I': r'(?P<hour12>(?:\d{1,2}|0?[1-9]|1[0-2]))',
51
+ '%M': r'(?P<minute>\d{1,2})',
52
+ '%-M': r'(?P<minute>\d{1,2})',
53
+ '%S': r'(?P<second>\d{1,2})',
54
+ '%-S': r'(?P<second>\d{1,2})',
55
+ '%f': r'(?P<microsecond>\d{1,6})',
56
+ '%p': r'(?P<am_pm>(?:AM|PM))',
57
+ '%-y': r'(?P<year>\d{2})',
58
+ '%y': r'(?P<year>\d{2})',
59
+ '%Y': r'(?P<year>\d{4})',
60
+ '%z': r'(?P<timezone>[\+\-](?:\d{4}|\d{2}:?\d{2}))',
61
+ '%Z': r'(?P<timezone>(?:[\+\-]\d{2}:[0-9]{2}|[A-Za-z\s]+|[A-Za-z]{2,4}|UTC|[\+\-]\d{2}:?\d{2}))',
62
+ '%q': r'\d',
63
+ '%U': r'\d{1,2}',
64
+ '%V': r'\d{1,2})',
65
+ }
66
+
67
+ cpdef object datetime(unicode fmt):
68
+ cdef unicode regex = fmt
69
+ cdef unicode key, value
70
+ for key, value in __datetime_named_group_patterns__.items():
71
+ regex = regex.replace(key, value)
72
+ return re.compile(u'^' + regex + u'$')
73
+
74
+ # Create regex patterns for timezone abbreviations and full timezone names
75
+ timezone_abbrv = list(time_zones_dict.keys())
76
+ timezone_abbrv_pattern = r'\b(' + '|'.join(timezone_abbrv) + r')\b'
77
+ full_names = [info['full_name'] for info in time_zones_dict.values()]
78
+ full_names_pattern = r'\b(' + '|'.join(full_names) + r')\b'
79
+
80
+ # Define the regex patterns in a dictionary
81
+ regex_patterns = {
82
+ "timemeridiem": r'\s*\b(AM|PM)\b\s*',
83
+ # Purpose: Matches a time string in the format HH:MM:SS or HH:MM:SS.microseconds.
84
+ "timeonly": r'(?P<hours>\d{1,2}):(?P<minutes>\d{2}):(?P<seconds>\d{2})(?:\.(?P<microseconds>\d+))?',
85
+ "timezone_offset": r'(?<!\d)[+-]?(?:\d{1,2}(?::\d{1,2})?|\d{3,4})(?!\d)',
86
+ "iana_timezone_identifier": r'\b[A-Za-z_]+/[A-Za-z_]+\b',
87
+ "anytime": (
88
+ r"(?<!\d)(\d{1,2}:\d{2}:\d{2}|\d{6})"
89
+ r"(?:\.\d{1,6})?"
90
+ r"(?:\s*[AP]M)?"
91
+ r"(?:\s*(?:[+-]\d{2}:?\d{2}|[+-]\d{4}|[A-Z]{3,4}|Z))?"
92
+ r"(?=\s|$)"
93
+ ),
94
+ # Purpose: Matches various time string formats, including HH:MM:SS, HHMMSS, with optional microseconds, AM/PM indicators, and time zone information.
95
+ "timeplus": (
96
+ r"(\d{1,2}:\d{2}:\d{2}|\d{6})"
97
+ r"(?:\.\d{1,6})?"
98
+ r"(?:\s*[AP]M)?"
99
+ r"(?:\s*(?:[+-]\d{2}:?\d{2}|[+-]\d{4}|[A-Z]{3,4}|Z))?"
100
+ ),
101
+ "establish_time_boundary": r'(?<!\d)(\d{1,2}:\S.*)',
102
+ "datetime_second": r'\d{2}:\d{2}:(\d{2})(?:\.\d+)?',
103
+ "datetime_minute": r'\d{2}:(\d{2})(:\d{2}(?:\.\d+)?)?',
104
+ "datetime_hour": r'(\d{1,2}):(\d{2})(:\d{2}(?:\.\d+)?)?',
105
+ "datetime_microsecond": r'\d{2}:\d{2}:\d{2}\.(\d+)',
106
+ "datetime_timezone": (
107
+ r'\b\d{1,2}(:\d{2})?Z\b|' # Zulu time (UTC)
108
+ r'[\+\-]\d{2}:?\d{2}|' # UTC offset
109
+ r'\b[A-Za-z]+/[A-Za-z_]+\b|' # Continent/City format
110
+ r'\bAM\b|\bPM\b' # AM/PM indicator
111
+ ),
112
+ "timezone_abbreviation": timezone_abbrv_pattern,
113
+ "full_timezone_name": full_names_pattern
114
+ }
115
+
116
+ # Function to compile the regex patterns
117
+ cpdef dict compile_regex_patterns():
118
+ cdef dict compiled_patterns = {}
119
+ for key, pattern in regex_patterns.items():
120
+ compiled_patterns[key] = re.compile(pattern, re.IGNORECASE)
121
+ return compiled_patterns
122
+
123
+ # Compile the regex patterns at the module level
124
+ compiled_patterns = compile_regex_patterns()
125
+
126
+ # Function to get compiled pattern by name
127
+ cdef object get_pattern(str name):
128
+ return compiled_patterns.get(name, None)
129
+
130
+ # Call the get_pattern function to retrieve the compiled regex
131
+ second_regex = get_pattern("datetime_second")
132
+ minute_regex = get_pattern("datetime_minute")
133
+ hour_regex = get_pattern("datetime_hour")
134
+ microsecond_regex = get_pattern("datetime_microsecond")
135
+ timezone_regex = get_pattern("datetime_timezone")
136
+ timezone_offset_regex = get_pattern("timezone_offset")
137
+ iana_timezone_identifier_regex = get_pattern("iana_timezone_identifier")
138
+ timemeridiem_regex = get_pattern("timemeridiem")
139
+ time_only_regex = get_pattern("timeonly")
140
+ anytime_regex = get_pattern("anytime")
141
+ timeboundary_regex = get_pattern("establish_time_boundary")
142
+ timezone_abbreviation_regex = get_pattern("timezone_abbreviation")
143
+ full_timezone_name_regex = get_pattern("full_timezone_name")
144
+ timeplus_regex = get_pattern("timeplus")
145
+
146
+ cdef class cRegexps:
147
+ cdef dict time_component_patterns
148
+
149
+ def __cinit__(self):
150
+ self.time_component_patterns = {
151
+ "second": second_regex,
152
+ "minute": minute_regex,
153
+ "hour": hour_regex,
154
+ "microsecond": microsecond_regex,
155
+ "tzinfo": timezone_regex
156
+ }
157
+
158
+ cpdef object get_time_fragments(self, str component):
159
+ return self.time_component_patterns.get(component)
160
+
161
+ # Create an instance of the cRegexps class
162
+ cdef cRegexps regexps = cRegexps()
163
+
164
+ # Define module-level variables for compiled regex patterns
165
+ datetime_regex = datetime
166
+ get_time_fragment = regexps.get_time_fragments
167
+
168
+ # Define public interface
169
+ __all__ = [
170
+ 'datetime_regex',
171
+ 'timemeridiem_regex',
172
+ 'anytime_regex',
173
+ 'timezone_regex',
174
+ 'timeboundary_regex',
175
+ 'second_regex',
176
+ 'minute_regex',
177
+ 'hour_regex',
178
+ 'microsecond_regex',
179
+ 'get_time_fragment',
180
+ 'time_only_regex',
181
+ 'timeplus_regex',
182
+ 'iana_timezone_identifier_regex',
183
+ 'timezone_offset_regex',
184
+ 'timezone_abbreviation_regex',
185
+ 'full_timezone_name_regex',
186
+ ]
@@ -0,0 +1,189 @@
1
+ from cython cimport cdivision
2
+ from cpython cimport bool
3
+ from cpython.datetime cimport datetime
4
+ import re
5
+
6
+ __all__ = [
7
+ 'zero_handling_date_formats',
8
+ 'non_padded_to_zero_padded_specifiers',
9
+ 'has_leading_zero',
10
+ 'date_format_leading_zero',
11
+ 'replace_non_padded_with_padded',
12
+ ]
13
+
14
+
15
+ # Declare the dictionary types for better performance
16
+ cdef dict _zero_handling_date_formats
17
+ cdef dict _non_padded_to_zero_padded_specifiers
18
+
19
+ # Initialize the dictionaries
20
+ _zero_handling_date_formats = {
21
+ 'day': {
22
+ 'no_leading_zero': {
23
+ 'format': '%-d',
24
+ 'description': 'Day of the month as a decimal number without leading zero (1 to 31)'
25
+ },
26
+ 'zero_padded': {
27
+ 'format': '%d',
28
+ 'description': 'Day of the month as a zero-padded decimal number (01 to 31)'
29
+ }
30
+ },
31
+ 'month': {
32
+ 'no_leading_zero': {
33
+ 'format': '%-m',
34
+ 'description': 'Month as a decimal number without leading zero (1 to 12)'
35
+ },
36
+ 'zero_padded': {
37
+ 'format': '%m',
38
+ 'description': 'Month as a zero-padded decimal number (01 to 12)'
39
+ }
40
+ },
41
+ 'year': {
42
+ 'no_leading_zero': {
43
+ 'format': '%-y',
44
+ 'description': 'Year without century as a decimal number without leading zero (0 to 99)'
45
+ },
46
+ 'zero_padded': {
47
+ 'format': '%y',
48
+ 'description': 'Year without century as a zero-padded decimal number (00 to 99)'
49
+ }
50
+ },
51
+ 'hour24': {
52
+ 'no_leading_zero': {
53
+ 'format': '%-H',
54
+ 'description': 'Hour (24-hour clock) as a decimal number without leading zero (0 to 23)'
55
+ },
56
+ 'zero_padded': {
57
+ 'format': '%H',
58
+ 'description': 'Hour (24-hour clock) as a zero-padded decimal number (00 to 23)'
59
+ }
60
+ },
61
+ 'hour12': {
62
+ 'no_leading_zero': {
63
+ 'format': '%-I',
64
+ 'description': 'Hour (12-hour clock) as a decimal number without leading zero (1 to 12)'
65
+ },
66
+ 'zero_padded': {
67
+ 'format': '%I',
68
+ 'description': 'Hour (12-hour clock) as a zero-padded decimal number (01 to 12)'
69
+ }
70
+ },
71
+ 'minute': {
72
+ 'no_leading_zero': {
73
+ 'format': '%-M',
74
+ 'description': 'Minute as a decimal number without leading zero (0 to 59)'
75
+ },
76
+ 'zero_padded': {
77
+ 'format': '%M',
78
+ 'description': 'Minute as a zero-padded decimal number (00 to 59)'
79
+ }
80
+ },
81
+ 'second': {
82
+ 'no_leading_zero': {
83
+ 'format': '%-S',
84
+ 'description': 'Second as a decimal number without leading zero (0 to 59)'
85
+ },
86
+ 'zero_padded': {
87
+ 'format': '%S',
88
+ 'description': 'Second as a zero-padded decimal number (00 to 59)'
89
+ }
90
+ }
91
+ }
92
+
93
+ _non_padded_to_zero_padded_specifiers = {
94
+ '%-d': '%d',
95
+ '%-m': '%m',
96
+ '%-y': '%y',
97
+ '%-H': '%H',
98
+ '%-I': '%I',
99
+ '%-M': '%M',
100
+ '%-S': '%S'
101
+ }
102
+
103
+ # Use cdef to declare return types for functions
104
+ cdef dict get_zero_handling_date_formats():
105
+ return _zero_handling_date_formats
106
+
107
+ cdef dict get_non_padded_to_zero_padded_specifiers():
108
+ return _non_padded_to_zero_padded_specifiers
109
+
110
+ def zero_handling_date_formats():
111
+ return get_zero_handling_date_formats()
112
+
113
+ def non_padded_to_zero_padded_specifiers(ret_format=None):
114
+ if ret_format == 'no_leading_zero':
115
+ return list(_non_padded_to_zero_padded_specifiers.keys())
116
+ elif ret_format == 'leading_zero':
117
+ return list(_non_padded_to_zero_padded_specifiers.values())
118
+ else:
119
+ return _non_padded_to_zero_padded_specifiers
120
+
121
+
122
+ @cdivision(True)
123
+ def has_leading_zero(object input_str):
124
+ cdef str integer_part
125
+ cdef int len_integer_part
126
+
127
+ input_str = str(input_str)
128
+
129
+ # Check if the converted input string is a valid number
130
+ if not input_str.replace('-', '', 1).replace('.', '', 1).isdigit():
131
+ return None # Return None if it's not a valid number
132
+
133
+ # Special case for "0"
134
+ if input_str == "0" or input_str == "-0":
135
+ return None # Return None for "0" or "-0"
136
+
137
+ # Check if the integer part has exactly two digits
138
+ integer_part = input_str.split('.')[0].strip('-')
139
+ len_integer_part = len(integer_part)
140
+
141
+ if len_integer_part == 2 and integer_part != '00':
142
+ return None # Return None for whole numbers with exactly two digits, except '00'
143
+
144
+ # Check if the integer part has more than two digits
145
+ if len_integer_part > 2:
146
+ return None # Return None if the integer part has more than two digits
147
+
148
+ # Check for leading zero in whole numbers or in the integer part of decimal numbers
149
+ if input_str[0] == '-' and input_str[1] == '0':
150
+ return True
151
+ elif input_str[0] == '0' and input_str[1] != '.':
152
+ return True
153
+ elif input_str.startswith('0.') and input_str != '0.':
154
+ return True
155
+
156
+ return False
157
+
158
+
159
+ # Helper function to check format specifications
160
+ cdef bint has_non_padded_specifier(str format_string):
161
+ non_padded_specifiers = non_padded_to_zero_padded_specifiers(ret_format='no_leading_zero')
162
+ return any(spec in format_string for spec in non_padded_specifiers)
163
+
164
+ cpdef str date_format_leading_zero(datetime date, str format_string):
165
+ cdef str platform_compatible_format, formatted_date, orig, repl
166
+ if has_non_padded_specifier(format_string):
167
+ platform_compatible_format = format_string
168
+ for orig, repl in non_padded_to_zero_padded_specifiers().items():
169
+ platform_compatible_format = platform_compatible_format.replace(orig, repl)
170
+
171
+ formatted_date = date.strftime(platform_compatible_format)
172
+
173
+ for orig in non_padded_to_zero_padded_specifiers(ret_format='no_leading_zero'):
174
+ if orig in format_string:
175
+ formatted_date = re.sub(r'\b0(?=\d)', '', formatted_date)
176
+ return formatted_date
177
+ else:
178
+ return date.strftime(format_string)
179
+
180
+
181
+ cpdef str replace_non_padded_with_padded(str input_format):
182
+ cdef str category, non_padded, zero_padded
183
+ # Directly use the dictionary without additional function calls for better performance
184
+ for category in _zero_handling_date_formats:
185
+ non_padded = _zero_handling_date_formats[category]['no_leading_zero']['format']
186
+ zero_padded = _zero_handling_date_formats[category]['zero_padded']['format']
187
+ input_format = input_format.replace(non_padded, zero_padded)
188
+ return input_format
189
+
@@ -0,0 +1,10 @@
1
+ # clean_str.pyx
2
+ cdef extern from "clean_str.h":
3
+ void clean_string(const char* input, char* output)
4
+
5
+ def cleanstr(input_string):
6
+ cdef bytes input_bytes = input_string.encode('utf-8') # Ensure the bytes object persists
7
+ cdef const char* input_c = input_bytes # Safe cast to a C string
8
+ cdef char output_c[150] # Adjust the size based on expected input lengths
9
+ clean_string(input_c, output_c)
10
+ return output_c.decode('utf-8')
@@ -0,0 +1,5 @@
1
+ cdef extern from "iso8601T.h":
2
+ int find_date_match(const char* date_string)
3
+
4
+ cpdef bint isISOT(str date_string):
5
+ return find_date_match(date_string.encode('utf-8')) != 0
@@ -0,0 +1,12 @@
1
+ cdef extern from "iso8601z.h":
2
+ void replace_zulu_suffix_with_utc(char *datetime_string)
3
+
4
+ cpdef str replaceZ(str datetime_string):
5
+ cdef bytes datetime_bytes = datetime_string.encode('utf-8')
6
+ cdef char *datetime_cstring = datetime_bytes
7
+
8
+ # Call the C function
9
+ replace_zulu_suffix_with_utc(datetime_cstring)
10
+
11
+ # Convert the C string back to a Python string
12
+ return datetime_cstring.decode('utf-8')
@@ -0,0 +1,41 @@
1
+ # time_zones.pyx
2
+ cdef extern from "time_zones.h":
3
+ ctypedef struct TimeZoneInfo:
4
+ char full_name[50]
5
+ char region[50]
6
+ char offset[11]
7
+ char type[10]
8
+ char dst[7]
9
+
10
+ ctypedef struct TimeZone:
11
+ char code[7]
12
+ TimeZoneInfo info
13
+
14
+ extern TimeZone time_zones[]
15
+ extern int time_zones_count
16
+
17
+ cdef dict get_time_zones_impl():
18
+ cdef dict result = {}
19
+ cdef int i
20
+ cdef TimeZone tz
21
+ for i in range(time_zones_count):
22
+ tz = time_zones[i]
23
+ result[tz.code.decode('utf-8')] = {
24
+ "full_name": tz.info.full_name.decode('utf-8'),
25
+ "region": tz.info.region.decode('utf-8'),
26
+ "offset": tz.info.offset.decode('utf-8'),
27
+ "type": tz.info.type.decode('utf-8'),
28
+ "dst": tz.info.dst.decode('utf-8')
29
+ }
30
+ return result
31
+
32
+ cpdef dict get_time_zones():
33
+ return get_time_zones_impl()
34
+
35
+ # Assign dict
36
+ time_zones_dict = get_time_zones()
37
+
38
+ # Define public interface
39
+ __all__ = [
40
+ 'time_zones_dict'
41
+ ]