diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,18 @@
 The format is based on Keep a Changelog and this project adheres to
 Semantic Versioning.
 
+## [0.1.1.0] - 2025-11-20
+### Added
+- `AWS.EventBridge.Schedule` module that pairs schedules with an IANA timezone
+	(via the `tz`/`tzdata` packages) and exposes the full matrix of
+	`nextRunTimes` variants for UTC, local, and zoned outputs.
+- Smart constructors such as `scheduleFromText` plus helper variants
+	(`nextRunTimesUTCFromLocal`, `nextRunTimesZonedFromUTC`, etc.) documented with
+	Haddock examples.
+- Property-style regression tests in `ScheduleSpec` covering cron/one-time rate
+	conversions, DST transitions, and timezone-dependent outputs.
+- README examples describing timezone-aware workflows and feature list updates.
+
 ## [0.1.0.1] - 2025-11-17
 ### Added
 - `ScheduleKind`, `scheduleKind`, and `isRecurring` helpers for classifying parsed schedules without re-parsing downstream.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -16,6 +16,9 @@
 - Schedule introspection helpers: `scheduleKind` returns a `ScheduleKind`, and
   `isRecurring` distinguishes recurring (`cron`/`rate`) expressions from
   `at(...)` one-time schedules.
+- Timezone-aware helpers: `AWS.EventBridge.Schedule` pairs expressions with an
+  IANA timezone (via the `tz`/`tzdata` packages) and exposes `nextRunTimes`
+  variants for UTC, local, or fully-zoned outputs.
 - Extensive property-based test suite that mirrors the behaviour documented by
   AWS.
 
