packages feed

neuron-1.0.0.0: src/lib/Data/Time/DateMayTime.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

module Data.Time.DateMayTime
  ( -- Date type
    DateMayTime,
    mkDateMayTime,
    getDay,
    -- Date formatting
    dateTimeFormat,
    formatDay,
    formatLocalTime,
    formatDateMayTime,
    -- Date parsing
    parseDateMayTime,
  )
where

import Data.Aeson (FromJSON, ToJSON)
import Data.Time
import Data.YAML
import Relude

-- | Like `Day` but with optional time.
newtype DateMayTime = DateMayTime {unDateMayTime :: (Day, Maybe TimeOfDay)}
  deriving (Eq, Show, Generic, Ord, ToJSON, FromJSON)

instance FromYAML DateMayTime where
  parseYAML =
    parseDateMayTime <=< parseYAML @Text

instance ToYAML DateMayTime where
  toYAML =
    toYAML . formatDateMayTime

mkDateMayTime :: Either Day LocalTime -> DateMayTime
mkDateMayTime =
  DateMayTime . \case
    Left day ->
      (day, Nothing)
    Right datetime ->
      localDay &&& Just . localTimeOfDay $ datetime

getDay :: DateMayTime -> Day
getDay = fst . unDateMayTime

formatDateMayTime :: DateMayTime -> Text
formatDateMayTime (DateMayTime (day, mtime)) =
  maybe (formatDay day) (formatLocalTime . LocalTime day) mtime

formatDay :: Day -> Text
formatDay = formatTime' dateFormat

formatLocalTime :: LocalTime -> Text
formatLocalTime = formatTime' dateTimeFormat

parseDateMayTime :: (MonadFail m, Alternative m) => Text -> m DateMayTime
parseDateMayTime (toString -> s) = do
  fmap mkDateMayTime $
    fmap Left (parseTimeM False defaultTimeLocale dateFormat s)
      <|> fmap Right (parseTimeM False defaultTimeLocale dateTimeFormat s)

dateFormat :: String
dateFormat = "%Y-%m-%d"

dateTimeFormat :: String
dateTimeFormat = "%Y-%m-%dT%H:%M"

-- | Like `formatTime` but with default time locale and returning Text
formatTime' :: FormatTime t => String -> t -> Text
formatTime' s = toText . formatTime defaultTimeLocale s