How to convert epoch to gregorian datetime in haskell
Asked Answered
K

2

5

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?

Kroeger answered 4/7, 2017 at 11:45 Comment(1)
The answer to this question depends heavily on what those seconds are. Are they unix time, which ignores leap seconds? Or are they seconds in UTC, and if so, since what epoch?Esme
R
5

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)
Richardricharda answered 4/7, 2017 at 11:57 Comment(4)
it is difficult because I have to import 3 modules and know exactly what each module contains, which for a newbie like me is tiresome.Kroeger
How many other languages do you know of where date conversion operations are just there by default and you don't need to know about them to use them?Crandale
Importing three modules is precisely three lines of code; I can't imagine how that is tiresome. Furthermore, you can have import Data.Time to get the first two modules, if you really consider 1 line vs. 2 an improvement. Finally, you don't have to "know" what each module contains - all you must be able to do is read documentation (that very documentation is linked in this answer!).Finnougric
I calmed down and found this useful tutorial on this matter: two-wrongs.com/haskell-time-library-tutorialRadiogram
R
5

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

Radiogram answered 11/4, 2018 at 18:35 Comment(3)
Thank you so much for your informative answer , and I think the downvotes are probably due to noob hostility in Haskell community. And after all this time, your provided link is greatly appreciatedKroeger
Glad to help. I don't think it's noob hostility. Haskellers feel very strongly about their language, which is a double edged sword. Ask a question, expect an answer. Ask a question while expressing a strong opinion, expect an answer with... a strong opinion.Radiogram
May be you are right. I guess I asked this question at a time of extreme frustrationKroeger

© 2022 - 2024 — McMap. All rights reserved.