Phase 2 · Foundation Data · Oracle RMS Series

The Oracle RMS Retail Calendar

The retail calendar is the heartbeat of Oracle RMS. Virtually every batch job, every financial report, and every inventory period closes against a retail period — not a calendar month. Understanding how the PERIOD table works and how RMS anchors its batches to fiscal time is foundational knowledge for every developer.

10 min read📅April 22, 2026✍️Priyanshu Pandey📚Oracle RMS Series

Why Retail Uses Its Own Calendar

Retail businesses don't operate on calendar months. A calendar month has between 28 and 31 days, which makes year-over-year comparisons meaningless — January 2024 had one more trading day than January 2025, and any sales comparison between the two is distorted before you even start.

Retail solves this with a fiscal calendar that divides the year into equal-length periods based on weeks, not arbitrary month boundaries. The most popular structure in global retail is the 4-5-4 calendar — named for the number of weeks in each month within a quarter: 4 weeks, then 5 weeks, then 4 weeks. Every quarter is 13 weeks, every year is exactly 52 weeks (364 days), and year-over-year period comparisons are always like-for-like.

Oracle RMS is built around this retail calendar at every level. The PERIOD table is one of the most referenced tables in all of RMS. Date-based queries in the stock ledger, purchasing, pricing, and replenishment all join to PERIOD rather than using TRUNC(date, 'MM') or calendar month functions.

📅
RMS has its own internal date — separate from SYSDATE

Oracle RMS maintains its own internal "current date" stored in SYSTEM_OPTIONS.VDATE (the virtual date). The SYSDATE batch advances this date each day. Batch jobs that run before the SYSDATE batch will process against yesterday's date. This is why batch sequencing matters so much — and why SYSDATE is always the first batch in the daily schedule.

Calendar Types

Calendar Types — 4-5-4, 4-4-5, and Gregorian

RMS supports three calendar structures, configured at system setup and fixed for the life of the instance:

ColumnTypeDescription
4-5-4
4 wks, 5 wks, 4 wks

Most common in US and large global retailers

4-4-5
4 wks, 4 wks, 5 wks

Common in UK and parts of Europe

Gregorian
Calendar months

Used by retailers who want month-end closes on the last calendar day. Loses the week-comparability advantage.

The calendar type determines how the PERIOD table is populated and how period boundaries are calculated. Once chosen, it cannot be changed — the entire history of your stock ledger, purchasing records, and financial closes are anchored to it.

⚠️
Calendar type cannot be changed post-go-live

The retail calendar structure is set once during implementation. Migrating from a 4-5-4 to Gregorian calendar requires rebuilding all historical period data and reprocessing all closed stock ledger periods. Treat this decision with the same weight as choosing your primary currency.

The PERIOD Table

The PERIOD Table — The Core Time Dimension

The PERIOD table is the master time dimension for Oracle RMS. It is pre-populated with periods spanning the full implementation history and several years into the future. Every date in RMS can be looked up against PERIOD to find which fiscal week, month, quarter, and year it falls in.

01PERIOD
Retail Calendar Master

Each row in PERIOD represents one atomic unit of time at the period's granularity level. The most granular level is the week (PERIOD_TYPE = 'WK'). Months, quarters, and fiscal years are all separate rows at their respective period types, linked hierarchically via PARENT_PERIOD_ID.

ColumnTypeDescription
PERIOD_IDPK
NUMBER(10)Surrogate primary key for this period
PERIOD_TYPE
VARCHAR2(2)WK = Week · MO = Month · QR = Quarter · YR = Year
PERIOD_DESC
VARCHAR2(60)Human-readable label (e.g. 'Week 14, FY2026' or 'Apr 2026')
START_DATE
DATEFirst calendar date of this fiscal period (inclusive)
END_DATE
DATELast calendar date of this fiscal period (inclusive)
PARENT_PERIOD_IDFK
NUMBER(10)FK → PERIOD.PERIOD_ID. Week → Month → Quarter → Year hierarchy
FISCAL_YEAR
NUMBER(4)The fiscal year this period belongs to
FISCAL_MONTH
NUMBER(2)Month number within the fiscal year (1–12)
FISCAL_WEEK
NUMBER(2)Week number within the fiscal month (1–5)
CLOSED_IND
VARCHAR2(1)Y = this period has been closed (stock ledger finalized)
Period Hierarchy

Period Hierarchy — Week, Month, Quarter, Year

The PERIOD table is self-referencing via PARENT_PERIOD_ID. The hierarchy flows upward:

Calendar Rollup Hierarchy
YearPERIOD_TYPE = 'YR'

The top-level anchor. Parent of all quarters.

QuarterPERIOD_TYPE = 'QR'

