
(This is a toy program to demonstrate only the part of my real program
that I'm having trouble with.)
Suppose I'm writing a program to print the current time in various
time zones. The time zones are to be given symbolically on the command
line in the form "Europe/London" or "America/New_York". The idea is
that either the operating system or the runtime library keeps track of
what time zones different places are in, and when they are on summer
time, so that my code doesn't have to worry about it.
Haskell has a TimeZone type in Data.Time.LocalTime, but it only
represents constant offsets from UTC — doesn't encode rules for when
the clocks change. And there doesn't seem to be any way of looking up
the time zone for a locality.
Data.Time.LocalTime has the getTimeZone function, which returns the
time zone for a given UTC time on the local machine — this takes care
of summer time, but by itself only works for the local machine's
locality.
If I was writing this program in C, I'd get round this by setting the
TZ environment variable to the locality I was interested in before
doing time conversions.
$ cat cnow.c
#include

I don't have anything to answer for the "interesting" part of your
question, but if you're just interested in getting something
working...
On Feb 16, 2008 3:13 PM, Dave Hinton
2. If GHC's implementation is working as designed, how do I translate the C program above into Haskell?
You can use the FFI to call localtime() directly; see
http://www.haskell.org/haskellwiki/FFI_Introduction
There's information on the types to use here:
http://haskell.org/ghc/docs/latest/html/libraries/base/Foreign-C.html
http://haskell.org/ghc/docs/latest/html/libraries/base/Foreign.html
Something along the lines of:
{-# LANGUAGE FFI #-}
{-# INCLUDE

On Feb 17, 2008 12:13 AM, Dave Hinton
(This is a toy program to demonstrate only the part of my real program that I'm having trouble with.)
Suppose I'm writing a program to print the current time in various time zones. The time zones are to be given symbolically on the command line in the form "Europe/London" or "America/New_York". The idea is that either the operating system or the runtime library keeps track of what time zones different places are in, and when they are on summer time, so that my code doesn't have to worry about it.
Haskell has a TimeZone type in Data.Time.LocalTime, but it only represents constant offsets from UTC — doesn't encode rules for when the clocks change. And there doesn't seem to be any way of looking up the time zone for a locality.
Data.Time.LocalTime has the getTimeZone function, which returns the time zone for a given UTC time on the local machine — this takes care of summer time, but by itself only works for the local machine's locality.
If I was writing this program in C, I'd get round this by setting the TZ environment variable to the locality I was interested in before doing time conversions.
$ cat cnow.c #include
#include #include #include void outTime (time_t utc, char *tzName) { char env[100] = "TZ="; strcat (env, tzName); putenv (env); printf ("%s\t%s", tzName, asctime (localtime (&utc))); } int main (int argc, char **argv) { int i; time_t utc = time (NULL); for (i = 1; i < argc; ++i) outTime (utc, argv[i]); return 0; } $ gcc cnow.c -o cnow $ ./cnow Europe/Paris Europe/Moscow Europe/London Europe/Paris Sat Feb 16 23:57:22 2008 Europe/Moscow Sun Feb 17 01:57:22 2008 Europe/London Sat Feb 16 22:57:22 2008 So far, so good. Here's the equivalent in Haskell:
$ cat hsnow.hs import Data.Time import Data.Time.LocalTime import System.Environment import System.Posix.Env outTime utc env = do putEnv ("TZ=" ++ env) tz <- getTimeZone utc putStrLn (env ++ "\t" ++ show (utcToLocalTime tz utc)) main = do utc <- getCurrentTime mapM_ (outTime utc) =<< getArgs $ ghc --make hsnow.hs -o hsnow [1 of 1] Compiling Main ( hsnow.hs, hsnow.o ) Linking hsnow ... $ ./hsnow Europe/Paris Europe/Moscow Europe/London Europe/Paris 2008-02-16 23:59:11.776151 Europe/Moscow 2008-02-16 23:59:11.776151 Europe/London 2008-02-16 23:59:11.776151 $ ./hsnow Europe/Moscow Europe/London Europe/Paris Europe/Moscow 2008-02-17 01:59:28.617711 Europe/London 2008-02-17 01:59:28.617711 Europe/Paris 2008-02-17 01:59:28.617711
Not good. GHC's runtime library seems to be taking the value of TZ the first time it is called as gospel, and ignoring subsequent changes to TZ.
So:
1. Is this a bug in GHC's Data.Time.LocalTime.getTimeZone? 2. If GHC's implementation is working as designed, how do I translate the C program above into Haskell?
I'm running on Debian stable, with GHC 6.6.
Interesting, it works for me: $ ghc --make hsnow.hs -o hsnow [1 of 1] Compiling Main ( hsnow.hs, hsnow.o ) Linking hsnow ... $ ./hsnow Europe/Paris Europe/Moscow Europe/London Europe/Paris 2008-02-17 16:07:43.009057 Europe/Moscow 2008-02-17 18:07:43.009057 Europe/London 2008-02-17 15:07:43.009057 $ ghc --version The Glorious Glasgow Haskell Compilation System, version 6.8.2 $ uname -srv Darwin 8.11.1 Darwin Kernel Version 8.11.1: Wed Oct 10 18:23:28 PDT 2007; root:xnu-792.25.20~1/RELEASE_I386 -- /Björn

Am Sonntag, 17. Februar 2008 16:09 schrieb Bjorn Bringert:
Interesting, it works for me:
$ ghc --make hsnow.hs -o hsnow [1 of 1] Compiling Main ( hsnow.hs, hsnow.o ) Linking hsnow ...
$ ./hsnow Europe/Paris Europe/Moscow Europe/London Europe/Paris 2008-02-17 16:07:43.009057 Europe/Moscow 2008-02-17 18:07:43.009057 Europe/London 2008-02-17 15:07:43.009057
$ ghc --version The Glorious Glasgow Haskell Compilation System, version 6.8.2
$ uname -srv Darwin 8.11.1 Darwin Kernel Version 8.11.1: Wed Oct 10 18:23:28 PDT 2007; root:xnu-792.25.20~1/RELEASE_I386
Doesn't work for me: dafis@linux:~> ghc --make hsnow.hs -o hsnw [1 of 1] Compiling Main ( hsnow.hs, hsnow.o ) Linking hsnw ... dafis@linux:~> ./hsnw Europe/Moscow Europe/Paris Europe/London Europe/Moscow 2008-02-17 18:37:24.107667 Europe/Paris 2008-02-17 18:37:24.107667 Europe/London 2008-02-17 18:37:24.107667 dafis@linux:~> ghc --version The Glorious Glasgow Haskell Compilation System, version 6.8.2 Adding some debug-code, we see that the TZ evironment variable is properly set, but the TimeZone returned by getTimeZone is always that of the first argument: dafis@linux:~> ./hsnow Europe/Moscow Europe/Paris Europe/London Just "Europe/Moscow" 2008-02-17 16:01:56.061005 UTC MSK Europe/Moscow 2008-02-17 19:01:56.061005 Just "Europe/Paris" 2008-02-17 16:01:56.061005 UTC MSK Europe/Paris 2008-02-17 19:01:56.061005 Just "Europe/London" 2008-02-17 16:01:56.061005 UTC MSK Europe/London 2008-02-17 19:01:56.061005 Looking at the code in HsTime.c, it might be a difference between localtime and localtime_r.

Am Sonntag, 17. Februar 2008 17:26 schrieb Daniel Fischer:
Looking at the code in HsTime.c, it might be a difference between localtime and localtime_r.
Indeed, mucking about a bit with HsTime.c, so that either a) localtime is called instead of localtime_r or b) tzset() is done before localtime_r, it works as it should (on my box at least). Cheers, Daniel

On Feb 17, 2008, at 13:22 , Daniel Fischer wrote:
Am Sonntag, 17. Februar 2008 17:26 schrieb Daniel Fischer:
Looking at the code in HsTime.c, it might be a difference between localtime and localtime_r.
Indeed, mucking about a bit with HsTime.c, so that either a) localtime is called instead of localtime_r or b) tzset() is done before localtime_r, it works as it should (on my box at least).
Yes, some platforms require tzset() to recognize timezone changes. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Hurrah, I am now calling the C function tzset() from my Haskell code via FFI, and I'm getting the results I want. $ cat hsc2now.hs {-# LANGUAGE ForeignFunctionInterface #-} import Data.Time import Data.Time.LocalTime import System.Environment import System.Posix.Env foreign import ccall "time.h tzset" tzset :: IO () outTime utc tzName = do putEnv ("TZ=" ++ tzName) tzset tz <- getTimeZone utc putStrLn (tzName ++ "\t" ++ show (utcToLocalTime tz utc)) main = do utc <- getCurrentTime mapM_ (outTime utc) =<< getArgs $ ghc --make hsc2now.hs -o hsc2now [1 of 1] Compiling Main ( hsc2now.hs, hsc2now.o ) Linking hsc2now ... $ ./hsc2now Europe/Moscow Europe/Paris Europe/London Europe/Moscow 2008-02-17 22:06:44.770153 Europe/Paris 2008-02-17 20:06:44.770153 Europe/London 2008-02-17 19:06:44.770153 Thank you everyone for your help :-)

