Есть ли лучший способ конвертировать UTCTime в EpochTime? - PullRequest
8 голосов
/ 16 ноября 2010

Я хочу установить время модификации файла на время, которое я получил из exif-данных.

Чтобы узнать время из exif, я нашел:

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

Чтобы установить время изменения файла, я нашел:

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

Предполагая, что я нахожу Time в Exif, мне нужно преобразовать String в EpochTime.

  • С parseTime я могу получить UTCTime.
  • С utcTimeToPOSIXSeconds я могу получить POSIXTime
  • С POSIXTime я могу более или менее получить EpochTime

Чтобы преобразовать из UTCTime в EpochTime этот тип проверки, но я не уверен, что это правильно:

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)

Мой вопрос

Есть ли лучший / более простой способ конвертировать время? (возможно, используя разные библиотеки)

Ответы [ 2 ]

8 голосов
/ 03 февраля 2012

Вы также можете использовать Data.Convertible.convert (из пакета кабриолет ):

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

utcTimeToEpochTime :: UTCTime -> EpochTime
utcTimeToEpochTime = convert
8 голосов
/ 16 ноября 2010

Data.Time - лучшая поддерживаемая библиотека времени, поэтому я определенно согласен с вашим выбором ее использования для анализа строкового представления даты и времени, которое вы получаете из данных Exif.

Являетесь ли выуверены, что вам нужно установить время модификации файла?Это необычноНо если да, то да, вам нужно использовать библиотеки System.Posix в системе Posix.

Если вам нужно только прочитать модификацию файла, вы былучше использовать более общую функцию System.Directory.getModificationTime.К сожалению, эта функция также использует нестандартную временную библиотеку, System.Time из давно устаревшего пакета old-time в этом случае.Таким образом, вам все равно придется выполнять некоторые подобные махинации.

В данном конкретном случае ваше преобразование из POSIXTime в EpochTime в порядке, но в целом это не идеальный способ.

Тип EpochTime, также известный как тип time_t из C, не поддерживает какой-либо прямой способ создания в Haskell без прохождения через интегральное значение, даже если он сам не обязательно является целочисленным в зависимости от вашей операционной системы.Вы можете пройти через C, используя FFI, если эти потенциальные доли секунды важны для вас.Здесь это, конечно, не важно, потому что вы получаете секунды из параметра формата %S, который не может иметь дробной части.Тем не мение.вам все равно нужно будет выполнить какое-то округление или усечение, чтобы перейти от нецелого типа UTCTime к EpochTime.

В настоящее время вы просто используете Enum экземпляр POSIXTime длясделать округление / усечение для вас, однако он решает.Опять же, в данном конкретном случае это не имеет большого значения, потому что мы знаем, что значение будет целым.Но в целом, лучше указать это непосредственно, используя floor, ceiling или round.Например,

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

(Обратите внимание, что вам не нужно явно записывать case, вы можете использовать функцию maybe из Prelude.)

Обратите внимание, что яЯ также явно использую fromInteger для принудительного преобразования через тип Integer.Если вы хотите вместо этого использовать Int (обратите внимание на «проблему 2038 года» на 32-разрядных машинах), я бы определил отдельную функцию преобразования, чтобы прояснить это:

  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
...