timezone-series (empty) → 0.1.0
raw patch · 5 files changed
+392/−0 lines, 5 filesdep +basedep +timesetup-changed
Dependencies added: base, time
Files
- Data/Time/LocalTime/TimeZone/Series.hs +232/−0
- LICENSE +29/−0
- README +62/−0
- Setup.hs +2/−0
- timezone-series.cabal +67/−0
+ Data/Time/LocalTime/TimeZone/Series.hs view
@@ -0,0 +1,232 @@+-----------------------------------------------------------------------------+-- |+-- Module : Data.Time.LocalTime.TimeZone.Series+-- Copyright : Yitzchak Gale 2010+--+-- Maintainer : Yitzchak Gale <gale@sefer.org>+-- Portability : portable+--+-- A @TimeZoneSeries@ describes a timezone by specifying the various+-- clock settings that occurred in the past and are scheduled to occur+-- in the future for the timezone.++{- Copyright (c) 2010 Yitzchak Gale. All rights reserved.+For licensing information, see the BSD3-style license in the file+LICENSE that was originally distributed by the author together with+this file. -}++module Data.Time.LocalTime.TimeZone.Series+(+ -- * Representing a timezone+ -- $abouttzs+ TimeZoneSeries(..),+ timeZoneFromSeries,+ isValidLocalTime,+ isRedundantLocalTime,+ latestNonSummer,++ -- ** Converting between UTC and local time+ -- $aboutfuncs+ utcToLocalTime',+ localTimeToUTC',++ -- * Representing a moment in a timezone+ ZoneSeriesTime(..),+ zonedTimeToZoneSeriesTime,+ zoneSeriesTimeToLocalTime,+ zoneSeriesTimeZone,+ localTimeToZoneSeriesTime+)+where++import Data.Time (UTCTime, LocalTime, TimeZone(timeZoneSummerOnly),+ ZonedTime(ZonedTime),+ ParseTime(buildTime), FormatTime(formatCharacter),+ utcToLocalTime, localTimeToUTC)+import Data.List (partition)+import Data.Maybe (listToMaybe, fromMaybe)+import Data.Typeable (mkTyCon, mkTyConApp, Typeable(typeOf))+import Control.Arrow (first)++-- $abouttzs+-- A @TimeZoneSeries@ describes a timezone with a set of 'TimeZone'+-- objects. Each @TimeZone@ object describes the clock setting in the+-- timezone for a specific period of history during which the clocks+-- do not change.+--+-- Most operating systems provide information about timezone series+-- for the local timezone and for many other timezones of the world.+-- On MS Windows systems, this information can be read from the+-- registry. On other systems, this information is typically provided+-- in the form of Olson timezone files: \/etc\/localtime (or some+-- other file) for the local timezone, and files located in+-- \/usr\/share\/zoneinfo\/ or \/etc\/zoneinfo\/ (or some other+-- directory) for other timezones.++-- | A @TimeZoneSeries@ consists of a default @TimeZone@ object and a+-- sequence of pairs of a @UTCTime@ and a @TimeZone@ object. Each+-- @UTCTime@ indicates a moment at which the clocks changed, and the+-- corresponding @TimeZone@ object describes the new state of the+-- clocks after the change. The default @TimeZone@ object is used for+-- times preceding the earliest @UTCTime@, or if the sequence of pairs+-- is empty. The times in the sequence are in order from latest to+-- earlist (note that this is the opposite of the way that they are+-- stored in an Olson timezone file).+data TimeZoneSeries =+ TimeZoneSeries {+ tzsTimeZone :: TimeZone,+ -- ^ The default timezone state+ tzsTransitions :: [(UTCTime, TimeZone)]+ -- ^ A list of pairs of the time of a+ -- change of clocks and the new timezone+ -- state after the change+ }+ deriving (Eq, Ord)++instance Typeable TimeZoneSeries where+ typeOf _ = mkTyConApp+ (mkTyCon "Data.Time.LocalTime.TimeZone.Series") []++instance Show TimeZoneSeries where+ show = show . latestNonSummer++instance Read TimeZoneSeries where+ readsPrec n = map (first $ flip TimeZoneSeries []) . readsPrec n++instance ParseTime TimeZoneSeries where+ buildTime locale = flip TimeZoneSeries [] . buildTime locale++-- | The latest non-summer @TimeZone@ in a @TimeZoneSeries@ is in some+-- sense representative of the timezone.+latestNonSummer :: TimeZoneSeries -> TimeZone+latestNonSummer (TimeZoneSeries d cs) = fromMaybe d . listToMaybe $+ filter (not . timeZoneSummerOnly) tzs ++ tzs+ where+ tzs = map snd cs++instance FormatTime TimeZoneSeries where+ formatCharacter = + fmap (\f locale mpado -> f locale mpado . latestNonSummer) .+ formatCharacter++-- | Given a timezone represented by a @TimeZoneSeries@, and a @UTCTime@,+-- provide the state of the timezone's clocks at that time.+timeZoneFromSeries :: TimeZoneSeries -> UTCTime -> TimeZone+timeZoneFromSeries (TimeZoneSeries dfault changes) t = fromMaybe dfault .+ fmap snd . listToMaybe . dropWhile ((> t) . fst) $ changes++-- The following functions attempt to deal correctly with corner cases+-- where multiple clock changes overlap with each other, even though+-- as far as I know such weird cases have never actually occurred in+-- any timezone. So far.++-- | When a clock change moves the clock forward, local times that+-- are between the wall clock time before the change and the wall+-- clock time after the change cannot occur.+isValidLocalTime :: TimeZoneSeries -> LocalTime -> Bool+isValidLocalTime tzs lt = gapsVsOverlaps tzs lt /= GT++-- | When a clock change moves the clock backward, local times that+-- are between the wall clock time before the change and the wall+-- clock time after the change occur twice.+isRedundantLocalTime :: TimeZoneSeries -> LocalTime -> Bool+isRedundantLocalTime tzs lt = gapsVsOverlaps tzs lt == LT++gapsVsOverlaps (TimeZoneSeries d cs) lt = compareLengths gaps overlaps+ where+ (gaps, overlaps) = partition (uncurry (<)) relevantIntervals+ relevantIntervals = takeWhile notTooEarly . dropWhile tooLate $+ gapsAndOverlaps+ tooLate (a, b) = a > lt && b > lt+ notTooEarly (a, b) = a > lt || b > lt+ gapsAndOverlaps = zip+ (zipWith utcToLocalTime (map snd (drop 1 cs) ++ [d]) (map fst cs))+ (map (uncurry $ flip utcToLocalTime) cs)++-- Fast lazy comparison of list lengths+compareLengths :: [a] -> [a] -> Ordering+compareLengths (_:xs) (_:ys) = compareLengths xs ys+compareLengths (_:_ ) _ = GT+compareLengths _ (_:_ ) = LT+compareLengths _ _ = EQ++-- | A @ZoneSeriesTime@ represents a moment of time in the context of+-- a particular timezone.+data ZoneSeriesTime = ZoneSeriesTime {+ zoneSeriesTimeToUTC :: UTCTime,+ zoneSeriesTimeSeries :: TimeZoneSeries+ }+ deriving (Eq, Ord)++instance Typeable ZoneSeriesTime where+ typeOf _ = mkTyConApp+ (mkTyCon "Data.Time.LocalTime.TimeZone.ZoneSeriesTime") []++instance Show ZoneSeriesTime where+ show tzs = show $ ZonedTime (zoneSeriesTimeToLocalTime tzs)+ (zoneSeriesTimeZone tzs)++instance Read ZoneSeriesTime where+ readsPrec n = map (first zonedTimeToZoneSeriesTime) . readsPrec n++instance ParseTime ZoneSeriesTime where+ buildTime locale = zonedTimeToZoneSeriesTime . buildTime locale++instance FormatTime ZoneSeriesTime where+ formatCharacter =+ fmap (\f locale mpado -> f locale mpado . zoneSeriesTimeToZonedTime) .+ formatCharacter++-- | The @ZonedTime@ of a @ZoneSeriesTime@.+zoneSeriesTimeToZonedTime :: ZoneSeriesTime -> ZonedTime+zoneSeriesTimeToZonedTime zst =+ ZonedTime (zoneSeriesTimeToLocalTime zst) (zoneSeriesTimeZone zst)++-- | Use a trivial @TimeZoneSeries@ containing only the @TimeZone@+-- of the @ZonedTime@, and use it to define a @ZoneSeriesTime@.+zonedTimeToZoneSeriesTime :: ZonedTime -> ZoneSeriesTime+zonedTimeToZoneSeriesTime (ZonedTime t tz) =+ ZoneSeriesTime (localTimeToUTC tz t) (TimeZoneSeries tz [])++-- | The local time represented by a @ZoneSeriesTime@+zoneSeriesTimeToLocalTime :: ZoneSeriesTime -> LocalTime+zoneSeriesTimeToLocalTime (ZoneSeriesTime t tzs) = utcToLocalTime' tzs t++-- | The @TimeZone@ that is in effect at the moment represented by+-- a @ZoneSeriesTime@.+zoneSeriesTimeZone :: ZoneSeriesTime -> TimeZone+zoneSeriesTimeZone (ZoneSeriesTime t tzs) = timeZoneFromSeries tzs t++-- | The @ZoneSeriesTime@ that represents the given local time in the+-- given timezone. Local times that are invalid or redundant are treated+-- as described below.+localTimeToZoneSeriesTime :: TimeZoneSeries -> LocalTime -> ZoneSeriesTime+localTimeToZoneSeriesTime tzs lt = ZoneSeriesTime (localTimeToUTC' tzs lt) tzs++-- $aboutfuncs+-- The following functions are variants on functions in+-- "Data.Time.LocalTime" that convert between UTC and local time. The+-- originals can give a wrong result if the 'TimeZone' used for the+-- conversion is not actually in effect at the specified time. These+-- variants use a 'TimeZoneSeries' instead of a 'TimeZone'.+--+-- When converting from an invalid local time, the local time is+-- interpreted as if the time change that made it invalid never+-- happened. When converting from a redundant local time, the latest+-- possible interpretation is used. Use the functions+-- 'isValidLocalTime' and 'isRedundantLocalTime' to detect these+-- conditions.++-- | Convert a UTC time to local time using the "TimeZone" that is in+-- effect at that time in the timezone represented by TimeZoneSeries.+utcToLocalTime' :: TimeZoneSeries -> UTCTime -> LocalTime+utcToLocalTime' tzs utc = utcToLocalTime (timeZoneFromSeries tzs utc) utc++-- | Convert a local time to UTC using the "TimeZone" that is in+-- effect at that time in the timezone represented by TimeZoneSeries.+-- Local times that are invalid or redundant are treated as described above.+localTimeToUTC' :: TimeZoneSeries -> LocalTime -> UTCTime+localTimeToUTC' (TimeZoneSeries dfault changes) lt =+ fromMaybe (localTimeToUTC dfault lt) . fmap snd . listToMaybe .+ dropWhile (uncurry (>)) $+ zip (map fst changes) (map (flip localTimeToUTC lt . snd) changes)
+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2010 Yitzchak Gale. All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Yitzchak Gale nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,62 @@+timezone-series version 0.1.0++This package endows Data.Time, from the time package, with several+additional data types for enhanced processing of timezones.++A TimeZoneSeries represents a timezone. It describes the timezone with+a set of Data.Time.TimeZone objects, each describing the clock setting+in the timezone for a specific period of history during which the+clocks do not change. The TimeZoneSeries as a whole describes what+clock setting was in effect in the timezone at any given time during+the period it covers.++A ZoneSeriesTime is a moment in time within a specific timezone. It+is different than a Data.Time.ZonedTime in that a ZonedTime specifies+a moment in time for a specific way that the clocks were set in a+timezone. It is the responsibilty of the creator of a ZonedTime to+verify that given clock setting was actually in effect at the given+moment in time. A ZonedTime for one moment in time cannot be used+reliably on its own to create another ZonedTime for a different moment+in the same timezone - information is needed about what clock setting+was in effect in the timezone at the new moment.++This package also provides variants for the functions in Data.Time+that convert between UTC time and local time. These variants take+a TimeZoneSeries, representing a timezone, not just a specific+clock setting with the timezone, so the correctness of their results+does not depend on independent knowledge of what clock setting+was in effect at the given time.++Most operating systems provide information about timezone series for+the local timezone and for many other timezones of the world. On MS+Windows systems, this information can be read from the registry. On+other systems, this information is typically provided in the form of+Olson timezone files: /etc/localtime (or some other file) for the+local timezone, and files located in /usr/share/zoneinfo/ or+/etc/zoneinfo/ (or some other directory) for other timezones.+See the timezone-olson package for more information about reading+and creating Olson timezone files.++This version of timezone-series is highly experimental. Expect there+to be bugs, and do not rely on any stability of the exposed+interfaces.++Copyright (c) 2010 Yitzchak Gale. All rights reserved.+For licensing information, see the BSD3-style license in the file+LICENSE that was originally distributed by the author together with+this file.++This package is part of the time-ng project:+http://projects.haskell.org/time-ng/++Send suggestions, bug reports, and patches to:+yitz@community.haskell.org++INSTALLATION:++To install the latest version of this package, make sure that+cabal-install is installed on your system (it is if you have installed+the Haskell Platform) and type the commands:++cabal update+cabal install timezone-series
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ timezone-series.cabal view
@@ -0,0 +1,67 @@+-- timezone-series.cabal auto-generated by cabal init. For additional+-- options, see+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.+-- The name of the package.+Name: timezone-series++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version: 0.1.0++-- A short (one-line) description of the package.+Synopsis: Enhanced timezone handling for Data.Time++-- A longer description of the package.+Description: This package endows Data.Time, from the time+ package, with several data types and functions+ for enhanced processing of timezones. For one way+ to create timezone series, see the timezone-olson+ package.+++-- URL for the project homepage or repository.+Homepage: http://projects.haskell.org/time-ng/++-- The license under which the package is released.+License: BSD3++-- The file containing the license text.+License-file: LICENSE++-- The package author(s).+Author: Yitzchak Gale++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer: yitz@community.haskell.org++-- A copyright notice.+Copyright: Copyright (c) 2010 Yitzchak Gale. All rights reserved.++Category: Data++Build-type: Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+Extra-source-files: README++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.2+++Library+ -- Modules exported by the library.+ Exposed-modules: Data.Time.LocalTime.TimeZone.Series+ + -- Packages needed in order to build this package.+ Build-depends: base >= 3.0 && < 5,+ time >= 1.1.4 && < 1.3+ + -- Modules not exported by this package.+ -- Other-modules: + + -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.+ -- Build-tools: +