PageRenderTime 209ms CodeModel.GetById 80ms app.highlight 88ms RepoModel.GetById 33ms app.codeStats 0ms

/pypy/module/rctime/interp_time.py

http://github.com/pypy/pypy
Python | 595 lines | 580 code | 4 blank | 11 comment | 8 complexity | 788c2c0a6d89008e0d3cf0916ab17aba MD5 | raw file
  1from pypy.rpython.tool import rffi_platform as platform
  2from pypy.rpython.lltypesystem import rffi
  3from pypy.interpreter.error import OperationError, operationerrfmt
  4from pypy.interpreter.gateway import unwrap_spec
  5from pypy.rpython.lltypesystem import lltype
  6from pypy.rlib.rarithmetic import ovfcheck_float_to_int, intmask
  7from pypy.rlib import rposix
  8from pypy.translator.tool.cbuild import ExternalCompilationInfo
  9import os
 10import sys
 11import time as pytime
 12
 13_POSIX = os.name == "posix"
 14_WIN = os.name == "nt"
 15
 16if _WIN:
 17    # Interruptible sleeps on Windows:
 18    # We install a specific Console Ctrl Handler which sets an 'event'.
 19    # time.sleep() will actually call WaitForSingleObject with the desired
 20    # timeout.  On Ctrl-C, the signal handler is called, the event is set,
 21    # and the wait function exits.
 22    from pypy.rlib import rwin32
 23    from pypy.interpreter.error import wrap_windowserror, wrap_oserror
 24    from pypy.module.thread import ll_thread as thread
 25
 26    eci = ExternalCompilationInfo(
 27        includes = ['windows.h'],
 28        post_include_bits = ["BOOL pypy_timemodule_setCtrlHandler(HANDLE event);"],
 29        separate_module_sources=['''
 30            static HANDLE interrupt_event;
 31
 32            static BOOL WINAPI CtrlHandlerRoutine(
 33              DWORD dwCtrlType)
 34            {
 35                SetEvent(interrupt_event);
 36                /* allow other default handlers to be called.
 37                 * Default Python handler will setup the
 38                 * KeyboardInterrupt exception.
 39                 */
 40                return 0;
 41            }
 42
 43            BOOL pypy_timemodule_setCtrlHandler(HANDLE event)
 44            {
 45                interrupt_event = event;
 46                return SetConsoleCtrlHandler(CtrlHandlerRoutine, TRUE);
 47            }
 48
 49        '''],
 50        export_symbols=['pypy_timemodule_setCtrlHandler'],
 51        )
 52    _setCtrlHandlerRoutine = rffi.llexternal(
 53        'pypy_timemodule_setCtrlHandler',
 54        [rwin32.HANDLE], rwin32.BOOL,
 55        compilation_info=eci)
 56
 57    class GlobalState:
 58        def __init__(self):
 59            self.init()
 60
 61        def init(self):
 62            self.interrupt_event = rwin32.NULL_HANDLE
 63
 64        def startup(self, space):
 65            # Initialize the event handle used to signal Ctrl-C
 66            try:
 67                globalState.interrupt_event = rwin32.CreateEvent(
 68                    rffi.NULL, True, False, rffi.NULL)
 69            except WindowsError, e:
 70                raise wrap_windowserror(space, e)
 71            if not _setCtrlHandlerRoutine(globalState.interrupt_event):
 72                raise wrap_windowserror(
 73                    space, rwin32.lastWindowsError("SetConsoleCtrlHandler"))
 74
 75    globalState = GlobalState()
 76
 77    class State:
 78        def __init__(self, space):
 79            self.main_thread = 0
 80
 81        def _freeze_(self):
 82            self.main_thread = 0
 83            globalState.init()
 84
 85        def startup(self, space):
 86            self.main_thread = thread.get_ident()
 87            globalState.startup(space)
 88
 89        def get_interrupt_event(self):
 90            return globalState.interrupt_event
 91
 92
 93_includes = ["time.h"]
 94if _POSIX:
 95    _includes.append('sys/time.h')
 96
 97class CConfig:
 98    _compilation_info_ = ExternalCompilationInfo(
 99        includes = _includes
100    )
101    CLOCKS_PER_SEC = platform.ConstantInteger("CLOCKS_PER_SEC")
102    clock_t = platform.SimpleType("clock_t", rffi.ULONG)
103    has_gettimeofday = platform.Has('gettimeofday')
104
105if _POSIX:
106    calling_conv = 'c'
107    CConfig.timeval = platform.Struct("struct timeval",
108                                      [("tv_sec", rffi.INT),
109                                       ("tv_usec", rffi.INT)])
110    CConfig.tm = platform.Struct("struct tm", [("tm_sec", rffi.INT),
111        ("tm_min", rffi.INT), ("tm_hour", rffi.INT), ("tm_mday", rffi.INT),
112        ("tm_mon", rffi.INT), ("tm_year", rffi.INT), ("tm_wday", rffi.INT),
113        ("tm_yday", rffi.INT), ("tm_isdst", rffi.INT), ("tm_gmtoff", rffi.LONG),
114        ("tm_zone", rffi.CCHARP)])
115elif _WIN:
116    calling_conv = 'win'
117    CConfig.tm = platform.Struct("struct tm", [("tm_sec", rffi.INT),
118        ("tm_min", rffi.INT), ("tm_hour", rffi.INT), ("tm_mday", rffi.INT),
119        ("tm_mon", rffi.INT), ("tm_year", rffi.INT), ("tm_wday", rffi.INT),
120        ("tm_yday", rffi.INT), ("tm_isdst", rffi.INT)])
121
122class cConfig:
123    pass
124
125for k, v in platform.configure(CConfig).items():
126    setattr(cConfig, k, v)
127cConfig.tm.__name__ = "_tm"
128
129def external(name, args, result, eci=CConfig._compilation_info_):
130    if _WIN and rffi.sizeof(rffi.TIME_T) == 8:
131        # Recent Microsoft compilers use 64bit time_t and
132        # the corresponding functions are named differently
133        if (rffi.TIME_T in args or rffi.TIME_TP in args
134            or result in (rffi.TIME_T, rffi.TIME_TP)):
135            name = '_' + name + '64'
136    return rffi.llexternal(name, args, result,
137                           compilation_info=eci,
138                           calling_conv=calling_conv,
139                           threadsafe=False)
140
141if _POSIX:
142    cConfig.timeval.__name__ = "_timeval"
143    timeval = cConfig.timeval
144
145CLOCKS_PER_SEC = cConfig.CLOCKS_PER_SEC
146clock_t = cConfig.clock_t
147tm = cConfig.tm
148glob_buf = lltype.malloc(tm, flavor='raw', zero=True, immortal=True)
149
150if cConfig.has_gettimeofday:
151    c_gettimeofday = external('gettimeofday', [rffi.VOIDP, rffi.VOIDP], rffi.INT)
152TM_P = lltype.Ptr(tm)
153c_clock = external('clock', [rffi.TIME_TP], clock_t)
154c_time = external('time', [rffi.TIME_TP], rffi.TIME_T)
155c_ctime = external('ctime', [rffi.TIME_TP], rffi.CCHARP)
156c_gmtime = external('gmtime', [rffi.TIME_TP], TM_P)
157c_mktime = external('mktime', [TM_P], rffi.TIME_T)
158c_asctime = external('asctime', [TM_P], rffi.CCHARP)
159c_localtime = external('localtime', [rffi.TIME_TP], TM_P)
160if _POSIX:
161    c_tzset = external('tzset', [], lltype.Void)
162if _WIN:
163    win_eci = ExternalCompilationInfo(
164        includes = ["time.h"],
165        post_include_bits = ["long pypy_get_timezone();",
166                             "int pypy_get_daylight();",
167                             "char** pypy_get_tzname();"],
168        separate_module_sources = ["""
169        long pypy_get_timezone() { return timezone; }
170        int pypy_get_daylight() { return daylight; }
171        char** pypy_get_tzname() { return tzname; }
172        """],
173        export_symbols = [
174        '_tzset', 'pypy_get_timezone', 'pypy_get_daylight', 'pypy_get_tzname'],
175        )
176    # Ensure sure that we use _tzset() and timezone from the same C Runtime.
177    c_tzset = external('_tzset', [], lltype.Void, win_eci)
178    c_get_timezone = external('pypy_get_timezone', [], rffi.LONG, win_eci)
179    c_get_daylight = external('pypy_get_daylight', [], rffi.INT, win_eci)
180    c_get_tzname = external('pypy_get_tzname', [], rffi.CCHARPP, win_eci)
181
182c_strftime = external('strftime', [rffi.CCHARP, rffi.SIZE_T, rffi.CCHARP, TM_P],
183                      rffi.SIZE_T)
184
185def _init_accept2dyear(space):
186    if os.environ.get("PYTHONY2K"):
187        accept2dyear = 0
188    else:
189        accept2dyear = 1
190    _set_module_object(space, "accept2dyear", space.wrap(accept2dyear))
191
192def _init_timezone(space):
193    timezone = daylight = altzone = 0
194    tzname = ["", ""]
195
196    if _WIN:
197         c_tzset()
198         timezone = c_get_timezone()
199         altzone = timezone - 3600
200         daylight = c_get_daylight()
201         tzname_ptr = c_get_tzname()
202         tzname = rffi.charp2str(tzname_ptr[0]), rffi.charp2str(tzname_ptr[1])
203
204    if _POSIX:
205        YEAR = (365 * 24 + 6) * 3600
206
207        t = (((c_time(lltype.nullptr(rffi.TIME_TP.TO))) / YEAR) * YEAR)
208        # we cannot have reference to stack variable, put it on the heap
209        t_ref = lltype.malloc(rffi.TIME_TP.TO, 1, flavor='raw')
210        t_ref[0] = rffi.cast(rffi.TIME_T, t)
211        p = c_localtime(t_ref)
212        janzone = -p.c_tm_gmtoff
213        tm_zone = rffi.charp2str(p.c_tm_zone)
214        janname = ["   ", tm_zone][bool(tm_zone)]
215        tt = t + YEAR / 2
216        t_ref[0] = rffi.cast(rffi.TIME_T, tt)
217        p = c_localtime(t_ref)
218        lltype.free(t_ref, flavor='raw')
219        tm_zone = rffi.charp2str(p.c_tm_zone)
220        julyzone = -p.c_tm_gmtoff
221        julyname = ["   ", tm_zone][bool(tm_zone)]
222
223        if janzone < julyzone:
224            # DST is reversed in the southern hemisphere
225            timezone = julyzone
226            altzone = janzone
227            daylight = int(janzone != julyzone)
228            tzname = [julyname, janname]
229        else:
230            timezone = janzone
231            altzone = julyzone
232            daylight = int(janzone != julyzone)
233            tzname = [janname, julyname]
234
235    _set_module_object(space, "timezone", space.wrap(timezone))
236    _set_module_object(space, 'daylight', space.wrap(daylight))
237    tzname_w = [space.wrap(tzname[0]), space.wrap(tzname[1])]
238    _set_module_object(space, 'tzname', space.newtuple(tzname_w))
239    _set_module_object(space, 'altzone', space.wrap(altzone))
240
241def _get_error_msg():
242    errno = rposix.get_errno()
243    return os.strerror(errno)
244
245if sys.platform != 'win32':
246    @unwrap_spec(secs=float)
247    def sleep(space, secs):
248        if secs < 0:
249            raise OperationError(space.w_IOError,
250                                 space.wrap("Invalid argument: negative time in sleep"))
251        pytime.sleep(secs)
252else:
253    from pypy.rlib import rwin32
254    from errno import EINTR
255    def _simple_sleep(space, secs, interruptible):
256        if secs == 0.0 or not interruptible:
257            pytime.sleep(secs)
258        else:
259            millisecs = int(secs * 1000)
260            interrupt_event = space.fromcache(State).get_interrupt_event()
261            rwin32.ResetEvent(interrupt_event)
262            rc = rwin32.WaitForSingleObject(interrupt_event, millisecs)
263            if rc == rwin32.WAIT_OBJECT_0:
264                # Yield to make sure real Python signal handler
265                # called.
266                pytime.sleep(0.001)
267                raise wrap_oserror(space,
268                                   OSError(EINTR, "sleep() interrupted"))
269    @unwrap_spec(secs=float)
270    def sleep(space, secs):
271        if secs < 0:
272            raise OperationError(space.w_IOError,
273                                 space.wrap("Invalid argument: negative time in sleep"))
274        # as decreed by Guido, only the main thread can be
275        # interrupted.
276        main_thread = space.fromcache(State).main_thread
277        interruptible = (main_thread == thread.get_ident())
278        MAX = sys.maxint / 1000.0 # > 24 days
279        while secs > MAX:
280            _simple_sleep(space, MAX, interruptible)
281            secs -= MAX
282        _simple_sleep(space, secs, interruptible)
283
284def _get_module_object(space, obj_name):
285    w_module = space.getbuiltinmodule('time')
286    w_obj = space.getattr(w_module, space.wrap(obj_name))
287    return w_obj
288
289def _set_module_object(space, obj_name, w_obj_value):
290    w_module = space.getbuiltinmodule('time')
291    space.setattr(w_module, space.wrap(obj_name), w_obj_value)
292
293def _get_inttime(space, w_seconds):
294    # w_seconds can be a wrapped None (it will be automatically wrapped
295    # in the callers, so we never get a real None here).
296    if space.is_w(w_seconds, space.w_None):
297        seconds = pytime.time()
298    else:
299        seconds = space.float_w(w_seconds)
300    try:
301        seconds = ovfcheck_float_to_int(seconds)
302        t = rffi.r_time_t(seconds)
303        if rffi.cast(lltype.Signed, t) != seconds:
304            raise OverflowError
305    except OverflowError:
306        raise OperationError(space.w_ValueError,
307                             space.wrap("time argument too large"))
308    return t
309
310def _tm_to_tuple(space, t):
311    time_tuple = [
312        space.wrap(rffi.getintfield(t, 'c_tm_year') + 1900),
313        space.wrap(rffi.getintfield(t, 'c_tm_mon') + 1), # want january == 1
314        space.wrap(rffi.getintfield(t, 'c_tm_mday')),
315        space.wrap(rffi.getintfield(t, 'c_tm_hour')),
316        space.wrap(rffi.getintfield(t, 'c_tm_min')),
317        space.wrap(rffi.getintfield(t, 'c_tm_sec')),
318        space.wrap((rffi.getintfield(t, 'c_tm_wday') + 6) % 7), # want monday == 0
319        space.wrap(rffi.getintfield(t, 'c_tm_yday') + 1), # want january, 1 == 1
320        space.wrap(rffi.getintfield(t, 'c_tm_isdst'))]
321
322    w_struct_time = _get_module_object(space, 'struct_time')
323    w_time_tuple = space.newtuple(time_tuple)
324    return space.call_function(w_struct_time, w_time_tuple)
325
326def _gettmarg(space, w_tup, allowNone=True):
327    if allowNone and space.is_w(w_tup, space.w_None):
328        # default to the current local time
329        tt = rffi.r_time_t(int(pytime.time()))
330        t_ref = lltype.malloc(rffi.TIME_TP.TO, 1, flavor='raw')
331        t_ref[0] = tt
332        pbuf = c_localtime(t_ref)
333        lltype.free(t_ref, flavor='raw')
334        if not pbuf:
335            raise OperationError(space.w_ValueError,
336                space.wrap(_get_error_msg()))
337        return pbuf
338
339    tup_w = space.fixedview(w_tup)
340    if len(tup_w) != 9:
341        raise operationerrfmt(space.w_TypeError,
342                              "argument must be sequence of "
343                              "length 9, not %d", len(tup_w))
344
345    y = space.int_w(tup_w[0])
346    tm_mon = space.int_w(tup_w[1])
347    if tm_mon == 0:
348        tm_mon = 1
349    tm_mday = space.int_w(tup_w[2])
350    if tm_mday == 0:
351        tm_mday = 1
352    tm_yday = space.int_w(tup_w[7])
353    if tm_yday == 0:
354        tm_yday = 1
355    rffi.setintfield(glob_buf, 'c_tm_mon', tm_mon)
356    rffi.setintfield(glob_buf, 'c_tm_mday', tm_mday)
357    rffi.setintfield(glob_buf, 'c_tm_hour', space.int_w(tup_w[3]))
358    rffi.setintfield(glob_buf, 'c_tm_min', space.int_w(tup_w[4]))
359    rffi.setintfield(glob_buf, 'c_tm_sec', space.int_w(tup_w[5]))
360    rffi.setintfield(glob_buf, 'c_tm_wday', space.int_w(tup_w[6]))
361    rffi.setintfield(glob_buf, 'c_tm_yday', tm_yday)
362    rffi.setintfield(glob_buf, 'c_tm_isdst', space.int_w(tup_w[8]))
363    if _POSIX:
364        # actually never happens, but makes annotator happy
365        glob_buf.c_tm_zone = lltype.nullptr(rffi.CCHARP.TO)
366        rffi.setintfield(glob_buf, 'c_tm_gmtoff', 0)
367
368    w_accept2dyear = _get_module_object(space, "accept2dyear")
369    accept2dyear = space.int_w(w_accept2dyear)
370
371    if y < 1900:
372        if not accept2dyear:
373            raise OperationError(space.w_ValueError,
374                space.wrap("year >= 1900 required"))
375
376        if 69 <= y <= 99:
377            y += 1900
378        elif 0 <= y <= 68:
379            y += 2000
380        else:
381            raise OperationError(space.w_ValueError,
382                space.wrap("year out of range"))
383
384    if rffi.getintfield(glob_buf, 'c_tm_wday') < 0:
385        raise OperationError(space.w_ValueError,
386                             space.wrap("day of week out of range"))
387
388    rffi.setintfield(glob_buf, 'c_tm_year', y - 1900)
389    rffi.setintfield(glob_buf, 'c_tm_mon',
390                     rffi.getintfield(glob_buf, 'c_tm_mon') - 1)
391    rffi.setintfield(glob_buf, 'c_tm_wday',
392                     (rffi.getintfield(glob_buf, 'c_tm_wday') + 1) % 7)
393    rffi.setintfield(glob_buf, 'c_tm_yday',
394                     rffi.getintfield(glob_buf, 'c_tm_yday') - 1)
395
396    return glob_buf
397
398def time(space):
399    """time() -> floating point number
400
401    Return the current time in seconds since the Epoch.
402    Fractions of a second may be present if the system clock provides them."""
403
404    secs = pytime.time()
405    return space.wrap(secs)
406
407if _WIN:
408    class PCCache:
409        pass
410    pccache = PCCache()
411    pccache.divisor = 0.0
412    pccache.ctrStart = 0
413
414def clock(space):
415    """clock() -> floating point number
416
417    Return the CPU time or real time since the start of the process or since
418    the first call to clock().  This has as much precision as the system
419    records."""
420
421    return space.wrap(pytime.clock())
422
423def ctime(space, w_seconds=None):
424    """ctime([seconds]) -> string
425
426    Convert a time in seconds since the Epoch to a string in local time.
427    This is equivalent to asctime(localtime(seconds)). When the time tuple is
428    not present, current time as returned by localtime() is used."""
429
430    seconds = _get_inttime(space, w_seconds)
431
432    t_ref = lltype.malloc(rffi.TIME_TP.TO, 1, flavor='raw')
433    t_ref[0] = seconds
434    p = c_ctime(t_ref)
435    lltype.free(t_ref, flavor='raw')
436    if not p:
437        raise OperationError(space.w_ValueError,
438            space.wrap("unconvertible time"))
439
440    return space.wrap(rffi.charp2str(p)[:-1]) # get rid of new line
441
442# by now w_tup is an optional argument (and not *args)
443# because of the ext. compiler bugs in handling such arguments (*args, **kwds)
444def asctime(space, w_tup=None):
445    """asctime([tuple]) -> string
446
447    Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.
448    When the time tuple is not present, current time as returned by localtime()
449    is used."""
450    buf_value = _gettmarg(space, w_tup)
451    p = c_asctime(buf_value)
452    if not p:
453        raise OperationError(space.w_ValueError,
454            space.wrap("unconvertible time"))
455
456    return space.wrap(rffi.charp2str(p)[:-1]) # get rid of new line
457
458def gmtime(space, w_seconds=None):
459    """gmtime([seconds]) -> (tm_year, tm_mon, tm_day, tm_hour, tm_min,
460                          tm_sec, tm_wday, tm_yday, tm_isdst)
461
462    Convert seconds since the Epoch to a time tuple expressing UTC (a.k.a.
463    GMT).  When 'seconds' is not passed in, convert the current time instead.
464    """
465
466    # rpython does not support that a variable has two incompatible builtins
467    # as value so we have to duplicate the code. NOT GOOD! see localtime() too
468    seconds = _get_inttime(space, w_seconds)
469    t_ref = lltype.malloc(rffi.TIME_TP.TO, 1, flavor='raw')
470    t_ref[0] = seconds
471    p = c_gmtime(t_ref)
472    lltype.free(t_ref, flavor='raw')
473
474    if not p:
475        raise OperationError(space.w_ValueError, space.wrap(_get_error_msg()))
476    return _tm_to_tuple(space, p)
477
478def localtime(space, w_seconds=None):
479    """localtime([seconds]) -> (tm_year, tm_mon, tm_day, tm_hour, tm_min,
480                             tm_sec, tm_wday, tm_yday, tm_isdst)
481
482    Convert seconds since the Epoch to a time tuple expressing local time.
483    When 'seconds' is not passed in, convert the current time instead."""
484
485    seconds = _get_inttime(space, w_seconds)
486    t_ref = lltype.malloc(rffi.TIME_TP.TO, 1, flavor='raw')
487    t_ref[0] = seconds
488    p = c_localtime(t_ref)
489    lltype.free(t_ref, flavor='raw')
490
491    if not p:
492        raise OperationError(space.w_ValueError, space.wrap(_get_error_msg()))
493    return _tm_to_tuple(space, p)
494
495def mktime(space, w_tup):
496    """mktime(tuple) -> floating point number
497
498    Convert a time tuple in local time to seconds since the Epoch."""
499
500    buf = _gettmarg(space, w_tup, allowNone=False)
501    rffi.setintfield(buf, "c_tm_wday", -1)
502    tt = c_mktime(buf)
503    # A return value of -1 does not necessarily mean an error, but tm_wday
504    # cannot remain set to -1 if mktime succeeds.
505    if tt == -1 and rffi.getintfield(buf, "c_tm_wday") == -1:
506        raise OperationError(space.w_OverflowError,
507            space.wrap("mktime argument out of range"))
508
509    return space.wrap(float(tt))
510
511if _POSIX:
512    def tzset(space):
513        """tzset()
514
515        Initialize, or reinitialize, the local timezone to the value stored in
516        os.environ['TZ']. The TZ environment variable should be specified in
517        standard Unix timezone format as documented in the tzset man page
518        (eg. 'US/Eastern', 'Europe/Amsterdam'). Unknown timezones will silently
519        fall back to UTC. If the TZ environment variable is not set, the local
520        timezone is set to the systems best guess of wallclock time.
521        Changing the TZ environment variable without calling tzset *may* change
522        the local timezone used by methods such as localtime, but this behaviour
523        should not be relied on"""
524
525        c_tzset()
526
527        # reset timezone, altzone, daylight and tzname
528        _init_timezone(space)
529
530@unwrap_spec(format=str)
531def strftime(space, format, w_tup=None):
532    """strftime(format[, tuple]) -> string
533
534    Convert a time tuple to a string according to a format specification.
535    See the library reference manual for formatting codes. When the time tuple
536    is not present, current time as returned by localtime() is used."""
537    buf_value = _gettmarg(space, w_tup)
538
539    # Checks added to make sure strftime() does not crash Python by
540    # indexing blindly into some array for a textual representation
541    # by some bad index (fixes bug #897625).
542    # No check for year since handled in gettmarg().
543    if rffi.getintfield(buf_value, 'c_tm_mon') < 0 or rffi.getintfield(buf_value, 'c_tm_mon') > 11:
544        raise OperationError(space.w_ValueError,
545                             space.wrap("month out of range"))
546    if rffi.getintfield(buf_value, 'c_tm_mday') < 1 or rffi.getintfield(buf_value, 'c_tm_mday') > 31:
547        raise OperationError(space.w_ValueError,
548                             space.wrap("day of month out of range"))
549    if rffi.getintfield(buf_value, 'c_tm_hour') < 0 or rffi.getintfield(buf_value, 'c_tm_hour') > 23:
550        raise OperationError(space.w_ValueError,
551                             space.wrap("hour out of range"))
552    if rffi.getintfield(buf_value, 'c_tm_min') < 0 or rffi.getintfield(buf_value, 'c_tm_min') > 59:
553        raise OperationError(space.w_ValueError,
554                             space.wrap("minute out of range"))
555    if rffi.getintfield(buf_value, 'c_tm_sec') < 0 or rffi.getintfield(buf_value, 'c_tm_sec') > 61:
556        raise OperationError(space.w_ValueError,
557                             space.wrap("seconds out of range"))
558    if rffi.getintfield(buf_value, 'c_tm_yday') < 0 or rffi.getintfield(buf_value, 'c_tm_yday') > 365:
559        raise OperationError(space.w_ValueError,
560                             space.wrap("day of year out of range"))
561    if rffi.getintfield(buf_value, 'c_tm_isdst') < -1 or rffi.getintfield(buf_value, 'c_tm_isdst') > 1:
562        raise OperationError(space.w_ValueError,
563                             space.wrap("daylight savings flag out of range"))
564
565    if _WIN:
566        # check that the format string contains only valid directives
567        length = len(format)
568        i = 0
569        while i < length:
570            if format[i] == '%':
571                i += 1
572                if i < length and format[i] == '#':
573                    # not documented by python
574                    i += 1
575                if i >= length or format[i] not in "aAbBcdHIjmMpSUwWxXyYzZ%":
576                    raise OperationError(space.w_ValueError,
577                                         space.wrap("invalid format string"))
578            i += 1
579
580    i = 1024
581    while True:
582        outbuf = lltype.malloc(rffi.CCHARP.TO, i, flavor='raw')
583        try:
584            buflen = c_strftime(outbuf, i, format, buf_value)
585            if buflen > 0 or i >= 256 * len(format):
586                # if the buffer is 256 times as long as the format,
587                # it's probably not failing for lack of room!
588                # More likely, the format yields an empty result,
589                # e.g. an empty format, or %Z when the timezone
590                # is unknown.
591                result = rffi.charp2strn(outbuf, intmask(buflen))
592                return space.wrap(result)
593        finally:
594            lltype.free(outbuf, flavor='raw')
595        i += i