Skip to content

Commit 45d93b6

Browse files
add split_datetime (#1811)
add split_datetime Made in [Fused Workbench](https://www.fused.io/workbench) --------- Co-authored-by: sina@fused.io <sina@fused.io> Co-authored-by: Sina Kashuk <sina@nyu.edu>
1 parent 3ff5f24 commit 45d93b6

1 file changed

Lines changed: 61 additions & 1 deletion

File tree

public/common/common.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,64 @@ def from_path(path: str):
24402440

24412441
return obj
24422442

2443+
2444+
def parse_date(date_str: str, is_end: bool = False):
2445+
"""
2446+
Parse a partial or full date string and return ISO8601 with UTC 'Z'.
2447+
Handles year-only, year-month, and full dates.
2448+
"""
2449+
from datetime import timedelta
2450+
import pandas as pd
2451+
2452+
# Handle partial dates
2453+
if len(date_str) == 4: # Year only
2454+
date_str = f"{date_str}-12-31" if is_end else f"{date_str}-01-01"
2455+
elif len(date_str) == 7: # Year-month
2456+
y, m = date_str.split('-')
2457+
if is_end:
2458+
last_day = pd.Timestamp(f"{y}-{m}-01").days_in_month
2459+
date_str = f"{y}-{m}-{last_day:02d}"
2460+
else:
2461+
date_str = f"{y}-{m}-01"
2462+
2463+
# Parse with pandas (handles most formats automatically)
2464+
dt = pd.to_datetime(date_str, errors='coerce', utc=True)
2465+
if pd.isna(dt):
2466+
raise ValueError(f"Could not parse date: {date_str}")
2467+
2468+
# Set end_date-of-day if needed
2469+
if is_end and dt.hour == 0 and dt.minute == 0 and dt.second == 0:
2470+
dt = dt.replace(hour=23, minute=59, second=59)
2471+
2472+
return dt
2473+
2474+
2475+
def split_datetime(start_date: str, end_date: str, n: int = 10, gap_milliseconds:int = 1000):
2476+
"""
2477+
Split start_date/end_date dates into n chunks, returning list of (start_iso, end_iso) strings.
2478+
"""
2479+
from datetime import timedelta
2480+
start_dt = parse_date(start_date, is_end=False)
2481+
end_dt = parse_date(end_date, is_end=True)
2482+
2483+
total_seconds = (end_dt - start_dt).total_seconds()
2484+
chunk_seconds = total_seconds / n
2485+
2486+
chunks = []
2487+
for i in range(n):
2488+
chunk_start = start_dt + timedelta(seconds=i * chunk_seconds)
2489+
chunk_end = start_dt + timedelta(seconds=(i + 1) * chunk_seconds)
2490+
if i < n - 1:
2491+
chunk_end = chunk_end - timedelta(milliseconds=gap_milliseconds)
2492+
2493+
chunks.append((
2494+
chunk_start.strftime("%Y-%m-%dT%H:%M:%SZ"),
2495+
chunk_end.strftime("%Y-%m-%dT%H:%M:%SZ")
2496+
))
2497+
2498+
return chunks
2499+
2500+
24432501
def split_gdf(gdf, n: int = None, zoom: int = None, clip: bool = False, return_type: str = "gdf"):
24442502
import geopandas as gpd
24452503
import pickle
@@ -3669,7 +3727,7 @@ def estimate_zoom(bounds, target_num_tiles=1):
36693727

36703728

36713729
def get_tiles(
3672-
bounds=None, target_num_tiles=1, zoom=None, max_tile_recursion=6, as_gdf=True, clip=False, verbose=False
3730+
bounds=None, target_num_tiles=1, zoom=None, max_tile_recursion=6, as_gdf=True, clip=False, add_bounds=False, verbose=False
36733731
):
36743732
bounds = to_gdf(bounds)
36753733
import mercantile
@@ -3724,6 +3782,8 @@ def get_tiles(
37243782
if verbose:
37253783
print(f"Generated {len(gdf)} tiles at zoom level {zoom_level}")
37263784

3785+
if add_bounds:
3786+
gdf["bounds"] = gdf["geometry"].map(lambda x: list(x.bounds))
37273787
if clip:
37283788
return gdf.clip(bounds) if as_gdf else gdf[["x", "y", "z"]].values
37293789
else:

0 commit comments

Comments
 (0)