beakerchu:
Hurrah, I am now calling the C function tzset() from my Haskell code via FFI, and I'm getting the results I want.
$ cat hsc2now.hs {-# LANGUAGE ForeignFunctionInterface #-} import Data.Time import Data.Time.LocalTime import System.Environment import System.Posix.Env foreign import ccall "time.h tzset" tzset :: IO () outTime utc tzName = do putEnv ("TZ=" ++ tzName) tzset tz <- getTimeZone utc putStrLn (tzName ++ "\t" ++ show (utcToLocalTime tz utc)) main = do utc <- getCurrentTime mapM_ (outTime utc) =<< getArgs $ ghc --make hsc2now.hs -o hsc2now [1 of 1] Compiling Main ( hsc2now.hs, hsc2now.o ) Linking hsc2now ... $ ./hsc2now Europe/Moscow Europe/Paris Europe/London Europe/Moscow 2008-02-17 22:06:44.770153 Europe/Paris 2008-02-17 20:06:44.770153 Europe/London 2008-02-17 19:06:44.770153
Thank you everyone for your help :-)
Perhaps we should get a binding to tzset in the unix library?

Don Stewart wrote:
Perhaps we should get a binding to tzset in the unix library?
That's probably preferable to calling tzset() before every localtime_r. But perhaps we want a call that combines the putenv and the tzset, just so it exposes fewer implementation details. This is essentially an interface to the zoneinfo database rather than time functionality as such. Ideally there would be a better C interface to zoneinfo that wouldn't involve mucking around with (global) environment variables. -- Ashley Yakeley Seattle, WA

