dately 2.1.3__tar.gz → 2.1.4__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 (27) hide show
  1. {dately-2.1.3 → dately-2.1.4}/PKG-INFO +11 -3
  2. {dately-2.1.3 → dately-2.1.4}/dately/core.py +27 -4
  3. {dately-2.1.3 → dately-2.1.4}/dately/timeutils.py +2 -50
  4. {dately-2.1.3 → dately-2.1.4}/dately/timezone.py +43 -64
  5. {dately-2.1.3 → dately-2.1.4}/dately.egg-info/PKG-INFO +11 -3
  6. dately-2.1.4/setup.py +53 -0
  7. dately-2.1.3/setup.py +0 -41
  8. {dately-2.1.3 → dately-2.1.4}/README.md +0 -0
  9. {dately-2.1.3 → dately-2.1.4}/dately/__init__.py +0 -0
  10. {dately-2.1.3 → dately-2.1.4}/dately/mold/__init__.py +0 -0
  11. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/Compiled.cp38-win_amd64.pyd +0 -0
  12. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/__init__.py +0 -0
  13. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/cdatetime/UniversalDateFormatter.cp38-win_amd64.pyd +0 -0
  14. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/cdatetime/__init__.py +0 -0
  15. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/cdatetime/iso8601T.cp38-win_amd64.pyd +0 -0
  16. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/cdatetime/iso8601Z.cp38-win_amd64.pyd +0 -0
  17. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/cdatetime/whichformat.cp38-win_amd64.pyd +0 -0
  18. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/clean_str.cp38-win_amd64.pyd +0 -0
  19. {dately-2.1.3 → dately-2.1.4}/dately/mold/pyd/time_zones.cp38-win_amd64.pyd +0 -0
  20. {dately-2.1.3 → dately-2.1.4}/dately/mskutils.py +0 -0
  21. {dately-2.1.3 → dately-2.1.4}/dately/sysutils.py +0 -0
  22. {dately-2.1.3 → dately-2.1.4}/dately/utils.py +0 -0
  23. {dately-2.1.3 → dately-2.1.4}/dately.egg-info/SOURCES.txt +0 -0
  24. {dately-2.1.3 → dately-2.1.4}/dately.egg-info/dependency_links.txt +0 -0
  25. {dately-2.1.3 → dately-2.1.4}/dately.egg-info/requires.txt +0 -0
  26. {dately-2.1.3 → dately-2.1.4}/dately.egg-info/top_level.txt +0 -0
  27. {dately-2.1.3 → dately-2.1.4}/setup.cfg +0 -0
@@ -1,15 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dately
3
- Version: 2.1.3
4
- Summary: A library for manipulating datetime strings.
3
+ Version: 2.1.4
4
+ Summary: A comprehensive Python library for advanced date and time manipulation.
5
5
  Home-page: https://github.com/cedricmoorejr/dately
6
6
  Author: Cedric Moore Jr.
7
- Author-email: cedric@doydl.tech
7
+ Author-email: cedricmoorejunior5@gmail.com
8
8
  License: MIT
9
9
  Project-URL: Source Code, https://github.com/cedricmoorejr/dately/tree/main/dately
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
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Natural Language :: English
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
13
21
  Requires-Python: >=3.8
14
22
  Description-Content-Type: text/markdown
15
23
 
@@ -66,7 +66,6 @@ class DatelyDate:
66
66
  """
67
67
 
68
68
  def __dir__(self):
69
- # Override the __dir__ method to exclude private methods
70
69
  original_dir = super().__dir__()
71
70
  return [item for item in original_dir if not item.startswith('_DatelyDate__')]
72
71
 
@@ -373,10 +372,34 @@ class DatelyDate:
373
372
  format, or if the tzinfo is invalid, raises an error to ensure that the function usage is clear and safe
374
373
  within expected data types.
375
374
  """
