Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert epoch to gregorian datetime in haskell

Let's say I have an integer representing Epoch time, for example epoch = 1499055085, and I want to convert it to UTCTime in Haskell. How can I do this?

In other languages this is a very trivial task, why is it so difficult in haskell?

like image 785
milad zahedi Avatar asked Dec 24 '22 16:12

milad zahedi


2 Answers

Who says it is difficult?

You can simply use fromIntegral :: (Integral a, Num b) => a -> b to convert the epoch (an integer) to POSIXTime.

Next we can use posixSecondsToUTCTime :: POSIXTime -> UTCTime to obtain the UTCTime, and finally we use the utctDay :: UTCTime -> Day to get the day part of the UTCTime.

In case you want a (year,month,day) tuple, we can use the toGregorian :: Day -> (Integer,Int,Int) method.

So we can use the following method:

import Data.Time.Calendar(toGregorian)
import Data.Time.Clock(utctDay,UTCTime)
import Data.Time.Clock.POSIX(posixSecondsToUTCTime)

epochToUTC :: Integral a => a -> UTCTime
epochToUTC = posixSecondsToUTCTime . fromIntegral

epochToGregorian :: Integral a => a -> (Integer,Int,Int)
epochToGregorian = toGregorian . utctDay . epochToUTC

And then for instance:

Main> epochToGregorian 1234567
(1970,1,15)
Main> epochToGregorian 123456789
(1973,11,29)
Main> epochToGregorian 1234567890
(2009,2,13)
like image 69
Willem Van Onsem Avatar answered Dec 26 '22 05:12

Willem Van Onsem


Not a bad question at all, in spite of the down votes. So what would be the equivalent of fairly standard "strptime" and "strftime" here?

You find this rather complicated, and I agree, as I spent a fair amount of time figuring this out (n00b here), so here are some of my findings. It is not simple, because:

  1. Handling time is complicated.
  2. The Data.Time library focuses on type safety (consistent with Haskell), which also means that there is no automatic type conversion done for you. Therefore, you must be aware of a thing or two on that matter.
  3. How to read epoch depends on what exactly it stands for and how it is stored. Thus if there is no implementation of a universal function epoch -> utc in the standard library, it is not an omission, it is because it cannot exist.

It's simpler in say python as datetime ship with "pretty good defaults" inferred from your environment. The concept of a "good enough default parameter value for most circumstances" is not something that works well with Haskell.

If the epoch value comes from some arbitrary integer, then Willem Van Onsem's epochToUTC works. It if is a timestamp from a file (EpochTime), we need another function as this is not quite an integer (it looks like an integer, it behaves like an integer, but it is neither Int nor Integer—that is some very deep water).

As for the import statements, you may simplify this to a mere import Data.Time, which will grab all we need from this module, with the exception of posixSecondsToUTCTime, because... I suppose it is deemed as sort of specialized.

import System.Posix.Types(EpochTime)
import Data.Time
import Data.Time.Clock.POSIX(posixSecondsToUTCTime)

epochToUTC :: EpochTime -> UTCTime
epochToUTC = posixSecondsToUTCTime . realToFrac

Example in ghci: (timestamp of current working directory)

import System.Posix.Files

fStat <- getFileStatus "."
let someTime = epochToUTC . modificationTime $ fStat

And we are good to manipulate this time value. Note that the EpochTime is specific to the System.Posix module. If it is a timestamp retrieved from say a database or a web page, it could be yet another story (type safety is not free).

Just as this "strptime" operation was not terribly familiar, a similar story goes with a "strftime"-like operation. This would be done with formatTime. Example:

formatTime defaultTimeLocale "%Y-%m-%d" someTime

--equivalent
formatTime defaultTimeLocale ( iso8601DateFormat Nothing ) someTime

--whichever dateFmt defined in that TimeLocale
formatTime defaultTimeLocale ( dateFmt defaultTimeLocale ) someTime

--"%a, %_d %b %Y %H:%M:%S %Z"
formatTime defaultTimeLocale rfc822DateFormat someTime

If always on defaultTimeLocale, then you can elegantly abbreviate to:

formatTime' = formatTime defaultTimeLocale

Readings:

A Haskell Time Library Tutorial (We need more like this!)

Time - HaskellWiki

like image 30
pbarill Avatar answered Dec 26 '22 04:12

pbarill