• Follow Us On :
How to Query Date and Time in Snowflake

Master How to Query Date and Time in Snowflake: The Ultimate Guide to Temporal Data Management

Understanding what is how to query date and time in Snowflake is essential for anyone working with cloud data warehouses. Whether you’re a data analyst, engineer, or business intelligence professional, mastering Snowflake’s temporal functions will dramatically enhance your ability to extract meaningful insights from time-sensitive data. This comprehensive guide reveals everything you need to know about date and time operations in Snowflake, from basic queries to advanced temporal analysis techniques.

Snowflake’s robust date and time functionality provides powerful tools for managing temporal data at scale. With its cloud-native architecture and SQL-based interface, Snowflake makes it remarkably straightforward to perform complex date calculations, time zone conversions, and temporal aggregations that would be challenging in traditional databases.

Understanding Snowflake’s Date and Time Architecture

Before diving into how to query date and time in Snowflake, it’s crucial to understand the underlying data types and architecture that make Snowflake’s temporal operations so efficient.

Snowflake Date and Time Data Types

Snowflake supports three primary temporal data types:

DATE: Stores calendar dates without time information. The DATE type represents dates from 1582-10-15 to 9999-12-31, making it suitable for most business applications. When you query date values in Snowflake, they display in YYYY-MM-DD format by default.

TIME: Represents time of day without date or time zone information. TIME values include hours, minutes, seconds, and optionally nanoseconds. This data type is perfect when you need to track events independent of specific dates, such as business hours or recurring schedules.

TIMESTAMP: The most versatile temporal data type, combining date and time information. Snowflake offers multiple timestamp variants:

  • TIMESTAMP_NTZ (no time zone)
  • TIMESTAMP_LTZ (local time zone)
  • TIMESTAMP_TZ (with time zone)

Understanding these distinctions is fundamental to mastering temporal queries in Snowflake.

Internal Storage and Performance Optimization

Snowflake stores temporal data efficiently using internal representations optimized for query performance. Dates are stored as integers representing days since a reference point, while timestamps use microsecond precision. This architecture enables lightning-fast date comparisons and calculations across billions of rows.

Essential Functions: How to Query Date and Time in Snowflake

Now let’s explore the powerful functions that enable you to query and manipulate date and time data effectively in Snowflake.

Retrieving Current Date and Time

The foundation of temporal queries starts with accessing current date and time values:

CURRENT_DATE(): Returns today’s date in the session’s time zone. This function is perfect for date-based filtering and calculating age or duration from the present.

sql
SELECT CURRENT_DATE() AS today;
-- Result: 2025-11-01

CURRENT_TIME(): Retrieves the current time without date information, useful for time-of-day analysis.

sql
SELECT CURRENT_TIME() AS current_time;
-- Result: 14:30:45.123456789

CURRENT_TIMESTAMP(): Returns the current date and time with timestamp precision. This is the most commonly used function for capturing exact moments.

sql
SELECT CURRENT_TIMESTAMP() AS now;
-- Result: 2025-11-01 14:30:45.123 -0700

SYSDATE(): Provides an alternative to CURRENT_TIMESTAMP(), maintaining compatibility with other database systems.

Extracting Components from Date and Time

When working with temporal data, you often need to extract specific components like year, month, day, or hour.

EXTRACT() Function: The primary method for pulling individual components from dates and timestamps.

sql
SELECT 
    EXTRACT(YEAR FROM CURRENT_DATE()) AS year,
    EXTRACT(MONTH FROM CURRENT_DATE()) AS month,
    EXTRACT(DAY FROM CURRENT_DATE()) AS day,
    EXTRACT(HOUR FROM CURRENT_TIMESTAMP()) AS hour,
    EXTRACT(MINUTE FROM CURRENT_TIMESTAMP()) AS minute;

DATE_PART() Function: Snowflake’s equivalent to EXTRACT with identical functionality but different syntax.

sql
SELECT 
    DATE_PART(YEAR, '2025-11-01'::DATE) AS year,
    DATE_PART(QUARTER, '2025-11-01'::DATE) AS quarter,
    DATE_PART(WEEK, '2025-11-01'::DATE) AS week_number;

YEAR(), MONTH(), DAY() Functions: Convenient shorthand functions for common extraction operations.

sql
SELECT 
    YEAR(order_date) AS order_year,
    MONTH(order_date) AS order_month,
    DAY(order_date) AS order_day
FROM sales_table;

These extraction functions are invaluable when grouping data by temporal periods or building date dimensions.

Date and Time Arithmetic Operations

Understanding how to query date and time in Snowflake requires mastery of arithmetic operations that add, subtract, and calculate intervals.

DATEADD() Function: Adds a specified interval to a date or timestamp. This function is essential for calculating future or past dates.

sql
SELECT 
    DATEADD(DAY, 7, CURRENT_DATE()) AS next_week,
    DATEADD(MONTH, -3, CURRENT_DATE()) AS three_months_ago,
    DATEADD(YEAR, 1, order_date) AS renewal_date
FROM subscriptions;

Supported interval units include:

  • YEAR, QUARTER, MONTH, WEEK
  • DAY, HOUR, MINUTE, SECOND
  • MILLISECOND, MICROSECOND, NANOSECOND

DATEDIFF() Function: Calculates the difference between two dates in specified units.

sql
SELECT 
    customer_id,
    order_date,
    ship_date,
    DATEDIFF(DAY, order_date, ship_date) AS shipping_days,
    DATEDIFF(HOUR, created_at, updated_at) AS processing_hours
FROM orders;

This function is crucial for calculating duration, age, tenure, or time-to-event metrics.

TIMESTAMPADD() and TIMESTAMPDIFF(): Timestamp-specific versions providing nanosecond precision.

sql
SELECT 
    TIMESTAMPADD(HOUR, 24, event_timestamp) AS tomorrow_same_time,
    TIMESTAMPDIFF(SECOND, start_time, end_time) AS duration_seconds
FROM event_logs;

Date and Time Formatting Functions

Presenting temporal data in human-readable formats is critical for reports and dashboards.

TO_CHAR() Function: Converts dates and timestamps to formatted strings using pattern specifications.

sql
SELECT 
    TO_CHAR(order_date, 'YYYY-MM-DD') AS iso_date,
    TO_CHAR(order_date, 'Month DD, YYYY') AS long_format,
    TO_CHAR(order_timestamp, 'HH24:MI:SS') AS time_24hr,
    TO_CHAR(order_timestamp, 'Day, Mon DD YYYY') AS full_description
