mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	Revert "[3.13] gh-120713: Normalize year with century for datetime.strftime (GH-120820) (GH-121144)" (GH-122408)
This reverts commit 009618f112.
			
			
This commit is contained in:
		
							parent
							
								
									10cf7d6d00
								
							
						
					
					
						commit
						9f6f8790ef
					
				
					 6 changed files with 16 additions and 172 deletions
				
			
		|  | @ -204,17 +204,6 @@ def _format_offset(off, sep=':'): | ||||||
|                 s += '.%06d' % ss.microseconds |                 s += '.%06d' % ss.microseconds | ||||||
|     return s |     return s | ||||||
| 
 | 
 | ||||||
| _normalize_century = None |  | ||||||
| def _need_normalize_century(): |  | ||||||
|     global _normalize_century |  | ||||||
|     if _normalize_century is None: |  | ||||||
|         try: |  | ||||||
|             _normalize_century = ( |  | ||||||
|                 _time.strftime("%Y", (99, 1, 1, 0, 0, 0, 0, 1, 0)) != "0099") |  | ||||||
|         except ValueError: |  | ||||||
|             _normalize_century = True |  | ||||||
|     return _normalize_century |  | ||||||
| 
 |  | ||||||
| # Correctly substitute for %z and %Z escapes in strftime formats. | # Correctly substitute for %z and %Z escapes in strftime formats. | ||||||
| def _wrap_strftime(object, format, timetuple): | def _wrap_strftime(object, format, timetuple): | ||||||
|     # Don't call utcoffset() or tzname() unless actually needed. |     # Don't call utcoffset() or tzname() unless actually needed. | ||||||
|  | @ -272,14 +261,6 @@ def _wrap_strftime(object, format, timetuple): | ||||||
|                                 # strftime is going to have at this: escape % |                                 # strftime is going to have at this: escape % | ||||||
|                                 Zreplace = s.replace('%', '%%') |                                 Zreplace = s.replace('%', '%%') | ||||||
|                     newformat.append(Zreplace) |                     newformat.append(Zreplace) | ||||||
|                 elif ch in 'YG' and object.year < 1000 and _need_normalize_century(): |  | ||||||
|                     # Note that datetime(1000, 1, 1).strftime('%G') == '1000' so |  | ||||||
|                     # year 1000 for %G can go on the fast path. |  | ||||||
|                     if ch == 'G': |  | ||||||
|                         year = int(_time.strftime("%G", timetuple)) |  | ||||||
|                     else: |  | ||||||
|                         year = object.year |  | ||||||
|                     push('{:04}'.format(year)) |  | ||||||
|                 else: |                 else: | ||||||
|                     push('%') |                     push('%') | ||||||
|                     push(ch) |                     push(ch) | ||||||
|  |  | ||||||
|  | @ -1697,26 +1697,18 @@ def test_bool(self): | ||||||
|         self.assertTrue(self.theclass.max) |         self.assertTrue(self.theclass.max) | ||||||
| 
 | 
 | ||||||
|     def test_strftime_y2k(self): |     def test_strftime_y2k(self): | ||||||
|         # Test that years less than 1000 are 0-padded; note that the beginning |         for y in (1, 49, 70, 99, 100, 999, 1000, 1970): | ||||||
|         # of an ISO 8601 year may fall in an ISO week of the year before, and |             d = self.theclass(y, 1, 1) | ||||||
|         # therefore needs an offset of -1 when formatting with '%G'. |             # Issue 13305:  For years < 1000, the value is not always | ||||||
|         dataset = ( |             # padded to 4 digits across platforms.  The C standard | ||||||
|             (1, 0), |             # assumes year >= 1900, so it does not specify the number | ||||||
|             (49, -1), |             # of digits. | ||||||
|             (70, 0), |             if d.strftime("%Y") != '%04d' % y: | ||||||
|             (99, 0), |                 # Year 42 returns '42', not padded | ||||||
|             (100, -1), |                 self.assertEqual(d.strftime("%Y"), '%d' % y) | ||||||
|             (999, 0), |                 # '0042' is obtained anyway | ||||||
|             (1000, 0), |                 if support.has_strftime_extensions: | ||||||
|             (1970, 0), |                     self.assertEqual(d.strftime("%4Y"), '%04d' % y) | ||||||
|         ) |  | ||||||
|         for year, offset in dataset: |  | ||||||
|             for specifier in 'YG': |  | ||||||
|                 with self.subTest(year=year, specifier=specifier): |  | ||||||
|                     d = self.theclass(year, 1, 1) |  | ||||||
|                     if specifier == 'G': |  | ||||||
|                         year += offset |  | ||||||
|                     self.assertEqual(d.strftime(f"%{specifier}"), f"{year:04d}") |  | ||||||
| 
 | 
 | ||||||
