module Data.Time.RealDayEnd where

import Data.Time

{- | Consider you are falling a sleep at 2:00am, so next day for you
starts from 2:00, not from 0:00. So, if now is 2017-10-10T01:00 and
you did not sleept yet, then this is still 2017-10-09 for you.

>>> let now = ZonedTime (LocalTime (fromGregorian 2017 10 10) (TimeOfDay 01 00 00)) utc
>>> now
2017-10-10 01:00:00 UTC
>>> realDay (TimeOfDay 0 0 0) now
2017-10-10
>>> realDay (TimeOfDay 2 0 0) now
2017-10-09

This function shifts day switch to the specified time. It may be
useful for some logging software and similar stuff.
-}

realDay
  :: TimeOfDay
  -- ^ Time when user's day ends.
  -> ZonedTime
  -- ^ Time to calculate date from
  -> Day
  -- ^ Real date of given time
realDay eod' zt =
  let
    u = zonedTimeToUTC zt
    shift = if timeOfDayToTime midnight <= eod && eod < timeOfDayToTime midday
      then owlShift
      else larkShift
    shifted = addUTCTime shift u
    shiftedZ = utcToZonedTime (zonedTimeZone zt) shifted
  in localDay $ zonedTimeToLocalTime shiftedZ
  where
    owlShift = negate $ realToFrac eod
    larkShift = realToFrac $ posixDayLength - eod
    eod = timeOfDayToTime eod'
    posixDayLength :: DiffTime
    posixDayLength = fromInteger 86400