@@ -71,6 +74,65 @@
 isRecurringExample = isRecurring <$> parseCronText "at(2025-11-16T09:30:00)"
 -- Right False
 ```
+
+### Timezone-Aware Schedules
+
+EventBridge rules can set a schedule timezone. Use `AWS.EventBridge.Schedule` to
+bind an expression to a `TZLabel` so you can request run times in UTC, as local
+wall-clock values, or as `ZonedTime`s tagged with the appropriate offset.
+
+```haskell
+import AWS.EventBridge.Schedule
+import Data.Time (UTCTime(..), LocalTime, ZonedTime, fromGregorian)
+import Data.Time.Zones.All (TZLabel(..))
+
+baseUTC :: UTCTime
+baseUTC = UTCTime (fromGregorian 2025 11 1) 0
+
+zonedSchedule :: Either String Schedule
+zonedSchedule = scheduleFromText America__New_York "cron(0 9 * NOV ? 2025)"
+
+utcRuns :: Either String [UTCTime]
+utcRuns = nextRunTimesUTC <$> zonedSchedule <*> pure baseUTC <*> pure 2
+-- Right [2025-11-01 13:00:00 UTC,2025-11-02 14:00:00 UTC]
+
+localRuns :: Either String [LocalTime]
+localRuns = nextRunTimesLocalFromUTC <$> zonedSchedule <*> pure baseUTC <*> pure 2
+-- Right [2025-11-01 09:00:00,2025-11-02 09:00:00]
+
+zonedRuns :: Either String [ZonedTime]
+zonedRuns = nextRunTimesZonedFromUTC <$> zonedSchedule <*> pure baseUTC <*> pure 2
+-- Right [2025-11-01 09:00:00 EDT,2025-11-02 09:00:00 EST]
+```
+
+Every combination of base input (`UTCTime`, `LocalTime`, `ZonedTime`) and output
+form is available, so you can normalize timestamps at the edges of your system
+and avoid comparing values that silently belong to different timezones.
+
+### API Overview
+
+1. Parse using `parseCronText` (UTC) or `scheduleFromText` (timezone-aware).
+2. Wrap with `scheduleFromExpr`/`scheduleFromText` if you need timezone metadata.
+3. Choose an evaluation helper based on the base input you have and the output
+  you need. Prefer the primary trio (`nextRunTimesUTC`, `nextRunTimesLocal`,
+  `nextRunTimesZoned`) and reach for the conversion helpers when you want to
+  avoid manual conversions.
+
+| Base input  | Output      | Function                           |
+|-------------|-------------|------------------------------------|
+| `UTCTime`   | `UTCTime`   | `nextRunTimesUTC`                  |
+| `LocalTime` | `UTCTime`   | `nextRunTimesUTCFromLocal`         |
+| `ZonedTime` | `UTCTime`   | `nextRunTimesUTCFromZoned`         |
+| `UTCTime`   | `LocalTime` | `nextRunTimesLocalFromUTC`         |
+| `LocalTime` | `LocalTime` | `nextRunTimesLocal`                |
+| `ZonedTime` | `LocalTime` | `nextRunTimesLocalFromZoned`       |
+| `UTCTime`   | `ZonedTime` | `nextRunTimesZonedFromUTC`         |
+| `LocalTime` | `ZonedTime` | `nextRunTimesZonedFromLocal`       |
+| `ZonedTime` | `ZonedTime` | `nextRunTimesZoned`                |
+
+Use the conversion helpers when you already have a base timestamp in a specific
+representation and want the library to handle the translation for you (for
+example, UI-provided `LocalTime` that needs to be compared against UTC events).
 
 ### Error Reporting
 
diff --git a/aws-eventbridge-cron.cabal b/aws-eventbridge-cron.cabal
--- a/aws-eventbridge-cron.cabal
+++ b/aws-eventbridge-cron.cabal
@@ -1,7 +1,7 @@
 cabal-version:      3.0
 
 name:               aws-eventbridge-cron
-version:            0.1.0.1
+version:            0.1.1.0
 synopsis:           AWS EventBridge cron, rate, and one-time parser with scheduler
 description:
   Parse AWS EventBridge cron, rate, and one-time expressions and compute
@@ -33,6 +33,7 @@
 library
   exposed-modules:
       AWS.EventBridge.Cron
+    , AWS.EventBridge.Schedule
   other-modules:
       AWS.EventBridge.DayOfMonth
     , AWS.EventBridge.DayOfWeek
@@ -53,6 +54,8 @@
     , megaparsec
     , text
     , time
+    , tz
+    , tzdata
 
 test-suite aws-eventbridge-cron-test
   type: exitcode-stdio-1.0
@@ -69,6 +72,8 @@
     , AWS.EventBridge.YearsSpec
     , AWS.EventBridge.RateSpec
     , AWS.EventBridge.CronSpec
+    , AWS.EventBridge.Schedule
+    , AWS.EventBridge.ScheduleSpec
     , AWS.EventBridge.OneTimeSpec
     , TestSupport
     , AWS.EventBridge.Cron
@@ -93,3 +98,5 @@
     , tasty-quickcheck >=0.10 && <0.11
     , text
     , time
+    , tz
+    , tzdata
diff --git a/src/AWS/EventBridge/Schedule.hs b/src/AWS/EventBridge/Schedule.hs
new file mode 100644
--- /dev/null
+++ b/src/AWS/EventBridge/Schedule.hs
@@ -0,0 +1,264 @@
+{-# LANGUAGE RecordWildCards #-}
+
+-- | Zone-aware helpers built on top of 'AWS.EventBridge.Cron'.
+--
+-- ## Choosing an entry point
+--
+-- 1. Parse the expression: use 'parseCronText' (UTC-only) or 'scheduleFromText'
+--    when the rule already specifies an IANA timezone.
+-- 2. Wrap the expression with 'scheduleFromExpr' or 'scheduleFromText' to carry
+--    timezone metadata.
+-- 3. Evaluate upcoming runs via the primary trio:
+--
+--    * 'nextRunTimesUTC'   – keep everything in UTC and compare against other
+--      absolute timestamps.
+--    * 'nextRunTimesLocal' – receive wall-clock values in the schedule's zone.
+--    * 'nextRunTimesZoned' – like 'nextRunTimesLocal' but tagged with the
+--      `TimeZone` used at each occurrence (captures DST changes).
+--
+-- Conversion helpers (the `*FromUTC`, `*FromLocal`, and `*FromZoned` variants)
+-- simply preprocess the base value before delegating to one of the three
+-- primary functions. They are handy when your caller already has a specific
+-- representation and you want to avoid manual conversions.
+--
+-- ### Base/Input vs Output quick reference
+--
+-- | Base type  | Desired output | Function                         |
+-- |------------|----------------|----------------------------------|
+-- | 'UTCTime'  | 'UTCTime'      | 'nextRunTimesUTC'                |
+-- | 'LocalTime'| 'UTCTime'      | 'nextRunTimesUTCFromLocal'       |
+-- | 'ZonedTime'| 'UTCTime'      | 'nextRunTimesUTCFromZoned'       |
+-- | 'UTCTime'  | 'LocalTime'    | 'nextRunTimesLocalFromUTC'       |
+-- | 'LocalTime'| 'LocalTime'    | 'nextRunTimesLocal'              |
+-- | 'ZonedTime'| 'LocalTime'    | 'nextRunTimesLocalFromZoned'     |
+-- | 'UTCTime'  | 'ZonedTime'    | 'nextRunTimesZonedFromUTC'       |
+-- | 'LocalTime'| 'ZonedTime'    | 'nextRunTimesZonedFromLocal'     |
+-- | 'ZonedTime'| 'ZonedTime'    | 'nextRunTimesZoned'              |
+module AWS.EventBridge.Schedule
+  ( -- * Schedule construction
+    Schedule(..)
+  , scheduleFromExpr
+  , scheduleFromText
+  , parseCronTextWithZone
+    -- * Primary evaluation helpers
+  , nextRunTimesUTC
+  , nextRunTimesLocal
+  , nextRunTimesZoned
+    -- * Conversion helpers
+  , nextRunTimesUTCFromLocal
+  , nextRunTimesUTCFromZoned
+  , nextRunTimesLocalFromUTC
+  , nextRunTimesLocalFromZoned
+  , nextRunTimesZonedFromUTC
+  , nextRunTimesZonedFromLocal
+  ) where
+
+import AWS.EventBridge.Cron
+  ( CronExprT
+  , ScheduleKind(..)
+  , nextRunTimes
+  , parseCronText
+  , scheduleKind
+  )
+import Data.Text (Text)
+import Data.Time
+  ( LocalTime(..)
+  , ZonedTime(..)
+  , UTCTime(..)
+  , timeOfDayToTime
+  , timeToTimeOfDay
+  , zonedTimeToUTC
+  )
+import Data.Time.Zones
+  ( TZ
+  , localTimeToUTCTZ
+  , timeZoneForUTCTime
+  , utcToLocalTimeTZ
+  )
+import Data.Time.Zones.All (TZLabel(..), tzByLabel)
+
+-- | Scheduling expression paired with its IANA timezone.
+--
+-- Use 'scheduleFromText' when you need to parse the expression and bind the
+-- timezone in one step, or 'scheduleFromExpr' if you already have a
+-- 'CronExprT'.
+data Schedule = Schedule
+  { scheduleExpr :: CronExprT
+  , scheduleZone :: TZ
+  , scheduleZoneLabel :: TZLabel
+  }
+
+-- | Construct a 'Schedule' from an existing 'CronExprT' and timezone label.
+--
+-- @tz@ ships with bindings for the entire IANA database through 'TZLabel'. The
+-- resulting schedule can be fed directly into the zone-aware 'nextRunTimes*'
+-- helpers below.
+scheduleFromExpr :: TZLabel -> CronExprT -> Schedule
+scheduleFromExpr label expr = Schedule
+  { scheduleExpr = expr
+  , scheduleZone = tzByLabel label
+  , scheduleZoneLabel = label
+  }
+
+-- | Parse an EventBridge expression and attach a timezone in the same step.
+--
+-- >>> :{
+-- let base = read "2025-11-16 03:30:00 UTC" :: UTCTime
+-- in case scheduleFromText Asia__Kolkata "cron(0 9 ? NOV SUN 2025)" of
+--      Left err -> Left err
+--      Right sched -> nextRunTimesUTC sched base 1
+-- :}
+-- Right [2025-11-16 03:30:00 UTC]
+scheduleFromText :: TZLabel -> Text -> Either String Schedule
+scheduleFromText label input =
+  fmap (scheduleFromExpr label) (parseCronText input)
+
+-- | Backwards-compatible alias for 'scheduleFromText'.
+parseCronTextWithZone :: TZLabel -> Text -> Either String Schedule
+parseCronTextWithZone = scheduleFromText
+
+-- | Local-time primary helper.
+--
+-- Evaluate run times in the schedule's local timezone, starting from a local
+-- base time that is already expressed in the schedule's zone. Choose this when
+-- you want to present results exactly as the rule owner configured them.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 08:00:00" :: LocalTime
+-- >>> nextRunTimesLocal sched base 1
+-- Right [2025-11-01 09:00:00]
+nextRunTimesLocal :: Schedule -> LocalTime -> Int -> Either String [LocalTime]
+nextRunTimesLocal = scheduleLocalOccurrences
+
+-- | Conversion helper.
+--
+-- Evaluate run times in the schedule's local timezone using a UTC base. Ideal
+-- when upstream systems give you absolute timestamps (e.g. database clocks) but
+-- UI clients expect the local wall clock.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 12:00:00 UTC" :: UTCTime
+-- >>> nextRunTimesLocalFromUTC sched base 1
+-- Right [2025-11-01 09:00:00]
+nextRunTimesLocalFromUTC :: Schedule -> UTCTime -> Int -> Either String [LocalTime]
+nextRunTimesLocalFromUTC schedule base = scheduleLocalOccurrences schedule (utcToLocalTimeTZ (scheduleZone schedule) base)
+
+-- | Conversion helper.
+--
+-- Evaluate run times in the schedule's local timezone using an arbitrary
+-- 'ZonedTime' base (the offset on the input is ignored; only the instant
+-- matters). Handy when you get user input such as "2025-11-01 09:00 EDT" and
+-- want to keep working in local values.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 09:00:00-04:00" :: ZonedTime
+-- >>> nextRunTimesLocalFromZoned sched base 2
+-- Right [2025-11-01 09:00:00,2025-11-02 09:00:00]
+nextRunTimesLocalFromZoned :: Schedule -> ZonedTime -> Int -> Either String [LocalTime]
+nextRunTimesLocalFromZoned schedule base = scheduleLocalOccurrences schedule (utcToLocalTimeTZ (scheduleZone schedule) (zonedTimeToUTC base))
+
+-- | UTC primary helper.
+--
+-- Evaluate run times in UTC while accepting a UTC base. This is the simplest
+-- option when the rest of your system already speaks UTC.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 12:30:00 UTC" :: UTCTime
+-- >>> nextRunTimesUTC sched base 1
+-- Right [2025-11-01 13:00:00 UTC]
+nextRunTimesUTC :: Schedule -> UTCTime -> Int -> Either String [UTCTime]
+nextRunTimesUTC schedule base limit =
+  fmap (map (localToUTC schedule)) (nextRunTimesLocalFromUTC schedule base limit)
+
+-- | Conversion helper.
+--
+-- Evaluate run times in UTC with a local base. Use this when your caller has
+-- local wall time but downstream systems expect UTC instants.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 08:00:00" :: LocalTime
+-- >>> nextRunTimesUTCFromLocal sched base 1
+-- Right [2025-11-01 13:00:00 UTC]
+nextRunTimesUTCFromLocal :: Schedule -> LocalTime -> Int -> Either String [UTCTime]
+nextRunTimesUTCFromLocal schedule base limit =
+  fmap (map (localToUTC schedule)) (nextRunTimesLocal schedule base limit)
+
+-- | Conversion helper.
+--
+-- Evaluate run times in UTC with a 'ZonedTime' base. Helpful when upstream APIs
+-- hand you zoned timestamps and you want to compare the schedule against other
+-- UTC data.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 09:00:00-04:00" :: ZonedTime
+-- >>> nextRunTimesUTCFromZoned sched base 1
+-- Right [2025-11-01 13:00:00 UTC]
+nextRunTimesUTCFromZoned :: Schedule -> ZonedTime -> Int -> Either String [UTCTime]
+nextRunTimesUTCFromZoned schedule base limit =
+  fmap (map (localToUTC schedule)) (nextRunTimesLocalFromZoned schedule base limit)
+
+-- | Zoned-time primary helper.
+--
+-- Evaluate run times as 'ZonedTime' values using a zoned base. This preserves
+-- the original offset used for the base and propagates DST changes into the
+-- outputs, which is useful for logs or API responses that must mention the
+-- effective offset explicitly.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 09:00:00-04:00" :: ZonedTime
+-- >>> nextRunTimesZoned sched base 2
+-- Right [2025-11-01 09:00:00-04:00,2025-11-02 09:00:00-05:00]
+nextRunTimesZoned :: Schedule -> ZonedTime -> Int -> Either String [ZonedTime]
+nextRunTimesZoned schedule base limit =
+  fmap (map (localToZoned schedule)) (nextRunTimesLocalFromZoned schedule base limit)
+
+-- | Conversion helper.
+--
+-- Evaluate run times as 'ZonedTime' values from a UTC base.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 12:30:00 UTC" :: UTCTime
+-- >>> nextRunTimesZonedFromUTC sched base 2
+-- Right [2025-11-01 09:00:00-04:00,2025-11-02 09:00:00-05:00]
+nextRunTimesZonedFromUTC :: Schedule -> UTCTime -> Int -> Either String [ZonedTime]
+nextRunTimesZonedFromUTC schedule base limit =
+  fmap (map (localToZoned schedule)) (nextRunTimesLocalFromUTC schedule base limit)
+
+-- | Conversion helper.
+--
+-- Evaluate run times as 'ZonedTime' values from a local base.
+--
+-- >>> let Right sched = scheduleFromText America__New_York "cron(0 9 * * ? *)"
+-- >>> let base = read "2025-11-01 09:00:00" :: LocalTime
+-- >>> nextRunTimesZonedFromLocal sched base 1
+-- Right [2025-11-01 09:00:00-04:00]
+nextRunTimesZonedFromLocal :: Schedule -> LocalTime -> Int -> Either String [ZonedTime]
+nextRunTimesZonedFromLocal schedule base limit =
+  fmap (map (localToZoned schedule)) (nextRunTimesLocal schedule base limit)
+
+scheduleLocalOccurrences :: Schedule -> LocalTime -> Int -> Either String [LocalTime]
+scheduleLocalOccurrences sched@Schedule{..} base limit =
+  case scheduleKind scheduleExpr of
+    RateSchedule -> do
+      let baseUtc = localToUTC sched base
+      timesUtc <- nextRunTimes scheduleExpr baseUtc limit
+      pure (map (utcToLocalTimeTZ scheduleZone) timesUtc)
+    _ ->
+      fmap (map utcToLocalNaive) (nextRunTimes scheduleExpr (localToNaiveUTC base) limit)
+
+localToNaiveUTC :: LocalTime -> UTCTime
+localToNaiveUTC (LocalTime day tod) = UTCTime day (timeOfDayToTime tod)
+
+utcToLocalNaive :: UTCTime -> LocalTime
+utcToLocalNaive (UTCTime day diff) = LocalTime day (timeToTimeOfDay diff)
+
+localToUTC :: Schedule -> LocalTime -> UTCTime
+localToUTC Schedule{..} = localTimeToUTCTZ scheduleZone
+
+localToZoned :: Schedule -> LocalTime -> ZonedTime
+localToZoned schedule local =
+  let utcVal = localToUTC schedule local
+      zone = scheduleZone schedule
+      localWall = utcToLocalTimeTZ zone utcVal
+      tzInfo = timeZoneForUTCTime zone utcVal
+   in ZonedTime localWall tzInfo
diff --git a/test/AWS/EventBridge/ScheduleSpec.hs b/test/AWS/EventBridge/ScheduleSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/AWS/EventBridge/ScheduleSpec.hs
@@ -0,0 +1,81 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module AWS.EventBridge.ScheduleSpec (tests) where
+
+import AWS.EventBridge.Schedule
+import AWS.EventBridge.Cron (parseCronText)
+import Data.Time
+  ( LocalTime
+  , UTCTime(..)
+  , ZonedTime
+  , addUTCTime
+  , fromGregorian
+  , secondsToDiffTime
+  )
+import Data.Time.Zones.All (TZLabel(..))
+import Test.Tasty (TestTree, testGroup)
+import Test.Tasty.HUnit
+
+tests :: TestTree
+tests = testGroup "schedule"
+  [ testCase "cron nextRunTimesUTC respects timezone" $ do
+      sched <- either assertFailure pure (scheduleFromText Asia__Kolkata "cron(0 9 * * ? *)")
+      let base = UTCTime (fromGregorian 2025 11 16) 0
+          expected = UTCTime (fromGregorian 2025 11 16) (secondsToDiffTime (3 * 3600 + 30 * 60))
+      nextRunTimesUTC sched base 1 @?= Right [expected]
+
+    , testCase "cron zoned outputs reflect DST" $ do
+      sched <- either assertFailure pure (scheduleFromText America__New_York "cron(0 9 * * ? *)")
+      let base = read "2025-11-01 09:00:00-04:00" :: ZonedTime
+          expected =
+            [ "2025-11-01 09:00:00 EDT"
+            , "2025-11-02 09:00:00 EST"
+            ]
+
+      fmap (map show) (nextRunTimesZoned sched base 2) @?= Right expected
+
+  , testCase "rate expressions remain absolute" $ do
+      expr <- either assertFailure pure (parseCronText "rate(1 hour)")
+      let sched = scheduleFromExpr Asia__Tokyo expr
+          base = UTCTime (fromGregorian 2025 11 1) 0
+          expected = take 2 (iterate (addUTCTime 3600) base)
+      nextRunTimesUTC sched base 2 @?= Right expected
+
+    , testCase "local helper trims times before base" $ do
+      sched <- either assertFailure pure (scheduleFromText Asia__Kolkata "cron(0 9 * * ? *)")
+      let baseLocal = read "2025-11-16 08:30:00" :: LocalTime
+          expected =
+            [ read "2025-11-16 09:00:00" :: LocalTime
+            , read "2025-11-17 09:00:00"
+            ]
+      nextRunTimesLocal sched baseLocal 2 @?= Right expected
+
+    , testCase "local-from-UTC matches local helper" $ do
+      sched <- either assertFailure pure (scheduleFromText Asia__Kolkata "cron(0 9 * * ? *)")
+      let baseUtc = UTCTime (fromGregorian 2025 11 16) (secondsToDiffTime (3 * 3600 + 30 * 60))
+      nextRunTimesLocalFromUTC sched baseUtc 1
+        @?= nextRunTimesLocal sched (read "2025-11-16 09:00:00" :: LocalTime) 1
+
+    , testCase "UTC-from-local converts to absolute" $ do
+      sched <- either assertFailure pure (scheduleFromText Asia__Kolkata "cron(0 9 * * ? *)")
+      let baseLocal = read "2025-11-16 09:00:00" :: LocalTime
+          expected = UTCTime (fromGregorian 2025 11 16) (secondsToDiffTime (3 * 3600 + 30 * 60))
+      nextRunTimesUTCFromLocal sched baseLocal 1 @?= Right [expected]
+
+    , testCase "zoned-from-UTC emits offsets" $ do
+      sched <- either assertFailure pure (scheduleFromText America__New_York "cron(0 9 * * ? *)")
+      let baseUtc = UTCTime (fromGregorian 2025 11 1) (secondsToDiffTime (12 * 3600))
+          expected =
+            [ "2025-11-01 09:00:00 EDT"
+            , "2025-11-02 09:00:00 EST"
+            ]
+      fmap (map show) (nextRunTimesZonedFromUTC sched baseUtc 2) @?= Right expected
+
+    , testCase "parseCronTextWithZone aliases scheduleFromText" $ do
+      let expr = "cron(0 0 1 1 ? 2025)"
+      schedA <- either assertFailure pure (parseCronTextWithZone Asia__Kolkata expr)
+      schedB <- either assertFailure pure (scheduleFromText Asia__Kolkata expr)
+      scheduleZoneLabel schedA @?= scheduleZoneLabel schedB
+      nextRunTimesUTC schedA (UTCTime (fromGregorian 2025 1 1) 0) 1
+        @?= nextRunTimesUTC schedB (UTCTime (fromGregorian 2025 1 1) 0) 1
+  ]
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -9,6 +9,7 @@
 import qualified AWS.EventBridge.RateSpec as RateSpec
 import qualified AWS.EventBridge.OneTimeSpec as OneTimeSpec
 import qualified AWS.EventBridge.CronSpec as CronSpec
+import qualified AWS.EventBridge.ScheduleSpec as ScheduleSpec
 import Test.Tasty (defaultMain, testGroup)
 
 main :: IO ()
@@ -24,4 +25,5 @@
       , RateSpec.tests
       , OneTimeSpec.tests
       , CronSpec.tests
+      , ScheduleSpec.tests
       ]