376
- if is_iso_date(datetime_strings):
377
- return self.__repl_iso_timestring(datetime_strings, *args, **kwargs)
375
+ def replace_timestring(self, datetime_strings, *args, **kwargs):
376
+ filtered_kwargs = {k: v for k, v in kwargs.items() if k != 'time_indicator'}
377
+
378
+ if isinstance(datetime_strings, str):
379
+ if is_iso_date(datetime_strings):
380
+ return self.__repl_iso_timestring(datetime_strings, *args, **filtered_kwargs)
381
+ else:
382
+ return self._repl_timestring(datetime_strings, *args, **kwargs)
383
+ elif isinstance(datetime_strings, list):
384
+ return [
385
+ self.__repl_iso_timestring(dt_string, *args, **filtered_kwargs) if is_iso_date(dt_string)
386
+ else self._repl_timestring(dt_string, *args, **kwargs)
387
+ for dt_string in datetime_strings
388
+ ]
389
+ elif isinstance(datetime_strings, np.ndarray):
390
+ vectorized_iso_func = np.vectorize(
391
+ lambda dt_string: self.__repl_iso_timestring(dt_string, *args, **filtered_kwargs) if is_iso_date(dt_string)
392
+ else self._repl_timestring(dt_string, *args, **kwargs),
393
+ otypes=[np.object]
394
+ )
395
+ return vectorized_iso_func(datetime_strings)
396
+ elif isinstance(datetime_strings, pd.Series):
397
+ return datetime_strings.apply(
398
+ lambda dt_string: self.__repl_iso_timestring(dt_string, *args, **filtered_kwargs) if is_iso_date(dt_string)
399
+ else self._repl_timestring(dt_string, *args, **kwargs)
400
+ )
378
401
  else:
379
- return self._repl_timestring(datetime_strings, *args, **kwargs)
402
+ raise ValueError("Unsupported data type. The input must be a scalar (str), list, numpy.ndarray, or pandas.Series.")
380
403
 
381
404
  def replace_datestring(self, date_strings, year=None, month=None, day=None):
382
405
  """
@@ -36,11 +36,8 @@ __all__ = [
36
36
  ]
37
37
 
38
38
  def get_time():
39
- # Get the current time as a timestamp
40
39
  current_time = time.time()
41
- # Convert timestamp to a time tuple in local time
42
40
  local_time = time.localtime(current_time)
43
- # Format the time to include hours, minutes, seconds, and microseconds
44
41
  formatted_time = time.strftime("%H:%M:%S", local_time) + f".{int(current_time % 1 * 1_000_000)}"
45
42
  return formatted_time
46
43
 
@@ -59,31 +56,23 @@ def offset_convert(number):
59
56
  str: The formatted time offset as a string with a leading sign (either '+' or '-') followed
60
57
  by two digits for hours and two digits for minutes, separated by a colon.
61
58
  """
62
- # Convert string input to float if necessary
63
59
  if isinstance(number, str):
64
60
  number = float(number)
65
-
66
61
  sign = '+' if number >= 0 else '-'
67
- # Get the absolute value
68
62
  abs_number = abs(number)
69
- # Extract hours and minutes
70
63
  hours = int(abs_number)
71
64
  minutes = int((abs_number - hours) * 60)
72
- # Format the result as +-HH:MM
73
65
  formatted_time = f"{sign}{hours:02}:{minutes:02}"
74
66
  return formatted_time
75
67
 
76
68
  def datetime_offset(offset):
77
- # Validate the offset
78
69
  if not isinstance(offset, (int, float)):
79
70
  raise ValueError("Offset must be an integer or float representing hours.")
80
- # Create a timezone object for datetime's tzinfo
81
71
  timezone = datetime.timezone(datetime.timedelta(hours=offset))
82
72
  return timezone
83
73
 
84
74
  def strTime(datetime_string):
85
75
  """ Extracts and returns detailed time information from a datetime string. """
86
- # Regex for finding time
87
76
  time_exists = timeboundary_regex.search(datetime_string)
