unix-time 0.3.8 → 0.5.0
raw patch · 18 files changed
Files
- ChangeLog.md +17/−0
- Data/UnixTime.hs +29/−26
- Data/UnixTime/Conv.hs +20/−42
- Data/UnixTime/Diff.hs +37/−40
- Data/UnixTime/Sys.hsc +3/−4
- Data/UnixTime/Types.hsc +54/−6
- Setup.hs +3/−0
- cbits/config.h.in +31/−0
- cbits/conv.c +41/−10
- cbits/strftime.c +632/−0
- cbits/strptime.c +721/−0
- cbits/win_patch.c +129/−0
- cbits/win_patch.h +177/−0
- configure +175/−1
- configure.ac +21/−0
- test/UnixTimeSpec.hs +33/−16
- test/doctests.hs +0/−11
- unix-time.cabal +76/−55
+ 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