Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions lib/mobility-core/src/Kernel/Types/Time.hs
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,32 @@ instance MonadTime IO where

instance MonadClock IO where
getClockTime = Clock.getTime Clock.Monotonic

data TimeWithZone = TimeWithZone
{ twzTime :: UTCTime,
twzZone :: TZone
}
deriving stock (Show, Read, Eq, Ord, Generic)
deriving anyclass (ToJSON, FromJSON, ToSchema)

data TZone = UTC | IST
deriving stock (Show, Read, Eq, Ord, Enum, Bounded, Generic)
deriving anyclass (ToJSON, FromJSON, ToSchema)

-- Beam instances for TimeWithZone
-- Store ONLY UTCTime in database (no schema change!)
instance HasSqlValueSyntax be UTCTime => HasSqlValueSyntax be TimeWithZone where
sqlValueSyntax (TimeWithZone utcTime _) = sqlValueSyntax utcTime

-- ✅ Only stores UTCTime, ignores TZone

instance FromBackendRow Postgres TimeWithZone where
fromBackendRow = do
utcTime <- fromBackendRow @Postgres @UTCTime
pure $ TimeWithZone utcTime IST

-- ✅ Always reconstructs with IST timezone

instance BeamSqlBackend be => B.HasSqlEqualityCheck be TimeWithZone

instance BeamSqlBackend be => B.HasSqlQuantifiedEqualityCheck be TimeWithZone
48 changes: 48 additions & 0 deletions lib/mobility-core/src/Kernel/Utils/Time.hs
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,51 @@ utcTimeToText = T.pack . formatTime defaultTimeLocale "%FT%T%z"

utcToEpochSeconds :: UTCTime -> Seconds
utcToEpochSeconds = nominalDiffTimeToSeconds . utcTimeToPOSIXSeconds

toTimeWithZone :: TZone -> UTCTime -> TimeWithZone
toTimeWithZone tz utcTime = TimeWithZone utcTime tz

toIST :: UTCTime -> TimeWithZone
toIST = toTimeWithZone IST

toUTCZone :: UTCTime -> TimeWithZone
toUTCZone = toTimeWithZone UTC

fromTimeWithZone :: TimeWithZone -> UTCTime
fromTimeWithZone = twzTime

getTimeZoneOffset :: TZone -> Time.TimeZone
getTimeZoneOffset UTC = Time.utc
getTimeZoneOffset IST = Time.minutesToTimeZone 330 -- UTC+5:30

getLocalHour :: TimeWithZone -> Int
getLocalHour (TimeWithZone utcTime z) =
let localTime = Time.utcToLocalTime (getTimeZoneOffset z) utcTime
in Time.todHour $ Time.localTimeOfDay localTime

getLocalMinute :: TimeWithZone -> Int
getLocalMinute (TimeWithZone utcTime z) =
let localTime = Time.utcToLocalTime (getTimeZoneOffset z) utcTime
in Time.todMin $ Time.localTimeOfDay localTime

addDaysToTimeWithZone :: Integer -> TimeWithZone -> TimeWithZone
addDaysToTimeWithZone n (TimeWithZone utcTime z) =
TimeWithZone (Time.addUTCTime (fromInteger n * 86400) utcTime) z

setTimeOfDay :: Int -> Int -> Int -> TimeWithZone -> TimeWithZone
setTimeOfDay hour minute sec (TimeWithZone utcTime z) =
let localTime = Time.utcToLocalTime (getTimeZoneOffset z) utcTime
localDay = Time.localDay localTime
newLocalTime = Time.LocalTime localDay (Time.TimeOfDay hour minute (fromIntegral sec))
newUTC = Time.localTimeToUTC (getTimeZoneOffset z) newLocalTime
in TimeWithZone newUTC z

formatTimeWithZone :: String -> TimeWithZone -> String
formatTimeWithZone fmt (TimeWithZone utcTime z) =
let localTime = Time.utcToLocalTime (getTimeZoneOffset z) utcTime
in Time.formatTime Time.defaultTimeLocale fmt localTime
++ " "
++ show z

formatStandard :: TimeWithZone -> String
formatStandard = formatTimeWithZone "%Y-%m-%d %H:%M:%S"