FROM orders;

Common format patterns include:

  • YYYY (4-digit year), YY (2-digit year)
  • MM (month number), Month (full name), Mon (abbreviated)
  • DD (day of month), DDD (day of year)
  • HH24 (24-hour), HH12 (12-hour), AM/PM indicators
  • MI (minutes), SS (seconds)

TO_DATE() and TO_TIMESTAMP() Functions: Convert strings to date and timestamp objects.

sql
SELECT 
    TO_DATE('2025-11-01', 'YYYY-MM-DD') AS parsed_date,
    TO_TIMESTAMP('2025-11-01 14:30:45', 'YYYY-MM-DD HH24:MI:SS') AS parsed_timestamp,
    TO_TIMESTAMP('11/01/2025', 'MM/DD/YYYY') AS american_format
FROM dual;

These conversion functions are essential when importing data from external sources with various date formats.

Advanced Temporal Query Techniques in Snowflake

Moving beyond basic operations, let’s explore sophisticated techniques for querying date and time in Snowflake that solve complex analytical challenges.

Time Zone Conversions and Management

Global businesses require careful time zone handling to ensure accurate temporal analysis.

CONVERT_TIMEZONE() Function: Transforms timestamps between time zones.

sql
SELECT 
    event_timestamp AS utc_time,
    CONVERT_TIMEZONE('UTC', 'America/New_York', event_timestamp) AS eastern_time,
    CONVERT_TIMEZONE('UTC', 'Europe/London', event_timestamp) AS london_time,
    CONVERT_TIMEZONE('UTC', 'Asia/Tokyo', event_timestamp) AS tokyo_time
FROM global_events;

Best practices for time zone management:

  • Store all timestamps in UTC for consistency
  • Convert to local time zones only for display purposes
  • Use TIMESTAMP_TZ when preserving original time zone context is critical
  • Document time zone assumptions in data pipelines

Working with Date Ranges and Intervals

Many analytical queries require filtering or aggregating data within specific date ranges.

BETWEEN Operator: Filter records within inclusive date ranges.

sql
SELECT 
    customer_id,
    SUM(order_amount) AS total_sales
FROM orders
WHERE order_date BETWEEN '2025-01-01' AND '2025-03-31'
GROUP BY customer_id;

Calculating Rolling Windows: Analyze data over moving time periods.

