@@ -394,7 +394,7 @@ class DateFormatter(ticker.Formatter):
394
394
395
395
def __init__ (self , fmt , tz = None ):
396
396
"""
397
- *fmt* is an :func:`strftime` format string; *tz* is the
397
+ *fmt* is a :func:`strftime` format string; *tz* is the
398
398
:class:`tzinfo` instance.
399
399
"""
400
400
if tz is None :
@@ -414,28 +414,54 @@ def __call__(self, x, pos=0):
414
414
def set_tzinfo (self , tz ):
415
415
self .tz = tz
416
416
417
- def _findall (self , text , substr ):
418
- # Also finds overlaps
419
- sites = []
417
+ def _replace_common_substr (self , s1 , s2 , sub1 , sub2 , replacement ):
418
+ """Helper function for replacing substrings sub1 and sub2
419
+ located at the same indexes in strings s1 and s2 respectively,
420
+ with the string replacement. It is expected that sub1 and sub2
421
+ have the same length. Returns the pair s1, s2 after the
422
+ substitutions.
423
+ """
424
+ # Find common indexes of substrings sub1 in s1 and sub2 in s2
425
+ # and make substitutions inplace. Because this is inplace,
426
+ # it is okay if len(replacement) != len(sub1), len(sub2).
420
427
i = 0
421
- while 1 :
422
- j = text .find (substr , i )
428
+ while True :
429
+ j = s1 .find (sub1 , i )
423
430
if j == - 1 :
424
431
break
425
- sites . append ( j )
432
+
426
433
i = j + 1
427
- return sites
434
+ if s2 [j :j + len (sub2 )] != sub2 :
435
+ continue
428
436
429
- # Dalke: I hope I did this math right. Every 28 years the
430
- # calendar repeats, except through century leap years excepting
431
- # the 400 year leap years. But only if you're using the Gregorian
432
- # calendar.
437
+ s1 = s1 [:j ] + replacement + s1 [j + len (sub1 ):]
438
+ s2 = s2 [:j ] + replacement + s2 [j + len (sub2 ):]
433
439
434
- def strftime (self , dt , fmt ):
435
- fmt = self .illegal_s .sub (r"\1" , fmt )
436
- fmt = fmt .replace ("%s" , "s" )
437
- if dt .year > 1900 :
438
- return cbook .unicode_safe (dt .strftime (fmt ))
440
+ return s1 , s2
441
+
442
+ def strftime_pre_1900 (self , dt , fmt = None ):
443
+ """Call time.strftime for years before 1900 by rolling
444
+ forward a multiple of 28 years.
445
+
446
+ *fmt* is a :func:`strftime` format string.
447
+
448
+ Dalke: I hope I did this math right. Every 28 years the
449
+ calendar repeats, except through century leap years excepting
450
+ the 400 year leap years. But only if you're using the Gregorian
451
+ calendar.
452
+ """
453
+ if fmt is None :
454
+ fmt = self .fmt
455
+
456
+ # Since python's time module's strftime implementation does not
457
+ # support %f microsecond (but the datetime module does), use a
458
+ # regular expression substitution to replace instances of %f.
459
+ # Note that this can be useful since python's floating-point
460
+ # precision representation for datetime causes precision to be
461
+ # more accurate closer to year 0 (around the year 2000, precision
462
+ # can be at 10s of microseconds).
463
+ fmt = re .sub (r'((^|[^%])(%%)*)%f' ,
464
+ r'\g<1>{0:06d}' .format (dt .microsecond ), fmt )
439
465
440
466
year = dt .year
441
467
# For every non-leap year century, advance by
@@ -444,26 +470,52 @@ def strftime(self, dt, fmt):
444
470
off = 6 * (delta // 100 + delta // 400 )
445
471
year = year + off
446
472
447
- # Move to around the year 2000
448
- year = year + ((2000 - year ) // 28 ) * 28
473
+ # Move to between the years 1973 and 2000
474
+ year1 = year + ((2000 - year ) // 28 ) * 28
475
+ year2 = year1 + 28
449
476
timetuple = dt .timetuple ()
450
- s1 = time .strftime (fmt , (year ,) + timetuple [1 :])
451
- sites1 = self ._findall (s1 , str (year ))
452
-
453
- s2 = time .strftime (fmt , (year + 28 ,) + timetuple [1 :])
454
- sites2 = self ._findall (s2 , str (year + 28 ))
455
-
456
- sites = []
457
- for site in sites1 :
458
- if site in sites2 :
459
- sites .append (site )
460
-
461
- s = s1
462
- syear = "%4d" % (dt .year ,)
463
- for site in sites :
464
- s = s [:site ] + syear + s [site + 4 :]
477
+ # Generate timestamp string for year and year+28
478
+ s1 = time .strftime (fmt , (year1 ,) + timetuple [1 :])
479
+ s2 = time .strftime (fmt , (year2 ,) + timetuple [1 :])
480
+
481
+ # Replace instances of respective years (both 2-digit and 4-digit)
482
+ # that are located at the same indexes of s1, s2 with dt's year.
483
+ # Note that C++'s strftime implementation does not use padded
484
+ # zeros or padded whitespace for %y or %Y for years before 100, but
485
+ # uses padded zeros for %x. (For example, try the runnable examples
486
+ # with .tm_year in the interval [-1900, -1800] on
487
+ # http://en.cppreference.com/w/c/chrono/strftime.) For ease of
488
+ # implementation, we always use padded zeros for %y, %Y, and %x.
489
+ s1 , s2 = self ._replace_common_substr (s1 , s2 ,
490
+ "{0:04d}" .format (year1 ),
491
+ "{0:04d}" .format (year2 ),
492
+ "{0:04d}" .format (dt .year ))
493
+ s1 , s2 = self ._replace_common_substr (s1 , s2 ,
494
+ "{0:02d}" .format (year1 % 100 ),
495
+ "{0:02d}" .format (year2 % 100 ),
496
+ "{0:02d}" .format (dt .year % 100 ))
497
+ return cbook .unicode_safe (s1 )
498
+
499
+ def strftime (self , dt , fmt = None ):
500
+ """Refer to documentation for datetime.strftime.
501
+
502
+ *fmt* is a :func:`strftime` format string.
503
+
504
+ Warning: For years before 1900, depending upon the current
505
+ locale it is possible that the year displayed with %x might
506
+ be incorrect. For years before 100, %y and %Y will yield
507
+ zero-padded strings.
508
+ """
509
+ if fmt is None :
510
+ fmt = self .fmt
511
+ fmt = self .illegal_s .sub (r"\1" , fmt )
512
+ fmt = fmt .replace ("%s" , "s" )
513
+ if dt .year >= 1900 :
514
+ # Note: in python 3.3 this is okay for years >= 1000,
515
+ # refer to http://bugs.python.org/issue177742
516
+ return cbook .unicode_safe (dt .strftime (fmt ))
465
517
466
- return cbook . unicode_safe ( s )
518
+ return self . strftime_pre_1900 ( dt , fmt )
467
519
468
520
469
521
class IndexDateFormatter (ticker .Formatter ):
0 commit comments