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