sql
SELECT 
    order_date,
    SUM(order_amount) AS daily_sales,
    AVG(SUM(order_amount)) OVER (
        ORDER BY order_date 
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS seven_day_avg
FROM orders
WHERE order_date >= DATEADD(MONTH, -3, CURRENT_DATE())
GROUP BY order_date
ORDER BY order_date;

This technique enables powerful trailing average, moving total, and trend analysis calculations.

Date Dimension Tables and Calendar Queries

Professional data warehouses typically include date dimension tables for enhanced temporal analysis.

Creating a Date Dimension:

sql
CREATE OR REPLACE TABLE date_dimension AS
SELECT 
    DATE AS date_key,
    YEAR(DATE) AS year,
    QUARTER(DATE) AS quarter,
    MONTH(DATE) AS month,
    MONTHNAME(DATE) AS month_name,
    WEEK(DATE) AS week_number,
    DAYOFWEEK(DATE) AS day_of_week,
    DAYNAME(DATE) AS day_name,
    DAY(DATE) AS day_of_month,
    DAYOFYEAR(DATE) AS day_of_year,
    CASE WHEN DAYOFWEEK(DATE) IN (0, 6) THEN TRUE ELSE FALSE END AS is_weekend,
    LAST_DAY(DATE) AS month_end_date
FROM (
    SELECT DATEADD(DAY, SEQ4(), '2020-01-01'::DATE) AS DATE
    FROM TABLE(GENERATOR(ROWCOUNT => 3652))
)
WHERE DATE <= '2030-12-31';

This dimension table accelerates queries requiring calendar intelligence like fiscal periods, holidays, or business day calculations.

Handling NULL and Missing Temporal Values

Robust temporal queries must account for missing or null date values.

COALESCE() for Default Dates:

sql
SELECT 
    customer_id,
    COALESCE(last_login_date, '1900-01-01'::DATE) AS last_login,
    DATEDIFF(DAY, COALESCE(last_login_date, account_created_date), CURRENT_DATE()) AS days_inactive
FROM customers;

IFNULL() and NVL() Alternatives:

sql
SELECT 
    order_id,
    NVL(ship_date, estimated_ship_date) AS expected_ship_date,
    IFNULL(delivery_date, DATEADD(DAY, 7, order_date)) AS projected_delivery
FROM orders;

Temporal Aggregations and Window Functions

Sophisticated analytics often require grouping and calculating across temporal windows.

Monthly Aggregations:

sql
SELECT 
    DATE_TRUNC('MONTH', order_date) AS month,
    COUNT(DISTINCT customer_id) AS unique_customers,
    COUNT(*) AS total_orders,
    SUM(order_amount) AS revenue,
    AVG(order_amount) AS avg_order_value
FROM orders
WHERE order_date >= DATEADD(YEAR, -1, CURRENT_DATE())
GROUP BY DATE_TRUNC('MONTH', order_date)
ORDER BY month;

LAG() and LEAD() for Time-Series Comparisons:

sql
SELECT 
    transaction_date,
    amount,
    LAG(amount, 1) OVER (PARTITION BY customer_id ORDER BY transaction_date) AS previous_amount,
    LEAD(amount, 1) OVER (PARTITION BY customer_id ORDER BY transaction_date) AS next_amount,
    amount - LAG(amount, 1) OVER (PARTITION BY customer_id ORDER BY transaction_date) AS change_from_previous
FROM transactions
ORDER BY customer_id, transaction_date;

Practical Use Cases: How to Query Date and Time in Snowflake

Let’s examine real-world scenarios demonstrating practical applications of Snowflake’s temporal capabilities.

Customer Lifetime Value and Cohort Analysis

Understanding customer behavior over time requires sophisticated date-based segmentation.

sql
WITH first_purchase AS (
    SELECT 
        customer_id,
        MIN(order_date) AS first_order_date,
        DATE_TRUNC('MONTH', MIN(order_date)) AS cohort_month
    FROM orders
    GROUP BY customer_id
),
monthly_revenue AS (
    SELECT 
        o.customer_id,
        DATE_TRUNC('MONTH', o.order_date) AS order_month,
        SUM(o.order_amount) AS revenue
    FROM orders o
    GROUP BY o.customer_id, DATE_TRUNC('MONTH', o.order_date)
)
SELECT 
    fp.cohort_month,
    DATEDIFF(MONTH, fp.cohort_month, mr.order_month) AS months_since_first_order,
    COUNT(DISTINCT mr.customer_id) AS active_customers,
    SUM(mr.revenue) AS cohort_revenue
FROM first_purchase fp
INNER JOIN monthly_revenue mr ON fp.customer_id = mr.customer_id
GROUP BY fp.cohort_month, months_since_first_order
ORDER BY fp.cohort_month, months_since_first_order;

This cohort analysis reveals customer retention patterns and revenue trends across acquisition periods.

Calculating Business Days and Working Hours

Many business processes require calculations excluding weekends and holidays.

sql
CREATE OR REPLACE FUNCTION calculate_business_days(start_date DATE, end_date DATE)
RETURNS INTEGER
AS
$$
    SELECT COUNT(*)
    FROM (
        SELECT DATEADD(DAY, SEQ4(), start_date) AS date
        FROM TABLE(GENERATOR(ROWCOUNT => 1000))
    )
    WHERE date <= end_date
    AND DAYOFWEEK(date) NOT IN (0, 6)  -- Exclude Saturday and Sunday
$$;

-- Usage
SELECT 
    order_id,
    order_date,
    ship_date,
    calculate_business_days(order_date, ship_date) AS business_days_to_ship
FROM orders;

Session Analysis and User Activity Tracking

Understanding user engagement requires precise temporal analysis of activity patterns.

sql
SELECT 
    user_id,
    session_start,
    session_end,
    TIMESTAMPDIFF(MINUTE, session_start, session_end) AS session_duration_minutes,
    HOUR(session_start) AS session_hour,
    CASE 
        WHEN HOUR(session_start) BETWEEN 6 AND 11 THEN 'Morning'
        WHEN HOUR(session_start) BETWEEN 12 AND 17 THEN 'Afternoon'
        WHEN HOUR(session_start) BETWEEN 18 AND 22 THEN 'Evening'
        ELSE 'Night'
    END AS session_period,
    DAYNAME(session_start) AS session_day
FROM user_sessions
WHERE session_start >= DATEADD(DAY, -30, CURRENT_TIMESTAMP())
ORDER BY user_id, session_start;

Subscription Renewal and Churn Prediction

SaaS businesses rely heavily on temporal queries for subscription management.

sql
WITH subscription_status AS (
    SELECT 
        subscription_id,
        customer_id,
        start_date,
        end_date,
        CURRENT_DATE() AS today,
        CASE 
            WHEN end_date < CURRENT_DATE() THEN 'Expired'
            WHEN DATEDIFF(DAY, CURRENT_DATE(), end_date) <= 30 THEN 'Expiring Soon'
            ELSE 'Active'
        END AS status,
        DATEDIFF(DAY, CURRENT_DATE(), end_date) AS days_until_expiration,
        DATEDIFF(MONTH, start_date, CURRENT_DATE()) AS months_subscribed
    FROM subscriptions
)
SELECT 
    status,
    COUNT(*) AS subscription_count,
    AVG(months_subscribed) AS avg_tenure_months,
    AVG(days_until_expiration) AS avg_days_remaining
FROM subscription_status
GROUP BY status;

Event Sequence and Time-to-Conversion Analysis

Marketing and product teams need to understand user journey timelines.

sql
WITH user_events AS (
    SELECT 
        user_id,
        event_type,
        event_timestamp,
        LAG(event_timestamp) OVER (PARTITION BY user_id ORDER BY event_timestamp) AS previous_event_time,
        FIRST_VALUE(event_timestamp) OVER (PARTITION BY user_id ORDER BY event_timestamp) AS first_event_time
    FROM events
    WHERE event_date >= DATEADD(DAY, -90, CURRENT_DATE())
)
SELECT 
    user_id,
    event_type,
    event_timestamp,
    TIMESTAMPDIFF(MINUTE, previous_event_time, event_timestamp) AS minutes_since_previous_event,
    TIMESTAMPDIFF(HOUR, first_event_time, event_timestamp) AS hours_since_first_event
FROM user_events
WHERE event_type = 'purchase'
ORDER BY user_id, event_timestamp;

Performance Optimization for Date and Time Queries

Efficient temporal queries require careful optimization to maintain performance at scale.

Clustering Keys on Date Columns

Snowflake’s automatic clustering can dramatically improve query performance on date-filtered datasets.

sql
ALTER TABLE orders CLUSTER BY (order_date);
ALTER TABLE events CLUSTER BY (DATE_TRUNC('DAY', event_timestamp));

Clustering organizes data physically by date, reducing the amount of data scanned for temporal filters.

Partition Pruning Strategies

Structure queries to enable Snowflake’s partition pruning capabilities:

Efficient Date Filtering:

sql
-- Good: Allows partition pruning
SELECT * FROM orders 
WHERE order_date >= '2025-01-01' AND order_date < '2025-04-01';

-- Less efficient: Function prevents pruning
SELECT * FROM orders 
WHERE YEAR(order_date) = 2025 AND MONTH(order_date) <= 3;

Materialized Views for Complex Temporal Aggregations

Pre-compute frequently-used temporal aggregations using materialized views:

sql
CREATE MATERIALIZED VIEW daily_sales_summary AS
SELECT 
    order_date,
    COUNT(*) AS order_count,
    COUNT(DISTINCT customer_id) AS unique_customers,
    SUM(order_amount) AS total_revenue,
    AVG(order_amount) AS avg_order_value
FROM orders
GROUP BY order_date;

Caching and Query Result Reuse

Snowflake automatically caches query results for 24 hours, significantly benefiting repetitive temporal queries.

sql
-- First execution scans data
SELECT DATE_TRUNC('MONTH', order_date) AS month, SUM(amount)
FROM orders
WHERE order_date >= DATEADD(YEAR, -1, CURRENT_DATE())
GROUP BY month;

-- Subsequent identical queries return instantly from cache

Common Pitfalls and Best Practices

Mastering how to query date and time in Snowflake means avoiding common mistakes and following proven best practices.

Avoiding Implicit Type Conversions

Snowflake performs automatic type conversion, but explicit casting prevents ambiguity and improves performance:

sql
-- Implicit conversion (can be slower)
SELECT * FROM orders WHERE order_date = '2025-11-01';

-- Explicit conversion (recommended)
SELECT * FROM orders WHERE order_date = '2025-11-01'::DATE;
SELECT * FROM orders WHERE order_date = TO_DATE('2025-11-01', 'YYYY-MM-DD');

Handling Daylight Saving Time Transitions

Time zone conversions during DST transitions require careful handling:

sql
-- Always specify complete time zone names
SELECT CONVERT_TIMEZONE('America/New_York', timestamp_column)
FROM events;

-- Be aware of ambiguous hours during "fall back"
-- Document assumptions about DST handling in your data pipeline

Date Arithmetic Edge Cases

Be mindful of month-end behaviors and leap years:

sql
-- Adding months to month-end dates
SELECT DATEADD(MONTH, 1, '2025-01-31'::DATE) AS result;
-- Returns 2025-02-28 (not 2025-03-03)

-- Last day of month function
SELECT LAST_DAY('2025-02-15'::DATE) AS feb_end;
-- Returns 2025-02-28

-- Leap year handling
SELECT LAST_DAY('2024-02-15'::DATE) AS leap_year_feb;
-- Returns 2024-02-29

Time Zone Consistency in Data Warehousing

Establish and document time zone standards across your organization:

Also Read: Snowflake Interview Questions

Best Practices:

  • Store all timestamps in UTC at the data warehouse layer
  • Convert to local time zones only in presentation/reporting layers
  • Document the time zone of source systems explicitly
  • Use TIMESTAMP_TZ only when preserving original time zone is business-critical
  • Implement validation checks for time zone consistency

Testing Date Logic Across Boundaries

Always test temporal logic at critical boundaries:

sql
-- Test month boundaries
SELECT DATEADD(DAY, 1, '2025-01-31'::DATE);  -- Should be 2025-02-01
SELECT DATEADD(MONTH, 1, '2025-01-31'::DATE);  -- Should be 2025-02-28

-- Test year boundaries
SELECT DATEADD(DAY, 1, '2025-12-31'::DATE);  -- Should be 2026-01-01

-- Test leap year logic
SELECT DATEADD(YEAR, 1, '2024-02-29'::DATE);  -- Should be 2025-02-28

-- Test DST transitions
SELECT CONVERT_TIMEZONE('America/New_York', '2025-03-09 02:30:00'::TIMESTAMP);

Advanced Snowflake Date Functions and Features

Snowflake Date Functions: Explore lesser-known but powerful temporal capabilities in Snowflake.

DATE_TRUNC for Time-Series Bucketing

Truncate timestamps to various precision levels for grouping and analysis:

sql
SELECT 
    DATE_TRUNC('HOUR', event_timestamp) AS hour_bucket,
    COUNT(*) AS event_count
FROM events
WHERE event_timestamp >= DATEADD(DAY, -7, CURRENT_TIMESTAMP())
GROUP BY DATE_TRUNC('HOUR', event_timestamp)
ORDER BY hour_bucket;

Supported truncation units:

  • MICROSECOND, MILLISECOND, SECOND, MINUTE, HOUR
  • DAY, WEEK, MONTH, QUARTER, YEAR
  • DECADE, CENTURY

NEXT_DAY Function for Recurring Events

Find the next occurrence of a specific weekday:

sql
SELECT 
    CURRENT_DATE() AS today,
    NEXT_DAY(CURRENT_DATE(), 'Monday') AS next_monday,
    NEXT_DAY(CURRENT_DATE(), 'Friday') AS next_friday
FROM dual;

PREVIOUS_DAY for Historical Lookbacks

Find the most recent occurrence of a specific weekday:

sql
SELECT 
    CURRENT_DATE() AS today,
    PREVIOUS_DAY(CURRENT_DATE(), 'Monday') AS last_monday,
    PREVIOUS_DAY(CURRENT_DATE(), 'Sunday') AS last_sunday
FROM dual;

TIME_SLICE for Regular Interval Bucketing

Align timestamps to regular intervals regardless of actual values:

sql
SELECT 
    TIME_SLICE(event_timestamp, 15, 'MINUTE') AS fifteen_min_bucket,
    COUNT(*) AS events_in_bucket
FROM events
WHERE event_date = CURRENT_DATE()
GROUP BY TIME_SLICE(event_timestamp, 15, 'MINUTE')
ORDER BY fifteen_min_bucket;

This function is invaluable for creating regular time-series data from irregular event streams.

Fiscal Calendar Support

Many organizations operate on fiscal years different from calendar years:

sql
CREATE OR REPLACE FUNCTION get_fiscal_year(input_date DATE, fiscal_year_start_month INTEGER)
RETURNS INTEGER
AS
$$
    CASE 
        WHEN MONTH(input_date) >= fiscal_year_start_month 
        THEN YEAR(input_date)
        ELSE YEAR(input_date) - 1
    END
$$;

-- Usage for fiscal year starting in July
SELECT 
    order_date,
    get_fiscal_year(order_date, 7) AS fiscal_year,
    SUM(order_amount) AS revenue
FROM orders
GROUP BY order_date, get_fiscal_year(order_date, 7);

Integration with BI Tools and Reporting

Understanding how to query date and time in Snowflake extends to integration with business intelligence platforms.

Date Filters for Tableau and Power BI

Optimize queries for dashboard filters:

sql
-- Parameter-based date filtering for dashboards
SELECT 
    order_date,
    customer_segment,
    SUM(order_amount) AS revenue
FROM orders
WHERE order_date BETWEEN :start_date AND :end_date
GROUP BY order_date, customer_segment;

Relative Date Calculations for Dynamic Reports

Create reports that automatically update based on current date:

sql
-- Yesterday's metrics
SELECT 'Yesterday' AS period, COUNT(*), SUM(amount)
FROM orders
WHERE order_date = DATEADD(DAY, -1, CURRENT_DATE())

UNION ALL

-- Last 7 days
SELECT 'Last 7 Days', COUNT(*), SUM(amount)
FROM orders
WHERE order_date >= DATEADD(DAY, -7, CURRENT_DATE())

UNION ALL

-- Month to date
SELECT 'Month to Date', COUNT(*), SUM(amount)
FROM orders
WHERE order_date >= DATE_TRUNC('MONTH', CURRENT_DATE())

UNION ALL

-- Year to date
SELECT 'Year to Date', COUNT(*), SUM(amount)
FROM orders
WHERE order_date >= DATE_TRUNC('YEAR', CURRENT_DATE());

Creating Date Parameter Tables

Build reusable date range definitions:

sql
CREATE OR REPLACE TABLE date_ranges AS
SELECT 'Today' AS range_name, CURRENT_DATE() AS start_date, CURRENT_DATE() AS end_date
UNION ALL
SELECT 'Yesterday', DATEADD(DAY, -1, CURRENT_DATE()), DATEADD(DAY, -1, CURRENT_DATE())
UNION ALL
SELECT 'Last 7 Days', DATEADD(DAY, -7, CURRENT_DATE()), CURRENT_DATE()
UNION ALL
SELECT 'Last 30 Days', DATEADD(DAY, -30, CURRENT_DATE()), CURRENT_DATE()
UNION ALL
SELECT 'This Month', DATE_TRUNC('MONTH', CURRENT_DATE()), CURRENT_DATE()
UNION ALL
SELECT 'Last Month', DATE_TRUNC('MONTH', DATEADD(MONTH, -1, CURRENT_DATE())), 
       LAST_DAY(DATEADD(MONTH, -1, CURRENT_DATE()))
UNION ALL
SELECT 'This Quarter', DATE_TRUNC('QUARTER', CURRENT_DATE()), CURRENT_DATE()
UNION ALL
SELECT 'This Year', DATE_TRUNC('YEAR', CURRENT_DATE()), CURRENT_DATE();

Security and Governance Considerations

Temporal data often contains sensitive information requiring proper access controls.

Row-Level Security with Date Filters

Implement time-based access restrictions:

sql
CREATE OR REPLACE ROW ACCESS POLICY date_based_access
AS (order_date DATE) RETURNS BOOLEAN ->
    CASE 
        WHEN CURRENT_ROLE() = 'ADMIN' THEN TRUE
        WHEN CURRENT_ROLE() = 'ANALYST' AND order_date >= DATEADD(YEAR, -2, CURRENT_DATE()) THEN TRUE
        ELSE FALSE
    END;

ALTER TABLE orders ADD ROW ACCESS POLICY date_based_access ON (order_date);

Audit Logging with Timestamps

Track data modifications with temporal audit trails:

sql
CREATE OR REPLACE TABLE orders_audit (
    audit_id INTEGER AUTOINCREMENT,
    order_id INTEGER,
    action VARCHAR(10),
    changed_by VARCHAR(100),
    changed_at TIMESTAMP_LTZ DEFAULT CURRENT_TIMESTAMP(),
    old_values VARIANT,
    new_values VARIANT
);

-- Trigger-like behavior through streams and tasks
CREATE OR REPLACE STREAM orders_stream ON TABLE orders;

CREATE OR REPLACE TASK orders_audit_task
WAREHOUSE = compute_wh
SCHEDULE = '1 MINUTE'
WHEN SYSTEM$STREAM_HAS_DATA('orders_stream')
AS
INSERT INTO orders_audit (order_id, action, changed_by, old_values, new_values)
SELECT 
    order_id,
    METADATA$ACTION,
    METADATA$ISUPDATE,
    OBJECT_CONSTRUCT(*),
    OBJECT_CONSTRUCT(*)
FROM orders_stream;

Troubleshooting Common Date and Time Issues

Resolve frequent challenges when working with temporal data in Snowflake.

Debugging Unexpected Date Results

Use systematic approaches to identify date-related bugs:

sql
-- Verify date parsing
SELECT 
    '2025-11-01' AS raw_string,
    TRY_TO_DATE('2025-11-01', 'YYYY-MM-DD') AS parsed_date,
    TYPEOF(TRY_TO_DATE('2025-11-01', 'YYYY-MM-DD')) AS data_type;

-- Check time zone conversions
SELECT 
    event_timestamp AS original,
    CONVERT_TIMEZONE('UTC', event_timestamp) AS utc,
    CONVERT_TIMEZONE('America/New_York', event_timestamp) AS eastern,
    TIMESTAMPDIFF(HOUR, CONVERT_TIMEZONE('UTC', event_timestamp), 
                  CONVERT_TIMEZONE('America/New_York', event_timestamp)) AS hour_diff;

Handling Invalid Dates

Gracefully manage bad date data:

sql
-- Use TRY_TO_DATE to avoid errors
SELECT 
    raw_date_string,
    TRY_TO_DATE(raw_date_string, 'YYYY-MM-DD') AS parsed_date,
    CASE 
        WHEN TRY_TO_DATE(raw_date_string, 'YYYY-MM-DD') IS NULL THEN 'Invalid'
        ELSE 'Valid'
    END AS validation_status
FROM staging_table;

-- Identify and quarantine bad dates
CREATE OR REPLACE TABLE date_validation_errors AS
SELECT *
FROM staging_table
WHERE TRY_TO_DATE(date_column, 'YYYY-MM-DD') IS NULL
AND date_column IS NOT NULL;

Performance Troubleshooting for Date Queries

Identify and resolve slow temporal queries:

sql
-- Check query profile for date filter efficiency
-- Use EXPLAIN to see execution plan
EXPLAIN
SELECT *
FROM large_table
WHERE order_date BETWEEN '2025-01-01' AND '2025-03-31';

-- Verify clustering effectiveness
SELECT SYSTEM$CLUSTERING_INFORMATION('large_table', '(order_date)');

-- Monitor partition pruning
-- Check query history for partitions scanned vs total partitions

Real-World Examples and Code Templates

Practical templates for common date and time query scenarios in Snowflake.

Template 1: Year-over-Year Comparison Report

sql
WITH current_year AS (
    SELECT 
        DATE_TRUNC('MONTH', order_date) AS month,
        SUM(order_amount) AS revenue
    FROM orders
    WHERE YEAR(order_date) = YEAR(CURRENT_DATE())
    GROUP BY DATE_TRUNC('MONTH', order_date)
),
previous_year AS (
    SELECT 
        DATE_TRUNC('MONTH', order_date) AS month,
        SUM(order_amount) AS revenue
    FROM orders
    WHERE YEAR(order_date) = YEAR(CURRENT_DATE()) - 1
    GROUP BY DATE_TRUNC('MONTH', order_date)
)
SELECT 
    MONTH(cy.month) AS month_number,
    TO_CHAR(cy.month, 'Month') AS month_name,
    cy.revenue AS current_year_revenue,
    py.revenue AS previous_year_revenue,
    cy.revenue - py.revenue AS revenue_difference,
    ROUND(((cy.revenue - py.revenue) / py.revenue) * 100, 2) AS yoy_growth_percent
FROM current_year cy
LEFT JOIN previous_year py ON MONTH(cy.month) = MONTH(py.month)
ORDER BY month_number;

Template 2: Customer Recency, Frequency, Monetary (RFM) Analysis

sql
WITH customer_metrics AS (
    SELECT 
        customer_id,
        MAX(order_date) AS last_order_date,
        DATEDIFF(DAY, MAX(order_date), CURRENT_DATE()) AS recency_days,
        COUNT(DISTINCT order_id) AS frequency,
        SUM(order_amount) AS monetary_value
    FROM orders
    WHERE order_date >= DATEADD(YEAR, -1, CURRENT_DATE())
    GROUP BY customer_id
),
rfm_scores AS (
    SELECT 
        customer_id,
        recency_days,
        frequency,
        monetary_value,
        NTILE(5) OVER (ORDER BY recency_days DESC) AS recency_score,
        NTILE(5) OVER (ORDER BY frequency ASC) AS frequency_score,
        NTILE(5) OVER (ORDER BY monetary_value ASC) AS monetary_score
    FROM customer_metrics
)
SELECT 
    customer_id,
    recency_days,
    frequency,
    monetary_value,
    recency_score,
    frequency_score,
    monetary_score,
    (recency_score + frequency_score + monetary_score) AS total_rfm_score,
    CASE 
        WHEN recency_score >= 4 AND frequency_score >= 4 THEN 'Champions'
        WHEN recency_score >= 3 AND frequency_score >= 3 THEN 'Loyal Customers'
        WHEN recency_score >= 4 AND frequency_score <= 2 THEN 'Promising'
        WHEN recency_score <= 2 AND frequency_score >= 4 THEN 'At Risk'
        WHEN recency_score <= 2 THEN 'Lost'
        ELSE 'Other'
    END AS customer_segment
FROM rfm_scores
ORDER BY total_rfm_score DESC;

Template 3: Sales Seasonality Analysis

sql
SELECT 
    YEAR(order_date) AS year,
    QUARTER(order_date) AS quarter,
    MONTH(order_date) AS month,
    TO_CHAR(order_date, 'Month') AS month_name,
    DAYOFWEEK(order_date) AS day_of_week,
    TO_CHAR(order_date, 'Day') AS day_name,
    COUNT(*) AS order_count,
    SUM(order_amount) AS total_revenue,
    AVG(order_amount) AS avg_order_value,
    AVG(SUM(order_amount)) OVER (
        PARTITION BY MONTH(order_date)
        ORDER BY YEAR(order_date)
    ) AS rolling_avg_monthly_revenue
FROM orders
WHERE order_date >= DATEADD(YEAR, -3, CURRENT_DATE())
GROUP BY 
    YEAR(order_date),
    QUARTER(order_date),
    MONTH(order_date),
    DAYOFWEEK(order_date),
    order_date
ORDER BY year, month, day_of_week;

Template 4: Event Funnel with Time-Based Steps

sql
WITH user_journey AS (
    SELECT 
        user_id,
        MIN(CASE WHEN event_type = 'page_view' THEN event_timestamp END) AS first_view,
        MIN(CASE WHEN event_type = 'add_to_cart' THEN event_timestamp END) AS first_cart,
        MIN(CASE WHEN event_type = 'checkout' THEN event_timestamp END) AS first_checkout,
        MIN(CASE WHEN event_type = 'purchase' THEN event_timestamp END) AS first_purchase
    FROM events
    WHERE event_date >= DATEADD(DAY, -30, CURRENT_DATE())
    GROUP BY user_id
)
SELECT 
    COUNT(DISTINCT user_id) AS total_users,
    COUNT(DISTINCT CASE WHEN first_view IS NOT NULL THEN user_id END) AS viewed,
    COUNT(DISTINCT CASE WHEN first_cart IS NOT NULL THEN user_id END) AS added_to_cart,
    COUNT(DISTINCT CASE WHEN first_checkout IS NOT NULL THEN user_id END) AS initiated_checkout,
    COUNT(DISTINCT CASE WHEN first_purchase IS NOT NULL THEN user_id END) AS completed_purchase,
    AVG(TIMESTAMPDIFF(MINUTE, first_view, first_cart)) AS avg_minutes_to_cart,
    AVG(TIMESTAMPDIFF(MINUTE, first_cart, first_checkout)) AS avg_minutes_to_checkout,
    AVG(TIMESTAMPDIFF(MINUTE, first_checkout, first_purchase)) AS avg_minutes_to_purchase,
    AVG(TIMESTAMPDIFF(HOUR, first_view, first_purchase)) AS avg_hours_to_conversion
FROM user_journey;

Template 5: Retention Cohort Analysis

sql
WITH user_cohorts AS (
    SELECT 
        user_id,
        DATE_TRUNC('MONTH', MIN(activity_date)) AS cohort_month
    FROM user_activity
    GROUP BY user_id
),
activity_periods AS (
    SELECT 
        uc.cohort_month,
        DATE_TRUNC('MONTH', ua.activity_date) AS activity_month,
        DATEDIFF(MONTH, uc.cohort_month, DATE_TRUNC('MONTH', ua.activity_date)) AS months_since_cohort,
        COUNT(DISTINCT ua.user_id) AS active_users
    FROM user_cohorts uc
    INNER JOIN user_activity ua ON uc.user_id = ua.user_id
    GROUP BY uc.cohort_month, DATE_TRUNC('MONTH', ua.activity_date)
),
cohort_sizes AS (
    SELECT 
        cohort_month,
        COUNT(DISTINCT user_id) AS cohort_size
    FROM user_cohorts
    GROUP BY cohort_month
)
SELECT 
    ap.cohort_month,
    ap.months_since_cohort,
    cs.cohort_size,
    ap.active_users,
    ROUND((ap.active_users::FLOAT / cs.cohort_size) * 100, 2) AS retention_rate
FROM activity_periods ap
INNER JOIN cohort_sizes cs ON ap.cohort_month = cs.cohort_month
WHERE ap.cohort_month >= DATEADD(MONTH, -12, CURRENT_DATE())
ORDER BY ap.cohort_month, ap.months_since_cohort;

Migration and Data Loading Considerations

Successfully loading and migrating temporal data requires careful planning.

Handling Different Source Date Formats

Convert various input formats to Snowflake standards:

sql
-- Multiple date format handling
SELECT 
    date_string,
    COALESCE(
        TRY_TO_DATE(date_string, 'YYYY-MM-DD'),
        TRY_TO_DATE(date_string, 'MM/DD/YYYY'),
        TRY_TO_DATE(date_string, 'DD-MON-YYYY'),
        TRY_TO_DATE(date_string, 'YYYY/MM/DD'),
        TRY_TO_DATE(date_string, 'Mon DD, YYYY')
    ) AS parsed_date
FROM staging_dates;

-- Creating a reusable date parsing function
CREATE OR REPLACE FUNCTION parse_flexible_date(date_str VARCHAR)
RETURNS DATE
AS
$
    COALESCE(
        TRY_TO_DATE(date_str, 'YYYY-MM-DD'),
        TRY_TO_DATE(date_str, 'MM/DD/YYYY'),
        TRY_TO_DATE(date_str, 'DD-MON-YYYY'),
        TRY_TO_DATE(date_str, 'YYYY/MM/DD'),
        TRY_TO_DATE(date_str, 'YYYYMMDD')
    )
$;

Timestamp Precision During Migration

Preserve timestamp precision when migrating from other systems:

sql
-- Maintain microsecond precision
CREATE OR REPLACE TABLE migrated_events (
    event_id INTEGER,
    event_timestamp TIMESTAMP_NTZ(9),  -- 9 = nanosecond precision
    event_description VARCHAR
);

-- Load with explicit precision
INSERT INTO migrated_events
SELECT 
    event_id,
    TO_TIMESTAMP_NTZ(timestamp_string, 'YYYY-MM-DD HH24:MI:SS.FF9'),
    event_description
FROM source_system_export;

Time Zone Conversion During Load

Standardize time zones when importing data:

sql
-- Convert source system timestamps to UTC
CREATE OR REPLACE TABLE standardized_events AS
SELECT 
    event_id,
    CONVERT_TIMEZONE('America/New_York', 'UTC', source_timestamp) AS event_timestamp_utc,
    event_type,
    'America/New_York' AS source_timezone
FROM raw_events;

Advanced Analytics with Date Functions

Leverage Snowflake’s temporal capabilities for sophisticated analytical techniques.

Moving Averages and Trend Analysis

Calculate various moving average types:

sql
SELECT 
    order_date,
    daily_revenue,
    -- Simple moving average (7-day)
    AVG(daily_revenue) OVER (
        ORDER BY order_date 
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS sma_7day,
    -- Exponential moving average approximation
    AVG(daily_revenue) OVER (
        ORDER BY order_date 
        ROWS BETWEEN 13 PRECEDING AND CURRENT ROW
    ) AS sma_14day,
    -- Weighted moving average (more recent days weighted higher)
    (daily_revenue * 4 + 
     LAG(daily_revenue, 1) OVER (ORDER BY order_date) * 3 +
     LAG(daily_revenue, 2) OVER (ORDER BY order_date) * 2 +
     LAG(daily_revenue, 3) OVER (ORDER BY order_date) * 1
    ) / 10 AS wma_4day
FROM (
    SELECT 
        order_date,
        SUM(order_amount) AS daily_revenue
    FROM orders
    GROUP BY order_date
)
ORDER BY order_date;

Detecting Anomalies with Date-Based Statistics

Identify unusual patterns in time-series data:

sql
WITH daily_stats AS (
    SELECT 
        order_date,
        SUM(order_amount) AS daily_revenue,
        AVG(SUM(order_amount)) OVER (
            ORDER BY order_date 
            ROWS BETWEEN 29 PRECEDING AND CURRENT ROW
        ) AS avg_30day,
        STDDEV(SUM(order_amount)) OVER (
            ORDER BY order_date 
            ROWS BETWEEN 29 PRECEDING AND CURRENT ROW
        ) AS stddev_30day
    FROM orders
    WHERE order_date >= DATEADD(DAY, -90, CURRENT_DATE())
    GROUP BY order_date
)
SELECT 
    order_date,
    daily_revenue,
    avg_30day,
    stddev_30day,
    (daily_revenue - avg_30day) / NULLIF(stddev_30day, 0) AS z_score,
    CASE 
        WHEN ABS((daily_revenue - avg_30day) / NULLIF(stddev_30day, 0)) > 3 THEN 'Extreme Anomaly'
        WHEN ABS((daily_revenue - avg_30day) / NULLIF(stddev_30day, 0)) > 2 THEN 'Moderate Anomaly'
        ELSE 'Normal'
    END AS anomaly_status
FROM daily_stats
WHERE stddev_30day IS NOT NULL
ORDER BY order_date;

Forecasting with Historical Trends

Simple trend-based forecasting using date calculations:

sql
WITH historical_data AS (
    SELECT 
        DATE_TRUNC('MONTH', order_date) AS month,
        SUM(order_amount) AS monthly_revenue
    FROM orders
    WHERE order_date >= DATEADD(MONTH, -24, CURRENT_DATE())
    GROUP BY DATE_TRUNC('MONTH', order_date)
),
trend_calculation AS (
    SELECT 
        month,
        monthly_revenue,
        DATEDIFF(MONTH, MIN(month) OVER (), month) AS month_index,
        AVG(monthly_revenue) OVER () AS avg_revenue,
        REGR_SLOPE(monthly_revenue, DATEDIFF(MONTH, MIN(month) OVER (), month)) 
            OVER () AS trend_slope,
        REGR_INTERCEPT(monthly_revenue, DATEDIFF(MONTH, MIN(month) OVER (), month)) 
            OVER () AS trend_intercept
    FROM historical_data
)
SELECT 
    month,
    monthly_revenue AS actual_revenue,
    trend_intercept + (trend_slope * month_index) AS forecasted_revenue,
    monthly_revenue - (trend_intercept + (trend_slope * month_index)) AS forecast_error
FROM trend_calculation
ORDER BY month;

Survival Analysis and Time-to-Event

Calculate survival curves and hazard rates:

sql
WITH user_lifecycle AS (
    SELECT 
        user_id,
        MIN(signup_date) AS cohort_start,
        MAX(last_activity_date) AS last_seen,
        DATEDIFF(DAY, MIN(signup_date), COALESCE(MAX(churn_date), CURRENT_DATE())) AS days_active,
        CASE WHEN MAX(churn_date) IS NOT NULL THEN 1 ELSE 0 END AS churned
    FROM users
    GROUP BY user_id
),
survival_table AS (
    SELECT 
        days_active,
        COUNT(*) AS users_at_risk,
        SUM(churned) AS churned_users,
        SUM(SUM(churned)) OVER (ORDER BY days_active) AS cumulative_churned,
        COUNT(*) - SUM(SUM(churned)) OVER (ORDER BY days_active) AS still_active
    FROM user_lifecycle
    GROUP BY days_active
)
SELECT 
    days_active,
    users_at_risk,
    churned_users,
    still_active,
    ROUND(still_active::FLOAT / FIRST_VALUE(users_at_risk) OVER (ORDER BY days_active), 4) AS survival_rate,
    ROUND(churned_users::FLOAT / users_at_risk, 4) AS hazard_rate
FROM survival_table
WHERE days_active <= 365
ORDER BY days_active;

Integration with Machine Learning Pipelines

Prepare temporal features for machine learning models.

Feature Engineering with Date Components

Extract meaningful temporal features:

sql
CREATE OR REPLACE TABLE ml_features AS
SELECT 
    transaction_id,
    customer_id,
    transaction_date,
    transaction_amount,
    -- Basic date components
    YEAR(transaction_date) AS year,
    QUARTER(transaction_date) AS quarter,
    MONTH(transaction_date) AS month,
    DAY(transaction_date) AS day,
    DAYOFWEEK(transaction_date) AS day_of_week,
    DAYOFYEAR(transaction_date) AS day_of_year,
    WEEK(transaction_date) AS week_of_year,
    -- Cyclical encoding for seasonality
    SIN(2 * PI() * DAYOFYEAR(transaction_date) / 365.0) AS day_of_year_sin,
    COS(2 * PI() * DAYOFYEAR(transaction_date) / 365.0) AS day_of_year_cos,
    SIN(2 * PI() * HOUR(transaction_timestamp) / 24.0) AS hour_of_day_sin,
    COS(2 * PI() * HOUR(transaction_timestamp) / 24.0) AS hour_of_day_cos,
    -- Business logic features
    CASE WHEN DAYOFWEEK(transaction_date) IN (0, 6) THEN 1 ELSE 0 END AS is_weekend,
    CASE WHEN DAY(transaction_date) <= 7 THEN 1 ELSE 0 END AS is_first_week_of_month,
    CASE WHEN DAY(transaction_date) = DAYOFMONTH(LAST_DAY(transaction_date)) THEN 1 ELSE 0 END AS is_month_end,
    -- Recency features
    DATEDIFF(DAY, transaction_date, CURRENT_DATE()) AS days_since_transaction,
    DATEDIFF(DAY, transaction_date, LAG(transaction_date) OVER (PARTITION BY customer_id ORDER BY transaction_date)) AS days_since_last_transaction
FROM transactions;

Time-Based Train/Test Splits

Create temporally-aware data splits for model training:

sql
-- Training set: Data up to cutoff date
CREATE OR REPLACE TABLE training_data AS
SELECT *
FROM ml_features
WHERE transaction_date < '2025-09-01';

-- Validation set: 1 month for validation
CREATE OR REPLACE TABLE validation_data AS
SELECT *
FROM ml_features
WHERE transaction_date >= '2025-09-01' 
AND transaction_date < '2025-10-01';

-- Test set: Most recent month
CREATE OR REPLACE TABLE test_data AS
SELECT *
FROM ml_features
WHERE transaction_date >= '2025-10-01';

Conclusion: Mastering Date and Time Queries in Snowflake

Understanding what is how to query date and time in Snowflake empowers data professionals to unlock powerful insights from temporal data. Throughout this comprehensive guide, we’ve explored everything from fundamental date functions to advanced analytical techniques that leverage Snowflake’s robust temporal capabilities.

Key takeaways for mastering date and time queries in Snowflake include:

Foundation Knowledge: Snowflake’s DATE, TIME, and TIMESTAMP data types provide flexible options for storing temporal information with appropriate precision. Understanding the distinctions between TIMESTAMP_NTZ, TIMESTAMP_LTZ, and TIMESTAMP_TZ ensures you choose the right type for your use case.

Essential Functions: Functions like CURRENT_DATE(), DATEADD(), DATEDIFF(), DATE_TRUNC(), and EXTRACT() form the core toolkit for temporal operations. Mastering these functions enables you to perform virtually any date calculation or transformation required in analytics.

Performance Optimization: Implementing clustering keys on date columns, structuring queries to enable partition pruning, and leveraging materialized views for common aggregations dramatically improves query performance on large temporal datasets.

Best Practices: Always store timestamps in UTC, use explicit type casting, handle NULL values gracefully, test date logic at boundaries, and document time zone assumptions throughout your data pipeline.

Advanced Techniques: Cohort analysis, retention calculations, seasonality detection, and time-series forecasting become straightforward when you understand how to combine Snowflake’s temporal functions with window functions and statistical operations.

Real-World Applications: From customer lifetime value analysis to subscription management, from event sequence tracking to anomaly detection, Snowflake’s date and time capabilities enable sophisticated analytical applications across industries.

The cloud data warehouse landscape continues evolving, but temporal analysis remains fundamental to extracting business value from data. Snowflake’s intuitive SQL interface combined with powerful date and time functions makes it an excellent platform for organizations seeking to build data-driven decision-making processes.

As you implement these techniques in your own projects, remember that effective temporal analysis requires both technical proficiency and business context. Always validate your date logic, test edge cases thoroughly, and ensure your queries return results that make sense in your business domain.

Whether you’re building executive dashboards, conducting cohort analyses, or developing machine learning features, the skills covered in this guide provide the foundation for successful temporal data analysis in Snowflake. Continue practicing with real datasets, experiment with different function combinations, and gradually build more sophisticated temporal queries as your confidence grows.

The journey to mastering how to query date and time in Snowflake is ongoing, but with the knowledge and examples provided in this ultimate guide, you’re well-equipped to tackle even the most complex temporal analytical challenges in your cloud data warehouse.

Leave a Reply

Your email address will not be published. Required fields are marked *