Using UTCTime with Hamlet

2020-03-24 09:23发布

I am using Yesod on my first site and I have a list of news items:

NewsItem
    date    UTCTime default=CURRENT_TIME
    title   String
    content String
    author  String

which are retrieved in my handler:

newsitems <- runDB $ selectList [] [Desc NewsItemDate]

and ultimately used in my template:

$if null newsitems
    <p>No news.
$else
    $forall Entity id entry <- newsitems
        <article>
            <h4>#{newsItemDate entry}
            <p>#{newsItemContent entry}

But I get an error about datatypes:

Handler/Home.hs:20:11:
    No instance for (Text.Blaze.ToMarkup
                       time-1.4:Data.Time.Clock.UTC.UTCTime)
      arising from a use of `toHtml'
    Possible fix:
      add an instance declaration for
      (Text.Blaze.ToMarkup time-1.4:Data.Time.Clock.UTC.UTCTime)
    In the first argument of `toWidget', namely
      `toHtml (newsItemDate entry_a6ev)'
    In a stmt of a 'do' block:
      toWidget (toHtml (newsItemDate entry_a6ev))
    In the expression:
      do { toWidget
             ((Text.Blaze.Internal.preEscapedText . Data.Text.pack)
                "<article><h4>");
           toWidget (toHtml (newsItemDate entry_a6ev));
           toWidget
             ((Text.Blaze.Internal.preEscapedText . Data.Text.pack)
                "</h4>\
                \<p>");
           toWidget (toHtml (newsItemContent entry_a6ev));
           .... }

So I figure I would go ahead and add to my Import.hs:

import Data.Time (UTCTime)
import Data.Time.Format (formatTime)
import Text.Blaze (ToMarkup, toMarkup)
import Text.Blaze.Internal (string)
import System.Locale (defaultTimeLocale)

-- format date as     26 July 2012
instance ToMarkup UTCTime where
   toMarkup a = string (formatTime defaultTimeLocale "%e %B %Y" a)

Which does compile, but gives me an error at runtime in the browser:

Internal Server Error
PersistMarshalError "Expected UTCTime, received PersistText \"2012-08-30\""

So I am not sure how to solve this, any ideas?

EDIT: Source code to the site in case it is needed or curious: https://github.com/iaefai/socrsite

2条回答
看我几分像从前
2楼-- · 2020-03-24 09:57

I'm pretty sure you could just use show in the hamlet? That's atleast what I've done...

#{show $ newsItemDate entry}

I've run into this instance thing before, and as this guy describes here it's something like this:

As part of this philosophy of frugality of expression Haskell doesn’t require type signatures — although an experienced Haskeller provides them for clarity — so type errors in this strongly typed language are often cryptic for the uninitiated. For instance, if you define a function f that adds two numbers and then call it with two strings, the compiler will not complain about bad arguments, it will complain about string not supporting operator plus. And it will formulate this complaint in a very non-obvious way. [1] Under "1. Haskell is Terse"...

[1] http://fpcomplete.com/ten-things-you-should-know-about-haskell-syntax/

查看更多
三岁会撩人
3楼-- · 2020-03-24 10:01

Without investigating the actual error, I think your approach is not great. You will very likely eventually want several ways of formatting a UTCTime, after all, the type is there to store times, not just dates. By giving a ToMarkup UTCTime instance, you fix this globally.

I would recommend to write functions renderAsDate :: UTCDate -> HTML, renderAsTime :: UTCDate -> HTML etc. and use them in your template, e.g. #{renderAsDate (newsItemDate entry)}.

But this won’t solve the runtime error, which comes from the serialization layer and is likely independent of your templates.

查看更多
登录 后发表回答