packages feed

unix-time 0.3.8 → 0.5.0

raw patch · 18 files changed

Files

+ ChangeLog.md view
@@ -0,0 +1,17 @@+# ChangeLog for unix-time++## v0.5.0++* Remove deprecated `old-time` dependency by dropping `fromClockTime` and `toClockTime`+  [#68](https://github.com/kazu-yamamoto/unix-time/pull/68)++## v0.4.17++* Fix MinGW64 linker error+  [#67](https://github.com/kazu-yamamoto/unix-time/pull/67)++## v0.4.16++* Make `tzset` optional.+  [#65](https://github.com/kazu-yamamoto/unix-time/pull/65)+
Data/UnixTime.hs view
@@ -1,31 +1,34 @@ module Data.UnixTime (-  -- * Data structure-    UnixTime(..)-  -- * Getting time-  , getUnixTime-  -- * Parsing and formatting time-  , parseUnixTime-  , parseUnixTimeGMT-  , formatUnixTime-  , formatUnixTimeGMT-  -- * Format-  , Format-  , webDateFormat-  , mailDateFormat-  -- * Difference time-  , UnixDiffTime(..)-  , diffUnixTime-  , addUnixDiffTime-  , secondsToUnixDiffTime-  , microSecondsToUnixDiffTime-  -- * Translating time-  , fromEpochTime-  , toEpochTime-  , fromClockTime-  , toClockTime-  ) where+    -- * Data structure+    UnixTime (..), +    -- * Getting time+    getUnixTime,++    -- * Parsing and formatting time+    parseUnixTime,+    parseUnixTimeGMT,+    formatUnixTime,+    formatUnixTimeGMT,++    -- * Format+    Format,+    webDateFormat,+    mailDateFormat,++    -- * Difference time+    UnixDiffTime (..),+    diffUnixTime,+    addUnixDiffTime,+    secondsToUnixDiffTime,+    microSecondsToUnixDiffTime,++    -- * Translating time+    fromEpochTime,+    toEpochTime+) where+ import Data.UnixTime.Conv+import Data.UnixTime.Diff import Data.UnixTime.Sys import Data.UnixTime.Types-import Data.UnixTime.Diff
Data/UnixTime/Conv.hs view
@@ -1,15 +1,19 @@-{-# LANGUAGE OverloadedStrings, ForeignFunctionInterface #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE OverloadedStrings #-}  module Data.UnixTime.Conv (-    formatUnixTime, formatUnixTimeGMT-  , parseUnixTime, parseUnixTimeGMT-  , webDateFormat, mailDateFormat-  , fromEpochTime, toEpochTime-  , fromClockTime, toClockTime-  ) where+    formatUnixTime,+    formatUnixTimeGMT,+    parseUnixTime,+    parseUnixTimeGMT,+    webDateFormat,+    mailDateFormat,+    fromEpochTime,+    toEpochTime+) where  import Control.Applicative-import Data.ByteString+import Data.ByteString.Char8 import Data.ByteString.Unsafe import Data.UnixTime.Types import Foreign.C.String@@ -17,22 +21,22 @@ import Foreign.Marshal.Alloc import System.IO.Unsafe (unsafePerformIO) import System.Posix.Types (EpochTime)-import System.Time (ClockTime(..))  -- $setup -- >>> import Data.Function (on)+-- >>> :set -XOverloadedStrings  foreign import ccall unsafe "c_parse_unix_time"-        c_parse_unix_time :: CString -> CString -> IO CTime+    c_parse_unix_time :: CString -> CString -> IO CTime  foreign import ccall unsafe "c_parse_unix_time_gmt"-        c_parse_unix_time_gmt :: CString -> CString -> IO CTime+    c_parse_unix_time_gmt :: CString -> CString -> IO CTime  foreign import ccall unsafe "c_format_unix_time"-        c_format_unix_time :: CString -> CTime -> CString -> CInt -> IO CSize+    c_format_unix_time :: CString -> CTime -> CString -> CInt -> IO CSize  foreign import ccall unsafe "c_format_unix_time_gmt"-        c_format_unix_time_gmt :: CString -> CTime -> CString -> CInt -> IO CSize+    c_format_unix_time_gmt :: CString -> CTime -> CString -> CInt -> IO CSize  ---------------------------------------------------------------- @@ -42,13 +46,13 @@ -- Many implementations of strptime_l() do not support %Z and -- some implementations of strptime_l() do not support %z, either. -- 'utMicroSeconds' is always set to 0.- parseUnixTime :: Format -> ByteString -> UnixTime parseUnixTime fmt str = unsafePerformIO $     useAsCString fmt $ \cfmt ->         useAsCString str $ \cstr -> do             sec <- c_parse_unix_time cfmt cstr             return $ UnixTime sec 0+ -- | -- Parsing 'ByteString' to 'UnixTime' interpreting as GMT. -- This is a wrapper for strptime_l().@@ -56,7 +60,6 @@ -- -- >>> parseUnixTimeGMT webDateFormat "Thu, 01 Jan 1970 00:00:00 GMT" -- UnixTime {utSeconds = 0, utMicroSeconds = 0}- parseUnixTimeGMT :: Format -> ByteString -> UnixTime parseUnixTimeGMT fmt str = unsafePerformIO $     useAsCString fmt $ \cfmt ->@@ -71,7 +74,6 @@ -- This is a wrapper for strftime_l(). -- 'utMicroSeconds' is ignored. -- The result depends on the TZ environment variable.- formatUnixTime :: Format -> UnixTime -> IO ByteString formatUnixTime fmt t =     formatUnixTimeHelper c_format_unix_time fmt t@@ -91,7 +93,6 @@ -- True -- >>> ((==) `on` utMicroSeconds) ut ut' -- False- formatUnixTimeGMT :: Format -> UnixTime -> ByteString formatUnixTimeGMT fmt t =     unsafePerformIO $ formatUnixTimeHelper c_format_unix_time_gmt fmt t@@ -99,7 +100,6 @@  -- | -- Helper handling memory allocation for formatUnixTime and formatUnixTimeGMT.- formatUnixTimeHelper     :: (CString -> CTime -> CString -> CInt -> IO CSize)     -> Format@@ -108,8 +108,8 @@ formatUnixTimeHelper formatFun fmt (UnixTime sec _) =     useAsCString fmt $ \cfmt -> do         let siz = 80-        ptr  <- mallocBytes siz-        len  <- fromIntegral <$> formatFun cfmt sec ptr (fromIntegral siz)+        ptr <- mallocBytes siz+        len <- fromIntegral <$> formatFun cfmt sec ptr (fromIntegral siz)         ptr' <- reallocBytes ptr (len + 1)         unsafePackMallocCString ptr' -- FIXME: Use unsafePackMallocCStringLen from bytestring-0.10.2.0 @@ -119,7 +119,6 @@ -- Format for web (RFC 2616). -- The value is \"%a, %d %b %Y %H:%M:%S GMT\". -- This should be used with 'formatUnixTimeGMT' and 'parseUnixTimeGMT'.- webDateFormat :: Format webDateFormat = "%a, %d %b %Y %H:%M:%S GMT" @@ -127,7 +126,6 @@ -- Format for e-mail (RFC 5322). -- The value is \"%a, %d %b %Y %H:%M:%S %z\". -- This should be used with 'formatUnixTime' and 'parseUnixTime'.- mailDateFormat :: Format mailDateFormat = "%a, %d %b %Y %H:%M:%S %z" @@ -135,30 +133,10 @@  -- | -- From 'EpochTime' to 'UnixTime' setting 'utMicroSeconds' to 0.- fromEpochTime :: EpochTime -> UnixTime fromEpochTime sec = UnixTime sec 0  -- | -- From 'UnixTime' to 'EpochTime' ignoring 'utMicroSeconds'.- toEpochTime :: UnixTime -> EpochTime toEpochTime (UnixTime sec _) = sec---- |--- From 'ClockTime' to 'UnixTime'.--fromClockTime :: ClockTime -> UnixTime-fromClockTime (TOD sec psec) = UnixTime sec' usec'-  where-    sec' = fromIntegral sec-    usec' = fromIntegral $ psec `div` 1000000---- |--- From 'UnixTime' to 'ClockTime'.--toClockTime :: UnixTime -> ClockTime-toClockTime (UnixTime sec usec) = TOD sec' psec'-  where-    sec' = truncate (toRational sec)-    psec' = fromIntegral $ usec * 1000000
Data/UnixTime/Diff.hs view
@@ -1,16 +1,19 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}  module Data.UnixTime.Diff (-    diffUnixTime-  , addUnixDiffTime-  , secondsToUnixDiffTime-  , microSecondsToUnixDiffTime-  ) where+    diffUnixTime,+    addUnixDiffTime,+    secondsToUnixDiffTime,+    microSecondsToUnixDiffTime,+) where -import Data.UnixTime.Types import Data.Int+import Data.UnixTime.Types import Foreign.C.Types +-- $setup+-- >>> :set -XOverloadedStrings+ ----------------------------------------------------------------  calc :: CTime -> Int32 -> UnixDiffTime@@ -30,23 +33,22 @@ -- UnixDiffTime {udtSeconds = -3, udtMicroSeconds = 0} -- >>> (3 :: UnixDiffTime) * 2 -- UnixDiffTime {udtSeconds = 6, udtMicroSeconds = 0}- instance Num UnixDiffTime where-    UnixDiffTime s1 u1 + UnixDiffTime s2 u2 = calc (s1+s2) (u1+u2)-    UnixDiffTime s1 u1 - UnixDiffTime s2 u2 = calc (s1-s2) (u1-u2)-    UnixDiffTime s1 u1 * UnixDiffTime s2 u2 = calc' (s1*s2) (u1*u2)+    UnixDiffTime s1 u1 + UnixDiffTime s2 u2 = calc (s1 + s2) (u1 + u2)+    UnixDiffTime s1 u1 - UnixDiffTime s2 u2 = calc (s1 - s2) (u1 - u2)+    UnixDiffTime s1 u1 * UnixDiffTime s2 u2 = calc' (s1 * s2) (u1 * u2)     negate (UnixDiffTime s u) = UnixDiffTime (-s) (-u)     abs (UnixDiffTime s u) = UnixDiffTime (abs s) (abs u)     signum (UnixDiffTime s u)-         | s == 0 && u == 0 = 0-         | s > 0            = 1-         | otherwise        = -1+        | s == 0 && u == 0 = 0+        | s > 0 = 1+        | otherwise = -1     fromInteger i = UnixDiffTime (fromInteger i) 0  {-# RULES "Integral->UnixDiffTime" fromIntegral = secondsToUnixDiffTime #-}  instance Real UnixDiffTime where-        toRational = toFractional+    toRational = toFractional  {-# RULES "UnixDiffTime->Fractional" realToFrac = toFractional #-} @@ -56,66 +58,61 @@ -- -- >>> UnixTime 100 2000 `diffUnixTime` UnixTime 98 2100 -- UnixDiffTime {udtSeconds = 1, udtMicroSeconds = 999900}---- diffUnixTime :: UnixTime -> UnixTime -> UnixDiffTime-diffUnixTime (UnixTime s1 u1) (UnixTime s2 u2) = calc (s1-s2) (u1-u2)+diffUnixTime (UnixTime s1 u1) (UnixTime s2 u2) = calc (s1 - s2) (u1 - u2)  -- | Adding difference to 'UnixTime'. ----- >>> UnixTime 100 2000 `addUnixDiffTime` microSecondsToUnixDiffTime (-1003000)+-- >>> UnixTime 100 2000 `addUnixDiffTime` microSecondsToUnixDiffTime ((-1003000) :: Int) -- UnixTime {utSeconds = 98, utMicroSeconds = 999000}- addUnixDiffTime :: UnixTime -> UnixDiffTime -> UnixTime-addUnixDiffTime (UnixTime s1 u1) (UnixDiffTime s2 u2) = calcU (s1+s2) (u1+u2)+addUnixDiffTime (UnixTime s1 u1) (UnixDiffTime s2 u2) = calcU (s1 + s2) (u1 + u2)  -- | Creating difference from seconds. ----- >>> secondsToUnixDiffTime 100+-- >>> secondsToUnixDiffTime (100 :: Int) -- UnixDiffTime {udtSeconds = 100, udtMicroSeconds = 0}--secondsToUnixDiffTime :: (Integral a) => a -> UnixDiffTime+secondsToUnixDiffTime :: Integral a => a -> UnixDiffTime secondsToUnixDiffTime sec = UnixDiffTime (fromIntegral sec) 0 {-# INLINE secondsToUnixDiffTime #-}  -- | Creating difference from micro seconds. ----- >>> microSecondsToUnixDiffTime 12345678+-- >>> microSecondsToUnixDiffTime (12345678 :: Int) -- UnixDiffTime {udtSeconds = 12, udtMicroSeconds = 345678} ----- >>> microSecondsToUnixDiffTime (-12345678)+-- >>> microSecondsToUnixDiffTime ((-12345678) :: Int) -- UnixDiffTime {udtSeconds = -12, udtMicroSeconds = -345678}--microSecondsToUnixDiffTime :: (Integral a) => a -> UnixDiffTime+microSecondsToUnixDiffTime :: Integral a => a -> UnixDiffTime microSecondsToUnixDiffTime usec = calc (fromIntegral s) (fromIntegral u)   where-    (s,u) = secondMicro usec+    (s, u) = secondMicro usec {-# INLINE microSecondsToUnixDiffTime #-}  ----------------------------------------------------------------  adjust :: CTime -> Int32 -> (CTime, Int32) adjust sec usec-  | sec >= 0  = ajp-  | otherwise = ajm+    | sec >= 0 = ajp+    | otherwise = ajm   where-    micro  = 1000000-    mmicro = - micro+    micro = 1000000+    mmicro = -micro     ajp-     | usec >= micro  = (sec + 1, usec - micro)-     | usec >= 0      = (sec, usec)-     | otherwise      = (sec - 1, usec + micro)+        | usec >= micro = (sec + 1, usec - micro)+        | usec >= 0 = (sec, usec)+        | otherwise = (sec - 1, usec + micro)     ajm-     | usec <= mmicro = (sec - 1, usec + micro)-     | usec <= 0      = (sec, usec)-     | otherwise      = (sec + 1, usec - micro)+        | usec <= mmicro = (sec - 1, usec + micro)+        | usec <= 0 = (sec, usec)+        | otherwise = (sec + 1, usec - micro)  slowAdjust :: CTime -> Int32 -> (CTime, Int32) slowAdjust sec usec = (sec + fromIntegral s, usec - u)   where-    (s,u) = secondMicro usec+    (s, u) = secondMicro usec -secondMicro :: Integral a => a -> (a,a)+secondMicro :: Integral a => a -> (a, a) secondMicro usec = usec `quotRem` 1000000  toFractional :: Fractional a => UnixDiffTime -> a
Data/UnixTime/Sys.hsc view
@@ -1,3 +1,4 @@+{-# LANGUAGE CApiFFI #-} {-# LANGUAGE ForeignFunctionInterface #-}  module Data.UnixTime.Sys (getUnixTime) where@@ -17,7 +18,7 @@ type CTimeVal = () type CTimeZone = () -foreign import ccall unsafe "gettimeofday"+foreign import capi unsafe "sys/time.h gettimeofday"     c_gettimeofday :: Ptr CTimeVal -> Ptr CTimeZone -> IO CInt  -- |@@ -26,6 +27,4 @@ getUnixTime :: IO UnixTime getUnixTime = allocaBytes (#const sizeof(struct timeval)) $ \ p_timeval -> do     throwErrnoIfMinus1_ "getClockTime" $ c_gettimeofday p_timeval nullPtr-    sec  <- (#peek struct timeval,tv_sec)  p_timeval-    usec <- (#peek struct timeval,tv_usec) p_timeval-    return $ UnixTime sec usec+    peek (castPtr p_timeval)
Data/UnixTime/Types.hsc view
@@ -1,14 +1,20 @@+{-# LANGUAGE DeriveGeneric #-}+ module Data.UnixTime.Types where  import Control.Applicative ((<$>), (<*>)) import Data.ByteString import Data.ByteString.Char8 () import Data.Int+#if defined(_WIN32)+import Data.Word+#endif import Foreign.C.Types import Foreign.Storable #if __GLASGOW_HASKELL__ >= 704 import Data.Binary #endif+import GHC.Generics  #include <sys/time.h> @@ -29,17 +35,59 @@     utSeconds :: {-# UNPACK #-} !CTime     -- | Micro seconds (i.e. 10^(-6))   , utMicroSeconds :: {-# UNPACK #-} !Int32-  } deriving (Eq,Ord,Show)+  } deriving (Eq,Ord,Show,Generic)  instance Storable UnixTime where     sizeOf _    = (#size struct timeval)     alignment _ = (#const offsetof(struct {char x__; struct timeval (y__); }, y__))-    peek ptr    = UnixTime-            <$> (#peek struct timeval, tv_sec)  ptr-            <*> (#peek struct timeval, tv_usec) ptr+#if defined(_WIN32)+    -- On windows, with mingw-w64, the struct `timeval` is defined as+    --+    --      struct timeval+    --      {+    --          long tv_sec;+    --          long tv_usec;+    --      };+    --+    -- The type `long` is 32bit on windows 64bit, however the CTime is 64bit, thus+    -- we must be careful about the layout of its foreign memory.+    --+    -- Here we try use Word32, rather than Int32, to support as large value as possible.+    -- For example, we use `4294967295` as utSeconds in testsuite, which already exceeds+    -- the range of Int32, but not for Word32.+    peek ptr    = do+            sec <- (#peek struct timeval, tv_sec) ptr :: IO Word32+            msec <- (#peek struct timeval, tv_usec) ptr+            return $ UnixTime (fromIntegral sec) msec     poke ptr ut = do-            (#poke struct timeval, tv_sec)  ptr (utSeconds ut)+            let CTime sec = utSeconds ut+            (#poke struct timeval, tv_sec)  ptr (fromIntegral sec :: Word32)             (#poke struct timeval, tv_usec) ptr (utMicroSeconds ut)+#else+    -- On Unix, the struct `timeval` is defined as+    --+    --      struct timeval+    --      {+    --          time_t      tv_sec;+    --          suseconds_t tv_usec;+    --      };+    --+    -- The type `suseconds_t` is a signed integer type capable of storing+    -- values at least in the range `[-1, 1000000]`. It's size is platform+    -- specific, and it is 8 bytes long on 64-bit platforms.+    --+    -- Here we peek `tv_usec` using the `CSUSeconds` type and then convert it+    -- to `Int32` (the type of `utMicroSeconds`) relying on the fact that+    -- `tv_usec` is no bigger than `1000000`, and hence we will not overflow.+    peek ptr    = do+            sec <- (#peek struct timeval, tv_sec) ptr+            CSUSeconds msec <- (#peek struct timeval, tv_usec) ptr+            return $ UnixTime sec (fromIntegral msec)+    poke ptr ut = do+            let msec = CSUSeconds $ fromIntegral (utMicroSeconds ut)+            (#poke struct timeval, tv_sec)  ptr (utSeconds ut)+            (#poke struct timeval, tv_usec) ptr msec+#endif  #if __GLASGOW_HASKELL__ >= 704 instance Binary UnixTime where@@ -65,7 +113,7 @@ -- create values that are in-range. This avoids any gotchas when then -- doing comparisons. data UnixDiffTime = UnixDiffTime {-    -- | Seconds from 1st Jan 1970+    -- | Seconds     udtSeconds :: {-# UNPACK #-} !CTime     -- | Micro seconds (i.e. 10^(-6))   , udtMicroSeconds :: {-# UNPACK #-} !Int32
Setup.hs view
@@ -1,3 +1,6 @@+module Main (main) where+ import Distribution.Simple +main :: IO () main = defaultMainWithHooks autoconfUserHooks
cbits/config.h.in view
@@ -1,5 +1,9 @@ /* cbits/config.h.in.  Generated from configure.ac by autoheader.  */ +/* Define to 1 if you have the declaration of `_mkgmtime', and to 0 if you+   don't. */+#undef HAVE_DECL__MKGMTIME+ /* Define to 1 if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H @@ -21,6 +25,12 @@ /* Define to 1 if you have the `strptime_l' function. */ #undef HAVE_STRPTIME_L +/* Define to 1 if you have the `strtoll_l' function. */+#undef HAVE_STRTOLL_L++/* Define to 1 if you have the `strtol_l' function. */+#undef HAVE_STRTOL_L+ /* Define to 1 if you have the <sys/stat.h> header file. */ #undef HAVE_SYS_STAT_H @@ -36,8 +46,29 @@ /* Define to 1 if you have the <xlocale.h> header file. */ #undef HAVE_XLOCALE_H +/* Define to 1 if you have the `_create_locale' function. */+#undef HAVE__CREATE_LOCALE++/* Define to 1 if you have the `_get_current_locale' function. */+#undef HAVE__GET_CURRENT_LOCALE++/* Define to 1 if you have the `_isblank_l' function. */+#undef HAVE__ISBLANK_L++/* Define to 1 if you have the `_isdigit_l' function. */+#undef HAVE__ISDIGIT_L++/* Define to 1 if you have the `_isspace_l' function. */+#undef HAVE__ISSPACE_L++/* Define to 1 if you have the `_isupper_l' function. */+#undef HAVE__ISUPPER_L+ /* "Is Linux" */ #undef IS_LINUX++/* "Is Windows NT 6.1" */+#undef IS_NT61  /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT
cbits/conv.c view
@@ -4,6 +4,7 @@ /* Linux cheats AC_CHECK_FUNCS(strptime_l), sigh. */ #define THREAD_SAFE 0 #define _XOPEN_SOURCE+#define _DEFAULT_SOURCE #define _BSD_SOURCE #elif HAVE_STRPTIME_L #define THREAD_SAFE 1@@ -16,7 +17,12 @@ #include <time.h> #include <locale.h> #include <stdlib.h>+#include <stdio.h> +#if defined(_WIN32)+#include "win_patch.h"+#endif // _WIN32+ #if THREAD_SAFE #if HAVE_XLOCALE_H #include <xlocale.h>@@ -32,7 +38,7 @@     static int initialized = 0;     if (initialized == 0) {         setlocale(LC_TIME, "C");-	initialized == 1;+        initialized = 1;     } } #endif@@ -44,8 +50,14 @@ char *set_tz_utc() {     char *tz;     tz = getenv("TZ");+#if defined(_WIN32)+    _patch_setenv("TZ", "UTC", 1);+#else     setenv("TZ", "", 1);+#endif+#if defined(HAVE_TZSET)     tzset();+#endif     return tz; } @@ -55,11 +67,21 @@  */ void set_tz(char *local_tz) {     if (local_tz) {+#if defined(_WIN32)+      _patch_setenv("TZ", local_tz, 1);+#else       setenv("TZ", local_tz, 1);+#endif     } else {+#if defined(_WIN32)+      _patch_unsetenv("TZ");+#else       unsetenv("TZ");+#endif     }+#if defined(HAVE_TZSET)     tzset();+#endif }  time_t c_parse_unix_time(char *fmt, char *src) {@@ -83,13 +105,6 @@  * All rights reserved.  */ -static int-is_leap(unsigned y)-{-  y += 1900;-  return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0);-}- static time_t timegm (struct tm *tm) {@@ -100,10 +115,10 @@   unsigned i;    for (i = 70; i < tm->tm_year; ++i)-    res += is_leap(i) ? 366 : 365;+    res += isleap(i + 1900) ? 366 : 365;    for (i = 0; i < tm->tm_mon; ++i)-    res += ndays[is_leap(tm->tm_year)][i];+    res += ndays[isleap(tm->tm_year + 1900)][i];   res += tm->tm_mday - 1;   res *= 24;   res += tm->tm_hour;@@ -135,9 +150,17 @@     init_locale();     localtime_r(&src, &tim); #if THREAD_SAFE+#if defined(_WIN32)+    return _patch_strftime_l(dst, siz, fmt, &tim, c_locale);+#else     return strftime_l(dst, siz, fmt, &tim, c_locale);+#endif // _WIN32 #else+#if defined(_WIN32)+    return _patch_strftime(dst, siz, fmt, &tim);+#else     return strftime(dst, siz, fmt, &tim);+#endif // _WIN32 #endif } @@ -151,9 +174,17 @@      local_tz = set_tz_utc(); #if THREAD_SAFE+#if defined(_WIN32)+    dst_size = _patch_strftime_l(dst, siz, fmt, &tim, c_locale);+#else     dst_size = strftime_l(dst, siz, fmt, &tim, c_locale);+#endif // _WIN32 #else+#if defined(_WIN32)+    dst_size = _patch_strftime(dst, siz, fmt, &tim);+#else     dst_size = strftime(dst, siz, fmt, &tim);+#endif // _WIN32 #endif     set_tz(local_tz);     return dst_size;
+ cbits/strftime.c view
@@ -0,0 +1,632 @@+/*+ * Copyright (c) 1989 The Regents of the University of California.+ * All rights reserved.+ *+ * Copyright (c) 2011 The FreeBSD Foundation+ * All rights reserved.+ * Portions of this software were developed by David Chisnall+ * under sponsorship from the FreeBSD Foundation.+ *+ * Redistribution and use in source and binary forms are permitted+ * provided that the above copyright notice and this paragraph are+ * duplicated in all such forms and that any documentation,+ * advertising materials, and other materials related to such+ * distribution and use acknowledge that the software was developed+ * by the University of California, Berkeley. The name of the+ * University may not be used to endorse or promote products derived+ * from this software without specific prior written permission.+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.+ */++#include <fcntl.h>+#include <sys/cdefs.h>+#include <sys/stat.h>++#include <time.h>+#include <ctype.h>+#include <errno.h>+#include <locale.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <pthread.h>++#include "win_patch.h"++static char *	_add(const char *, char *, const char *);+static char *	_conv(int, const char *, char *, const char *, _locale_t);+static char *	_fmt(const char *, const struct tm *, char *, const char *,+			int *, _locale_t);+static char *	_yconv(int, int, int, int, char *, const char *, _locale_t);++#ifndef YEAR_2000_NAME+#define YEAR_2000_NAME	"CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"+#endif /* !defined YEAR_2000_NAME */++#define	IN_NONE	0+#define	IN_SOME	1+#define	IN_THIS	2+#define	IN_ALL	3++#define	PAD_DEFAULT	0+#define	PAD_LESS	1+#define	PAD_SPACE	2+#define	PAD_ZERO	3++static const char fmt_padding[][4][5] = {+	/* DEFAULT,	LESS,	SPACE,	ZERO */+#define	PAD_FMT_MONTHDAY	0+#define	PAD_FMT_HMS		0+#define	PAD_FMT_CENTURY		0+#define	PAD_FMT_SHORTYEAR	0+#define	PAD_FMT_MONTH		0+#define	PAD_FMT_WEEKOFYEAR	0+#define	PAD_FMT_DAYOFMONTH	0+	{ "%02d",	"%d",	"%2d",	"%02d" },+#define	PAD_FMT_SDAYOFMONTH	1+#define	PAD_FMT_SHMS		1+	{ "%2d",	"%d",	"%2d",	"%02d" },+#define	PAD_FMT_DAYOFYEAR	2+	{ "%03d",	"%d",	"%3d",	"%03d" },+#define	PAD_FMT_YEAR		3+	{ "%04d",	"%d",	"%4d",	"%04d" }+};++size_t+_patch_strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format,+    const struct tm * __restrict t, _locale_t loc)+{+	char *	p;+	int	warn;++	tzset();+	warn = IN_NONE;+	p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn, loc);+#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU+	if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {+		(void) fprintf_l(stderr, loc, "\n");+		if (format == NULL)+			(void) fputs("NULL strftime format ", stderr);+		else	(void) fprintf_l(stderr, loc, "strftime format \"%s\" ",+				format);+		(void) fputs("yields only two digits of years in ", stderr);+		if (warn == IN_SOME)+			(void) fputs("some locales", stderr);+		else if (warn == IN_THIS)+			(void) fputs("the current locale", stderr);+		else	(void) fputs("all locales", stderr);+		(void) fputs("\n", stderr);+	}+#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */+	if (p == s + maxsize)+		return (0);+	*p = '\0';+	return p - s;+}++size_t+_patch_strftime(char * __restrict s, size_t maxsize, const char * __restrict format,+    const struct tm * __restrict t)+{+#if HAVE__GET_CURRENT_LOCALE+	return _patch_strftime_l(s, maxsize, format, t, _get_current_locale());+#elif HAVE__CREATE_LOCALE && !IS_NT61+	return _patch_strftime_l(s, maxsize, format, t, _create_locale(LC_TIME, "C"));+#else+	_locale_t locale;+	return _patch_strftime_l(s, maxsize, format, t, locale);+#endif+}++static char *+_fmt(const char *format, const struct tm * const t, char *pt,+    const char * const ptlim, int *warnp, _locale_t loc)+{+	int Ealternative, Oalternative, PadIndex;+	const struct lc_time_T *tptr = &_C_time_locale;++	for ( ; *format; ++format) {+		if (*format == '%') {+			Ealternative = 0;+			Oalternative = 0;+			PadIndex	 = PAD_DEFAULT;+label:+			switch (*++format) {+			case '\0':+				--format;+				break;+			case 'A':+				pt = _add((t->tm_wday < 0 ||+					t->tm_wday >= DAYSPERWEEK) ?+					"?" : tptr->weekday[t->tm_wday],+					pt, ptlim);+				continue;+			case 'a':+				pt = _add((t->tm_wday < 0 ||+					t->tm_wday >= DAYSPERWEEK) ?+					"?" : tptr->wday[t->tm_wday],+					pt, ptlim);+				continue;+			case 'B':+				pt = _add((t->tm_mon < 0 ||+					t->tm_mon >= MONSPERYEAR) ?+					"?" : (Oalternative ? tptr->alt_month :+					tptr->month)[t->tm_mon],+					pt, ptlim);+				continue;+			case 'b':+			case 'h':+				pt = _add((t->tm_mon < 0 ||+					t->tm_mon >= MONSPERYEAR) ?+					"?" : tptr->mon[t->tm_mon],+					pt, ptlim);+				continue;+			case 'C':+				/*+				 * %C used to do a...+				 *	_fmt("%a %b %e %X %Y", t);+				 * ...whereas now POSIX 1003.2 calls for+				 * something completely different.+				 * (ado, 1993-05-24)+				 */+				pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,+					pt, ptlim, loc);+				continue;+			case 'c':+				{+				int warn2 = IN_SOME;++				pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2, loc);+				if (warn2 == IN_ALL)+					warn2 = IN_THIS;+				if (warn2 > *warnp)+					*warnp = warn2;+				}+				continue;+			case 'D':+				pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp, loc);+				continue;+			case 'd':+				pt = _conv(t->tm_mday,+					fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'E':+				if (Ealternative || Oalternative)+					break;+				Ealternative++;+				goto label;+			case 'O':+				/*+				 * C99 locale modifiers.+				 * The sequences+				 *	%Ec %EC %Ex %EX %Ey %EY+				 *	%Od %oe %OH %OI %Om %OM+				 *	%OS %Ou %OU %OV %Ow %OW %Oy+				 * are supposed to provide alternate+				 * representations.+				 *+				 * FreeBSD extension+				 *      %OB+				 */+				if (Ealternative || Oalternative)+					break;+				Oalternative++;+				goto label;+			case 'e':+				pt = _conv(t->tm_mday,+					fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'F':+				pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp, loc);+				continue;+			case 'H':+				pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'I':+				pt = _conv((t->tm_hour % 12) ?+					(t->tm_hour % 12) : 12,+					fmt_padding[PAD_FMT_HMS][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'j':+				pt = _conv(t->tm_yday + 1,+					fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'k':+				/*+				 * This used to be...+				 *	_conv(t->tm_hour % 12 ?+				 *		t->tm_hour % 12 : 12, 2, ' ');+				 * ...and has been changed to the below to+				 * match SunOS 4.1.1 and Arnold Robbins'+				 * strftime version 3.0. That is, "%k" and+				 * "%l" have been swapped.+				 * (ado, 1993-05-24)+				 */+				pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex],+					pt, ptlim, loc);+				continue;+#ifdef KITCHEN_SINK+			case 'K':+				/*+				** After all this time, still unclaimed!+				*/+				pt = _add("kitchen sink", pt, ptlim);+				continue;+#endif /* defined KITCHEN_SINK */+			case 'l':+				/*+				 * This used to be...+				 *	_conv(t->tm_hour, 2, ' ');+				 * ...and has been changed to the below to+				 * match SunOS 4.1.1 and Arnold Robbin's+				 * strftime version 3.0. That is, "%k" and+				 * "%l" have been swapped.+				 * (ado, 1993-05-24)+				 */+				pt = _conv((t->tm_hour % 12) ?+					(t->tm_hour % 12) : 12,+					fmt_padding[PAD_FMT_SHMS][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'M':+				pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'm':+				pt = _conv(t->tm_mon + 1,+					fmt_padding[PAD_FMT_MONTH][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'n':+				pt = _add("\n", pt, ptlim);+				continue;+			case 'p':+				pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?+					tptr->pm : tptr->am,+					pt, ptlim);+				continue;+			case 'R':+				pt = _fmt("%H:%M", t, pt, ptlim, warnp, loc);+				continue;+			case 'r':+				pt = _fmt(tptr->ampm_fmt, t, pt, ptlim,+					warnp, loc);+				continue;+			case 'S':+				pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex],+					pt, ptlim, loc);+				continue;+			case 's':+				{+					struct tm	tm;+					char		buf[INT_STRLEN_MAXIMUM(+								time_t) + 1];+					time_t		mkt;++					tm = *t;+					mkt = mktime(&tm);+					if (TYPE_SIGNED(time_t))+						(void) sprintf_l(buf, loc, "%lld", (long long)mkt);+					else+						(void) sprintf_l(buf, loc, "%llu", (unsigned long long)mkt);+					pt = _add(buf, pt, ptlim);+				}+				continue;+			case 'T':+				pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp, loc);+				continue;+			case 't':+				pt = _add("\t", pt, ptlim);+				continue;+			case 'U':+				pt = _conv((t->tm_yday + DAYSPERWEEK -+					t->tm_wday) / DAYSPERWEEK,+					fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'u':+				/*+				 * From Arnold Robbins' strftime version 3.0:+				 * "ISO 8601: Weekday as a decimal number+				 * [1 (Monday) - 7]"+				 * (ado, 1993-05-24)+				 */+				pt = _conv((t->tm_wday == 0) ?+					DAYSPERWEEK : t->tm_wday,+					"%d", pt, ptlim, loc);+				continue;+			case 'V':	/* ISO 8601 week number */+			case 'G':	/* ISO 8601 year (four digits) */+			case 'g':	/* ISO 8601 year (two digits) */+/*+ * From Arnold Robbins' strftime version 3.0: "the week number of the+ * year (the first Monday as the first day of week 1) as a decimal number+ * (01-53)."+ * (ado, 1993-05-24)+ *+ * From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:+ * "Week 01 of a year is per definition the first week which has the+ * Thursday in this year, which is equivalent to the week which contains+ * the fourth day of January. In other words, the first week of a new year+ * is the week which has the majority of its days in the new year. Week 01+ * might also contain days from the previous year and the week before week+ * 01 of a year is the last week (52 or 53) of the previous year even if+ * it contains days from the new year. A week starts with Monday (day 1)+ * and ends with Sunday (day 7). For example, the first week of the year+ * 1997 lasts from 1996-12-30 to 1997-01-05..."+ * (ado, 1996-01-02)+ */+				{+					int	year;+					int	base;+					int	yday;+					int	wday;+					int	w;++					year = t->tm_year;+					base = TM_YEAR_BASE;+					yday = t->tm_yday;+					wday = t->tm_wday;+					for ( ; ; ) {+						int	len;+						int	bot;+						int	top;++						len = isleap_sum(year, base) ?+							DAYSPERLYEAR :+							DAYSPERNYEAR;+						/*+						 * What yday (-3 ... 3) does+						 * the ISO year begin on?+						 */+						bot = ((yday + 11 - wday) %+							DAYSPERWEEK) - 3;+						/*+						 * What yday does the NEXT+						 * ISO year begin on?+						 */+						top = bot -+							(len % DAYSPERWEEK);+						if (top < -3)+							top += DAYSPERWEEK;+						top += len;+						if (yday >= top) {+							++base;+							w = 1;+							break;+						}+						if (yday >= bot) {+							w = 1 + ((yday - bot) /+								DAYSPERWEEK);+							break;+						}+						--base;+						yday += isleap_sum(year, base) ?+							DAYSPERLYEAR :+							DAYSPERNYEAR;+					}+#ifdef XPG4_1994_04_09+					if ((w == 52 &&+						t->tm_mon == TM_JANUARY) ||+						(w == 1 &&+						t->tm_mon == TM_DECEMBER))+							w = 53;+#endif /* defined XPG4_1994_04_09 */+					if (*format == 'V')+						pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],+							pt, ptlim, loc);+					else if (*format == 'g') {+						*warnp = IN_ALL;+						pt = _yconv(year, base, 0, 1,+							pt, ptlim, loc);+					} else	pt = _yconv(year, base, 1, 1,+							pt, ptlim, loc);+				}+				continue;+			case 'v':+				/*+				 * From Arnold Robbins' strftime version 3.0:+				 * "date as dd-bbb-YYYY"+				 * (ado, 1993-05-24)+				 */+				pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp, loc);+				continue;+			case 'W':+				pt = _conv((t->tm_yday + DAYSPERWEEK -+					(t->tm_wday ?+					(t->tm_wday - 1) :+					(DAYSPERWEEK - 1))) / DAYSPERWEEK,+					fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],+					pt, ptlim, loc);+				continue;+			case 'w':+				pt = _conv(t->tm_wday, "%d", pt, ptlim, loc);+				continue;+			case 'X':+				pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp, loc);+				continue;+			case 'x':+				{+				int	warn2 = IN_SOME;++				pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2, loc);+				if (warn2 == IN_ALL)+					warn2 = IN_THIS;+				if (warn2 > *warnp)+					*warnp = warn2;+				}+				continue;+			case 'y':+				*warnp = IN_ALL;+				pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,+					pt, ptlim, loc);+				continue;+			case 'Y':+				pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,+					pt, ptlim, loc);+				continue;+			case 'Z':+#ifdef TM_ZONE+				if (t->TM_ZONE != NULL)+					pt = _add(t->TM_ZONE, pt, ptlim);+				else+#endif /* defined TM_ZONE */+				if (t->tm_isdst >= 0)+					pt = _add(_tzname[t->tm_isdst != 0],+						pt, ptlim);+				/*+				 * C99 says that %Z must be replaced by the+				 * empty string if the time zone is not+				 * determinable.+				 */+				continue;+			case 'z':+				{+				int		diff;+				char const *	sign;++				if (t->tm_isdst < 0)+					continue;+#ifdef TM_GMTOFF+				diff = t->TM_GMTOFF;+#else /* !defined TM_GMTOFF */+				/*+				 * C99 says that the UTC offset must+				 * be computed by looking only at+				 * tm_isdst. This requirement is+				 * incorrect, since it means the code+				 * must rely on magic (in this case+				 * altzone and timezone), and the+				 * magic might not have the correct+				 * offset. Doing things correctly is+				 * tricky and requires disobeying C99;+				 * see GNU C strftime for details.+				 * For now, punt and conform to the+				 * standard, even though it's incorrect.+				 *+				 * C99 says that %z must be replaced by the+				 * empty string if the time zone is not+				 * determinable, so output nothing if the+				 * appropriate variables are not available.+				 */+				if (t->tm_isdst == 0)+					diff = -_timezone;+				else+#ifdef ALTZONE+					diff = -altzone;+#else /* !defined ALTZONE */+					// Fix the daylight saving time, see #54.+					diff = -(_timezone - 3600 * t->tm_isdst);+#endif /* !defined ALTZONE */+#endif /* !defined TM_GMTOFF */+				if (diff < 0) {+					sign = "-";+					diff = -diff;+				} else+					sign = "+";+				pt = _add(sign, pt, ptlim);+				diff /= SECSPERMIN;+				diff = (diff / MINSPERHOUR) * 100 ++					(diff % MINSPERHOUR);+				pt = _conv(diff,+					fmt_padding[PAD_FMT_YEAR][PadIndex],+					pt, ptlim, loc);+				}+				continue;+			case '+':+				pt = _fmt(tptr->date_fmt, t, pt, ptlim,+					warnp, loc);+				continue;+			case '-':+				if (PadIndex != PAD_DEFAULT)+					break;+				PadIndex = PAD_LESS;+				goto label;+			case '_':+				if (PadIndex != PAD_DEFAULT)+					break;+				PadIndex = PAD_SPACE;+				goto label;+			case '0':+				if (PadIndex != PAD_DEFAULT)+					break;+				PadIndex = PAD_ZERO;+				goto label;+			case '%':+			/*+			 * X311J/88-090 (4.12.3.5): if conversion char is+			 * undefined, behavior is undefined. Print out the+			 * character itself as printf(3) also does.+			 */+			default:+				break;+			}+		}+		if (pt == ptlim)+			break;+		*pt++ = *format;+	}+	return (pt);+}++static char *+_conv(const int n, const char * const format, char * const pt,+    const char * const ptlim, _locale_t  loc)+{+	char	buf[INT_STRLEN_MAXIMUM(int) + 1];++	(void) sprintf_l(buf, loc, format, n);+	return _add(buf, pt, ptlim);+}++static char *+_add(const char *str, char *pt, const char * const ptlim)+{+	while (pt < ptlim && (*pt = *str++) != '\0')+		++pt;+	return (pt);+}++/*+ * POSIX and the C Standard are unclear or inconsistent about+ * what %C and %y do if the year is negative or exceeds 9999.+ * Use the convention that %C concatenated with %y yields the+ * same output as %Y, and that %Y contains at least 4 bytes,+ * with more only if necessary.+ */++static char *+_yconv(const int a, const int b, const int convert_top, const int convert_yy,+    char *pt, const char * const ptlim, _locale_t  loc)+{+	register int	lead;+	register int	trail;++#define	DIVISOR	100+	trail = a % DIVISOR + b % DIVISOR;+	lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;+	trail %= DIVISOR;+	if (trail < 0 && lead > 0) {+		trail += DIVISOR;+		--lead;+	} else if (lead < 0 && trail > 0) {+		trail -= DIVISOR;+		++lead;+	}+	if (convert_top) {+		if (lead == 0 && trail < 0)+			pt = _add("-0", pt, ptlim);+		else	pt = _conv(lead, "%02d", pt, ptlim, loc);+	}+	if (convert_yy)+		pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt,+		     ptlim, loc);+	return (pt);+}
+ cbits/strptime.c view
@@ -0,0 +1,721 @@+/*-+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD+ *+ * Copyright (c) 2014 Gary Mills+ * Copyright 2011, Nexenta Systems, Inc.  All rights reserved.+ * Copyright (c) 1994 Powerdog Industries.  All rights reserved.+ *+ * Copyright (c) 2011 The FreeBSD Foundation+ * All rights reserved.+ * Portions of this software were developed by David Chisnall+ * under sponsorship from the FreeBSD Foundation.+ *+ * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ *    notice, this list of conditions and the following disclaimer.+ * 2. 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.+ *+ * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``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 POWERDOG INDUSTRIES 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.+ *+ * The views and conclusions contained in the software and documentation+ * are those of the authors and should not be interpreted as representing+ * official policies, either expressed or implied, of Powerdog Industries.+ */++#include <sys/cdefs.h>++#include <time.h>+#include <ctype.h>+#include <errno.h>+#include <locale.h>+#include <stdlib.h>+#include <string.h>+#include <pthread.h>++#include "win_patch.h"++static char * _strptime(const char *, const char *, struct tm *, int *, _locale_t);++#define	asizeof(a)	(sizeof(a) / sizeof((a)[0]))++#define FLAG_NONE   (1 << 0)+#define FLAG_YEAR   (1 << 1)+#define FLAG_MONTH  (1 << 2)+#define FLAG_YDAY   (1 << 3)+#define FLAG_MDAY   (1 << 4)+#define FLAG_WDAY   (1 << 5)++/*+ * Calculate the week day of the first day of a year. Valid for+ * the Gregorian calendar, which began Sept 14, 1752 in the UK+ * and its colonies. Ref:+ * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week+ */++static int+first_wday_of(int year)+{+	return (((2 * (3 - (year / 100) % 4)) + (year % 100) ++		((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);+}++static char *+_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,+		_locale_t locale)+{+	char	c;+	const char *ptr;+	int	day_offset = -1, wday_offset;+	int week_offset;+	int	i, len;+	int flags;+	int Ealternative, Oalternative;+	int century, year;+	const struct lc_time_T *tptr = &_C_time_locale;+	static int start_of_month[2][13] = {+		{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},+		{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}+	};++	flags = FLAG_NONE;+	century = -1;+	year = -1;++	ptr = fmt;+	while (*ptr != 0) {+		c = *ptr++;++		if (c != '%') {+			if (isspace_l((unsigned char)c, locale))+				while (*buf != 0 && +				       isspace_l((unsigned char)*buf, locale))+					buf++;+			else if (c != *buf++)+				return (NULL);+			continue;+		}++		Ealternative = 0;+		Oalternative = 0;+label:+		c = *ptr++;+		switch (c) {+		case '%':+			if (*buf++ != '%')+				return (NULL);+			break;++		case '+':+			buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;+			break;++		case 'C':+			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			/* XXX This will break for 3-digit centuries. */+			len = 2;+			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++) {+				i *= 10;+				i += *buf - '0';+				len--;+			}++			century = i;+			flags |= FLAG_YEAR;++			break;++		case 'c':+			buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;+			break;++		case 'D':+			buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;+			break;++		case 'E':+			if (Ealternative || Oalternative)+				break;+			Ealternative++;+			goto label;++		case 'O':+			if (Ealternative || Oalternative)+				break;+			Oalternative++;+			goto label;++		case 'F':+			buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;+			break;++		case 'R':+			buf = _strptime(buf, "%H:%M", tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			break;++		case 'r':+			buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			break;++		case 'T':+			buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			break;++		case 'X':+			buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			break;++		case 'x':+			buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);+			if (buf == NULL)+				return (NULL);+			flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;+			break;++		case 'j':+			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			len = 3;+			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++){+				i *= 10;+				i += *buf - '0';+				len--;+			}+			if (i < 1 || i > 366)+				return (NULL);++			tm->tm_yday = i - 1;+			flags |= FLAG_YDAY;++			break;++		case 'M':+		case 'S':+			if (*buf == 0 ||+				isspace_l((unsigned char)*buf, locale))+				break;++			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			len = 2;+			for (i = 0; len && *buf != 0 &&+				isdigit_l((unsigned char)*buf, locale); buf++){+				i *= 10;+				i += *buf - '0';+				len--;+			}++			if (c == 'M') {+				if (i > 59)+					return (NULL);+				tm->tm_min = i;+			} else {+				if (i > 60)+					return (NULL);+				tm->tm_sec = i;+			}++			break;++		case 'H':+		case 'I':+		case 'k':+		case 'l':+			/*+			 * %k and %l specifiers are documented as being+			 * blank-padded.  However, there is no harm in+			 * allowing zero-padding.+			 *+			 * XXX %k and %l specifiers may gobble one too many+			 * digits if used incorrectly.+			 */++			len = 2;+			if ((c == 'k' || c == 'l') &&+			    isblank_l((unsigned char)*buf, locale)) {+				buf++;+				len = 1;+			}++			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++) {+				i *= 10;+				i += *buf - '0';+				len--;+			}+			if (c == 'H' || c == 'k') {+				if (i > 23)+					return (NULL);+			} else if (i == 0 || i > 12)+				return (NULL);++			tm->tm_hour = i;++			break;++		case 'p':+			/*+			 * XXX This is bogus if parsed before hour-related+			 * specifiers.+			 */+			if (tm->tm_hour > 12)+				return (NULL);++			len = strlen(tptr->am);+			if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {+				if (tm->tm_hour == 12)+					tm->tm_hour = 0;+				buf += len;+				break;+			}++			len = strlen(tptr->pm);+			if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {+				if (tm->tm_hour != 12)+					tm->tm_hour += 12;+				buf += len;+				break;+			}++			return (NULL);++		case 'A':+		case 'a':+			for (i = 0; i < asizeof(tptr->weekday); i++) {+				len = strlen(tptr->weekday[i]);+				if (strncasecmp_l(buf, tptr->weekday[i],+						len, locale) == 0)+					break;+				len = strlen(tptr->wday[i]);+				if (strncasecmp_l(buf, tptr->wday[i],+						len, locale) == 0)+					break;+			}+			if (i == asizeof(tptr->weekday))+				return (NULL);++			buf += len;+			tm->tm_wday = i;+			flags |= FLAG_WDAY;+			break;++		case 'U':+		case 'W':+			/*+			 * XXX This is bogus, as we can not assume any valid+			 * information present in the tm structure at this+			 * point to calculate a real value, so just check the+			 * range for now.+			 */+			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			len = 2;+			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++) {+				i *= 10;+				i += *buf - '0';+				len--;+			}+			if (i > 53)+				return (NULL);++			if (c == 'U')+				day_offset = TM_SUNDAY;+			else+				day_offset = TM_MONDAY;+++			week_offset = i;++			break;++		case 'u':+		case 'w':+			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			i = *buf++ - '0';+			if (i < 0 || i > 7 || (c == 'u' && i < 1) ||+			    (c == 'w' && i > 6))+				return (NULL);++			tm->tm_wday = i % 7;+			flags |= FLAG_WDAY;++			break;++		case 'e':+			/*+			 * With %e format, our strftime(3) adds a blank space+			 * before single digits.+			 */+			if (*buf != 0 &&+			    isspace_l((unsigned char)*buf, locale))+			       buf++;+			/* FALLTHROUGH */+		case 'd':+			/*+			 * The %e specifier was once explicitly documented as+			 * not being zero-padded but was later changed to+			 * equivalent to %d.  There is no harm in allowing+			 * such padding.+			 *+			 * XXX The %e specifier may gobble one too many+			 * digits if used incorrectly.+			 */+			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			len = 2;+			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++) {+				i *= 10;+				i += *buf - '0';+				len--;+			}+			if (i == 0 || i > 31)+				return (NULL);++			tm->tm_mday = i;+			flags |= FLAG_MDAY;++			break;++		case 'B':+		case 'b':+		case 'h':+			for (i = 0; i < asizeof(tptr->month); i++) {+				if (Oalternative) {+					if (c == 'B') {+						len = strlen(tptr->alt_month[i]);+						if (strncasecmp_l(buf,+								tptr->alt_month[i],+								len, locale) == 0)+							break;+					}+				} else {+					len = strlen(tptr->month[i]);+					if (strncasecmp_l(buf, tptr->month[i],+							len, locale) == 0)+						break;+				}+			}+			/*+			 * Try the abbreviated month name if the full name+			 * wasn't found and Oalternative was not requested.+			 */+			if (i == asizeof(tptr->month) && !Oalternative) {+				for (i = 0; i < asizeof(tptr->month); i++) {+					len = strlen(tptr->mon[i]);+					if (strncasecmp_l(buf, tptr->mon[i],+							len, locale) == 0)+						break;+				}+			}+			if (i == asizeof(tptr->month))+				return (NULL);++			tm->tm_mon = i;+			buf += len;+			flags |= FLAG_MONTH;++			break;++		case 'm':+			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			len = 2;+			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++) {+				i *= 10;+				i += *buf - '0';+				len--;+			}+			if (i < 1 || i > 12)+				return (NULL);++			tm->tm_mon = i - 1;+			flags |= FLAG_MONTH;++			break;++		case 's':+			{+			char *cp;+			int sverrno;+			long long n;+			time_t t;++			sverrno = errno;+			errno = 0;+			n = strtoll_l(buf, &cp, 10, locale);+			if (errno == ERANGE || (long long)(t = n) != n) {+				errno = sverrno;+				return (NULL);+			}+			errno = sverrno;+			buf = cp;+			if (gmtime_r(&t, tm) == NULL)+				return (NULL);+			*GMTp = 1;+			flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |+			    FLAG_MDAY | FLAG_YEAR;+			}+			break;++		case 'Y':+		case 'y':+			if (*buf == 0 ||+			    isspace_l((unsigned char)*buf, locale))+				break;++			if (!isdigit_l((unsigned char)*buf, locale))+				return (NULL);++			len = (c == 'Y') ? 4 : 2;+			for (i = 0; len && *buf != 0 &&+			     isdigit_l((unsigned char)*buf, locale); buf++) {+				i *= 10;+				i += *buf - '0';+				len--;+			}+			if (c == 'Y')+				century = i / 100;+			year = i % 100;++			flags |= FLAG_YEAR;++			break;++		case 'Z':+			{+			const char *cp;+			char *zonestr;++			for (cp = buf; *cp &&+			     isupper_l((unsigned char)*cp, locale); ++cp) {+				/*empty*/}+			if (cp - buf) {+				zonestr = alloca(cp - buf + 1);+				strncpy(zonestr, buf, cp - buf);+				zonestr[cp - buf] = '\0';+				tzset();+				if (0 == strcmp(zonestr, "GMT") ||+				    0 == strcmp(zonestr, "UTC")) {+				    *GMTp = 1;+				} else if (0 == strcmp(zonestr, _tzname[0])) {+				    tm->tm_isdst = 0;+				} else if (0 == strcmp(zonestr, _tzname[1])) {+				    tm->tm_isdst = 1;+				} else {+				    return (NULL);+				}+				buf += cp - buf;+			}+			}+			break;++		case 'z':+			{+			int sign = 1;++			if (*buf != '+') {+				if (*buf == '-')+					sign = -1;+				else+					return (NULL);+			}++			buf++;+			i = 0;+			for (len = 4; len > 0; len--) {+				if (isdigit_l((unsigned char)*buf, locale)) {+					i *= 10;+					i += *buf - '0';+					buf++;+				} else if (len == 2) {+					i *= 100;+					break;+				} else+					return (NULL);+			}++			if (i > 1400 || (sign == -1 && i > 1200) ||+			    (i % 100) >= 60)+				return (NULL);+			tm->tm_hour -= sign * (i / 100);+			tm->tm_min  -= sign * (i % 100);+			*GMTp = 1;+			}+			break;++		case 'n':+		case 't':+			while (isspace_l((unsigned char)*buf, locale))+				buf++;+			break;++		default:+			return (NULL);+		}+	}++	if (century != -1 || year != -1) {+		if (year == -1)+			year = 0;+		if (century == -1) {+			if (year < 69)+				year += 100;+		} else+			year += century * 100 - TM_YEAR_BASE;+		tm->tm_year = year;+	}++	if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {+		if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==+		    (FLAG_MONTH | FLAG_MDAY)) {+			tm->tm_yday = start_of_month[isleap(tm->tm_year ++			    TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);+			flags |= FLAG_YDAY;+		} else if (day_offset != -1) {+			int tmpwday, tmpyday, fwo;++			fwo = first_wday_of(tm->tm_year + TM_YEAR_BASE);+			/* No incomplete week (week 0). */+			if (week_offset == 0 && fwo == day_offset)+				return (NULL);++			/* Set the date to the first Sunday (or Monday)+			 * of the specified week of the year.+			 */+			tmpwday = (flags & FLAG_WDAY) ? tm->tm_wday :+			    day_offset;+			tmpyday = (7 - fwo + day_offset) % 7 ++			    (week_offset - 1) * 7 ++			    (tmpwday - day_offset + 7) % 7;+			/* Impossible yday for incomplete week (week 0). */+			if (tmpyday < 0) {+				if (flags & FLAG_WDAY)+					return (NULL);+				tmpyday = 0;+			}+			tm->tm_yday = tmpyday;+			flags |= FLAG_YDAY;+		}+	}++	if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {+		if (!(flags & FLAG_MONTH)) {+			i = 0;+			while (tm->tm_yday >=+			    start_of_month[isleap(tm->tm_year ++			    TM_YEAR_BASE)][i])+				i++;+			if (i > 12) {+				i = 1;+				tm->tm_yday -=+				    start_of_month[isleap(tm->tm_year ++				    TM_YEAR_BASE)][12];+				tm->tm_year++;+			}+			tm->tm_mon = i - 1;+			flags |= FLAG_MONTH;+		}+		if (!(flags & FLAG_MDAY)) {+			tm->tm_mday = tm->tm_yday -+			    start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]+			    [tm->tm_mon] + 1;+			flags |= FLAG_MDAY;+		}+		if (!(flags & FLAG_WDAY)) {+			i = 0;+			wday_offset = first_wday_of(tm->tm_year);+			while (i++ <= tm->tm_yday) {+				if (wday_offset++ >= 6)+					wday_offset = 0;+			}+			tm->tm_wday = wday_offset;+			flags |= FLAG_WDAY;+		}+	}++	return ((char *)buf);+}++char *+strptime_l(const char * __restrict buf, const char * __restrict fmt,+    struct tm * __restrict tm, _locale_t loc)+{+	char *ret;+	int gmt;+	// FIX_LOCALE(loc);++	gmt = 0;+	ret = _strptime(buf, fmt, tm, &gmt, loc);+	if (ret && gmt) {+		time_t t = timegm(tm);++		localtime_s(tm, &t);+	}++	return (ret);+}++char *+strptime(const char * __restrict buf, const char * __restrict fmt,+    struct tm * __restrict tm)+{+#if HAVE__GET_CURRENT_LOCALE+	return strptime_l(buf, fmt, tm, _get_current_locale());+#elif HAVE__CREATE_LOCALE && !IS_NT61+	return strptime_l(buf, fmt, tm, _create_locale(LC_TIME, "C"));+#else+	_locale_t locale;+	return strptime_l(buf, fmt, tm, locale);+#endif+}
+ cbits/win_patch.c view
@@ -0,0 +1,129 @@+#include "win_patch.h"++#if !HAVE_STRTOL_L+long strtol_l(const char *nptr, char **endptr, int base, _locale_t locale) {+    return strtol(nptr, endptr, base);+}+#endif++#if !HAVE_STRTOLL_L+long long strtoll_l(const char *nptr, char **endptr, int base, _locale_t locale) {+    return strtoll(nptr, endptr, base);+}+#endif++#if !HAVE__ISBLANK_L+inline int isblank_l( int c, _locale_t _loc)+{+    return ( c == ' ' || c == '\t' );+}+#endif++inline int strncasecmp_l(const char *s1, const char *s2, int len, _locale_t _loc) {+    return strncasecmp(s1, s2, len);+}++inline struct tm *gmtime_r(const time_t *_time_t, struct tm *_tm) {+    errno_t err = gmtime_s(_tm, _time_t);+    if (err) {+        return NULL;+    }+    return _tm;+}++inline struct tm *localtime_r(const time_t *_time_t, struct tm *_tm) {+    errno_t err = localtime_s(_tm, _time_t);+    if (err) {+        return NULL;+    }+    return _tm;+}++const struct lc_time_T   _C_time_locale = {+    {+        "Jan", "Feb", "Mar", "Apr", "May", "Jun",+        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"+    }, {+        "January", "February", "March", "April", "May", "June",+        "July", "August", "September", "October", "November", "December"+    }, {+        "Sun", "Mon", "Tue", "Wed",+        "Thu", "Fri", "Sat"+    }, {+        "Sunday", "Monday", "Tuesday", "Wednesday",+        "Thursday", "Friday", "Saturday"+    },++    /* X_fmt */+    "%H:%M:%S",++    /*+     * x_fmt+     * Since the C language standard calls for+     * "date, using locale's date format," anything goes.+     * Using just numbers (as here) makes Quakers happier;+     * it's also compatible with SVR4.+     */+    "%m/%d/%y",++    /*+     * c_fmt+     */+    "%a %b %e %H:%M:%S %Y",++    /* am */+    "AM",++    /* pm */+    "PM",++    /* date_fmt */+    "%a %b %e %H:%M:%S %Z %Y",+    +    /* alt_month+     * Standalone months forms for %OB+     */+    {+        "January", "February", "March", "April", "May", "June",+        "July", "August", "September", "October", "November", "December"+    },++    /* md_order+     * Month / day order in dates+     */+    "md",++    /* ampm_fmt+     * To determine 12-hour clock format time (empty, if N/A)+     */+    "%I:%M:%S %p"+};+++int _patch_setenv(const char *var, const char *val, int _ovr) {+    if (val == NULL) {+        return _patch_unsetenv(var);+    }+    int varlen = strlen(var);+    int vallen = strlen(val);+    int len = varlen + vallen + 2;+    char *sname = (char *)malloc(len);+    strcpy(sname, var);+    sname[varlen] = '=';+    strcpy(sname + varlen + 1, val);+    sname[varlen + vallen + 1] = '\0';+    int r = _putenv(sname);+    free(sname);+    return r;+}++int _patch_unsetenv(const char *name) {+    int len = strlen(name) + 2;+    char *sname = (char *)malloc(len);+    strcpy(sname, name);+    sname[len] = '=';+    sname[len + 1] = '\0';+    int r = _putenv(sname);+    free(sname);+    return r;+}
+ cbits/win_patch.h view
@@ -0,0 +1,177 @@+#ifndef UNIX_TIME_WIN_PATCH_H+#define UNIX_TIME_WIN_PATCH_H++#include "config.h"++#include <sys/cdefs.h>++#include <time.h>+#include <ctype.h>+#include <errno.h>+#include <locale.h>+#include <stdlib.h>+#include <string.h>+#include <pthread.h>++#if defined(_WIN32)+#include <windows.h>+#endif++#if !defined(TM_YEAR_BASE)+#define TM_YEAR_BASE 1900+#endif++#if !defined(TM_SUNDAY)+#define TM_SUNDAY   0+#endif++#if !defined(TM_MONDAY)+#define TM_MONDAY   1+#endif++#if !defined(DAYSPERLYEAR)+#define DAYSPERLYEAR    366+#endif++#if !defined(SECSPERMIN)+#define SECSPERMIN  60+#endif++#if !defined(MINSPERHOUR)+#define MINSPERHOUR 60+#endif++#if !defined(HOURSPERDAY)+#define HOURSPERDAY 24+#endif++#if !defined(TM_YEAR_BASE)+#define TM_YEAR_BASE    1900+#endif++#if !defined(MONSPERYEAR)+#define MONSPERYEAR 12+#endif++#if !defined(DAYSPERWEEK)+#define DAYSPERWEEK 7+#endif++#if !defined(DAYSPERNYEAR)+#define DAYSPERNYEAR    365+#endif++#ifndef TYPE_BIT+#define TYPE_BIT(type)  (sizeof (type) * CHAR_BIT)+#endif /* !defined TYPE_BIT */++#ifndef TYPE_SIGNED+#define TYPE_SIGNED(type) (((type) -1) < 0)+#endif /* !defined TYPE_SIGNED */++#ifndef INT_STRLEN_MAXIMUM+/*+** 302 / 1000 is log10(2.0) rounded up.+** Subtract one for the sign bit if the type is signed;+** add one for integer division truncation;+** add one more for a minus sign if the type is signed.+*/+#define INT_STRLEN_MAXIMUM(type) \+    ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \+    1 + TYPE_SIGNED(type))+#endif /* !defined INT_STRLEN_MAXIMUM */++#if HAVE__ISSPACE_L+#define isspace_l _isspace_l+#else+#define isspace_l(ch, locale) isspace(ch)+#endif++#if HAVE__ISUPPER_L+#define isupper_l _isupper_l+#else+#define isupper_l(ch, locale) isupper(ch)+#endif++#if HAVE__ISDIGIT_L+#define isdigit_l _isdigit_l+#else+#define isdigit_l(ch, locale) isdigit(ch)+#endif++#if !HAVE_STRTOL_L+long strtol_l(const char *nptr, char **endptr, int base, _locale_t locale);+#endif++#if !HAVE_STRTOLL_L+long long strtoll_l(const char *nptr, char **endptr, int base, _locale_t locale);+#endif++#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))++#ifndef isleap_sum+/*+** See tzfile.h for details on isleap_sum.+*/+#define isleap_sum(a, b)    isleap((a) % 400 + (b) % 400)+#endif /* !defined isleap_sum */++#if HAVE__ISBLANK_L+#define isblank_l _isblank_l+#ifndef _isblank_l+// Needed to avoid -Wimplicit-function-declaration warnings+int _isblank_l(int c, _locale_t  loc);+#endif+#else+int isblank_l( int c, _locale_t _loc);+#endif++int strncasecmp_l(const char *s1, const char *s2, int len, _locale_t _loc);++struct tm *gmtime_r(const time_t *_time_t, struct tm *_tm);++struct tm *localtime_r(const time_t *_time_t, struct tm *_tm);++#if HAVE_DECL__MKGMTIME+#define timegm _mkgmtime+#define HAVE_TIMEGM 1+#endif++#define fprintf_l(fp, loc, ...) fprintf(fp, ##__VA_ARGS__)+#define sprintf_l(buf, loc, ...) sprintf(buf, ##__VA_ARGS__)++struct lc_time_T {+    const char  *mon[12];+    const char  *month[12];+    const char  *wday[7];+    const char  *weekday[7];+    const char  *X_fmt;+    const char  *x_fmt;+    const char  *c_fmt;+    const char  *am;+    const char  *pm;+    const char  *date_fmt;+    const char  *alt_month[12];+    const char  *md_order;+    const char  *ampm_fmt;+};++extern const struct lc_time_T   _C_time_locale;++int _patch_setenv(const char *var, const char *val, int ovr);++int _patch_unsetenv(const char *name);++size_t _patch_strftime(char * __restrict s, size_t maxsize, const char * __restrict format,+    const struct tm * __restrict t);++size_t _patch_strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format,+    const struct tm * __restrict t, _locale_t loc);++char *strptime_l(const char * __restrict buf, const char * __restrict fmt,+    struct tm * __restrict tm, _locale_t loc);++char *strptime(const char * __restrict buf, const char * __restrict fmt,+    struct tm * __restrict tm);++#endif // UNIX_TIME_WIN_PATCH_H
configure view
@@ -651,6 +651,7 @@ docdir oldincludedir includedir+runstatedir localstatedir sharedstatedir sysconfdir@@ -721,6 +722,7 @@ sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var'+runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}'@@ -973,6 +975,15 @@   | -silent | --silent | --silen | --sile | --sil)     silent=yes ;; +  -runstatedir | --runstatedir | --runstatedi | --runstated \+  | --runstate | --runstat | --runsta | --runst | --runs \+  | --run | --ru | --r)+    ac_prev=runstatedir ;;+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \+  | --run=* | --ru=* | --r=*)+    runstatedir=$ac_optarg ;;+   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)     ac_prev=sbindir ;;   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \@@ -1110,7 +1121,7 @@ for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \ 		datadir sysconfdir sharedstatedir localstatedir includedir \ 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \-		libdir localedir mandir+		libdir localedir mandir runstatedir do   eval ac_val=\$$ac_var   # Remove trailing slashes.@@ -1263,6 +1274,7 @@   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]   --libdir=DIR            object code libraries [EPREFIX/lib]   --includedir=DIR        C header files [PREFIX/include]   --oldincludedir=DIR     C header files for non-gcc [/usr/include]@@ -1723,6 +1735,52 @@   eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno  } # ac_fn_c_check_func++# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES+# ---------------------------------------------+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR+# accordingly.+ac_fn_c_check_decl ()+{+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack+  as_decl_name=`echo $2|sed 's/ *(.*//'`+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }+if eval \${$3+:} false; then :+  $as_echo_n "(cached) " >&6+else+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext+/* end confdefs.h.  */+$4+int+main ()+{+#ifndef $as_decl_name+#ifdef __cplusplus+  (void) $as_decl_use;+#else+  (void) $as_decl_name;+#endif+#endif++  ;+  return 0;+}+_ACEOF+if ac_fn_c_try_compile "$LINENO"; then :+  eval "$3=yes"+else+  eval "$3=no"+fi+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+eval ac_res=\$$3+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5+$as_echo "$ac_res" >&6; }+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno++} # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake.@@ -3304,7 +3362,107 @@ fi done +ac_fn_c_check_decl "$LINENO" "_mkgmtime" "ac_cv_have_decl__mkgmtime" "#include <time.h>+"+if test "x$ac_cv_have_decl__mkgmtime" = xyes; then :+  ac_have_decl=1+else+  ac_have_decl=0+fi +cat >>confdefs.h <<_ACEOF+#define HAVE_DECL__MKGMTIME $ac_have_decl+_ACEOF++for ac_func in _get_current_locale+do :+  ac_fn_c_check_func "$LINENO" "_get_current_locale" "ac_cv_func__get_current_locale"+if test "x$ac_cv_func__get_current_locale" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE__GET_CURRENT_LOCALE 1+_ACEOF++fi+done++for ac_func in _create_locale+do :+  ac_fn_c_check_func "$LINENO" "_create_locale" "ac_cv_func__create_locale"+if test "x$ac_cv_func__create_locale" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE__CREATE_LOCALE 1+_ACEOF++fi+done++for ac_func in strtol_l+do :+  ac_fn_c_check_func "$LINENO" "strtol_l" "ac_cv_func_strtol_l"+if test "x$ac_cv_func_strtol_l" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE_STRTOL_L 1+_ACEOF++fi+done++for ac_func in strtoll_l+do :+  ac_fn_c_check_func "$LINENO" "strtoll_l" "ac_cv_func_strtoll_l"+if test "x$ac_cv_func_strtoll_l" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE_STRTOLL_L 1+_ACEOF++fi+done++for ac_func in _isspace_l+do :+  ac_fn_c_check_func "$LINENO" "_isspace_l" "ac_cv_func__isspace_l"+if test "x$ac_cv_func__isspace_l" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE__ISSPACE_L 1+_ACEOF++fi+done++for ac_func in _isupper_l+do :+  ac_fn_c_check_func "$LINENO" "_isupper_l" "ac_cv_func__isupper_l"+if test "x$ac_cv_func__isupper_l" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE__ISUPPER_L 1+_ACEOF++fi+done++for ac_func in _isdigit_l+do :+  ac_fn_c_check_func "$LINENO" "_isdigit_l" "ac_cv_func__isdigit_l"+if test "x$ac_cv_func__isdigit_l" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE__ISDIGIT_L 1+_ACEOF++fi+done++for ac_func in _isblank_l+do :+  ac_fn_c_check_func "$LINENO" "_isblank_l" "ac_cv_func__isblank_l"+if test "x$ac_cv_func__isblank_l" = xyes; then :+  cat >>confdefs.h <<_ACEOF+#define HAVE__ISBLANK_L 1+_ACEOF++fi+done++ host=`uname -a` case $host in   Linux*)@@ -3319,6 +3477,22 @@ cat >>confdefs.h <<_ACEOF #define IS_LINUX $LINUX _ACEOF+++case $host in+  MINGW??_NT-6.1*)+	NT61=1+	;;+  *)+	NT61=0+	;;+esac+++cat >>confdefs.h <<_ACEOF+#define IS_NT61 $NT61+_ACEOF+  cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure
configure.ac view
@@ -8,6 +8,15 @@  AC_CHECK_FUNCS(strptime_l) AC_CHECK_FUNCS(timegm)+AC_CHECK_DECLS([_mkgmtime], [], [], [[#include <time.h>]])+AC_CHECK_FUNCS(_get_current_locale)+AC_CHECK_FUNCS(_create_locale)+AC_CHECK_FUNCS(strtol_l)+AC_CHECK_FUNCS(strtoll_l)+AC_CHECK_FUNCS(_isspace_l)+AC_CHECK_FUNCS(_isupper_l)+AC_CHECK_FUNCS(_isdigit_l)+AC_CHECK_FUNCS(_isblank_l)  host=`uname -a` case $host in@@ -20,4 +29,16 @@ esac  AC_DEFINE_UNQUOTED(IS_LINUX,$LINUX,"Is Linux")++case $host in+  MINGW??_NT-6.1*)+	NT61=1+	;;+  *)+	NT61=0+	;;+esac++AC_DEFINE_UNQUOTED(IS_NT61,$NT61,"Is Windows NT 6.1")+ AC_OUTPUT
test/UnixTimeSpec.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE OverloadedStrings, FlexibleInstances, CPP #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-}  module UnixTimeSpec (main, spec) where@@ -9,9 +12,10 @@ import Data.Time import Data.Time.Clock.POSIX (posixSecondsToUTCTime) import Data.UnixTime-import Foreign.Ptr (Ptr) import Foreign.Marshal.Alloc (alloca)+import Foreign.Ptr (Ptr) import Foreign.Storable (peek, poke)+import qualified Language.Haskell.TH as TH (runIO) import Test.Hspec import Test.Hspec.QuickCheck (prop) import Test.QuickCheck hiding ((===))@@ -25,12 +29,13 @@  instance Arbitrary UnixTime where     arbitrary = do-        a <- choose (0,4294967295) :: Gen Int-        b <- choose (0,999999) :: Gen Int-        let ut = UnixTime {-                utSeconds = abs (fromIntegral a)-              , utMicroSeconds = fromIntegral b-              }+        a <- choose (0, 4294967295) :: Gen Int+        b <- choose (0, 999999) :: Gen Int+        let ut =+                UnixTime+                    { utSeconds = abs (fromIntegral a)+                    , utMicroSeconds = fromIntegral b+                    }         return ut  spec :: Spec@@ -46,32 +51,44 @@     describe "parseUnixTimeGMT & formatUnixTimeGMT" $ do         let (===) = (==) `on` utSeconds         prop "inverses the result" $ \ut ->-            let dt  = formatUnixTimeGMT webDateFormat ut-                ut' = parseUnixTimeGMT  webDateFormat dt+            let dt = formatUnixTimeGMT webDateFormat ut+                ut' = parseUnixTimeGMT webDateFormat dt                 dt' = formatUnixTimeGMT webDateFormat ut'-            in ut === ut' && dt == dt'+             in ut === ut' && dt == dt'         prop "inverses the result (2)" $ \ut ->             let str = formatUnixTimeGMT "%s" ut                 ut' = parseUnixTimeGMT "%s" str-            in ut === ut'+             in ut === ut'      describe "addUnixDiffTime & diffUnixTime" $         prop "invrses the result" $ \(ut0, ut1) ->             let ut0' = addUnixDiffTime ut1 $ diffUnixTime ut0 ut1                 ut1' = addUnixDiffTime ut0 $ diffUnixTime ut1 ut0-            in ut0' == ut0 && ut1' == ut1+             in ut0' == ut0 && ut1' == ut1      describe "UnixTime Storable instance" $         prop "peek . poke = id" $ \ut ->             let pokePeek :: Ptr UnixTime -> IO UnixTime                 pokePeek ptr = poke ptr ut >> peek ptr-            in shouldReturn (alloca pokePeek) ut+             in shouldReturn (alloca pokePeek) ut +    describe "getUnixTime" $ do+        it "works well" $ do+            x <- getUnixTime+            x `shouldBe` x+        it "should work in Template Haskell" $+            $( do+                time <- TH.runIO getUnixTime+                let b = time == time+                [|b|]+             )+                `shouldBe` True+ formatMailModel :: UTCTime -> TimeZone -> ByteString formatMailModel ut zone = BS.pack $ formatTime defaultTimeLocale fmt zt   where-   zt = utcToZonedTime zone ut-   fmt = BS.unpack mailDateFormat+    zt = utcToZonedTime zone ut+    fmt = BS.unpack mailDateFormat  toUTCTime :: UnixTime -> UTCTime toUTCTime = posixSecondsToUTCTime . realToFrac . toEpochTime
− test/doctests.hs
@@ -1,11 +0,0 @@-module Main where--import Test.DocTest--main :: IO ()-main = doctest [-    "-XOverloadedStrings"-  , "-idist/build"-  , "dist/build/cbits/conv.o"-  , "Data/UnixTime.hs"-  ]
unix-time.cabal view
@@ -1,59 +1,80 @@-Name:                   unix-time-Version:                0.3.8-Author:                 Kazu Yamamoto <kazu@iij.ad.jp>-Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>-License:                BSD3-License-File:           LICENSE-Synopsis:               Unix time parser/formatter and utilities-Description:            Fast parser\/formatter\/utilities for Unix time-Category:               Data-Cabal-Version:          >= 1.10-Build-Type:             Configure-Extra-Source-Files:     cbits/conv.c cbits/config.h.in configure configure.ac-Extra-Tmp-Files:        config.log config.status autom4te.cache cbits/config.h+cabal-version:      1.18+name:               unix-time+version:            0.5.0+license:            BSD3+license-file:       LICENSE+maintainer:         Kazu Yamamoto <kazu@iij.ad.jp>+author:             Kazu Yamamoto <kazu@iij.ad.jp>+synopsis:           Unix time parser/formatter and utilities+description:        Fast parser\/formatter\/utilities for Unix time+category:           Data+build-type:         Configure+extra-source-files:+    cbits/config.h.in+    cbits/conv.c+    cbits/strftime.c+    cbits/strptime.c+    cbits/win_patch.c+    cbits/win_patch.h+    configure+    configure.ac -Library-  Default-Language:     Haskell2010-  GHC-Options:          -Wall-  if impl(ghc >= 7.8)-    CC-Options:         -fPIC-  Exposed-Modules:      Data.UnixTime-  Other-Modules:        Data.UnixTime.Conv-                        Data.UnixTime.Diff-                        Data.UnixTime.Types-                        Data.UnixTime.Sys-  Build-Depends:        base >= 4 && < 5-                      , bytestring-                      , old-time-                      , binary-  C-Sources:            cbits/conv.c-  include-dirs:         cbits+extra-tmp-files:+    config.log+    config.status+    autom4te.cache+    cbits/config.h -Test-Suite doctest-  Type:                 exitcode-stdio-1.0-  Default-Language:     Haskell2010-  HS-Source-Dirs:       test-  Ghc-Options:          -threaded -Wall-  Main-Is:              doctests.hs-  Build-Depends:        base-                      , doctest >= 0.9.3+extra-doc-files:    ChangeLog.md -Test-Suite spec-  Type:                 exitcode-stdio-1.0-  Default-Language:     Haskell2010-  Hs-Source-Dirs:       test-  Ghc-Options:          -Wall-  Main-Is:              Spec.hs-  Other-Modules:        UnixTimeSpec-  Build-Depends:        base-                      , bytestring-                      , hspec >= 1.5-                      , old-locale-                      , old-time-                      , QuickCheck-                      , time-                      , unix-time+source-repository head+    type:     git+    location: https://github.com/kazu-yamamoto/unix-time -Source-Repository head-  Type:                 git-  Location:             https://github.com/kazu-yamamoto/unix-time+library+    exposed-modules:  Data.UnixTime+    build-tools:      hsc2hs >=0+    c-sources:        cbits/conv.c+    other-modules:+        Data.UnixTime.Conv+        Data.UnixTime.Diff+        Data.UnixTime.Types+        Data.UnixTime.Sys++    default-language: Haskell2010+    include-dirs:     cbits+    ghc-options:      -Wall+    build-depends:+        base >=4.4 && <5,+        bytestring,+        binary++    if impl(ghc >=7.8)+        cc-options: -fPIC++    if os(windows)+        if ((impl(ghc >=9.4.5) && !impl(ghc >=9.4.6)) || (impl(ghc >=9.6.1) && !impl(ghc >=9.6.3)))+            extra-libraries: mingwex++    if os(windows)+        c-sources:+            cbits/strftime.c+            cbits/strptime.c+            cbits/win_patch.c++test-suite spec+    type:             exitcode-stdio-1.0+    main-is:          Spec.hs+    build-tools:      hspec-discover >=2.6+    hs-source-dirs:   test+    other-modules:    UnixTimeSpec+    default-language: Haskell2010+    ghc-options:      -Wall+    build-depends:+        base,+        bytestring,+        QuickCheck,+        template-haskell,+        time,+        unix-time,+        hspec >=2.6