|     def test_replace(self): |     def test_replace(self): | ||||||
|         cls = self.theclass |         cls = self.theclass | ||||||
|  |  | ||||||
|  | @ -1848,11 +1848,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, | ||||||
|     const char *ptoappend;      /* ptr to string to append to output buffer */ |     const char *ptoappend;      /* ptr to string to append to output buffer */ | ||||||
|     Py_ssize_t ntoappend;       /* # of bytes to append to output buffer */ |     Py_ssize_t ntoappend;       /* # of bytes to append to output buffer */ | ||||||
| 
 | 
 | ||||||
| #ifdef Py_NORMALIZE_CENTURY |  | ||||||
|     /* Buffer of maximum size of formatted year permitted by long. */ |  | ||||||
|     char buf[SIZEOF_LONG*5/2+2]; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|     assert(object && format && timetuple); |     assert(object && format && timetuple); | ||||||
|     assert(PyUnicode_Check(format)); |     assert(PyUnicode_Check(format)); | ||||||
|     /* Convert the input format to a C string and size */ |     /* Convert the input format to a C string and size */ | ||||||
|  | @ -1860,11 +1855,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, | ||||||
|     if (!pin) |     if (!pin) | ||||||
|         return NULL; |         return NULL; | ||||||
| 
 | 
 | ||||||