Parent of months. Usually defined by the 4-5-4 pattern.

MonthPERIOD_TYPE = 'MO'

Parent of weeks. Represents a retail fiscal month.

WeekPERIOD_TYPE = 'WK'

The leaf node. The lowest level of granularity for sales and stock aggregation.

To find which fiscal month a specific date falls in, you join from the week-level period (found by matching START_DATE ≤ date ≤ END_DATE) up to its parent month via PARENT_PERIOD_ID.

The standard pattern for date-to-period lookup

The pattern used throughout RMS to find the fiscal week for a date: WHERE :v_date BETWEEN p.start_date AND p.end_date AND p.period_type = 'WK'. This is more reliable than computing fiscal week numbers manually — always join to PERIOD for any fiscal time lookups.

SYSDATE Batch

The SYSDATE Batch — Advancing RMS Time

Oracle RMS does not use Oracle's built-in SYSDATE function to determine "today" for business operations. Instead, it maintains its own virtual date in SYSTEM_OPTIONS.VDATE. The SYSDATE batch job (also called RMSDATE at some retailers) advances this virtual date by one day each time it runs.

This design has important implications for developers:

1. Batch sequencing is time-sensitive. Any batch that runs before the SYSDATE batch on a given day will process against yesterday's VDATE. Any batch that runs after it will process against today's. If SYSDATE doesn't run, no batch for that day will process against the correct date — every date-based calculation will be one day behind.

2. Custom logic must read VDATE, not SYSDATE. Custom PL/SQL in RMS should always read the current date from SYSTEM_OPTIONS.VDATE rather than calling Oracle's SYSDATE. In production these will agree most of the time, but in test environments, the VDATE is often deliberately set to a past or future date for testing.

3. VDATE advancement triggers period boundaries. When the SYSDATE batch advances VDATE across a period boundary (e.g., from the last day of a fiscal week to the first day of the next), it triggers all period-boundary processing — the stock ledger snapshot for the closing week, replenishment period recalculations, and more.

SQL

-- Always read the RMS virtual date this way in custom code SELECT vdate AS rms_current_date FROM system_options;

-- DO NOT use this in RMS business logic: -- SELECT TRUNC(SYSDATE) FROM DUAL; -- SYSDATE and VDATE may differ in test/staging environments

RC:OMS · Retail-Grade Inventory Ledger
Building your own date-aware inventory system?

RC:OMS uses the same period-aware design philosophy as Oracle RMS — every stock movement is timestamped, every period can be closed and re-opened, and historical snapshots are immutable. Without the Oracle licence cost.

See RC:OMS in action
Period Close

Period Close & the Stock Ledger

At the end of every fiscal week, RMS's period-close batch takes a snapshot of the stock ledger — recording opening stock, sales, purchases, adjustments, and closing stock for every item at every location for that fiscal period. Once a period is closed (CLOSED_IND = 'Y' on the PERIOD row), the historical snapshot is immutable.

This has direct implications for custom development:

  • Never query STKLEDGR (the stock ledger) for a closed period expecting to find the "current" position — use ITEM_LOC_SOH for current stock on hand.
  • Reprocessing or backdating inventory adjustments into a closed period requires either a period re-open (a controlled DBA operation) or a bridging adjustment in the current open period.
  • Financial reporting should always join to PERIOD and filter by CLOSED_IND = 'Y' to ensure only finalized data is included.
Quick Reference

Key Tables — Quick Reference

Calendar & Time Tables — Oracle RMS
PERIOD
Core Time Dimension
SYSTEM_OPTIONS
Virtual Date (VDATE)
STKLEDGR
Stock Ledger
ITEM_LOC_SOH
Current SOH
Practical SQL

Practical SQL Examples

1. Find the current fiscal week, month, and year

SQL

-- Current RMS period context — week, month, quarter, year WITH current_date_cte AS ( SELECT vdate AS rms_date FROM system_options ) SELECT wk.period_id week_id, wk.period_desc fiscal_week, wk.fiscal_week week_no_in_month, mo.period_desc fiscal_month, mo.fiscal_month month_no_in_year, qr.period_desc fiscal_quarter, yr.period_desc fiscal_year, yr.fiscal_year year_number FROM period wk JOIN period mo ON mo.period_id = wk.parent_period_id JOIN period qr ON qr.period_id = mo.parent_period_id JOIN period yr ON yr.period_id = qr.parent_period_id WHERE wk.period_type = 'WK' AND (SELECT rms_date FROM current_date_cte) BETWEEN wk.start_date AND wk.end_date;

2. Stock ledger summary for a closed fiscal month

