{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE TypeSynonymInstances #-}
-- |
-- Module      : Data.Time.RFC3339
-- Copyright   : (c) 2011 Hugo Daniel Gomes
--
-- License     : BSD-style
-- Maintainer  : mr.hugo.gomes@gmail.com
-- Stability   : experimental
-- Portability : GHC
--
-- Support for reading and displaying time in the format specified by
-- the RFC3339 <http://www.ietf.org/rfc/rfc3339.txt>
--
-- Example of usage:
--
-- > import Data.Time.LocalTime
-- >
-- > showTime :: IO Text
-- > showTime = formatTimeRFC3339 <$> getZonedTime
-- >
-- > example1 = "1985-04-12T23:20:50.52Z"
-- > example2 = "1996-12-19T16:39:57-08:00"
-- > example3 = "1990-12-31T23:59:60Z"
-- > example4 = "1990-12-31T15:59:60-08:00"
-- > example5 = "1937-01-01T12:00:27.87+00:20"
-- > examples = [example1,example2,example3,example4,example5]
-- >
-- > readAll = map parseTimeRFC3339 examples

module Data.Time.RFC3339 (
    -- * Basic type class
    -- $basic
    formatTimeRFC3339, formatDateRFC3339, parseTimeRFC3339, parseDateRFC3339
) where

import           Control.Applicative

import           Data.Maybe
import           Data.Monoid         ((<>))
import           Data.Monoid.Textual hiding (foldr, map)
import           Data.String         (fromString)
import           Data.Text           (Text)
import           Data.Time.Calendar
import           Data.Time.Format
import           Data.Time.LocalTime
import           Data.Time.Util


formatTimeRFC3339 :: (TextualMonoid t) => ZonedTime -> t
formatTimeRFC3339 :: ZonedTime -> t
formatTimeRFC3339 zt :: ZonedTime
zt@(ZonedTime LocalTime
lt TimeZone
z) = String -> t
forall a. IsString a => String -> a
fromString (TimeLocale -> String -> ZonedTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%FT%T" ZonedTime
zt) t -> t -> t
forall a. Semigroup a => a -> a -> a
<> String -> t
forall a. IsString a => String -> a
fromString String
printZone
  where timeZoneStr :: String
timeZoneStr = TimeZone -> String
timeZoneOffsetString TimeZone
z
        printZone :: String
printZone = if String
timeZoneStr String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== TimeZone -> String
timeZoneOffsetString TimeZone
utc
                    then String
"Z"
                    else Int -> String -> String
forall a. Int -> [a] -> [a]
take Int
3 String
timeZoneStr String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
":" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
3 String
timeZoneStr

formatDateRFC3339 :: (TextualMonoid t, FormatTime time) => time -> t
formatDateRFC3339 :: time -> t
formatDateRFC3339 time
day = String -> t
forall a. IsString a => String -> a
fromString (TimeLocale -> String -> time -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%F" time
day)

formatsRFC3339 :: [Text]
formatsRFC3339 :: [Text]
formatsRFC3339 = do
  Text
fraction <- [Text
"%Q", Text
""]
  Text
zone <- [Text
"Z", Text
"%z"]
  Text -> [Text]
forall (m :: * -> *) a. Monad m => a -> m a
return (Text -> [Text]) -> Text -> [Text]
forall a b. (a -> b) -> a -> b
$ Text
"%FT%T" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
fraction Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
zone

parseTimeRFC3339 :: (TextualMonoid t) => t -> Maybe ZonedTime
parseTimeRFC3339 :: t -> Maybe ZonedTime
parseTimeRFC3339 = [Text] -> t -> Maybe ZonedTime
forall t t' time.
(TextualMonoid t, TextualMonoid t', ParseTime time) =>
[t] -> t' -> Maybe time
parseTimeUsing [Text]
formatsRFC3339

parseDateRFC3339 :: (TextualMonoid t) => t -> Maybe Day
parseDateRFC3339 :: t -> Maybe Day
parseDateRFC3339 = [Text] -> t -> Maybe Day
forall t t' time.
(TextualMonoid t, TextualMonoid t', ParseTime time) =>
[t] -> t' -> Maybe time
parseTimeUsing [Text
"%F" :: Text]