|     PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime"); |  | ||||||
|     if (strftime == NULL) { |  | ||||||
|         goto Done; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Scan the input format, looking for %z/%Z/%f escapes, building
 |     /* Scan the input format, looking for %z/%Z/%f escapes, building
 | ||||||
|      * a new format.  Since computing the replacements for those codes |      * a new format.  Since computing the replacements for those codes | ||||||
|      * is expensive, don't unless they're actually used. |      * is expensive, don't unless they're actually used. | ||||||
|  | @ -1946,47 +1936,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, | ||||||
|             ptoappend = PyBytes_AS_STRING(freplacement); |             ptoappend = PyBytes_AS_STRING(freplacement); | ||||||
|             ntoappend = PyBytes_GET_SIZE(freplacement); |             ntoappend = PyBytes_GET_SIZE(freplacement); | ||||||
|         } |         } | ||||||
| #ifdef Py_NORMALIZE_CENTURY |  | ||||||
|         else if (ch == 'Y' || ch == 'G') { |  | ||||||
|             /* 0-pad year with century as necessary */ |  | ||||||
|             PyObject *item = PyTuple_GET_ITEM(timetuple, 0); |  | ||||||
|             long year_long = PyLong_AsLong(item); |  | ||||||
| 
 |  | ||||||
|             if (year_long == -1 && PyErr_Occurred()) { |  | ||||||
|                 goto Done; |  | ||||||
|             } |  | ||||||
|             /* Note that datetime(1000, 1, 1).strftime('%G') == '1000' so year
 |  | ||||||
|                1000 for %G can go on the fast path. */ |  | ||||||
|             if (year_long >= 1000) { |  | ||||||
|                 goto PassThrough; |  | ||||||
|             } |  | ||||||
|             if (ch == 'G') { |  | ||||||
|                 PyObject *year_str = PyObject_CallFunction(strftime, "sO", |  | ||||||
|                                                            "%G", timetuple); |  | ||||||
|                 if (year_str == NULL) { |  | ||||||
|                     goto Done; |  | ||||||
|                 } |  | ||||||
|                 PyObject *year = PyNumber_Long(year_str); |  | ||||||
|                 Py_DECREF(year_str); |  | ||||||
|                 if (year == NULL) { |  | ||||||
|                     goto Done; |  | ||||||
|                 } |  | ||||||
|                 year_long = PyLong_AsLong(year); |  | ||||||
|                 Py_DECREF(year); |  | ||||||
|                 if (year_long == -1 && PyErr_Occurred()) { |  | ||||||
|                     goto Done; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             ntoappend = PyOS_snprintf(buf, sizeof(buf), "%04ld", year_long); |  | ||||||
|             ptoappend = buf; |  | ||||||
|         } |  | ||||||
| #endif |  | ||||||
|         else { |         else { | ||||||
|             /* percent followed by something else */ |             /* percent followed by something else */ | ||||||
| #ifdef Py_NORMALIZE_CENTURY |  | ||||||
|  PassThrough: |  | ||||||
| #endif |  | ||||||
|             ptoappend = pin - 2; |             ptoappend = pin - 2; | ||||||
|             ntoappend = 2; |             ntoappend = 2; | ||||||
|         } |         } | ||||||
|  | @ -2018,13 +1969,17 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, | ||||||
|         goto Done; |         goto Done; | ||||||
|     { |     { | ||||||
|         PyObject *format; |         PyObject *format; | ||||||
|  |         PyObject *strftime = _PyImport_GetModuleAttrString("time", "strftime"); | ||||||
| 
 | 
 | ||||||
|  |         if (strftime == NULL) | ||||||
|  |             goto Done; | ||||||
|         format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt)); |         format = PyUnicode_FromString(PyBytes_AS_STRING(newfmt)); | ||||||
|         if (format != NULL) { |         if (format != NULL) { | ||||||
|             result = PyObject_CallFunctionObjArgs(strftime, |             result = PyObject_CallFunctionObjArgs(strftime, | ||||||
|                                                    format, timetuple, NULL); |                                                    format, timetuple, NULL); | ||||||
|             Py_DECREF(format); |             Py_DECREF(format); | ||||||
|         } |         } | ||||||
|  |         Py_DECREF(strftime); | ||||||
|     } |     } | ||||||
|  Done: |  Done: | ||||||
|     Py_XDECREF(freplacement); |     Py_XDECREF(freplacement); | ||||||
|  | @ -2032,7 +1987,6 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, | ||||||
|     Py_XDECREF(colonzreplacement); |     Py_XDECREF(colonzreplacement); | ||||||
|     Py_XDECREF(Zreplacement); |     Py_XDECREF(Zreplacement); | ||||||
|     Py_XDECREF(newfmt); |     Py_XDECREF(newfmt); | ||||||
|     Py_XDECREF(strftime); |  | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										52
									
								
								configure
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								configure
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -25925,58 +25925,6 @@ printf "%s\n" "#define HAVE_STAT_TV_NSEC2 1" >>confdefs.h | ||||||
| 
 | 
 | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether year with century should be normalized for strftime" >&5 |  | ||||||
| printf %s "checking whether year with century should be normalized for strftime... " >&6; } |  | ||||||
| if test ${ac_cv_normalize_century+y} |  | ||||||
| then : |  | ||||||
|   printf %s "(cached) " >&6 |  | ||||||
| else $as_nop |  | ||||||
| 
 |  | ||||||
| if test "$cross_compiling" = yes |  | ||||||
| then : |  | ||||||
|   ac_cv_normalize_century=yes |  | ||||||
| else $as_nop |  | ||||||
|   cat confdefs.h - <<_ACEOF >conftest.$ac_ext |  | ||||||
| /* end confdefs.h.  */ |  | ||||||
| 
 |  | ||||||