Brandon S. Allbery KF8NH wrote:
some platforms require tzset() to recognize timezone changes.
I think it is something like - POSIX requires the tzset() call, but in ANSI C 98 there is no tzset() and localtime() always rechecks TZ automatically. Right? In the man page on Mac OS X Tiger (Darwin 8.11.1), it says that localtime() calls tzset() automatically if the current process has not called it yet. Which implies that it would only be called once, so I don't understand Bjorn's successful result on that platform. Don Stewart wrote:
Perhaps we should get a binding to tzset in the unix library?
I agree - but to make it worthwhile, we should either: a) make things work the same on all platforms, or b) faithfully expose the underlying local platform, but make it easy (and well documented) for a program to discover what needs to be done to make things work. (Even if the information provided by System.Info is sufficient, how easy is it to use it for this?) In any case, full information or a clear pointer to it should appear in the Haddocks for Data.Time.LocalTime, System.Time, and wherever else appropriate. For comparison, Python does (a) - the behavior of localtime() and tzset() are always like POSIX, even on a platform that does otherwise. And also on Windows. This behavior is clearly described in the documentation for the time module. Regards, Yitz
participants (8)
-
Ashley Yakeley
-
Bjorn Bringert
-
Brandon S. Allbery KF8NH
-
Daniel Fischer
-
Dave Hinton
-
Don Stewart
-
Ryan Ingram
-
Yitzchak Gale