LINUX.ORG.RU

Часовые пояса и Data.Time

 ,


0

2

Есть простейший код:

import Data.Time.Clock.POSIX
import Data.Time.Clock
import Data.Time.Format
import Data.Time.LocalTime
import System.Locale

main = do
  print $ zonedTimeToUTC t1
  print $ zonedTimeToUTC t1'

t1 :: ZonedTime
t1 = readTime defaultTimeLocale
  "%B %e %Y %l:%M%P %Z"
  "March 8 2009 7:30pm EST"

t1' :: ZonedTime
t1' = readTime defaultTimeLocale
  "%B %e %Y %l:%M%P %Z"
  "March 8 2009 7:30pm MSK"

Выполняю и получаю:

*Main> main 
2009-03-09 00:30:00 UTC
2009-03-08 19:30:00 UTC

Что за?… Модуль не умеет зоны разбирать стандартные? Если посмотреть внутрь t1', то видно, что:

*Main> zonedTimeZone t1' 
MSK
*Main> zonedTimeToLocalTime  t1' 
2009-03-08 19:30:00

как и написано в документации http://hackage.haskell.org/package/time-1.4.2/docs/Data-Time-LocalTime.html#t...

Пробовал играться с http://hackage.haskell.org/package/timezone-series-0.1.3 — результат не изменился.

Интересно, где лыжи не едут и как получить корректный разбор часовых зон, отличных от тех, что в Америке живут?



Последнее исправление: HolyBoy (всего исправлений: 1)

делай раз:

zonedTimeToUTC :: ZonedTime -> UTCTime
zonedTimeToUTC (ZonedTime t zone) = localTimeToUTC zone t

делай два:

-- | find out what UTC time a given LocalTime in a given time zone is
localTimeToUTC :: TimeZone -> LocalTime -> UTCTime
localTimeToUTC tz (LocalTime day tod) = UTCTime (addDays i day) (timeOfDayToTime todUTC) where
	(i,todUTC) = localToUTCTimeOfDay tz tod

делай три:

-- | Convert a ToD in some timezone to a ToD in UTC, together with a day adjustment.
localToUTCTimeOfDay :: TimeZone -> TimeOfDay -> (Integer,TimeOfDay)
localToUTCTimeOfDay zone = utcToLocalTimeOfDay (minutesToTimeZone (negate (timeZoneMinutes zone)))

проверяй:

  print $ timeZoneMinutes $ zonedTimeZone t1'
> 0

в чем проблема ясно?

как решить, поменять таймзону на таймзону с данными из timezone-series или timezone-olson (хз что там надо)

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Да, но

main = do
  tmr <- getZonedTime
  print $ tmr
  print $ zonedTimeToUTC tmr
  print $ timeZoneMinutes $ zonedTimeZone tmr

даёт

*Main> main 
2014-07-28 17:39:02.602268 MSK
2014-07-28 13:39:02.602268 UTC
240

Т.е. тут, скорее, проблема в парсере, почему-то, парся

t1 :: ZonedTime
t1 = readTime defaultTimeLocale
  "%F %X %Z"
  "2014-07-28 17:04:18 MSK"

, MSK не принимается как название таймзоны, тогда как из getTimeZone всё ок. Вот и выяснить хочу, вдруг это баг.

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy
  print . timeZoneMinutes . zonedTimeZone =<< getZonedTime
> 240
getZonedTime :: IO ZonedTime
getZonedTime = do
	t <- getCurrentTime
	zone <- getTimeZone t
	return (utcToZonedTime zone t)
-- | Get the local time-zone for a given time (varying as per summertime adjustments)
getTimeZone :: UTCTime -> IO TimeZone
getTimeZone time = with 0 (\pdst -> with nullPtr (\pcname -> do
	secs <- get_current_timezone_seconds (posixToCTime (utcTimeToPOSIXSeconds time)) pdst pcname
	case secs of
		0x80000000 -> fail "localtime_r failed"
		_ -> do
			dst <- peek pdst
			cname <- peek pcname
			name <- peekCString cname
			return (TimeZone (div (fromIntegral secs) 60) (dst == 1) name)
	))

т.е. при получении текущей TZ он честно берёт данные из системы, при парсе, откуда брать неясно, поэтому тебе тупо выдается имя таймзоны с пустыми настройками. Дальше ты можешь брать свою бд таймзон (/usr/share/zoneinfo) библиотеку умеющую их парсить (timezone-olson) и получаешь данные по твоей TZ.

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от anonymous

(ну хоть thyme во втором случае «распарсит» как Nothing)

anonymous
()
Ответ на: комментарий от anonymous

С этими вшиваниями можно проблем кучу по иметь, вот если из MSK переводить, то поди найди где там было зимнее время, где нет, да и везде в разное время перевод.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

С этими вшиваниями можно проблем кучу по иметь, вот если из MSK переводить

А говорю, что прибитые зоны - комильфо? С другой стороны тогда IO будет (если добавлять параметр вроде TZ: https://hackage.haskell.org/package/tz-0.0.0.8/docs/Data-Time-Zones.html).

anonymous
()
Ответ на: комментарий от qnikst

Да, так работает:

import Data.Time.Clock.POSIX
import Data.Time.Clock
import Data.Time.Format
import Data.Time.LocalTime
import System.Locale

import Data.Time.LocalTime.TimeZone.Olson
import Data.Time.LocalTime.TimeZone.Series

main = do
  tzs <- getTimeZoneSeriesFromOlsonFile "/usr/share/zoneinfo/Europe/Moscow"
  print t1
  print $ localTimeToUTC' tzs (zonedTimeToLocalTime t1) 

t1 :: ZonedTime
t1 = readTime defaultTimeLocale
  "%F %X %Z"
  "2014-07-28 17:04:18 MSK"

и

*Main> main 
2014-07-28 17:04:18 MSK
2014-07-28 13:04:18 UTC

Спасибо!

Теперь, решив более простую проблему с преобразованием аббревиатуры названия временной зоны в имя файла зоны, можно пускать это дело в работу.

Тут уже проще: или все файлы в /usr/share/zoneinfo распарсить на предмет этой аббревиатуры или руками задать тот небольшой список зон, который вообще будет использоваться мной.

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

ну смотри, как ты и пишешь, есть 2 варианта:

1). простой - тупо вбить соответствие зона-файл и использовать его (не скейлится при добавлении новых зон), т.к. надо менять или конфиг или код

2). сложный - судя по просмотру olson файла там есть список возможных аббривеатур таймзоны, хотя судя по всему для Europe/Moscow он возвращает MSK-4, а что он говорит просто про MSK, до меня не доходит.. В общем поидее можно распарсить все файлы и создать список возможных аббривеатур таймзон.

  print =<< getOlsonFromFile "/usr/share/zoneinfo/Europe/Moscow"
qnikst ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.