9

我想将文件的修改时间设置为从 exif 数据中获得的时间。

为了从 exif 中获取时间,我发现:

Graphics.Exif.getTag :: Exif -> String -> IO (Maybe String)

要设置文件修改时间,我发现:

System.Posix.Files.setFileTimes :: FilePath -> EpochTime -> EpochTime -> IO ()

假设我确实在 Exif 中找到了时间,我需要将字符串转换为 EpochTime。

  • 有了parseTime我可以得到一个UTCTime.
  • utcTimeToPOSIXSeconds我可以得到一个POSIXTime
  • 有了一个POSIXTime我或多或少可以得到一个EpochTime

要从 a 转换UTCTimeEpochTime这种类型检查,但我不确定它是否正确:

fromIntegral . fromEnum . utcTimeToPOSIXSeconds $ etime

这是函数 getTime 的一部分,它将从 Exif 数据中返回时间(如果存在),否则返回文件的修改时间:

getTime (path,stat) = do
 let ftime                 = modificationTime $ stat
     err (SomeException _) = return ftime
 time <- liftIO $ handle err $ do
   exif <- Exif.fromFile path
   let getExifTime = MaybeT . liftIO . Exif.getTag exif
   res <- runMaybeT $ do
     tmp <- msum . map getExifTime $ [ "DateTimeOriginal","DateTimeDigitized", "DateTime" ]
     MaybeT . return . parseTime defaultTimeLocale "%Y:%m:%d %H:%M:%S" $ tmp
   case res of
     Nothing    -> return ftime
     Just etime -> return . fromIntegral . fromEnum . utcTimeToPOSIXSeconds $ etime
 return (path,time)

我的问题是

有没有更好/更简单的方法来转换时间?(也许使用不同的库)

4

2 回答 2

9

您还可以使用Data.Convertible.convert(来自可转换包):

import Data.Convertible (convert)
import System.Posix.Types (EpochTime(..))
import Data.Time.Clock (UTCTime(..))

utcTimeToEpochTime :: UTCTime -> EpochTime
utcTimeToEpochTime = convert
于 2012-02-02T23:32:49.020 回答
8

Data.Time是最受支持的时间库,因此我绝对同意您选择使用它来解析您从 Exif 数据中获取的日期和时间的字符串表示形式。

您确定需要设置文件的修改时间吗?这很不寻常。但如果是这样,那么是的,您需要System.Posix在 Posix 系统上使用这些库。

如果您只需要读取文件的修改,则最好使用更通用的函数 System.Directory.getModificationTime。不幸的是,该函数还使用了非标准时间库,在这种情况下System.Time来自长期弃用的old-time包。所以你仍然需要做一些类似的阴谋。

在这种特殊情况下,您从POSIXTimeto的转换EpochTime恰好是可以的,但总的来说这不是理想的方式。

EpochTime类型,也就是time_t来自 C 的类型,不支持任何直接在 Haskell 中构造而不通过整数值的方式,即使它本身不一定是整数,具体取决于您的操作系统。如果这些潜在的几分之一秒对您很重要,您可以使用 FFI 通过 C。在这里这当然不重要,因为您从%S格式参数中获取秒数,该参数不能有小数部分。反正。您仍然需要进行某种舍入或截断才能从非整数UTCTime类型变为EpochTime.

您目前只是使用 的Enum实例POSIXTime为您进行舍入/截断,但它决定这样做。同样,在这种特殊情况下,它并不重要,因为我们碰巧知道该值将是整数。但一般来说,最好使用 、 或 自己floor明确ceiling指定round。例如,

return $ maybe ftime (fromInteger . round . utcTimeToPOSIXSeconds) etime

(注意不需要case显式写出,可以使用maybePrelude 中的函数。)

请注意,我还明确地使用fromInteger该类型来推动转换Integer。如果您想Int转而使用(注意 32 位机器上的“2038 年问题”),我会定义一个单独的转换函数来说明这一点:

  return $ maybe ftime utcTimeToEpochTime etime
...

-- Convert from UTCTime to EpochTime via Int
utcTimeToEpochTime :: UTCTime -> EpochTime
utcTimeToEpochTime = fromIntegral . toSecs
  where
    toSecs :: UTCTime -> Int
    toSecs = round . utcTimeToPOSIXSeconds
于 2010-11-16T18:04:50.377 回答