Skip to content

Feature/date support#12

Merged
nkanu17 merged 3 commits into
mainfrom
feature/date-support
Mar 16, 2026
Merged

Feature/date support#12
nkanu17 merged 3 commits into
mainfrom
feature/date-support

Conversation

@nkanu17

@nkanu17 nkanu17 commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Add DATE/DATETIME Support

Summary

Adds DATE/DATETIME support to sql-redis, enabling date filtering with ISO 8601 literals and date part extraction using SQL functions.

New Features

1. Date Literal Parsing (Phase 1)

Use ISO 8601 date strings directly in WHERE clauses:

-- Date literal automatically converted to Unix timestamp
SELECT * FROM events WHERE created_at > '2024-01-01'

-- With time component
SELECT * FROM events WHERE created_at > '2024-01-01T12:00:00'

-- Date ranges
SELECT * FROM events WHERE created_at BETWEEN '2024-01-01' AND '2024-03-31'

2. Date Extraction Functions (Phase 2)

Extract date parts using SQL functions that map to Redis APPLY expressions:

SQL Function Redis Function Returns
YEAR(field) year(@field) Year (e.g., 2024)
MONTH(field) monthofyear(@field) Month (0-11)
DAY(field) dayofmonth(@field) Day of month (1-31)
HOUR(field) hour(@field) Hour (rounds timestamp)
MINUTE(field) minute(@field) Minute (rounds timestamp)
DAYOFWEEK(field) dayofweek(@field) Day of week (0=Sunday)
DAYOFYEAR(field) dayofyear(@field) Day of year (0-365)
-- Extract year and month
SELECT name, YEAR(created_at) AS year, MONTH(created_at) AS month FROM events

-- Filter by date parts
SELECT * FROM events WHERE YEAR(created_at) = 2024 AND MONTH(created_at) >= 6

3. Date Formatting (Phase 3)

Format timestamps as human-readable strings:

SELECT name, DATE_FORMAT(created_at, '%Y-%m-%d') AS date FROM events

Maps to Redis's timefmt(@field, format) function.

4. GROUP BY Date Parts

Aggregate data by date components:

-- Count events per year
SELECT YEAR(created_at) AS year, COUNT(*) FROM events GROUP BY year

-- Events per month per category
SELECT category, MONTH(created_at) AS month, COUNT(*) 
FROM events GROUP BY category, month

Files Changed

File Changes
sql_redis/parser.py DateFunctionSpec dataclass, date literal detection, date function parsing
sql_redis/translator.py APPLY generation for date functions, FILTER for date conditions
sql_redis/analyzer.py Date function field resolution, alias handling for GROUP BY
tests/test_date_fields.py 11 tests for date literal parsing
tests/test_date_functions.py 14 tests for date functions
README.md Comprehensive date documentation

What's NOT Supported (and Why)

DATE_ADD / DATE_SUB

-- NOT SUPPORTED
SELECT * FROM events WHERE created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)

Why: Redis RediSearch has no native date arithmetic functions. This would require:

  1. Computing NOW() at query time
  2. Translating INTERVAL syntax to seconds
  3. Generating arithmetic like @field + 604800

Workaround: Compute the timestamp in application code:

from datetime import datetime, timedelta
cutoff = int((datetime.now() - timedelta(days=7)).timestamp())
sql = f"SELECT * FROM events WHERE created_at > {cutoff}"

SECOND()

-- NOT SUPPORTED
SELECT SECOND(created_at) FROM events

Why: Redis RediSearch doesn't have a second() function. The available time functions are:

  • hour() - rounds to hour
  • minute() - rounds to minute

No sub-minute extraction is available.

NOW() / CURRENT_TIMESTAMP

-- NOT SUPPORTED
SELECT * FROM events WHERE created_at > NOW()

Why: These are dynamic values that must be evaluated at query time. Redis queries are static - there's no concept of "current time" in the query language.

Workaround: Pass the current timestamp from application code:

import time
now = int(time.time())
sql = f"SELECT * FROM events WHERE created_at > {now}"

Redis Implementation Details

Storage Format

Dates are stored as Unix timestamps in NUMERIC fields:

# Store
redis.hset("event:1", mapping={
    "name": "Meeting",
    "created_at": 1704067200  # 2024-01-01 00:00:00 UTC
})

Query Routing

Feature Redis Command
Date literals in WHERE FT.SEARCH with numeric range
Date functions in SELECT FT.AGGREGATE with APPLY
Date functions in WHERE FT.AGGREGATE with APPLY + FILTER
GROUP BY date parts FT.AGGREGATE with GROUPBY