| #include <time.h> |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| int main(void) |  | ||||||
| { |  | ||||||
|   char year[5]; |  | ||||||
|   struct tm date = { |  | ||||||
|     .tm_year = -1801, |  | ||||||
|     .tm_mon = 0, |  | ||||||
|     .tm_mday = 1 |  | ||||||
|   }; |  | ||||||
|   if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { |  | ||||||
|     return 1; |  | ||||||
|   } |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| _ACEOF |  | ||||||
| if ac_fn_c_try_run "$LINENO" |  | ||||||
| then : |  | ||||||
|   ac_cv_normalize_century=yes |  | ||||||
| else $as_nop |  | ||||||
|   ac_cv_normalize_century=no |  | ||||||
| fi |  | ||||||
| rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ |  | ||||||
|   conftest.$ac_objext conftest.beam conftest.$ac_ext |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| fi |  | ||||||
| { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_normalize_century" >&5 |  | ||||||
| printf "%s\n" "$ac_cv_normalize_century" >&6; } |  | ||||||
| if test "$ac_cv_normalize_century" = yes |  | ||||||
| then |  | ||||||
| 
 |  | ||||||
| printf "%s\n" "#define Py_NORMALIZE_CENTURY 1" >>confdefs.h |  | ||||||
| 
 |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| have_curses=no | have_curses=no | ||||||
| have_panel=no | have_panel=no | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										28
									
								
								configure.ac
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								configure.ac
									
										
									
									
									
								
							|  | @ -6612,34 +6612,6 @@ then | ||||||
|   [Define if you have struct stat.st_mtimensec]) |   [Define if you have struct stat.st_mtimensec]) | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| AC_CACHE_CHECK([whether year with century should be normalized for strftime], [ac_cv_normalize_century], [ |  | ||||||
| AC_RUN_IFELSE([AC_LANG_SOURCE([[ |  | ||||||
| #include <time.h> |  | ||||||
| #include <string.h> |  | ||||||
| 
 |  | ||||||
| int main(void) |  | ||||||
| { |  | ||||||
|   char year[5]; |  | ||||||
|   struct tm date = { |  | ||||||
|     .tm_year = -1801, |  | ||||||
|     .tm_mon = 0, |  | ||||||
|     .tm_mday = 1 |  | ||||||
|   }; |  | ||||||
|   if (strftime(year, sizeof(year), "%Y", &date) && !strcmp(year, "0099")) { |  | ||||||
|     return 1; |  | ||||||
|   } |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
| ]])], |  | ||||||
| [ac_cv_normalize_century=yes], |  | ||||||
| [ac_cv_normalize_century=no], |  | ||||||
| [ac_cv_normalize_century=yes])]) |  | ||||||
| if test "$ac_cv_normalize_century" = yes |  | ||||||
| then |  | ||||||
|   AC_DEFINE([Py_NORMALIZE_CENTURY], [1], |  | ||||||
|   [Define if year with century should be normalized for strftime.]) |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| dnl check for ncursesw/ncurses and panelw/panel | dnl check for ncursesw/ncurses and panelw/panel | ||||||
| dnl NOTE: old curses is not detected. | dnl NOTE: old curses is not detected. | ||||||
| dnl have_curses=[no, yes] | dnl have_curses=[no, yes] | ||||||
|  |  | ||||||
|  | @ -1686,9 +1686,6 @@ | ||||||
|    SipHash13: 3, externally defined: 0 */ |    SipHash13: 3, externally defined: 0 */ | ||||||
| #undef Py_HASH_ALGORITHM | #undef Py_HASH_ALGORITHM | ||||||
| 
 | 
 | ||||||
| /* Define if year with century should be normalized for strftime. */ |  | ||||||
| #undef Py_NORMALIZE_CENTURY |  | ||||||
| 
 |  | ||||||
| /* Define if rl_startup_hook takes arguments */ | /* Define if rl_startup_hook takes arguments */ | ||||||
| #undef Py_RL_STARTUP_HOOK_TAKES_ARGS | #undef Py_RL_STARTUP_HOOK_TAKES_ARGS | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka