thyme-0.3.0.0: src/Data/Thyme/Calendar/WeekdayOfMonth.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
#include "thyme.h"
module Data.Thyme.Calendar.WeekdayOfMonth where
import Prelude
import Control.Applicative
import Control.DeepSeq
import Control.Lens
import Control.Monad
import Data.AffineSpace
import Data.Data
import Data.Thyme.Calendar
import Data.Thyme.Calendar.Internal
import Data.Thyme.TH
import System.Random
import Test.QuickCheck
data WeekdayOfMonth = WeekdayOfMonth
{ womYear :: {-# UNPACK #-}!Year
, womMonth :: {-# UNPACK #-}!Month
, womNth :: {-# UNPACK #-}!Int -- ^ ±1–5, negative means n-th last
, womDayOfWeek :: {-# UNPACK #-}!DayOfWeek
} deriving (INSTANCES_USUAL, Show)
instance NFData WeekdayOfMonth
instance Bounded WeekdayOfMonth where
minBound = minBound ^. weekdayOfMonth
maxBound = maxBound ^. weekdayOfMonth
instance Random WeekdayOfMonth where
randomR = randomIsoR weekdayOfMonth
random = over _1 (^. weekdayOfMonth) . random
instance Arbitrary WeekdayOfMonth where
arbitrary = view weekdayOfMonth <$> arbitrary
{-# INLINE weekdayOfMonth #-}
weekdayOfMonth :: Iso' Day WeekdayOfMonth
weekdayOfMonth = iso toWeekday fromWeekday where
{-# INLINEABLE toWeekday #-}
toWeekday :: Day -> WeekdayOfMonth
toWeekday day@(view ordinalDate -> ord) = WeekdayOfMonth y m n wd where
YearMonthDay y m d = ord ^. yearMonthDay
WeekDate _ _ wd = toWeekOrdinal ord day
n = div (d - 1) 7
{-# INLINEABLE fromWeekday #-}
fromWeekday :: WeekdayOfMonth -> Day
fromWeekday (WeekdayOfMonth y m n wd) = refDay .+^ s * offset where
refOrd = yearMonthDay # YearMonthDay y m
(if n < 0 then monthLength (isLeapYear y) m else 1)
refDay = ordinalDate # refOrd
WeekDate _ _ wd1 = toWeekOrdinal refOrd refDay
s = signum n
wo = s * (wd - wd1)
offset = (abs n - 1) * 7 + if wo < 0 then wo + 7 else wo
{-# INLINEABLE weekdayOfMonthValid #-}
weekdayOfMonthValid :: WeekdayOfMonth -> Maybe Day
weekdayOfMonthValid (WeekdayOfMonth y m n wd) = (refDay .+^ s * offset)
<$ guard (n /= 0 && 1 <= wd && wd <= 7 && offset < len) where
len = monthLength (isLeapYear y) m
refOrd = yearMonthDay # YearMonthDay y m (if n < 0 then len else 1)
refDay = ordinalDate # refOrd
WeekDate _ _ wd1 = toWeekOrdinal refOrd refDay
s = signum n
wo = s * (wd - wd1)
offset = (abs n - 1) * 7 + if wo < 0 then wo + 7 else wo
-- * Lenses
thymeLenses ''WeekdayOfMonth