packages feed

megaparsec-time-0.3.0.0: src/Text/Megaparsec/Time/Byte.hs

{-# 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