SQL
-- Stock ledger for a specific fiscal month, by department
SELECT dp.dept,
dp.dept_name,
SUM(sl.opening_stock_cost)  opening_cost,
SUM(sl.net_sales_cost)      sales_cost,
SUM(sl.purchases_cost)      purchases_cost,
SUM(sl.closing_stock_cost)  closing_cost
FROM   stkledgr   sl
JOIN   period     wk   ON  wk.period_id   = sl.period_id
JOIN   period     mo   ON  mo.period_id   = wk.parent_period_id
JOIN   item_master im  ON  im.item        = sl.item
JOIN   deps        dp  ON  dp.dept        = im.dept
WHERE  mo.fiscal_year  = :v_fiscal_year
AND  mo.fiscal_month = :v_fiscal_month
AND  mo.period_type  = 'MO'
AND  mo.closed_ind   = 'Y'
GROUP BY dp.dept, dp.dept_name
ORDER BY dp.dept;

3. Last 13 fiscal weeks — rolling year comparison scaffold

SQL

-- Last 13 fiscal weeks with start/end dates (one quarter rolling) -- Useful for building YoY sales comparison queries SELECT wk.period_id, wk.period_desc, wk.start_date, wk.end_date, wk.fiscal_year, wk.fiscal_month, wk.fiscal_week, RANK() OVER (ORDER BY wk.start_date DESC) AS weeks_ago FROM period wk JOIN system_options so ON 1=1 WHERE wk.period_type = 'WK' AND wk.end_date [lt or eq to] so.vdate ORDER BY wk.start_date DESC FETCH FIRST 13 ROWS ONLY;

Gotchas

Common Gotchas for Developers

  • !

    Using Oracle SYSDATE instead of SYSTEM_OPTIONS.VDATE. In production they're the same. In UAT, staging, or when testing future scenarios, VDATE may be set weeks or months different from SYSDATE. Any custom PL/SQL that calls Oracle's SYSDATE directly will behave differently from standard RMS code and break date-sensitive testing.

  • !

    Querying STKLEDGR for current stock instead of ITEM_LOC_SOH.

    The stock ledger holds period-close snapshots. The current open period's data in STKLEDGR is partial — it accumulates through the week but isn't finalized until period close. For current stock on hand, always use ITEM_LOC_SOH.

  • !

    Not joining through PERIOD for fiscal date lookups. Using calendar month functions (TRUNC(date, 'MM')) in RMS reporting produces wrong groupings because fiscal months don't align with calendar months. Always join to PERIOD for any fiscal time aggregation.

  • !

    Assuming PERIOD rows always exist for future dates. The PERIOD table is pre-populated for a fixed number of future years. If your RMS instance is old and PERIOD rows haven't been extended, future-dated POs, promotions, or replenishment dates will fail validation. Check the maximum END_DATE in PERIOD periodically and extend when needed.

  • !

    Backdating transactions into a closed period. Once a period is closed (CLOSED_IND = 'Y'), its stock ledger snapshot is final. You cannot simply insert an inventory adjustment with a date in that closed period and expect the ledger to update — it won't. Any correction to a closed period requires a controlled re-open followed by re-posting, which is a DBA-supervised operation.

Key Takeaways

Key Takeaways
  • Oracle RMS uses a fiscal retail calendar (4-5-4, 4-4-5, or Gregorian) configured at go-live and fixed for the life of the instance.
  • The PERIOD table is the master time dimension. Every week, month, quarter, and year is a row with START_DATE and END_DATE. Always join to PERIOD for fiscal time lookups — never use calendar month functions.
  • SYSTEM_OPTIONS.VDATE is RMS's internal current date. Custom code must read VDATE, not Oracle's SYSDATE.
  • The SYSDATE batch advances VDATE daily and must be the first batch in every day's schedule. All other batches process against the VDATE in effect when they run.
  • STKLEDGR holds closed-period stock snapshots. ITEM_LOC_SOH holds current open-period stock. Use the right table for the right question.
  • PERIOD rows must exist for future dates to work. Monitor the maximum END_DATE in PERIOD and extend well in advance of expiry.
RetailCoder
All systems operational
v1.0 Live

RC:OMS

Multi-channel order management with double-entry inventory ledger. Amazon, Flipkart, Shopify, WooCommerce — one source of truth.

Launch demo →
v1.0 Live

RC:Storefront

Self-hosted headless e-commerce. Your server, your data, zero transaction fees. Native RC:OMS inventory sync.

Visit Storefront →
Pipeline

RC:Pulse

AI-powered retail analytics and demand forecasting — built natively on top of your RC:OMS and Storefront data.

Request early access →
Built in India 🇮🇳  ·  Architected by Priyanshu PandeyTalk to an engineer →