MONTH Returns 0-11

Important: Redis's monthofyear() returns 0-11, not 1-12:

Month Redis Value
January 0
February 1
... ...
December 11
-- Find January events (month = 0, not 1)
SELECT * FROM events WHERE MONTH(created_at) = 0

Usage Examples

Date Literals

-- After a date
SELECT * FROM events WHERE created_at > '2024-01-01'

-- Before a date
SELECT * FROM events WHERE created_at < '2024-12-31'

-- Date range
SELECT * FROM events WHERE created_at BETWEEN '2024-01-01' AND '2024-06-30'

-- With timestamp
SELECT * FROM events WHERE created_at > '2024-01-01T09:00:00'

Date Functions in SELECT

-- Single function
SELECT name, YEAR(created_at) AS year FROM events

-- Multiple functions
SELECT name, YEAR(created_at) AS y, MONTH(created_at) AS m, DAY(created_at) AS d
FROM events

-- Formatted output
SELECT name, DATE_FORMAT(created_at, '%Y-%m-%d %H:%M') AS datetime FROM events

Date Functions in WHERE

-- Filter by year
SELECT * FROM events WHERE YEAR(created_at) = 2024

-- Filter by month range
SELECT * FROM events WHERE MONTH(created_at) >= 6

-- Combined conditions
SELECT * FROM events WHERE YEAR(created_at) = 2024 AND MONTH(created_at) = 0

Aggregations

-- Count per year
SELECT YEAR(created_at) AS year, COUNT(*) AS total
FROM events GROUP BY year

-- Count per category per year
SELECT category, YEAR(created_at) AS year, COUNT(*) AS cnt
FROM events GROUP BY category, year

Combined with Other Filters

-- Date + category filter
SELECT name FROM events
WHERE category = 'meeting' AND created_at > '2024-01-01'

-- Date function + category
SELECT name FROM events
WHERE category = 'release' AND YEAR(created_at) = 2024

@nkanu17 nkanu17 marked this pull request as ready for review March 16, 2026 13:25
@nkanu17 nkanu17 requested review from Copilot and rbs333 and removed request for Copilot March 16, 2026 13:27

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds DATE/DATETIME support to sql-redis by parsing ISO 8601 date literals into Unix timestamps and translating SQL date-part/format functions into RediSearch FT.AGGREGATE APPLY + FILTER expressions.

Changes:

  • Parse ISO 8601 date/datetime literals in WHERE (including BETWEEN) and convert them to Unix timestamps.
  • Parse date-part and formatting functions (e.g., YEAR(), MONTH(), DATE_FORMAT()) and translate them into RediSearch APPLY (and FILTER when used in WHERE).
  • Add analyzer support for date-function field resolution / GROUP BY alias handling, plus new tests and README documentation.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
uv.lock Bumps lockfile revision and package version to reflect the new feature release.
sql_redis/parser.py Adds ISO 8601 literal detection + parsing, plus date-function parsing into ParsedQuery.date_functions and date-function operators in conditions.
sql_redis/translator.py Adds FT.AGGREGATE generation for date functions and date-function WHERE conditions via APPLY + FILTER.
sql_redis/analyzer.py Incorporates date functions into field reference analysis and treats computed/date aliases as valid GROUP BY targets.
tests/test_date_fields.py New tests validating date literal parsing and translation to numeric range queries.
tests/test_date_functions.py New tests validating date function parsing and translation to APPLY/FILTER/GROUPBY.
README.md Documents date literal handling, supported formats, and date functions mapping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread sql_redis/parser.py
Comment thread sql_redis/parser.py Outdated
Comment thread sql_redis/parser.py Outdated
Comment thread sql_redis/translator.py Outdated
Comment thread sql_redis/translator.py
Comment thread sql_redis/translator.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds DATE/DATETIME support to sql-redis by parsing ISO-8601 literals into Unix timestamps and translating SQL date functions into RediSearch FT.AGGREGATE APPLY/FILTER expressions.

Changes:

  • Extend the parser/analyzer/translator pipeline to support date literals and date-part/formatting functions (YEAR, MONTH, DAY, DATE_FORMAT, etc.).
  • Add new test suites for date literal parsing and date function translation, plus small test cleanups.
  • Document date behavior in README and bump the package version in the lockfile.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
