Revenue Analysis with Date/Time Functions
Learning Objectives
After completing this recipe, you will be able to:
- Understand BigQuery’s date/time data types (DATE, DATETIME, TIMESTAMP)
- Extract time units with EXTRACT and DATE_TRUNC functions
- Perform period comparison analysis with DATE_ADD/SUB
- Calculate periods and derive KPIs with DATE_DIFF
BigQuery Execution Environment Setup
from google.cloud import bigquery
import pandas as pd
# Authentication setup (when using service account key)
# import os
# os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "path/to/your-key.json"
# Create BigQuery client
client = bigquery.Client(project='your-project-id')
# Query execution function
def run_query(query):
return client.query(query).to_dataframe()Replace your-project-id with your GCP project ID in the code above.
1. DATE vs DATETIME vs TIMESTAMP
Theory
BigQuery provides three main time data types:
| Type | Format | Example | Characteristics |
|---|---|---|---|
| DATE | YYYY-MM-DD | 2024-01-15 | Date only, no time information |
| DATETIME | YYYY-MM-DD HH:MM:SS | 2024-01-15 14:30:00 | Date + time, no timezone |
| TIMESTAMP | YYYY-MM-DD HH:MM:SS UTC | 2024-01-15 14:30:00+00:00 | Date + time + timezone (UTC-based) |
- DATE: When time isn’t important, like birthdate, signup date
- DATETIME: Local time-based events (reservation time, business hours)
- TIMESTAMP: Recording exact moments of worldwide events (order time, logs)
Key Functions
-- Get current date/time
CURRENT_DATE() -- Current date
CURRENT_DATETIME() -- Current datetime
CURRENT_TIMESTAMP() -- Current timestamp
-- Type conversion
CAST(timestamp_col AS DATE) -- TIMESTAMP → DATE
CAST(timestamp_col AS DATETIME) -- TIMESTAMP → DATETIMEPractice: Comparing Date Types
SELECT
CURRENT_DATE() AS current_date,
CURRENT_DATETIME() AS current_datetime,
CURRENT_TIMESTAMP() AS current_timestamp,
-- Convert created_at to each type
CAST(created_at AS DATE) AS order_date,
CAST(created_at AS DATETIME) AS order_datetime,
created_at AS order_timestamp
FROM `your-project-id.thelook_ecommerce.orders`
LIMIT 5| current_date | current_datetime | current_timestamp | order_date | order_datetime | order_timestamp |
|---|---|---|---|---|---|
| 2024-11-19 | 2024-11-19 10:26:43 | 2024-11-19 10:26:43+00:00 | 2023-10-11 | 2023-10-11 15:31:00 | 2023-10-11 15:31:00+00:00 |
2. EXTRACT Function - Extracting Year/Month/Day/Day of Week
Theory
The EXTRACT function extracts a specific part from a date/time.
EXTRACT(part FROM date_expression)| Part | Description | Return Value Range |
|---|---|---|
| YEAR | Year | e.g., 2024 |
| MONTH | Month | 1-12 |
| DAY | Day | 1-31 |
| DAYOFWEEK | Day of week | 1(Sun)~7(Sat) |
| DAYOFYEAR | Day of year | 1-366 |
| WEEK | Week number | 0-53 |
| QUARTER | Quarter | 1-4 |
| HOUR | Hour | 0-23 |
| MINUTE | Minute | 0-59 |
BigQuery’s DAYOFWEEK has Sunday as 1, Saturday as 7.
Practice: Order Analysis by Day of Week
SELECT
EXTRACT(DAYOFWEEK FROM created_at) AS day_of_week,
CASE EXTRACT(DAYOFWEEK FROM created_at)
WHEN 1 THEN 'Sunday'
WHEN 2 THEN 'Monday'
WHEN 3 THEN 'Tuesday'
WHEN 4 THEN 'Wednesday'
WHEN 5 THEN 'Thursday'
WHEN 6 THEN 'Friday'
WHEN 7 THEN 'Saturday'
END AS day_name,
COUNT(*) AS order_count
FROM `your-project-id.thelook_ecommerce.orders`
GROUP BY day_of_week, day_name
ORDER BY day_of_week| day_of_week | day_name | order_count |
|---|---|---|
| 1 | Sunday | 18,384 |
| 2 | Monday | 17,916 |
| 3 | Tuesday | 17,617 |
| 4 | Wednesday | 17,313 |
| 5 | Thursday | 17,526 |
| 6 | Friday | 17,725 |
| 7 | Saturday | 18,312 |
Weekend (Sat/Sun) orders are slightly higher than weekdays. This is a common pattern in e-commerce and can be used to develop weekend promotion strategies.
3. DATE_TRUNC - Period Unit Aggregation
Theory
DATE_TRUNC truncates a date to a specific unit. Useful for monthly, weekly, and quarterly aggregations.
DATE_TRUNC(date_expression, date_part)| date_part | Input | Result |
|---|---|---|
| DAY | 2024-03-15 14:30:00 | 2024-03-15 |
| WEEK | 2024-03-15 14:30:00 | 2024-03-11 (Monday) |
| MONTH | 2024-03-15 14:30:00 | 2024-03-01 |
| QUARTER | 2024-03-15 14:30:00 | 2024-01-01 |
| YEAR | 2024-03-15 14:30:00 | 2024-01-01 |
Practice: Monthly Revenue Trend
SELECT
DATE_TRUNC(CAST(o.created_at AS DATE), MONTH) AS month,
COUNT(DISTINCT o.order_id) AS order_count,
COUNT(DISTINCT o.user_id) AS customer_count,
SUM(oi.sale_price) AS total_revenue,
ROUND(SUM(oi.sale_price) / COUNT(DISTINCT o.order_id), 2) AS avg_order_value
FROM `your-project-id.thelook_ecommerce.orders` o
JOIN `your-project-id.thelook_ecommerce.order_items` oi ON o.order_id = oi.order_id
WHERE o.created_at >= '2024-01-01'
GROUP BY month
ORDER BY month| month | order_count | customer_count | total_revenue | avg_order_value |
|---|---|---|---|---|
| 2024-01-01 | 1,983 | 1,892 | 156,234.50 | 78.78 |
| 2024-02-01 | 1,928 | 1,845 | 148,923.75 | 77.24 |
| 2024-03-01 | 2,209 | 2,098 | 172,456.30 | 78.07 |
| … | … | … | … | … |
4. DATE_DIFF - Period Calculation
Theory
DATE_DIFF calculates the difference between two dates.
DATE_DIFF(end_date, start_date, date_part)| Calculation | SQL | Result |
|---|---|---|
| Days difference | DATE_DIFF('2024-03-15', '2024-03-10', DAY) | 5 |
| Months difference | DATE_DIFF('2024-06-01', '2024-03-01', MONTH) | 3 |
| Years difference | DATE_DIFF('2025-01-01', '2024-01-01', YEAR) | 1 |
Practice: Customer Activity Period Analysis
SELECT
user_id,
MIN(CAST(created_at AS DATE)) AS first_order_date,
MAX(CAST(created_at AS DATE)) AS last_order_date,
DATE_DIFF(MAX(CAST(created_at AS DATE)), MIN(CAST(created_at AS DATE)), DAY) AS days_active,
COUNT(DISTINCT order_id) AS total_orders,
ROUND(
COUNT(DISTINCT order_id) * 1.0 /
NULLIF(DATE_DIFF(MAX(CAST(created_at AS DATE)), MIN(CAST(created_at AS DATE)), MONTH), 0),
2
) AS orders_per_month
FROM `your-project-id.thelook_ecommerce.orders`
GROUP BY user_id
HAVING COUNT(DISTINCT order_id) > 1 -- Only customers with 2+ orders
ORDER BY days_active DESC
LIMIT 10| user_id | first_order_date | last_order_date | days_active | total_orders | orders_per_month |
|---|---|---|---|---|---|
| 4523 | 2023-01-02 | 2024-11-15 | 682 | 8 | 0.35 |
| 7891 | 2023-01-05 | 2024-10-28 | 662 | 5 | 0.23 |
| 2345 | 2023-01-08 | 2024-10-20 | 651 | 6 | 0.28 |
| … | … | … | … | … | … |
5. Month-over-Month and Year-over-Year Analysis
Theory
The most important thing in time series analysis is period-over-period comparison. Let’s learn how to calculate month-over-month (MoM) and year-over-year (YoY) growth rates.
-- Previous month data join
DATE_SUB(current_month, INTERVAL 1 MONTH)
-- Same month last year data join
DATE_SUB(current_month, INTERVAL 1 YEAR)Practice: Month-over-Month Revenue Growth Rate
WITH monthly_revenue AS (
SELECT
DATE_TRUNC(CAST(o.created_at AS DATE), MONTH) AS month,
SUM(oi.sale_price) AS revenue
FROM `your-project-id.thelook_ecommerce.orders` o
JOIN `your-project-id.thelook_ecommerce.order_items` oi ON o.order_id = oi.order_id
GROUP BY month
)
SELECT
curr.month,
curr.revenue AS current_revenue,
prev.revenue AS previous_revenue,
ROUND((curr.revenue - prev.revenue) / prev.revenue * 100, 2) AS mom_growth_pct
FROM monthly_revenue curr
LEFT JOIN monthly_revenue prev
ON curr.month = DATE_ADD(prev.month, INTERVAL 1 MONTH)
WHERE curr.month >= '2024-01-01'
ORDER BY curr.month| month | current_revenue | previous_revenue | mom_growth_pct |
|---|---|---|---|
| 2024-01-01 | 156,234.50 | 142,567.80 | 9.58 |
| 2024-02-01 | 148,923.75 | 156,234.50 | -4.68 |
| 2024-03-01 | 172,456.30 | 148,923.75 | 15.80 |
- MoM (Month over Month): Short-term trend identification, heavily influenced by seasonality
- YoY (Year over Year): Removes seasonality, captures medium to long-term growth trends
- QoQ (Quarter over Quarter): Measures quarterly business goal achievement
Quiz
Problem 1: Order Analysis by Hour
From the orders table:
- Extract the order hour and aggregate order count by hour
- Identify which hour has the most orders
View Answer
SELECT
EXTRACT(HOUR FROM created_at) AS hour,
COUNT(*) AS order_count,
ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) AS pct
FROM `your-project-id.thelook_ecommerce.orders`
GROUP BY hour
ORDER BY hourExplanation:
- Extract the hour with
EXTRACT(HOUR FROM created_at) - Calculate the percentage of total using the window function
SUM(...) OVER() - Typically, orders concentrate around 2-4 PM and 8-10 PM
Problem 2: Delivery Time from Order to Shipment
From the order_items table:
- Calculate the average delivery days for delivered orders
- Analyze the monthly trend of delivery days
View Answer
SELECT
DATE_TRUNC(CAST(shipped_at AS DATE), MONTH) AS month,
COUNT(*) AS delivered_count,
ROUND(AVG(DATE_DIFF(CAST(delivered_at AS DATE), CAST(shipped_at AS DATE), DAY)), 2) AS avg_delivery_days
FROM `your-project-id.thelook_ecommerce.order_items`
WHERE delivered_at IS NOT NULL
GROUP BY month
ORDER BY monthExplanation:
- Calculate the difference between shipping date and delivery date with
DATE_DIFF - Calculate the average with
AVG - Filter only actually delivered items with NULL check
Summary
| Function | Use Case | Example |
|---|---|---|
EXTRACT | Extract specific part from date | Day of week, hourly analysis |
DATE_TRUNC | Truncate date to specific unit | Monthly, quarterly aggregation |
DATE_DIFF | Calculate difference between two dates | Retention, delivery days |
DATE_ADD/SUB | Add/subtract from date | Month-over-month, year-over-year analysis |
Next Steps
You’ve mastered date/time functions! Next, learn more complex aggregation analysis in Grouping and Aggregation.