diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,18 @@
 Changelog
 
-## [2.0.1](https://github.com/drlkf/megaparsec-time/compare/2.0.0...2.0.1) (2025-07-29)
+# [3.0.0](https://github.com/haskell-github-trust/megaparsec-time/compare/2.0.2...3.0.0) (2026-10-25)
 
-# [2.0.0](https://github.com/drlkf/megaparsec-time/compare/1.0.1...2.0.0) (2025-07-28)
+## [2.0.2](https://github.com/haskell-github-trust/megaparsec-time/compare/2.0.1...2.0.2) (2025-08-07)
 
-## [1.0.1](https://github.com/drlkf/megaparsec-time/compare/1.0.0...1.0.1) (2025-07-28)
+## [2.0.1](https://github.com/haskell-github-trust/megaparsec-time/compare/2.0.0...2.0.1) (2025-07-29)
 
+# [2.0.0](https://github.com/haskell-github-trust/megaparsec-time/compare/1.0.1...2.0.0) (2025-07-28)
+
+## [1.0.1](https://github.com/haskell-github-trust/megaparsec-time/compare/1.0.0...1.0.1) (2025-07-28)
+
 # 1.0.0 (2025-07-28)
 
 
 ### Features
 
-* initial release ([fcab952](https://github.com/drlkf/megaparsec-time/commit/fcab952d7f6331dd8b169cb6ef70ab08edec910b))
+* initial release ([fcab952](https://github.com/haskell-github-trust/megaparsec-time/commit/fcab952d7f6331dd8b169cb6ef70ab08edec910b))
diff --git a/megaparsec-time.cabal b/megaparsec-time.cabal
--- a/megaparsec-time.cabal
+++ b/megaparsec-time.cabal
@@ -1,16 +1,16 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.37.0.
+-- This file has been generated from package.yaml by hpack version 0.39.6.
 --
 -- see: https://github.com/sol/hpack
 
 name:           megaparsec-time
-version:        0.2.0.1
+version:        0.3.0.0
 synopsis:       Parsers and utilities for the Megaparsec library.
 description:    Common parsers and utilities to use with the Megaparsec library.
 category:       Parsing
-homepage:       https://github.com/drlkf/megaparsec-time
-bug-reports:    https://github.com/drlkf/megaparsec-time/issues
+homepage:       https://github.com/haskell-github-trust/megaparsec-time
+bug-reports:    https://github.com/haskell-github-trust/megaparsec-time/issues
 author:         drlkf
 maintainer:     drlkf@drlkf.net
 copyright:      2025 drlkf
@@ -18,20 +18,23 @@
 license-file:   LICENSE
 build-type:     Simple
 tested-with:
-    GHC == 9.8
+    GHC == 9.10
+  , GHC == 9.8
   , GHC == 9.6
-  , GHC == 9.4
 extra-source-files:
     README.md
     CHANGELOG.md
 
 source-repository head
   type: git
-  location: https://github.com/drlkf/megaparsec-time
+  location: https://github.com/haskell-github-trust/megaparsec-time
 
 library
   exposed-modules:
       Text.Megaparsec.Time
+      Text.Megaparsec.Time.Byte
+      Text.Megaparsec.Time.Char
+      Text.Megaparsec.Time.Common
   other-modules:
       Paths_megaparsec_time
   hs-source-dirs:
@@ -40,7 +43,7 @@
   build-depends:
       base >=4.7 && <5
     , megaparsec >=9.0 && <10
-    , megaparsec-utils >=0.1 && <1
+    , megaparsec-utils >=0.2 && <1
     , parser-combinators >=1.0 && <2
     , time >=1.10 && <2
   default-language: Haskell2010
@@ -50,6 +53,7 @@
   main-is: Spec.hs
   other-modules:
       Text.Megaparsec.TimeSpec
+      Text.Megaparsec.TimeStreamSpec
       Paths_megaparsec_time
   hs-source-dirs:
       test
@@ -57,11 +61,13 @@
   build-depends:
       QuickCheck
     , base >=4.7 && <5
+    , bytestring
     , hspec
     , megaparsec >=9.0 && <10
     , megaparsec-time
-    , megaparsec-utils >=0.1 && <1
+    , megaparsec-utils >=0.2 && <1
     , parser-combinators >=1.0 && <2
+    , text
     , time >=1.10 && <2
   default-language: Haskell2010
   build-tool-depends: hspec-discover:hspec-discover == 2.*
diff --git a/src/Text/Megaparsec/Time.hs b/src/Text/Megaparsec/Time.hs
--- a/src/Text/Megaparsec/Time.hs
+++ b/src/Text/Megaparsec/Time.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE FlexibleContexts #-}
-
 -- |
 -- Module      : Text.Megaparsec.Time
 -- Description : Various parsers for types related to time.
@@ -8,151 +6,24 @@
 -- Maintainer  : drlkf@drlkf.net
 -- Stability   : experimental
 --
--- Parsers for types related to time to use with 'megaparsec'.
+-- Re-exports of 'Text.Megaparsec.Time.Char'. New code should use
+-- 'Text.Megaparsec.Time.Char' or 'Text.Megaparsec.Time.Byte' directly.
 module Text.Megaparsec.Time (
   -- * Types
   DayResult,
 
   -- * Simple parsers
-  hoursParser,
-  minutesParser,
-  secondsParser,
-  timeParser,
-  dayParser,
-  gregorianDayParser,
+  Char.hoursParser,
+  Char.minutesParser,
+  Char.secondsParser,
+  Char.timeParser,
+  Char.dayParser,
+  Char.gregorianDayParser,
 
   -- * Composite parsers
-  dateParser,
-  durationParser,
+  Char.dateParser,
+  Char.durationParser,
 ) where
 
-import Control.Applicative (optional, (<|>))
-import Control.Monad (replicateM)
-import Control.Monad.Combinators (choice, some)
-import Data.Char (toLower)
-import Data.Functor (($>))
-import Data.Maybe (fromMaybe)
-import Data.Time (
-  Day,
-  DayOfWeek (..),
-  NominalDiffTime,
-  TimeOfDay (..),
-  defaultTimeLocale,
-  makeTimeOfDayValid,
-  parseTimeM,
-  secondsToNominalDiffTime,
- )
-import Text.Megaparsec (Parsec, takeRest, try)
-import Text.Megaparsec.Char (
-  char,
-  digitChar,
-  space,
-  space1,
-  string',
- )
-import Text.Printf (printf)
-import Text.Megaparsec.Utils (posNumParser)
-
--- | Representation of a parser result with either a number of days relative to
--- the current day, or a 'DayOfWeek'.
-type DayResult = Either Int DayOfWeek
-
--- | Parse a tuple containing a day or not, and a 'TimeOfDay', separated with a
--- space.
-dateParser
-  :: Ord e
-  => Parsec e String (Maybe DayResult, TimeOfDay)
-dateParser = (,) <$> optional (try (dayParser <* space1)) <*> timeParser
-
--- | Parse a day using one of the following, all case-insensitive:
---
---   * a short (3-letters) or long day name e.g @mon@ or @monday@
---   * @yesterday@ or @tomorrow@
---   * a day number relative to the current day i.e @+2@ is two days from today
---   * an absolute number for a 'DayOfWeek', refer to its 'Num' instance for more information.
-dayParser
-  :: Ord e
-  => Parsec e String DayResult
-dayParser =
-  choice
-    [ Right <$> longDay
-    , Right <$> shortDay
-    , Left <$> (string' "yesterday" $> -1)
-    , Left <$> (string' "tomorrow" $> 1)
-    , Right <$> absoluteDay
-    , Left <$> relativeDay
-    ]
- where
-  shortDay = choice $ map (ciString (fmap toLower . take 3 . show)) weekDays
-  longDay = choice $ map (ciString (fmap toLower . show)) weekDays
-  ciString f d = try (string' (f d)) $> d
-  weekDays = [Monday .. Friday]
-  sign = (char '-' $> negate) <|> (char '+' $> id)
-  absoluteDay = toEnum . read <$> some digitChar
-  relativeDay = ($) <$> sign <*> (read <$> some digitChar)
-
--- | Parse a 'NominalDiffTime' using strings like @1h23m45s@, with all
--- components being optional as long as one is present.
-durationParser
-  :: Ord e
-  => Parsec e String NominalDiffTime
-durationParser = try hours <|> try minutes <|> secondsParser
- where
-  hours = do
-    h <- hoursParser <* space
-    m <- fromMaybe zero <$> optional (try minutes)
-    s <- fromMaybe zero <$> optional secondsParser
-
-    return (h + m + s)
-
-  minutes = do
-    m <- minutesParser <* space
-    s <- fromMaybe zero <$> optional secondsParser
-
-    return (m + s)
-
--- | Parse a Gregorian 'Day' from a @%F@ or @%d\/%m\/%Y@ format.
-gregorianDayParser
-  :: Ord e
-  => Parsec e String Day
-gregorianDayParser = do
-  s <- takeRest
-
-  let parseTime = flip (parseTimeM False defaultTimeLocale) s
-
-  parseTime "%F" <|> parseTime "%d/%m/%Y"
-
--- | Parse a 'NominalDiffTime' from a number of hours from a string like @1h@.
-hoursParser
-  :: Ord e
-  => Parsec e String NominalDiffTime
-hoursParser = secondsToNominalDiffTime . (* 3600) <$> posNumParser <* char 'h'
-
--- | Parse a 'NominalDiffTime' from a number of minutes from a string like @1m@.
-minutesParser
-  :: Ord e
-  => Parsec e String NominalDiffTime
-minutesParser = secondsToNominalDiffTime . (* 60) <$> posNumParser <* char 'm'
-
--- | Parse a 'NominalDiffTime' from a number of seconds from a string like @1s@.
-secondsParser
-  :: Ord e
-  => Parsec e String NominalDiffTime
-secondsParser = secondsToNominalDiffTime <$> posNumParser <* optional (char 's')
-
--- | Parse a 'TimeOfDay' from a string like @01:23@.
-timeParser
-  :: Ord e
-  => Parsec e String TimeOfDay
-timeParser = do
-  h <- read <$> replicateM 2 digitChar <* char ':'
-  m <- read <$> replicateM 2 digitChar
-
-  maybe
-    (fail (printf "invalid hours or minutes %02d:%02d" h m))
-    pure
-    (makeTimeOfDayValid h m 0)
-
--- | Zero seconds in 'NominalDiffTime'.
-zero :: NominalDiffTime
-zero = secondsToNominalDiffTime 0
+import qualified Text.Megaparsec.Time.Char as Char
+import Text.Megaparsec.Time.Common (DayResult)
diff --git a/src/Text/Megaparsec/Time/Byte.hs b/src/Text/Megaparsec/Time/Byte.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Megaparsec/Time/Byte.hs
@@ -0,0 +1,129 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- |
+-- Module      : Text.Megaparsec.Time.Byte
+-- Description : Time parsers for streams of 'Word8'.
+-- Copyright   : (c) drlkf, 2026
+-- License     : GPL-3
+-- Maintainer  : drlkf@drlkf.net
+-- Stability   : experimental
+--
+-- Parsers for types related to time for streams of 'Word8' (strict and lazy
+-- 'Data.ByteString.ByteString').
+module Text.Megaparsec.Time.Byte (
+  -- * Simple parsers
+  hoursParser,
+  minutesParser,
+  secondsParser,
+  timeParser,
+  dayParser,
+  gregorianDayParser,
+
+  -- * Composite parsers
+  dateParser,
+  durationParser,
+) where
+
+import Control.Applicative (optional)
+import Data.Time (Day, NominalDiffTime, TimeOfDay, secondsToNominalDiffTime)
+import Data.Word (Word8)
+import Text.Megaparsec (MonadParsec, Token)
+import Text.Megaparsec.Byte (char, char', digitChar, space, space1)
+import Text.Megaparsec.Time.Common (
+  DayResult,
+  mkDateParser,
+  mkDayParser,
+  mkDurationParser,
+  mkGregorianDayParser,
+  mkTimeParser,
+ )
+import Text.Megaparsec.Utils.Byte (posNumParser)
+
+fromChar :: Char -> Word8
+fromChar = toEnum . fromEnum
+
+toChar :: Word8 -> Char
+toChar = toEnum . fromIntegral
+
+-- | Parse a tuple containing a day or not, and a 'TimeOfDay', separated with a
+-- space.
+--
+-- @since 0.3.0.0
+dateParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m (Maybe DayResult, TimeOfDay)
+dateParser = mkDateParser space1 dayParser timeParser
+
+-- | Parse a day, see 'Text.Megaparsec.Time.Char.dayParser'.
+--
+-- @since 0.3.0.0
+dayParser
+  :: MonadParsec e s m
+  => Token s ~ Word8
+  => m DayResult
+dayParser = mkDayParser char' fromChar digitChar toChar
+
+-- | Parse a 'NominalDiffTime' using strings like @1h23m45s@, with all
+-- components being optional as long as one is present.
+--
+-- @since 0.3.0.0
+durationParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m NominalDiffTime
+durationParser = mkDurationParser space hoursParser minutesParser secondsParser
+
+-- | Parse a Gregorian 'Day' from a @%F@ or @%d\/%m\/%Y@ format.
+--
+-- @since 0.3.0.0
+gregorianDayParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m Day
+gregorianDayParser = mkGregorianDayParser toChar
+
+-- | Parse a 'NominalDiffTime' from a number of hours from a string like @1h@.
+--
+-- @since 0.3.0.0
+hoursParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m NominalDiffTime
+hoursParser = secondsToNominalDiffTime . (* 3600) <$> posNumParser <* char (fromChar 'h')
+
+-- | Parse a 'NominalDiffTime' from a number of minutes from a string like @1m@.
+--
+-- @since 0.3.0.0
+minutesParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m NominalDiffTime
+minutesParser = secondsToNominalDiffTime . (* 60) <$> posNumParser <* char (fromChar 'm')
+
+-- | Parse a 'NominalDiffTime' from a number of seconds from a string like @1s@.
+--
+-- @since 0.3.0.0
+secondsParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m NominalDiffTime
+secondsParser = secondsToNominalDiffTime <$> posNumParser <* optional (char (fromChar 's'))
+
+-- | Parse a 'TimeOfDay' from a string like @01:23@.
+--
+-- @since 0.3.0.0
+timeParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Word8
+  => m TimeOfDay
+timeParser = mkTimeParser digitChar (char (fromChar ':')) toChar
diff --git a/src/Text/Megaparsec/Time/Char.hs b/src/Text/Megaparsec/Time/Char.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Megaparsec/Time/Char.hs
@@ -0,0 +1,127 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+
+-- |
+-- Module      : Text.Megaparsec.Time.Char
+-- Description : Time parsers for streams of 'Char'.
+-- Copyright   : (c) drlkf, 2024
+-- License     : GPL-3
+-- Maintainer  : drlkf@drlkf.net
+-- Stability   : experimental
+--
+-- Parsers for types related to time for streams of 'Char' ('String', 'Text',
+-- lazy 'Text').
+module Text.Megaparsec.Time.Char (
+  -- * Simple parsers
+  hoursParser,
+  minutesParser,
+  secondsParser,
+  timeParser,
+  dayParser,
+  gregorianDayParser,
+
+  -- * Composite parsers
+  dateParser,
+  durationParser,
+) where
+
+import Control.Applicative (optional)
+import Data.Time (Day, NominalDiffTime, TimeOfDay, secondsToNominalDiffTime)
+import Text.Megaparsec (MonadParsec, Token)
+import Text.Megaparsec.Char (char, char', digitChar, space, space1)
+import Text.Megaparsec.Time.Common (
+  DayResult,
+  mkDateParser,
+  mkDayParser,
+  mkDurationParser,
+  mkGregorianDayParser,
+  mkTimeParser,
+ )
+import Text.Megaparsec.Utils.Char (posNumParser)
+
+-- | Parse a tuple containing a day or not, and a 'TimeOfDay', separated with a
+-- space.
+--
+-- @since 0.3.0.0
+dateParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m (Maybe DayResult, TimeOfDay)
+dateParser = mkDateParser space1 dayParser timeParser
+
+-- | Parse a day using one of the following, all case-insensitive:
+--
+--   * a short (3-letters) or long day name e.g @mon@ or @monday@
+--   * @yesterday@ or @tomorrow@
+--   * a day number relative to the current day i.e @+2@ is two days from today
+--   * an absolute number for a 'Data.Time.DayOfWeek', refer to its 'Num' instance for more information.
+--
+-- @since 0.3.0.0
+dayParser
+  :: MonadParsec e s m
+  => Token s ~ Char
+  => m DayResult
+dayParser = mkDayParser char' id digitChar id
+
+-- | Parse a 'NominalDiffTime' using strings like @1h23m45s@, with all
+-- components being optional as long as one is present.
+--
+-- @since 0.3.0.0
+durationParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m NominalDiffTime
+durationParser = mkDurationParser space hoursParser minutesParser secondsParser
+
+-- | Parse a Gregorian 'Day' from a @%F@ or @%d\/%m\/%Y@ format.
+--
+-- @since 0.3.0.0
+gregorianDayParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m Day
+gregorianDayParser = mkGregorianDayParser id
+
+-- | Parse a 'NominalDiffTime' from a number of hours from a string like @1h@.
+--
+-- @since 0.3.0.0
+hoursParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m NominalDiffTime
+hoursParser = secondsToNominalDiffTime . (* 3600) <$> posNumParser <* char 'h'
+
+-- | Parse a 'NominalDiffTime' from a number of minutes from a string like @1m@.
+--
+-- @since 0.3.0.0
+minutesParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m NominalDiffTime
+minutesParser = secondsToNominalDiffTime . (* 60) <$> posNumParser <* char 'm'
+
+-- | Parse a 'NominalDiffTime' from a number of seconds from a string like @1s@.
+--
+-- @since 0.3.0.0
+secondsParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m NominalDiffTime
+secondsParser = secondsToNominalDiffTime <$> posNumParser <* optional (char 's')
+
+-- | Parse a 'TimeOfDay' from a string like @01:23@.
+--
+-- @since 0.3.0.0
+timeParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => Token s ~ Char
+  => m TimeOfDay
+timeParser = mkTimeParser digitChar (char ':') id
diff --git a/src/Text/Megaparsec/Time/Common.hs b/src/Text/Megaparsec/Time/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Megaparsec/Time/Common.hs
@@ -0,0 +1,163 @@
+{-# LANGUAGE FlexibleContexts #-}
+
+-- |
+-- Module      : Text.Megaparsec.Time.Common
+-- Description : Stream-agnostic time parser builders.
+-- Copyright   : (c) drlkf, 2024
+-- License     : GPL-3
+-- Maintainer  : drlkf@drlkf.net
+-- Stability   : experimental
+--
+-- Builders shared by 'Text.Megaparsec.Time.Char' and 'Text.Megaparsec.Time.Byte'.
+module Text.Megaparsec.Time.Common (
+  -- * Types
+  DayResult,
+
+  -- * Shared parser builders
+  mkDateParser,
+  mkDayParser,
+  mkDurationParser,
+  mkGregorianDayParser,
+  mkTimeParser,
+) where
+
+import Control.Applicative (many, optional, (<|>))
+import Control.Monad (replicateM)
+import Control.Monad.Combinators (choice, some)
+import Data.Foldable (traverse_)
+import Data.Functor (($>))
+import Data.Maybe (fromMaybe)
+import Data.Time (
+  Day,
+  DayOfWeek (..),
+  NominalDiffTime,
+  TimeOfDay (..),
+  defaultTimeLocale,
+  makeTimeOfDayValid,
+  parseTimeM,
+ )
+import Text.Megaparsec (MonadParsec, Token, anySingle, try)
+import Text.Printf (printf)
+
+-- | Representation of a parser result with either a number of days relative to
+-- the current day, or a 'DayOfWeek'.
+--
+-- @since 0.3.0.0
+type DayResult = Either Int DayOfWeek
+
+-- | Build a parser for a tuple containing a day or not, and a 'TimeOfDay',
+-- separated with a space.
+--
+-- @since 0.3.0.0
+mkDateParser
+  :: MonadParsec e s m
+  => m ()
+  -- ^ Non-empty space parser.
+  -> m DayResult
+  -- ^ Day parser.
+  -> m TimeOfDay
+  -- ^ Time parser.
+  -> m (Maybe DayResult, TimeOfDay)
+mkDateParser space1 day time = (,) <$> optional (try (day <* space1)) <*> time
+
+-- | Build a case-insensitive day parser, see 'Text.Megaparsec.Time.Char.dayParser'.
+--
+-- @since 0.3.0.0
+mkDayParser
+  :: MonadParsec e s m
+  => (Token s -> m (Token s))
+  -- ^ Case-insensitive single-token parser.
+  -> (Char -> Token s)
+  -- ^ Convert a literal 'Char' to a token.
+  -> m (Token s)
+  -- ^ Digit parser.
+  -> (Token s -> Char)
+  -- ^ Convert a token to a 'Char'.
+  -> m DayResult
+mkDayParser char' fromChar digitChar toChar =
+  choice
+    [ Right <$> longDay
+    , Right <$> shortDay
+    , Left <$> (str "yesterday" $> -1)
+    , Left <$> (str "tomorrow" $> 1)
+    , Right <$> absoluteDay
+    , Left <$> relativeDay
+    ]
+ where
+  str = try . traverse_ (char' . fromChar)
+  shortDay = choice $ map (dayString (take 3 . show)) weekDays
+  longDay = choice $ map (dayString show) weekDays
+  dayString f d = str (f d) $> d
+  weekDays = [Monday .. Friday]
+  sign = (str "-" $> negate) <|> (str "+" $> id)
+  digits = map toChar <$> some digitChar
+  absoluteDay = toEnum . read <$> digits
+  relativeDay = ($) <$> sign <*> (read <$> digits)
+
+-- | Build a 'NominalDiffTime' parser from hours, minutes and seconds parsers,
+-- with all components being optional as long as one is present.
+--
+-- @since 0.3.0.0
+mkDurationParser
+  :: MonadParsec e s m
+  => m ()
+  -- ^ Space parser.
+  -> m NominalDiffTime
+  -- ^ Hours parser.
+  -> m NominalDiffTime
+  -- ^ Minutes parser.
+  -> m NominalDiffTime
+  -- ^ Seconds parser.
+  -> m NominalDiffTime
+mkDurationParser space hoursParser minutesParser secondsParser =
+  try hours <|> try minutes <|> secondsParser
+ where
+  hours = do
+    h <- hoursParser <* space
+    m <- fromMaybe 0 <$> optional (try minutes)
+    s <- fromMaybe 0 <$> optional secondsParser
+
+    return (h + m + s)
+
+  minutes = do
+    m <- minutesParser <* space
+    s <- fromMaybe 0 <$> optional secondsParser
+
+    return (m + s)
+
+-- | Build a Gregorian 'Day' parser from a @%F@ or @%d\/%m\/%Y@ format.
+--
+-- @since 0.3.0.0
+mkGregorianDayParser
+  :: MonadFail m
+  => MonadParsec e s m
+  => (Token s -> Char)
+  -- ^ Convert a token to a 'Char'.
+  -> m Day
+mkGregorianDayParser toChar = do
+  s <- map toChar <$> many anySingle
+
+  let parseTime = flip (parseTimeM False defaultTimeLocale) s
+
+  parseTime "%F" <|> parseTime "%d/%m/%Y"
+
+-- | Build a 'TimeOfDay' parser from a string like @01:23@.
+--
+-- @since 0.3.0.0
+mkTimeParser
+  :: MonadFail m
+  => m t
+  -- ^ Digit parser.
+  -> m t
+  -- ^ Colon parser.
+  -> (t -> Char)
+  -- ^ Convert a token to a 'Char'.
+  -> m TimeOfDay
+mkTimeParser digitChar colon toChar = do
+  h <- read . map toChar <$> replicateM 2 digitChar <* colon
+  m <- read . map toChar <$> replicateM 2 digitChar
+
+  maybe
+    (fail (printf "invalid hours or minutes %02d:%02d" h m))
+    pure
+    (makeTimeOfDayValid h m 0)
diff --git a/test/Text/Megaparsec/TimeStreamSpec.hs b/test/Text/Megaparsec/TimeStreamSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Text/Megaparsec/TimeStreamSpec.hs
@@ -0,0 +1,51 @@
+module Text.Megaparsec.TimeStreamSpec (
+  spec,
+) where
+
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as B
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Time (DayOfWeek (..), TimeOfDay (..), fromGregorian)
+import Data.Void (Void)
+import Test.Hspec (Spec, describe, it, shouldBe)
+import Text.Megaparsec (ParseErrorBundle, Parsec, runParser)
+import qualified Text.Megaparsec.Time.Byte as Byte
+import qualified Text.Megaparsec.Time.Char as Char
+
+parseText :: Parsec Void Text a -> Text -> Either (ParseErrorBundle Text Void) a
+parseText p = runParser p "test"
+
+parseBytes
+  :: Parsec Void ByteString a
+  -> ByteString
+  -> Either (ParseErrorBundle ByteString Void) a
+parseBytes p = runParser p "test"
+
+spec :: Spec
+spec = do
+  describe "Char parsers on Text" $ do
+    it "dateParser" $
+      parseText Char.dateParser (T.pack "Tue 11:03")
+        `shouldBe` Right (Just (Right Tuesday), TimeOfDay 11 3 0)
+
+    it "durationParser" $
+      parseText Char.durationParser (T.pack "1h 2m 3s") `shouldBe` Right 3723
+
+    it "gregorianDayParser" $
+      parseText Char.gregorianDayParser (T.pack "25/10/2026")
+        `shouldBe` Right (fromGregorian 2026 10 25)
+
+  describe "Byte parsers on ByteString" $ do
+    it "dateParser" $ do
+      parseBytes Byte.dateParser (B.pack "Tue 11:03")
+        `shouldBe` Right (Just (Right Tuesday), TimeOfDay 11 3 0)
+      parseBytes Byte.dateParser (B.pack "-2 11:03")
+        `shouldBe` Right (Just (Left (-2)), TimeOfDay 11 3 0)
+
+    it "durationParser" $
+      parseBytes Byte.durationParser (B.pack "1h 2m 3s") `shouldBe` Right 3723
+
+    it "gregorianDayParser" $
+      parseBytes Byte.gregorianDayParser (B.pack "2026-10-25")
+        `shouldBe` Right (fromGregorian 2026 10 25)
