@@ 278,15 278,18 @@ 2024-01-06 06:00:00+00:00 5.0
# when called with the option 'inferred_freq'
# new index are created (good)
- # but some values disappeared (bad)
+ # and the old ones are also kept (also good)
ts = tsx.get('irregular_freq', inferred_freq=True)
assert_df("""
2024-01-01 00:00:00+00:00 0.0
2024-01-02 00:00:00+00:00 1.0
2024-01-03 00:00:00+00:00 2.0
2024-01-04 00:00:00+00:00 NaN
+2024-01-04 06:00:00+00:00 3.0
2024-01-05 00:00:00+00:00 NaN
+2024-01-05 06:00:00+00:00 4.0
2024-01-06 00:00:00+00:00 NaN
+2024-01-06 06:00:00+00:00 5.0
""", ts)
@@ 448,6 448,96 @@ def parse_delta(td):
minutes=int(minutes), seconds=int(seconds)
)
+ old_index = ts.index
+ freq = infer_freq(ts)[0]
+ tzaware = ts_start.tz is not None
+ to_value_date = compatible_date(tzaware, to_value_date)
+ from_value_date = compatible_date(tzaware, from_value_date)
+
+ if from_value_date is None and to_value_date is None:
+ new_index = pd.date_range(
+ start=ts_start,
+ end=ts_end,
+ freq=freq
+ )
+ return ts.reindex(new_index.union(old_index))
+
+ if from_value_date is None:
+ new_index = pd.date_range(
+ start=ts_start,
+ end=to_value_date,
+ freq=freq
+ )
+ return ts.reindex(new_index.union(old_index))
+
+ if to_value_date is None:
+ new_index = pd.date_range(
+ start=ts_end,
+ end=from_value_date,
+ freq=-freq
+ ).sort_values()
+ return ts.reindex(new_index.union(old_index))
+
+ # we have to build the index in two parts
+ new_index = pd.date_range(
+ start=ts_start,
+ end=to_value_date,
+ freq=freq
+ )
+ complement = pd.date_range(
+ start=ts_start,
+ end=from_value_date,
+ freq=-freq
+ )
+ new_index = new_index.union(complement).sort_values()
+ return ts.reindex(new_index.union(old_index))
+
+
+def guard_insert(newts, name, author, metadata, insertion_date):
+ assert len(name), 'Name is an empty string'
+ assert isinstance(author, str), 'Author is not a string'
+ assert metadata is None or isinstance(metadata, dict), (
+ f'Bad format for metadata ({repr(metadata)})'
+ )
+ assert (insertion_date is None or
+ isinstance(insertion_date, datetime)), 'Bad format for insertion date'
+ assert isinstance(newts, pd.Series), 'Not a pd.Series'
+ index = newts.index
+ assert isinstance(index, pd.DatetimeIndex), 'You must provide a DatetimeIndex'
+ assert not index.duplicated().any(), 'There are some duplicates in the index'
+
+ assert index.notna().all(), 'The index contains NaT entries'
+ if index.tz is not None:
+ newts.index = index.tz_convert('UTC')
+ if not index.is_monotonic_increasing:
+ newts = newts.sort_index()
+
+ return num2float(newts)
+
+
+def guard_query_dates(*dates):
+ assert all(
+ isinstance(dt, datetime)
+ for dt in filter(None, dates)
+ ), 'all query dates must be datetime-compatible objects'
+
+
+# timedelta (de)serialisation
+
+def delta_isoformat(td):
+ return f'P{td.days}DT0H0M{td.seconds}S'
+
+
+_DELTA = re.compile('P(.*)DT(.*)H(.*)M(.*)S')
+def parse_delta(td):
+ match = _DELTA.match(td)
+ if not match:
+ raise Exception(f'unparseable time delta `{td}`')
+ days, hours, minutes, seconds = match.groups()
+ return pd.Timedelta(
+ days=int(days), hours=int(hours),
+ minutes=int(minutes), seconds=int(seconds)
+ )
# metadata