@@ -2949,11 +2949,32 @@ def test_more_strftime(self):
2949
2949
self .assertEqual (t .strftime ("%z" ), "-0200" + z )
2950
2950
self .assertEqual (t .strftime ("%:z" ), "-02:00:" + z )
2951
2951
2952
- # bpo-34482: Check that surrogates don't cause a crash.
2953
- try :
2954
- t .strftime ('%y\ud800 %m %H\ud800 %M' )
2955
- except UnicodeEncodeError :
2956
- pass
2952
+ def test_strftime_special (self ):
2953
+ t = self .theclass (2004 , 12 , 31 , 6 , 22 , 33 , 47 )
2954
+ s1 = t .strftime ('%c' )
2955
+ s2 = t .strftime ('%B' )
2956
+ # gh-52551, gh-78662: Unicode strings should pass through strftime,
2957
+ # independently from locale.
2958
+ self .assertEqual (t .strftime ('\U0001f40d ' ), '\U0001f40d ' )
2959
+ self .assertEqual (t .strftime ('\U0001f4bb %c\U0001f40d %B' ), f'\U0001f4bb { s1 } \U0001f40d { s2 } ' )
2960
+ self .assertEqual (t .strftime ('%c\U0001f4bb %B\U0001f40d ' ), f'{ s1 } \U0001f4bb { s2 } \U0001f40d ' )
2961
+ # Lone surrogates should pass through.
2962
+ self .assertEqual (t .strftime ('\ud83d ' ), '\ud83d ' )
2963
+ self .assertEqual (t .strftime ('\udc0d ' ), '\udc0d ' )
2964
+ self .assertEqual (t .strftime ('\ud83d %c\udc0d %B' ), f'\ud83d { s1 } \udc0d { s2 } ' )
2965
+ self .assertEqual (t .strftime ('%c\ud83d %B\udc0d ' ), f'{ s1 } \ud83d { s2 } \udc0d ' )
2966
+ self .assertEqual (t .strftime ('%c\udc0d %B\ud83d ' ), f'{ s1 } \udc0d { s2 } \ud83d ' )
2967
+ # Surrogate pairs should not recombine.
2968
+ self .assertEqual (t .strftime ('\ud83d \udc0d ' ), '\ud83d \udc0d ' )
2969
+ self .assertEqual (t .strftime ('%c\ud83d \udc0d %B' ), f'{ s1 } \ud83d \udc0d { s2 } ' )
2970
+ # Surrogate-escaped bytes should not recombine.
2971
+ self .assertEqual (t .strftime ('\udcf0 \udc9f \udc90 \udc8d ' ), '\udcf0 \udc9f \udc90 \udc8d ' )
2972
+ self .assertEqual (t .strftime ('%c\udcf0 \udc9f \udc90 \udc8d %B' ), f'{ s1 } \udcf0 \udc9f \udc90 \udc8d { s2 } ' )
2973
+ # gh-124531: The null character should not terminate the format string.
2974
+ self .assertEqual (t .strftime ('\0 ' ), '\0 ' )
2975
+ self .assertEqual (t .strftime ('\0 ' * 1000 ), '\0 ' * 1000 )
2976
+ self .assertEqual (t .strftime ('\0 %c\0 %B' ), f'\0 { s1 } \0 { s2 } ' )
2977
+ self .assertEqual (t .strftime ('%c\0 %B\0 ' ), f'{ s1 } \0 { s2 } \0 ' )
2957
2978
2958
2979
def test_extract (self ):
2959
2980
dt = self .theclass (2002 , 3 , 4 , 18 , 45 , 3 , 1234 )
@@ -3736,6 +3757,33 @@ def test_strftime(self):
3736
3757
# gh-85432: The parameter was named "fmt" in the pure-Python impl.
3737
3758
t .strftime (format = "%f" )
3738
3759
3760
+ def test_strftime_special (self ):
3761
+ t = self .theclass (1 , 2 , 3 , 4 )
3762
+ s1 = t .strftime ('%I%p%Z' )
3763
+ s2 = t .strftime ('%X' )
3764
+ # gh-52551, gh-78662: Unicode strings should pass through strftime,
3765
+ # independently from locale.
3766
+ self .assertEqual (t .strftime ('\U0001f40d ' ), '\U0001f40d ' )
3767
+ self .assertEqual (t .strftime ('\U0001f4bb %I%p%Z\U0001f40d %X' ), f'\U0001f4bb { s1 } \U0001f40d { s2 } ' )
3768
+ self .assertEqual (t .strftime ('%I%p%Z\U0001f4bb %X\U0001f40d ' ), f'{ s1 } \U0001f4bb { s2 } \U0001f40d ' )
3769
+ # Lone surrogates should pass through.
3770
+ self .assertEqual (t .strftime ('\ud83d ' ), '\ud83d ' )
3771
+ self .assertEqual (t .strftime ('\udc0d ' ), '\udc0d ' )
3772
+ self .assertEqual (t .strftime ('\ud83d %I%p%Z\udc0d %X' ), f'\ud83d { s1 } \udc0d { s2 } ' )
3773
+ self .assertEqual (t .strftime ('%I%p%Z\ud83d %X\udc0d ' ), f'{ s1 } \ud83d { s2 } \udc0d ' )
3774
+ self .assertEqual (t .strftime ('%I%p%Z\udc0d %X\ud83d ' ), f'{ s1 } \udc0d { s2 } \ud83d ' )
3775
+ # Surrogate pairs should not recombine.
3776
+ self .assertEqual (t .strftime ('\ud83d \udc0d ' ), '\ud83d \udc0d ' )
3777
+ self .assertEqual (t .strftime ('%I%p%Z\ud83d \udc0d %X' ), f'{ s1 } \ud83d \udc0d { s2 } ' )
3778
+ # Surrogate-escaped bytes should not recombine.
3779
+ self .assertEqual (t .strftime ('\udcf0 \udc9f \udc90 \udc8d ' ), '\udcf0 \udc9f \udc90 \udc8d ' )
3780
+ self .assertEqual (t .strftime ('%I%p%Z\udcf0 \udc9f \udc90 \udc8d %X' ), f'{ s1 } \udcf0 \udc9f \udc90 \udc8d { s2 } ' )
3781
+ # gh-124531: The null character should not terminate the format string.
3782
+ self .assertEqual (t .strftime ('\0 ' ), '\0 ' )
3783
+ self .assertEqual (t .strftime ('\0 ' * 1000 ), '\0 ' * 1000 )
3784
+ self .assertEqual (t .strftime ('\0 %I%p%Z\0 %X' ), f'\0 { s1 } \0 { s2 } ' )
3785
+ self .assertEqual (t .strftime ('%I%p%Z\0 %X\0 ' ), f'{ s1 } \0 { s2 } \0 ' )
3786
+
3739
3787
def test_format (self ):
3740
3788
t = self .theclass (1 , 2 , 3 , 4 )
3741
3789
self .assertEqual (t .__format__ ('' ), str (t ))
@@ -4259,9 +4307,8 @@ def tzname(self, dt): return self.tz
4259
4307
self .assertRaises (TypeError , t .strftime , "%Z" )
4260
4308
4261
4309
# Issue #6697:
4262
- if '_Fast' in self .__class__ .__name__ :
4263
- Badtzname .tz = '\ud800 '
4264
- self .assertRaises (ValueError , t .strftime , "%Z" )
4310
+ Badtzname .tz = '\ud800 '
4311
+ self .assertEqual (t .strftime ("%Z" ), '\ud800 ' )
4265
4312
4266
4313
def test_hash_edge_cases (self ):
4267
4314
# Offsets that overflow a basic time.
0 commit comments