On 9/25/2014 1:52 PM, Pedro F. Giffuni wrote:
> Author: pfg
> Date: Thu Sep 25 18:52:17 2014
> New Revision: 272122
> URL: http://svnweb.freebsd.org/changeset/base/272122
> 
> Log:
>   Add strptime(3) support for %U and %W
>   
>   Add support for the missing POSIX-2001 %U and %W features: the
>   existing  FreeBSD strptime code recognizes both directives and
>   validates that the week number lies in the permitted range,
>   but then simply discards the value.
>   
>   Initial support for the feature was written by Paul Green with
>   important fixes by Andrey Chernov. Additional support for
>   handling tm_wday/tm_yday was written by David Carlier.
>   
>   PR:         137307
>   MFC after:  1 month

Seems Relnotes worthy.

> 
> Modified:
>   head/lib/libc/stdtime/strptime.c
> 
> Modified: head/lib/libc/stdtime/strptime.c
> ==============================================================================
> --- head/lib/libc/stdtime/strptime.c  Thu Sep 25 18:43:52 2014        
> (r272121)
> +++ head/lib/libc/stdtime/strptime.c  Thu Sep 25 18:52:17 2014        
> (r272122)
> @@ -55,10 +55,32 @@ __FBSDID("$FreeBSD$");
>  #include "un-namespace.h"
>  #include "libc_private.h"
>  #include "timelocal.h"
> +#include "tzfile.h"
>  
>  static char * _strptime(const char *, const char *, struct tm *, int *, 
> locale_t);
>  
> -#define      asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
> +#define      asizeof(a)      (sizeof(a) / sizeof((a)[0]))
> +
> +#define      FLAG_NONE       (1 << 0)
> +#define      FLAG_YEAR       (1 << 1)
> +#define      FLAG_MONTH      (1 << 2)
> +#define      FLAG_YDAY       (1 << 3)
> +#define      FLAG_MDAY       (1 << 4)
> +#define      FLAG_WDAY       (1 << 5)
> +
> +/*
> + * Calculate the week day of the first day of a year. Valid for
> + * the Gregorian calendar, which began Sept 14, 1752 in the UK
> + * and its colonies. Ref:
> + * http://en.wikipedia.org/wiki/Calculating_the_day_of_the_week/
> + */
> +
> +static int
> +first_wday_of(int year)
> +{
> +     return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
> +             ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
> +}
>  
>  static char *
>  _strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
> @@ -66,9 +88,17 @@ _strptime(const char *buf, const char *f
>  {
>       char    c;
>       const char *ptr;
> +     int     day_offset = -1, wday_offset;
>       int     i, len;
> +     int flags;
>       int Ealternative, Oalternative;
> -     struct lc_time_T *tptr = __get_current_time_locale(locale);
> +     const struct lc_time_T *tptr = __get_current_time_locale(locale);
> +     static int start_of_month[2][13] = {
> +             {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
> +             {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
> +     };
> +
> +     flags = FLAG_NONE;
>  
>       ptr = fmt;
>       while (*ptr != 0) {
> @@ -119,7 +149,9 @@ label:
>                       if (i < 19)
>                               return (NULL);
>  
> -                     tm->tm_year = i * 100 - 1900;
> +                     tm->tm_year = i * 100 - TM_YEAR_BASE;
> +                     flags |= FLAG_YEAR;
> +
>                       break;
>  
>               case 'c':
> @@ -197,6 +229,8 @@ label:
>                               return (NULL);
>  
>                       tm->tm_yday = i - 1;
> +                     flags |= FLAG_YDAY;
> +
>                       break;
>  
>               case 'M':
> @@ -303,7 +337,32 @@ label:
>                               return (NULL);
>  
>                       tm->tm_wday = i;
> +                     if (day_offset >= 0 && (i - day_offset) != 0) {
> +                             tm->tm_yday += i - day_offset;
> +                             i = 0;
> +                             while (tm->tm_yday >=
> +                                     start_of_month[isleap(tm->tm_year +
> +                                                     TM_YEAR_BASE)][i])
> +                                     i++;
> +                             if (i > 12)
> +                             {
> +                                     i = 1;
> +                                     tm->tm_yday -=
> +                                             
> start_of_month[isleap(tm->tm_year +
> +                                                             TM_YEAR_BASE)]
> +                                             [12];
> +                                     tm->tm_year++;
> +                             }
> +                             tm->tm_mon = i - 1;
> +                             tm->tm_mday = tm->tm_yday -
> +                                     start_of_month[isleap(tm->tm_year +
> +                                                     TM_YEAR_BASE)]
> +                                     [i - 1] + 1;
> +                     }
>                       buf += len;
> +                     flags |= FLAG_YEAR | FLAG_MONTH | FLAG_YDAY |
> +                              FLAG_MDAY | FLAG_WDAY;
> +
>                       break;
>  
>               case 'U':
> @@ -313,6 +372,8 @@ label:
>                        * information present in the tm structure at this
>                        * point to calculate a real value, so just check the
>                        * range for now.
> +                      * We expect that the year has already been
> +                      * parsed.
>                        */
>                       if (!isdigit_l((unsigned char)*buf, locale))
>                               return (NULL);
> @@ -327,6 +388,45 @@ label:
>                       if (i > 53)
>                               return (NULL);
>  
> +                     /* Week numbers are l-origin. So that we can always
> +                      * return the date of a Sunday (or Monday), treat week
> +                      * 0 as week 1.
> +                      */
> +
> +                     if (i == 0)
> +                             i = 1;
> +
> +                     if (c == 'U')
> +                             day_offset = TM_SUNDAY;
> +                     else
> +                             day_offset = TM_MONDAY;
> +
> +                     /* Set the date to the first Sunday (or Monday)
> +                      * of the specified week of the year.
> +                      */
> +
> +                     tm->tm_yday = (7 - first_wday_of(tm->tm_year +
> +                             TM_YEAR_BASE) + day_offset) % 7 + (i - 1) * 7;
> +                     i = 0;
> +                     while (tm->tm_yday >=
> +                             start_of_month[isleap(tm->tm_year + 
> TM_YEAR_BASE)][i])
> +                             i++;
> +                     if (i > 12)
> +                     {
> +                             i = 1;
> +                             tm->tm_yday -=
> +                                     start_of_month[isleap(tm->tm_year +
> +                                                     TM_YEAR_BASE)][12];
> +                             tm->tm_year++;
> +                     }
> +                     tm->tm_mon = i - 1;
> +                     tm->tm_mday = tm->tm_yday -
> +                             start_of_month[isleap(tm->tm_year + 
> TM_YEAR_BASE)]
> +                             [i - 1] + 1;
> +                     tm->tm_wday = day_offset;
> +                     flags |= FLAG_YEAR | FLAG_MONTH | FLAG_YDAY |
> +                              FLAG_MDAY | FLAG_WDAY;
> +
>                       break;
>  
>               case 'w':
> @@ -338,6 +438,7 @@ label:
>                               return (NULL);
>  
>                       tm->tm_wday = i;
> +                     flags != FLAG_WDAY;
>  
>                       break;
>  
> @@ -374,6 +475,7 @@ label:
>                               return (NULL);
>  
>                       tm->tm_mday = i;
> +                     flags |= FLAG_MDAY;
>  
>                       break;
>  
> @@ -413,6 +515,8 @@ label:
>  
>                       tm->tm_mon = i;
>                       buf += len;
> +                     flags |= FLAG_MONTH;
> +
>                       break;
>  
>               case 'm':
> @@ -430,6 +534,7 @@ label:
>                               return (NULL);
>  
>                       tm->tm_mon = i - 1;
> +                     flags |= FLAG_MONTH;
>  
>                       break;
>  
> @@ -471,13 +576,14 @@ label:
>                               len--;
>                       }
>                       if (c == 'Y')
> -                             i -= 1900;
> +                             i -= TM_YEAR_BASE;
>                       if (c == 'y' && i < 69)
>                               i += 100;
>                       if (i < 0)
>                               return (NULL);
>  
>                       tm->tm_year = i;
> +                     flags |= FLAG_YEAR;
>  
>                       break;
>  
> @@ -543,10 +649,25 @@ label:
>                       break;
>               }
>       }
> +
> +     if (flags & (FLAG_YEAR | FLAG_MONTH)) {
> +             if (!tm->tm_yday && (flags & FLAG_MDAY))
> +                     tm->tm_yday = start_of_month[isleap(tm->tm_year
> +                             + TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 
> 1);
> +             if (!tm->tm_wday) {
> +                     i = 0;
> +                     wday_offset = first_wday_of(tm->tm_year);
> +                     while (i++ <= tm->tm_yday)
> +                             if (wday_offset++ >= 6)
> +                                     wday_offset = 0;
> +
> +                     tm->tm_wday = wday_offset;
> +             }
> +     }
> +
>       return ((char *)buf);
>  }
>  
> -
>  char *
>  strptime_l(const char * __restrict buf, const char * __restrict fmt,
>      struct tm * __restrict tm, locale_t loc)
> 


-- 
Regards,
Bryan Drewery

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to