sql_redis/parser.py Adds ISO-8601 detection + timestamp conversion and parses date functions into a new date_functions structure.
sql_redis/analyzer.py Tracks date functions and handles GROUP BY aliases for computed/date fields.
sql_redis/translator.py Emits FT.AGGREGATE with APPLY/FILTER for date functions and routes date-function WHERE clauses to FILTER.
tests/test_date_fields.py New tests for date literal parsing and numeric filter translation.
tests/test_date_functions.py New tests for date function parsing and translation (APPLY/FILTER/GROUP BY shape).
README.md Adds DATE/DATETIME usage + function mapping documentation.
tests/test_translator.py Updates GROUPBY assertion to expect @category.
tests/test_sql_queries.py Minor assertion formatting changes.
tests/test_sql_parser.py Removes a test for SELECT without FROM.
tests/test_redis_queries.py Minor assertion formatting + removes unused import.
tests/test_executor.py Removes unused import.
tests/test_analyzer.py Removes unused import.
tests/conftest.py Formatting-only changes in key strings.
uv.lock Bumps editable package version to 0.2.0.
.ipynb_checkpoints/geo_examples_consolidated-checkpoint.ipynb Adds a Jupyter checkpoint artifact (likely unintended).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread sql_redis/translator.py
Comment thread sql_redis/translator.py Outdated
Comment thread sql_redis/parser.py
Comment thread sql_redis/parser.py Outdated
Comment thread README.md
Comment thread .ipynb_checkpoints/geo_examples_consolidated-checkpoint.ipynb Outdated
Comment thread tests/test_sql_parser.py
Comment thread .ipynb_checkpoints/geo_examples_consolidated-checkpoint.ipynb Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds DATE/DATETIME capabilities to sql-redis, enabling ISO-8601 date literals in WHERE clauses and date-part extraction/formatting via SQL functions that translate to RediSearch FT.AGGREGATE (APPLY/FILTER) expressions.

Changes:

  • Parse ISO-8601 date/datetime literals and (currently) convert them to Unix timestamps during parsing.
  • Add support for date functions (e.g., YEAR, MONTH, DATE_FORMAT) in SELECT and WHERE, translating to Redis APPLY and FILTER.
  • Extend analyzer + add new tests and README documentation for date support.

Reviewed changes

Copilot reviewed 12 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
sql_redis/parser.py Adds date literal detection + date function parsing into ParsedQuery.
sql_redis/analyzer.py Tracks date functions and improves GROUP BY alias handling.
sql_redis/translator.py Generates FT.AGGREGATE pipelines for date functions + date-function WHERE filtering.
tests/test_date_fields.py New tests for ISO-8601 literal conversion + numeric range translation.
tests/test_date_functions.py New tests for parsing/translating date functions in SELECT/WHERE/GROUP BY.
tests/test_translator.py Updates GROUP BY expectation to use @field syntax.
tests/test_sql_parser.py Removes/adjusts parser test coverage around FROM-less SELECT.
tests/test_redis_queries.py Minor import cleanup.
tests/test_executor.py Import cleanup.
tests/test_analyzer.py Import cleanup.
tests/conftest.py Minor formatting cleanup in f-strings.
README.md Adds DATE/DATETIME docs + supported function mapping/limitations.
uv.lock Bumps project version to 0.2.0 (lock revision update).
.gitignore Adds nitin_docs/ ignore entry.
.ipynb_checkpoints/geo_examples_consolidated-checkpoint.ipynb Adds a notebook checkpoint artifact (likely unintended).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread sql_redis/parser.py
Comment thread sql_redis/translator.py
Comment thread sql_redis/translator.py
Comment thread sql_redis/translator.py
Comment thread .ipynb_checkpoints/geo_examples_consolidated-checkpoint.ipynb Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds DATE/DATETIME capabilities to sql-redis, including ISO-8601 date literal conversion in WHERE clauses and support for date-part extraction/formatting functions that translate to RediSearch FT.AGGREGATE APPLY/FILTER.

Changes:

  • Add ISO-8601 DATE/DATETIME literal detection and conversion to Unix timestamps during parsing.
  • Add parsing/analysis/translation support for date functions (e.g., YEAR, MONTH, DATE_FORMAT) using FT.AGGREGATE APPLY and FILTER.
  • Add new test suites for date literals and date functions, plus documentation updates in README.md.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