88
77
 
89
78
  if time_exists:
@@ -91,11 +80,9 @@ def strTime(datetime_string):
91
80
  full_time_end_position = time_exists.end()
92
81
  full_time_string = time_exists.group()
93
82
 
94
- # Search for more specific time details within the extracted time string
95
83
  time_match = time_only_regex.search(full_time_string)
96
84
 
97
85
  if time_match:
98
- # If a specific time format is found, return start, end, and the actual time value as a dictionary
99
86
  time_details = {
100
87
  'time_found': time_match.group(),
101
88
  'start': time_match.start() + full_time_start_position,
@@ -132,16 +119,10 @@ def stripTimeIndicator(datetime_string):
132
119
  time_end = match['time_details']["end"]
133
120
  fulltime_start = match['full_time_details']['start']
134
121
  fulltime_end = match['full_time_details']["end"]
135
-
136
122
  timezone_data = datetime_string[time_end:]
137
-
138
123
  if timezone_data == '':
139
124
  return datetime_string
140
-
141
- # Remove the time indicator
142
125
  cleaned_string = re.sub(timemeridiem_regex, ' ', timezone_data)
143
-
144
- # Combine the parts
145
126
  return datetime_string[:fulltime_start] + time_match + cleaned_string + datetime_string[fulltime_end:]
146
127
 
147
128
  return datetime_string
@@ -182,10 +163,8 @@ def remove_marker(text):
182
163
  Returns:
183
164
  str: The cleaned text without the specified marker.
184
165
  """
185
- # Define the regex pattern to match the specific string to remove
186
166
  pattern = re.compile(r" NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET")
187
167
 
188
- # Replace the matched string with nothing (effectively removing it)
189
168
  cleaned_text = pattern.sub("", text)
190
169
  return cleaned_text
191
170
 
@@ -205,15 +184,11 @@ def make_datetime_string(date_string):
205
184
  str: A standardized datetime string. If the format is date-only, '00:00:00.000000 NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET' is appended to the date string. If the format
206
185
  includes time information or is not detected, the original date string is returned.
207
186
  """
208
- # Define the regex pattern for time extraction
209
187
  time_pattern = anytime_regex
210
188
  no_other_time_placeholder = "NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET"
211
-
212
- # Search for the time component in the date string
213
189
  match = time_pattern.search(date_string)
214
190
 
215
191
  if match:
216
- # Check if the matched time component has a valid tzinfo
217
192
  time_component = match.group()
218
193
  tzinfo_pattern = gtf('tzinfo')
219
194
  if tzinfo_pattern.search(time_component):
@@ -221,7 +196,6 @@ def make_datetime_string(date_string):
221
196
  else:
222
197
  return f"{date_string.strip()} {get_time()} {no_other_time_placeholder}"
223
198
  else:
224
- # If no time component is found, append '00:00:00.000000 NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET' to the date string
225
199
  return f"{date_string.strip()} {get_time()} {no_other_time_placeholder}"
226
200
 
227
201
  def validate_timezone(datetime_string):
@@ -233,7 +207,6 @@ def validate_timezone(datetime_string):
233
207
  timezone_data = timezone_data.lstrip()
234
208
  if timezone_data == '':
235
209
  return True, "The time string is valid."
236
- # Check the occurrences of each component
237
210
  if len(timemeridiem_regex.findall(timezone_data)) > 1:
238
211
  return False, "More than one time indicator found."
239
212
  if len(timezone_offset_regex.findall(timezone_data)) > 1:
@@ -254,27 +227,19 @@ def validate_date(date_string, date_format):
254
227
  then identifies the components of the date (month, day, year) and
255
228
  checks their validity based on standard calendar rules.
256
229
  """
257
- # Extract any localized time fragment and clean the string.
258
230
  date_str = stripTime(date_string)
259
-
260
- # This dictionary will store the spans of each component.
261
231
  components_spans = {"month": None, "day": None, "year": None}
262
-
263
- # Single pass to find month, day, and year
264
232
  pattern = datetime_pattern_search(date_format)
265
233
  match = pattern.match(date_str)
266
234
  if match:
267
- # Iterate through possible date components
268
235
  for key in components_spans.keys():
269
- if key in match.groupdict(): # Check if the current component was found in the match
270
- components_spans[key] = match.span(key) # Store the span of the component
236
+ if key in match.groupdict():
237
+ components_spans[key] = match.span(key)
271
238
 
272
- # Extract day, month, and year based on their spans in the string
273
239
  day = int(date_str[slice(*components_spans['day'])])
274
240
  month = int(date_str[slice(*components_spans['month'])])
275
241
  year = int(date_str[slice(*components_spans['year'])])
276
242
 
277
- # Define the maximum days in each month, adjusting February based on leap year
278
243
  month_days = {1: 31, 2: 29 if is_leap_year(year) else 28, 3: 31, 4: 30, 5: 31, 6: 30,
279
244
  7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31}
280
245
  if month < 1 or month > 12:
@@ -369,42 +334,29 @@ def replace_time_by_position(datetime_string, component, new_value):
369
334
 
370
335
  time_component_pattern = gtf(component)
371
336
  gt_time_pattern_match = timeboundary_regex.search(datetime_string)
372
-
373
337
  if gt_time_pattern_match:
374
338
  time_start_position = gt_time_pattern_match.start()
375
339
  time_end_position = gt_time_pattern_match.end()
376
340
  time_matched_value = gt_time_pattern_match.group()
377
-
378
341
  if component == 'tzinfo':
379
342
  new_value = offset_convert(new_value)
380
343
  time_component_matches = list(time_component_pattern.finditer(time_matched_value))
381
344
  if time_component_matches:
382
- # Find the match with the largest end position
383
345
  largest_match = max(time_component_matches, key=lambda m: m.end())
384
- # Replace all tzinfo matches with the new value
385
346
  part_before = datetime_string[:time_start_position + time_component_matches[0].start()]
386
347
  part_after = datetime_string[time_start_position + largest_match.end():]
387
348
  datetime_string = part_before + new_value + part_after
388
349
  return datetime_string.replace('NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET', new_value).strip()
389
350
  else:
390
- # Replace the placeholders with the new tzinfo value
391
351
  return datetime_string.replace('NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET', new_value).strip()
392
-
393
- # Match the specific component within the matched time
394
352
  time_component_match = time_component_pattern.search(time_matched_value)
395
353
  if time_component_match:
396
- # Replace the component with the new value
397
354
  part_before = datetime_string[:time_start_position + time_component_match.start(1)]
398
355
  part_after = datetime_string[time_start_position + time_component_match.end(1):]
399
356
 
400
357
  if component == 'microsecond':
401
358
  new_value = hundred_thousandths_place(new_value, decimal=False)
402
-
403
359
  datetime_string = part_before + new_value + part_after
404
-
405
360
  return datetime_string.replace('NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET', '').strip()
406
-
407
- # Always remove placeholders if they exist
408
361
  datetime_string = datetime_string.replace('NO_MERIDIEM_NO_TIMEZONE_NO_OFFSET', '').strip()
409
-
410
362
  return datetime_string
@@ -3,7 +3,6 @@ import json
3
3
  import tempfile
4
4
  import shutil
5
5
  import requests
6
- from requests.exceptions import ConnectionError, HTTPError, Timeout
7
6
  import time
8
7
  import random
9
8
  import re
@@ -12,8 +11,13 @@ import pandas as pd
12
11
  from .mskutils import *
13
12
  from .sysutils import *
14
13
 
14
+ TimeZoner = None
15
15
 
16
- ## API Handler
16
+
17
+ class NoAPIKeysError(Exception):
18
+ """Exception raised when no API keys are available."""
19
+ pass
20
+
17
21
  class Request:
18
22
  def __init__(self, base_url='aHR0cDovL2FwaS50aW1lem9uZWRiLmNvbS92Mi4xL2xpc3QtdGltZS16b25lP2tleT0=', use_api_key=True):
19
23
  """ Initialize the API utility with the given base URL. """
@@ -42,14 +46,27 @@ class Request:
42
46
  response = requests.get(url, timeout=10)
43
47
  response.raise_for_status()
44
48
  self.api_keys = response.json()
49
+ except requests.exceptions.HTTPError as e:
50
+ print(f"HTTP error occurred: {e}")
51
+ self.api_keys = {}
52
+ except requests.exceptions.ConnectionError as e:
53
+ print(f"Connection error occurred: {e}")
54
+ self.api_keys = {}
55
+ except requests.exceptions.Timeout as e:
56
+ print(f"Timeout occurred: {e}")
57
+ self.api_keys = {}
45
58
  except Exception as e:
59
+ print(f"An unexpected error occurred: {e}")
46
60
  self.api_keys = {}
47
-
61
+ except requests.exceptions.SSLError as e:
62
+ print(f"SSL error occurred: {e}")
63
+ self.api_keys = {}
64
+
48
65
  def get_random_key(self):
49
66
  """Get a random API key from the available keys."""
50
67
  keys = list(self.api_keys.keys())
51
68
  if not keys:
52
- raise Exception("No API keys available")
69
+ raise NoAPIKeysError("No API keys available")
53
70
 
54
71
  random_key = random.choice(keys)
55
72
  while random_key == self.last_key and len(keys) > 1:
@@ -61,12 +78,7 @@ class Request:
61
78
  return api_key
62
79
 
63
80
  def update_base_url(self, new_url):
64
- """
65
- Update the base URL for the API utility.
66
-
67
- Parameters:
68
- new_url (str): The new base URL for the API endpoint.
69
- """
81
+ """ Update the base URL for the API utility. """
70
82
  self.base_url = new_url
71
83
 
72
84
  def set_use_api_key(self, use_api_key):
@@ -97,21 +109,15 @@ class Request:
97
109
  reset_time = UnixTime.Date(self.rate_limit_reset)
98
110
  else:
99
111
  reset_time = 'unknown'
100
- # print(f"Rate limit status:\n"
101
- # f" Limit: {self.rate_limit_limit}\n"
102
- # f" Remaining: {self.rate_limit_remaining}\n"
103
- # f" Reset: {reset_time}")
104
112
 
105
113
  def make_request(self, params, headers=None):
106
114
  """ Make a request to the specified API. """
107
115
  current_time = time.time()
108
116
  time_since_last_request = current_time - self.last_request_time
109
117
 
110
- # Ensure at least 2 seconds have passed since the last request
111
118
  if time_since_last_request < 2:
112
119
  time.sleep(2 - time_since_last_request)
113
120
 
114
- # Ensure we are not exceeding the rate limit
115
121
  if self.rate_limit_remaining is not None and self.rate_limit_remaining <= 0:
116
122
  if self.rate_limit_reset is not None:
117
123
  reset_time = UnixTime.Date(self.rate_limit_reset)
@@ -132,29 +138,21 @@ class Request:
132
138
  if self.use_api_key:
133
139
  api_key = self.get_random_key()
134
140
  params['key'] = api_key
135
-
136
- # Ensure the 'format' parameter is always set to 'json' if not already specified
141
+
137
142
  if 'format' not in params:
138
143
  params['format'] = 'json'
139
144
 
140
145
  try:
141
146
  response = requests.get(self.base_url, params=params, headers=headers, timeout=10)
142
147
  response.raise_for_status()
143
-
144
- # Extract and update rate limit information
145
148
  rate_limit_info = self.extract_rate_limit_info(response.headers)
146
-
147
- self.last_request_time = time.time() # Update the last request time
148
-
149
- # Log the rate limit status automatically
149
+ self.last_request_time = time.time()
150
150
  self.log_rate_limit_status()
151
-
152
151
  return {
153
152
  'response': response.json(),
154
153
  'rate_limit_info': rate_limit_info
155
154
  }
156
155
  except Exception as e:
157
- # Log the rate limit status automatically even in case of error
158
156
  self.log_rate_limit_status()
159
157
 
160
158
  return {
@@ -165,7 +163,6 @@ class Request:
165
163
 
166
164
 
167
165
 
168
-
169
166
  class parse_zoneinfo_html_table:
170
167
  """
171
168
  This class is designed to parse an HTML IANA Time Zones table from a specified URL, clean the HTML content,
@@ -205,18 +202,13 @@ class parse_zoneinfo_html_table:
205
202
 
206
203
  def extract_table_from_html(self):
207
204
  try:
208
- # Find and extract the whole table
209
205
  table_html = self.extract_tag_contents(self.html_content, 'table', first_only=True)
210
206
  if not table_html:
211
207
  return None, None
212
-
213
- # Extract table headers
214
208
  headers = []
215
209
  headers_html = self.extract_tag_contents(table_html, 'tr', first_only=True)
216
210
  for header_content in self.extract_tag_contents(headers_html, 'th'):
217
211
  headers.append(self.clean_html(header_content).strip())
218
-
219
- # Extract table rows
220
212
  rows = []
221
213
  row_tags = self.extract_tag_contents(table_html, 'tr')[1:]
222
214
  for row_html in row_tags:
@@ -251,23 +243,18 @@ class TimezoneOffset:
251
243
  """Converts a numeric or string time offset into a formatted string representing the offset in hours and minutes."""
252
244
  @staticmethod
253
245
  def format(offset):
254
- # Check if the value is a string
255
246
  if isinstance(offset, str):
256
247
  offset = offset.strip()
257
248
 
258
- # Handle the case where the offset is "UTC"
259
249
  if offset == "UTC":
260
250
  return "+00:00"
261
251
 
262
- # Handle the case where the offset is an all-caps abbreviation
263
252
  if offset.isupper():
264
253
  return offset
265
254
 
266
- # Check if the value is already in the correct HH:MM format
267
255
  if re.match(r'^[+-]\d{2}:\d{2}$', offset):
268
256
  return offset
269
257
 
270
- # Handle the case where offset is given as a string with hours and optional minutes
271
258
  pattern_hm = r'^([+-]?)(\d{1,2})(?::?(\d{1,2})?)?$'
272
259
  match_hm = re.match(pattern_hm, str(offset).strip())
273
260
  if match_hm:
@@ -292,7 +279,6 @@ class TimezoneOffset:
292
279
  except ValueError:
293
280
  return None
294
281
 
295
- # Handle the case where offset is given as total seconds
296
282
  pattern_seconds = r'^([+-]?)(\d+)$'
297
283
  match_seconds = re.match(pattern_seconds, str(offset).strip())
298
284
  if match_seconds:
@@ -423,24 +409,21 @@ class tzoneDataManager:
423
409
  shutil.rmtree(self.storage_dir)
424
410
 
425
411
 
426
-
427
-
428
412
  class DatelyTz:
429
413
  """DatelyTz class is responsible for managing timezone data retrieval."""
430
414
  def __init__(self, rq_instance=None):
431
415
  """Initialize the DatelyTz class. Loads data from file if it exists, otherwise fetches from the API and saves it."""
432
416
  self.tzone_manager = tzoneDataManager()
433
- self.Request = rq_instance # Assign rq_instance to self.Request
417
+ self.Request = rq_instance
434
418
 
435
419
  if tzoneDataManager.data_file_exists():
436
420
  self.tzone_manager.load_data_from_file()
437
421
  self.timezonedata = self.tzone_manager.timezonedata
438
422
  else:
439
- self.timezonedata = self._from_api() # Note the change here
423
+ self.timezonedata = self._from_api()
440
424
  self.tzone_manager.timezonedata = self.timezonedata
441
425
  self.tzone_manager.save_data_to_file()
442
426
 
443
- # Update self.timezonedata to include the full dataset with IANA table data
444
427
  self.tzone_manager.load_data_from_file()
445
428
  self.timezonedata = self.tzone_manager.timezonedata
446
429
 
@@ -452,7 +435,7 @@ class DatelyTz:
452
435
  result = self.Request.make_request(params)
453
436
  if 'response' in result:
454
437
  data = result['response']
455
- return self._format_api_data(data) # Changed cls to self
438
+ return self._format_api_data(data)
456
439
  else:
457
440
  return result
458
441
 
@@ -472,18 +455,6 @@ class DatelyTz:
472
455
  return data
473
456
 
474
457
 
475
-
476
- rq_instance = Request(use_api_key=True)
477
-
478
- # Create an instance of DatelyTz
479
- tz = DatelyTz(rq_instance)
480
-
481
- # Access the time zone data from tzoneDataManager instance
482
- timezonedata = tz.tzone_manager.timezonedata
483
-
484
-
485
-
486
-
487
458
  class ZoneInfoManager:
488
459
  """
489
460
  A class to manage and query time zone information.
@@ -523,7 +494,6 @@ class ZoneInfoManager:
523
494
  self.ObservesDST = self.__get_zones_by_dst_observance()
524
495
 
525
496
  def __dir__(self):
526
- # Override the __dir__ method to exclude private methods and attributes
527
497
  original_dir = super().__dir__()
528
498
  return [item for item in original_dir if not item.startswith('_')]
529
499
 
@@ -626,16 +596,13 @@ class ZoneInfoManager:
626
596
  if not self.__Request:
627
597
  return None
628
598
 
629
- # Temporarily update the base URL
630
599
  original_url = self.__Request.base_url
631
600
  temp = Shift.type.map('aHR0cDovL3dvcmxkdGltZWFwaS5vcmcvYXBpL3RpbWV6b25lLw==', zone_name, ret=True)
632
601
  self.__Request.update_base_url(temp)
633
602
 
634
603
  try:
635
- # Make the request without parameters
636
604
  result = self.__Request.make_request(params={})
637
605
  finally:
638
- # Restore the original base URL
639
606
  self.__Request.update_base_url(original_url)
640
607
 
641
608
  if 'response' in result:
@@ -643,11 +610,23 @@ class ZoneInfoManager:
643
610
  else:
644
611
  return result
645
612
 
646
- # Create an instance of ZoneInfoManager
647
- TimeZoner = ZoneInfoManager(timezonedata, rq_instance)
648
613
 
614
+ try:
615
+ rq_instance = Request(use_api_key=True)
616
+ tz = DatelyTz(rq_instance)
617
+ timezonedata = tz.tzone_manager.timezonedata
618
+ TimeZoner = ZoneInfoManager(timezonedata, rq_instance)
619
+ except Exception as e:
620
+ print(f"Failed to initialize TimeZoner due to: {e}")
649
621
 
650
-
651
- __all__ = ['TimeZoner']
622
+ # TimeZoner Fail
623
+ if TimeZoner is None:
624
+ class ImportError(Exception):
625
+ def __init__(self, message="TimeZoner could not be imported correctly and cannot be used."):
626
+ self.message = message
627
+ super().__init__(self.message)
628
+
629
+ TimeZoner = lambda *args, **kwargs: (_ for _ in ()).throw(ImportError())
652
630
 
653
631
 
632
+ __all__ = ['TimeZoner']
@@ -1,15 +1,23 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dately
3
- Version: 2.1.3
4
- Summary: A library for manipulating datetime strings.
3
+ Version: 2.1.4
4
+ Summary: A comprehensive Python library for advanced date and time manipulation.
5
5
  Home-page: https://github.com/cedricmoorejr/dately
6
6
  Author: Cedric Moore Jr.
7
- Author-email: cedric@doydl.tech
7
+ Author-email: cedricmoorejunior5@gmail.com
8
8
  License: MIT
9
9
  Project-URL: Source Code, https://github.com/cedricmoorejr/dately/tree/main/dately
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
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Natural Language :: English
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
13
21
  Requires-Python: >=3.8
14
22
  Description-Content-Type: text/markdown
15
23
 
dately-2.1.4/setup.py ADDED
@@ -0,0 +1,53 @@
1
+ from setuptools import setup, find_packages
2
+ import sys
3
+
4
+ # Check if the system is Windows
5
+ if sys.platform != 'win32':
6
+ raise RuntimeError('This package is only compatible with Windows.')
7
+
8
+
9
+ setup(
10
+ name='dately',
11
+ version="2.1.4",
12
+ author='Cedric Moore Jr.',
13
+ author_email='cedricmoorejunior5@gmail.com',
14
+ description='A comprehensive Python library for advanced date and time manipulation.',
15
+ long_description=open('README.md').read(),
16
+ long_description_content_type='text/markdown',
17
+ url='https://github.com/cedricmoorejr/dately',
18
+ project_urls={
19
+ 'Source Code': 'https://github.com/cedricmoorejr/dately/tree/main/dately',
20
+ },
21
+ packages=find_packages(),
22
+ package_data={
23
+ 'dately': [
24
+ 'mold/pyd/*.pyd',
25
+ 'mold/pyd/cdatetime/*.pyd'
26
+ ],
27
+ },
28
+ classifiers=[
29
+ 'Programming Language :: Python :: 3',
30
+ 'License :: OSI Approved :: MIT License',
31
+ 'Operating System :: Microsoft :: Windows',
32
+ 'Development Status :: 5 - Production/Stable',
33
+ 'Intended Audience :: Developers',
34
+ 'Natural Language :: English',
35
+ 'License :: OSI Approved :: MIT License',
36
+ 'Programming Language :: Python :: 3',
37
+ 'Programming Language :: Python :: 3.8',
38
+ 'Programming Language :: Python :: 3.9',
39
+ 'Programming Language :: Python :: 3.10',
40
+ ],
41
+ python_requires='>=3.8',
42
+ install_requires=[
43
+ 'numpy',
44
+ 'pandas',
45
+ 'pytz',
46
+ 'requests',
47
+ ],
48
+ license='MIT',
49
+ )
50
+
51
+
52
+
53
+
dately-2.1.3/setup.py DELETED
@@ -1,41 +0,0 @@
1
- from setuptools import setup, find_packages
2
- import sys
3
-
4
- # Check if the system is Windows
5
- if sys.platform != 'win32':
6
- raise RuntimeError("This package is only compatible with Windows.")
7
-
8
-
9
- setup(
10
- name="dately",
11
- version="2.1.3",
12
- author="Cedric Moore Jr.",
13
- author_email="cedric@doydl.tech",
14
- description="A library for manipulating datetime strings.",
15
- long_description=open("README.md").read(),
16
- long_description_content_type="text/markdown",
17
- url="https://github.com/cedricmoorejr/dately",
18
- project_urls={
19
- "Source Code": "https://github.com/cedricmoorejr/dately/tree/main/dately",
20
- },
21
- packages=find_packages(),
22
- package_data={
23
- 'dately': [
24
- 'mold/pyd/*.pyd',
25
- 'mold/pyd/cdatetime/*.pyd'
26
- ],
27
- },
28
- classifiers=[
29
- "Programming Language :: Python :: 3",
30
- "License :: OSI Approved :: MIT License",
31
- "Operating System :: Microsoft :: Windows",
32
- ],
33
- python_requires='>=3.8',
34
- install_requires=[
35
- "numpy",
36
- "pandas",
37
- "pytz",
38
- "requests",
39
- ],
40
- license="MIT",
41
- )
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes