Here are changes to GNU's gcc-2.7.2.1 "c-common.c" file that allow strftime
formats to be checked against what GNU's glibc-1.09.1 "strftime" accepts.
Formats are also checked for specifications that may or must print only the
last two digits of years.

				--Arthur David Olson, 1996-09-08

*** 1.1/c-common.c	Sun Sep  8 16:18:09 1996
--- 1/c-common.c	Sun Sep  8 16:18:09 1996
***************
*** 556,561 ****
--- 556,566 ----
  			 || !strcmp (IDENTIFIER_POINTER (format_type),
  				     "__scanf__")))
  	      is_scan = 1;
+ 	    else if (TREE_CODE (format_type) == IDENTIFIER_NODE
+ 		     && (!strcmp (IDENTIFIER_POINTER (format_type), "strftime")
+ 			 || !strcmp (IDENTIFIER_POINTER (format_type),
+ 				     "__strftime__")))
+ 	      is_scan = 2;
  	    else
  	      {
  		error ("unrecognized format specifier for `%s'");
***************
*** 725,730 ****
--- 730,749 ----
    { NULL }
  };
  
+ /*
+ ** Only format characters recognized by glibc 2.0's strftime (as of 1996-08-19) handled.
+ ** XPG4 'E' and 'O' modifier stuff is not handled since glibc strftime does not.
+ ** "2" is used to for formats which MUST do years as only two digits;
+ ** "3" is used for formats which MAY do years as only two digits (depending on locale).
+ */
+ 
+ static format_char_info time_char_table[] = {
+   { "Dy",	 0,	NULL,	NULL,	NULL,	NULL,	NULL,	"-_2"	},
+   { "xc",	 0,	NULL,	NULL,	NULL,	NULL,	NULL,	"-_3"	},
+   { "aAbhBdeHIkljMmnpRrsSTtUVWwXYzZ",	 0,	NULL,	NULL,	NULL,	NULL,	NULL,	"-_"	},
+   { NULL }
+ };
+ 
  typedef struct function_format_info {
    struct function_format_info *next;  /* next structure on the list */
    tree name;			/* identifier such as "printf" */
***************
*** 757,771 ****
    record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
    record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
    record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
  }
  
  /* Record information for argument format checking.  FUNCTION_IDENT is
     the identifier node for the name of the function to check (its decl
!    need not exist yet).  IS_SCAN is true for scanf-type format checking;
!    false indicates printf-style format checking.  FORMAT_NUM is the number
     of the argument which is the format control string (starting from 1).
     FIRST_ARG_NUM is the number of the first actual argument to check
!    against teh format string, or zero if no checking is not be done
     (e.g. for varargs such as vfprintf).  */
  
  void
--- 776,792 ----
    record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
    record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
    record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
+   record_function_format (get_identifier ("strftime"), NULL_TREE, 2, 3, 0);
  }
  
  /* Record information for argument format checking.  FUNCTION_IDENT is
     the identifier node for the name of the function to check (its decl
!    need not exist yet).
!    IS_SCAN is 1 for scanf-type format checking; 2 indicates strftime-style format checking;
!    0 indicates printf-style format checking.  FORMAT_NUM is the number
     of the argument which is the format control string (starting from 1).
     FIRST_ARG_NUM is the number of the first actual argument to check
!    against the format string, or zero if no checking is not be done
     (e.g. for varargs such as vfprintf).  */
  
  void
***************
*** 928,934 ****
  	}
        flag_chars[0] = 0;
        suppressed = wide = precise = FALSE;
!       if (info->is_scan)
  	{
  	  suppressed = *format_chars == '*';
  	  if (suppressed)
--- 949,956 ----
  	}
        flag_chars[0] = 0;
        suppressed = wide = precise = FALSE;
!       aflag = 0;
!       if (info->is_scan == 1)
  	{
  	  suppressed = *format_chars == '*';
  	  if (suppressed)
***************
*** 936,942 ****
  	  while (isdigit (*format_chars))
  	    ++format_chars;
  	}
!       else
  	{
  	  /* See if we have a number followed by a dollar sign.  If we do,
  	     it is an operand number, so set PARAMS to that operand.  */
--- 958,964 ----
  	  while (isdigit (*format_chars))
  	    ++format_chars;
  	}
!       else if (info->is_scan == 0)
  	{
  	  /* See if we have a number followed by a dollar sign.  If we do,
  	     it is an operand number, so set PARAMS to that operand.  */
***************
*** 966,972 ****
  		}
  	    }
  
! 	  while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
  	    {
  	      if (index (flag_chars, *format_chars) != 0)
  		{
--- 988,994 ----
  		}
  	    }
  
! 	  while (*format_chars != 0 && index (" +#0-_", *format_chars) != 0)
  	    {
  	      if (index (flag_chars, *format_chars) != 0)
  		{
***************
*** 1067,1072 ****
--- 1089,1096 ----
  		}
  	    }
  	}
+       if (info->is_scan != 2)
+       {
        if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'q' ||
  	  *format_chars == 'L')
  	length_char = *format_chars++;
***************
*** 1080,1085 ****
--- 1104,1110 ----
  	  aflag = 1;
  	  format_chars++;
  	}
+       }
        if (suppressed && length_char != 0)
  	{
  	  sprintf (message,
***************
*** 1094,1100 ****
  	  continue;
  	}
        format_chars++;
!       fci = info->is_scan ? scan_char_table : print_char_table;
        while (fci->format_chars != 0
  	     && index (fci->format_chars, format_char) == 0)
  	  ++fci;
--- 1119,1130 ----
  	  continue;
  	}
        format_chars++;
!       if (info->is_scan == 0)
! 	fci = print_char_table;
!       else if (info->is_scan == 1)
! 	fci = scan_char_table;
!       else
! 	fci = time_char_table;
        while (fci->format_chars != 0
  	     && index (fci->format_chars, format_char) == 0)
  	  ++fci;
***************
*** 1117,1122 ****
--- 1147,1164 ----
  		   format_char);
  	  warning (message);
  	}
+       if (index (fci->flag_chars, '2') != 0)
+ 	{
+ 	  sprintf (message, "`%c' format only yields last two digits of years",
+ 		   format_char);
+ 	  warning (message);
+ 	}
+       if (index (fci->flag_chars, '3') != 0)
+ 	{
+ 	  sprintf (message, "`%c' format may only yield last two digits of years in some locales",
+ 		   format_char);
+ 	  warning (message);
+ 	}
        if (precise && index (fci->flag_chars, 'p') == 0)
  	{
  	  sprintf (message, "precision used with `%c' format",
***************
*** 1129,1135 ****
  		   format_char);
  	  warning (message);
  	}
!       if (info->is_scan && format_char == '[')
  	{
  	  /* Skip over scan set, in case it happens to have '%' in it.  */
  	  if (*format_chars == '^')
--- 1171,1177 ----
  		   format_char);
  	  warning (message);
  	}
!       if (info->is_scan == 1 && format_char == '[')
  	{
  	  /* Skip over scan set, in case it happens to have '%' in it.  */
  	  if (*format_chars == '^')
***************
*** 1182,1188 ****
  	case 'q': wanted_type = fci->qlen ? *(fci->qlen) : 0; break;
  	case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
  	}
!       if (wanted_type == 0)
  	{
  	  sprintf (message,
  		   "use of `%c' length character with `%c' type character",
--- 1224,1230 ----
  	case 'q': wanted_type = fci->qlen ? *(fci->qlen) : 0; break;
  	case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
  	}
!       if (info->is_scan != 2 && wanted_type == 0)
  	{
  	  sprintf (message,
  		   "use of `%c' length character with `%c' type character",