sql_redis/parser.py Adds date literal parsing + date function parsing; updates function-name handling in SELECT/WHERE.
sql_redis/analyzer.py Tracks date functions and adjusts field resolution/alias handling for GROUP BY.
sql_redis/translator.py Switches to FT.AGGREGATE for date functions/conditions; generates APPLY and FILTER for date functions.
tests/test_date_fields.py New integration-style tests for date literal parsing + translation to numeric ranges.
tests/test_date_functions.py New tests for date functions parsing + translation (APPLY, FILTER, GROUP BY).
README.md Documents supported date literals/functions and limitations.
tests/test_sql_queries.py Minor assertion formatting.
tests/test_redis_queries.py Minor assertion formatting.
.gitignore Adds (duplicated) Jupyter checkpoint ignore entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread README.md
Comment thread sql_redis/parser.py Outdated
Comment thread sql_redis/parser.py
Comment thread sql_redis/parser.py Outdated
Comment thread sql_redis/translator.py
Comment thread sql_redis/translator.py
Comment thread sql_redis/translator.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds DATE/DATETIME support to sql-redis, enabling ISO 8601 date literal filtering (translated to Unix timestamps) and date-part extraction/formatting via Redis FT.AGGREGATE APPLY + FILTER.

Changes:

  • Parse ISO 8601 date/datetime literals and translate them into NUMERIC timestamp filters when the schema field type is NUMERIC.
  • Add parsing/analyzing/translating for date functions (YEAR, MONTH, DAY, DAYOFWEEK, DAYOFYEAR, HOUR, MINUTE, DATE_FORMAT) using APPLY and FILTER.
  • Add integration tests and update README documentation for date usage and limitations.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sql_redis/parser.py Adds ISO 8601 detection/parsing helpers, date-function specs, and parsing support for date functions in SELECT/WHERE.
sql_redis/analyzer.py Tracks date function source fields and handles GROUP BY aliases for computed/date fields.
sql_redis/translator.py Converts date literals for NUMERIC fields; emits APPLY/FILTER for date functions and rejects OR with date-function predicates.
tests/test_date_fields.py Adds tests for preserving date literals in parsing and timestamp conversion during translation for NUMERIC fields.
tests/test_date_functions.py Adds tests for date-function parsing and FT.AGGREGATE translation behavior (APPLY, FILTER, GROUPBY).
README.md Documents new date literal behavior, date functions, and limitations.
.gitignore Minor change (duplicate entry added).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread sql_redis/translator.py
Comment on lines +202 to +205
# Handle tuple values (e.g., BETWEEN) - try date conversion for each
low, high = condition.value
low_val = self._convert_to_numeric(low)
high_val = self._convert_to_numeric(high)
Comment thread README.md Outdated

- `NOT YEAR(field) = 2024` is not supported (raises `ValueError`)
- `DATE_FORMAT()` is only supported in SELECT, not in WHERE (raises `ValueError`)
- Date functions with `OR` require careful handling
Comment thread sql_redis/translator.py
Comment on lines +391 to +395
if date_func.function == "DATE_FORMAT" and date_func.format_string:
# DATE_FORMAT(field, format) -> timefmt(@field, format)
expression = (
f'{redis_func}(@{date_func.field}, "{date_func.format_string}")'
)
Comment thread sql_redis/translator.py Outdated
Comment on lines +643 to +644
# Quote string values for FILTER
return f'"{value}"'
nkanu17 added 3 commits March 16, 2026 12:42
- Parse DATE/DATETIME literals in WHERE clauses (ISO 8601 format)
- Add date functions: YEAR(), MONTH(), DAY(), HOUR(), MINUTE(), DAYOFWEEK(), DAYOFYEAR()
- Add DATE_FORMAT() for custom formatting via Redis timefmt()
- Translate to FT.AGGREGATE with APPLY/FILTER clauses
- Defer date-to-timestamp conversion to translator (field-type aware)
- Escape special characters in format strings and filter values
- Validate: reject OR/NOT with date functions, require literal format in DATE_FORMAT
- Test date literal parsing and preservation
- Test datetime with various formats (T separator, space, Z suffix, timezones)
- Test BETWEEN with date ranges
- Test date function extraction (YEAR, MONTH, DAY, etc.)
- Test DATE_FORMAT translation to timefmt()
- Test date function conditions in WHERE clauses
- Test validation errors for OR/NOT with date functions
- Document date function syntax and examples
- Document DATE_FORMAT with Redis timefmt() mapping
- Add limitations section for date functions
- Add .ipynb_checkpoints to gitignore
@nkanu17 nkanu17 force-pushed the feature/date-support branch from 1a47f93 to 5025eb5 Compare March 16, 2026 16:43
@nkanu17 nkanu17 merged commit ed50ce5 into main Mar 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants