src/glpmpl05.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 05 Dec 2010 17:35:23 +0100
changeset 2 4c8956a7bdf4
permissions -rw-r--r--
Set up CMAKE build environment
     1 /* glpmpl05.c */
     2 
     3 /***********************************************************************
     4 *  This code is part of GLPK (GNU Linear Programming Kit).
     5 *
     6 *  Authors: Andrew Makhorin <mao@gnu.org>
     7 *           Heinrich Schuchardt <xypron.glpk@gmx.de>
     8 *
     9 *  Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
    10 *  2009, 2010 Andrew Makhorin, Department for Applied Informatics,
    11 *  Moscow Aviation Institute, Moscow, Russia. All rights reserved.
    12 *  E-mail: <mao@gnu.org>.
    13 *
    14 *  GLPK is free software: you can redistribute it and/or modify it
    15 *  under the terms of the GNU General Public License as published by
    16 *  the Free Software Foundation, either version 3 of the License, or
    17 *  (at your option) any later version.
    18 *
    19 *  GLPK is distributed in the hope that it will be useful, but WITHOUT
    20 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    21 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
    22 *  License for more details.
    23 *
    24 *  You should have received a copy of the GNU General Public License
    25 *  along with GLPK. If not, see <http://www.gnu.org/licenses/>.
    26 ***********************************************************************/
    27 
    28 #define _GLPSTD_STDIO
    29 #define _GLPSTD_TIME
    30 #include "glpmpl.h"
    31 
    32 double fn_gmtime(MPL *mpl)
    33 {     /* obtain the current calendar time (UTC) */
    34       time_t timer;
    35       struct tm *tm;
    36       int j;
    37       time(&timer);
    38       if (timer == (time_t)(-1))
    39 err:     error(mpl, "gmtime(); unable to obtain current calendar time");
    40       tm = gmtime(&timer);
    41       if (tm == NULL) goto err;
    42       j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year);
    43       if (j < 0) goto err;
    44       return (((double)(j - jday(1, 1, 1970)) * 24.0 +
    45          (double)tm->tm_hour) * 60.0 + (double)tm->tm_min) * 60.0 +
    46          (double)tm->tm_sec;
    47 }
    48 
    49 static char *week[] = { "Monday", "Tuesday", "Wednesday", "Thursday",
    50       "Friday", "Saturday", "Sunday" };
    51 
    52 static char *moon[] = { "January", "February", "March", "April", "May",
    53       "June", "July", "August", "September", "October", "November",
    54       "December" };
    55 
    56 static void error1(MPL *mpl, const char *str, const char *s,
    57       const char *fmt, const char *f, const char *msg)
    58 {     xprintf("Input string passed to str2time:\n");
    59       xprintf("%s\n", str);
    60       xprintf("%*s\n", (s - str) + 1, "^");
    61       xprintf("Format string passed to str2time:\n");
    62       xprintf("%s\n", fmt);
    63       xprintf("%*s\n", (f - fmt) + 1, "^");
    64       error(mpl, "%s", msg);
    65       /* no return */
    66 }
    67 
    68 double fn_str2time(MPL *mpl, const char *str, const char *fmt)
    69 {     /* convert character string to the calendar time */
    70       int j, year, month, day, hh, mm, ss, zone;
    71       const char *s, *f;
    72       year = month = day = hh = mm = ss = -1, zone = INT_MAX;
    73       s = str;
    74       for (f = fmt; *f != '\0'; f++)
    75       {  if (*f == '%')
    76          {  f++;
    77             if (*f == 'b' || *f == 'h')
    78             {  /* the abbreviated month name */
    79                int k;
    80                char *name;
    81                if (month >= 0)
    82                   error1(mpl, str, s, fmt, f, "month multiply specified"
    83                      );
    84                while (*s == ' ') s++;
    85                for (month = 1; month <= 12; month++)
    86                {  name = moon[month-1];
    87                   for (k = 0; k <= 2; k++)
    88                   {  if (toupper((unsigned char)s[k]) !=
    89                          toupper((unsigned char)name[k])) goto next;
    90                   }
    91                   s += 3;
    92                   for (k = 3; name[k] != '\0'; k++)
    93                   {  if (toupper((unsigned char)*s) !=
    94                          toupper((unsigned char)name[k])) break;
    95                      s++;
    96                   }
    97                   break;
    98 next:             ;
    99                }
   100                if (month > 12)
   101                   error1(mpl, str, s, fmt, f, "abbreviated month name m"
   102                      "issing or invalid");
   103             }
   104             else if (*f == 'd')
   105             {  /* the day of the month as a decimal number (01..31) */
   106                if (day >= 0)
   107                   error1(mpl, str, s, fmt, f, "day multiply specified");
   108                while (*s == ' ') s++;
   109                if (!('0' <= *s && *s <= '9'))
   110                   error1(mpl, str, s, fmt, f, "day missing or invalid");
   111                day = (*s++) - '0';
   112                if ('0' <= *s && *s <= '9')
   113                   day = 10 * day + ((*s++) - '0');
   114                if (!(1 <= day && day <= 31))
   115                   error1(mpl, str, s, fmt, f, "day out of range");
   116             }
   117             else if (*f == 'H')
   118             {  /* the hour as a decimal number, using a 24-hour clock
   119                   (00..23) */
   120                if (hh >= 0)
   121                   error1(mpl, str, s, fmt, f, "hour multiply specified")
   122                      ;
   123                while (*s == ' ') s++;
   124                if (!('0' <= *s && *s <= '9'))
   125                   error1(mpl, str, s, fmt, f, "hour missing or invalid")
   126                      ;
   127                hh = (*s++) - '0';
   128                if ('0' <= *s && *s <= '9')
   129                   hh = 10 * hh + ((*s++) - '0');
   130                if (!(0 <= hh && hh <= 23))
   131                   error1(mpl, str, s, fmt, f, "hour out of range");
   132             }
   133             else if (*f == 'm')
   134             {  /* the month as a decimal number (01..12) */
   135                if (month >= 0)
   136                   error1(mpl, str, s, fmt, f, "month multiply specified"
   137                      );
   138                while (*s == ' ') s++;
   139                if (!('0' <= *s && *s <= '9'))
   140                   error1(mpl, str, s, fmt, f, "month missing or invalid"
   141                      );
   142                month = (*s++) - '0';
   143                if ('0' <= *s && *s <= '9')
   144                   month = 10 * month + ((*s++) - '0');
   145                if (!(1 <= month && month <= 12))
   146                   error1(mpl, str, s, fmt, f, "month out of range");
   147             }
   148             else if (*f == 'M')
   149             {  /* the minute as a decimal number (00..59) */
   150                if (mm >= 0)
   151                   error1(mpl, str, s, fmt, f, "minute multiply specifie"
   152                      "d");
   153                while (*s == ' ') s++;
   154                if (!('0' <= *s && *s <= '9'))
   155                   error1(mpl, str, s, fmt, f, "minute missing or invali"
   156                      "d");
   157                mm = (*s++) - '0';
   158                if ('0' <= *s && *s <= '9')
   159                   mm = 10 * mm + ((*s++) - '0');
   160                if (!(0 <= mm && mm <= 59))
   161                   error1(mpl, str, s, fmt, f, "minute out of range");
   162             }
   163             else if (*f == 'S')
   164             {  /* the second as a decimal number (00..60) */
   165                if (ss >= 0)
   166                   error1(mpl, str, s, fmt, f, "second multiply specifie"
   167                      "d");
   168                while (*s == ' ') s++;
   169                if (!('0' <= *s && *s <= '9'))
   170                   error1(mpl, str, s, fmt, f, "second missing or invali"
   171                      "d");
   172                ss = (*s++) - '0';
   173                if ('0' <= *s && *s <= '9')
   174                   ss = 10 * ss + ((*s++) - '0');
   175                if (!(0 <= ss && ss <= 60))
   176                   error1(mpl, str, s, fmt, f, "second out of range");
   177             }
   178             else if (*f == 'y')
   179             {  /* the year without a century as a decimal number
   180                   (00..99); the values 00 to 68 mean the years 2000 to
   181                   2068 while the values 69 to 99 mean the years 1969 to
   182                   1999 */
   183                if (year >= 0)
   184                   error1(mpl, str, s, fmt, f, "year multiply specified")
   185                      ;
   186                while (*s == ' ') s++;
   187                if (!('0' <= *s && *s <= '9'))
   188                   error1(mpl, str, s, fmt, f, "year missing or invalid")
   189                      ;
   190                year = (*s++) - '0';
   191                if ('0' <= *s && *s <= '9')
   192                   year = 10 * year + ((*s++) - '0');
   193                year += (year >= 69 ? 1900 : 2000);
   194             }
   195             else if (*f == 'Y')
   196             {  /* the year as a decimal number, using the Gregorian
   197                   calendar */
   198                if (year >= 0)
   199                   error1(mpl, str, s, fmt, f, "year multiply specified")
   200                      ;
   201                while (*s == ' ') s++;
   202                if (!('0' <= *s && *s <= '9'))
   203                   error1(mpl, str, s, fmt, f, "year missing or invalid")
   204                      ;
   205                year = 0;
   206                for (j = 1; j <= 4; j++)
   207                {  if (!('0' <= *s && *s <= '9')) break;
   208                   year = 10 * year + ((*s++) - '0');
   209                }
   210                if (!(1 <= year && year <= 4000))
   211                   error1(mpl, str, s, fmt, f, "year out of range");
   212             }
   213             else if (*f == 'z')
   214             {  /* time zone offset in the form zhhmm */
   215                int z, hh, mm;
   216                if (zone != INT_MAX)
   217                   error1(mpl, str, s, fmt, f, "time zone offset multipl"
   218                      "y specified");
   219                while (*s == ' ') s++;
   220                if (*s == 'Z')
   221                {  z = hh = mm = 0, s++;
   222                   goto skip;
   223                }
   224                if (*s == '+')
   225                   z = +1, s++;
   226                else if (*s == '-')
   227                   z = -1, s++;
   228                else
   229                   error1(mpl, str, s, fmt, f, "time zone offset sign mi"
   230                      "ssing");
   231                hh = 0;
   232                for (j = 1; j <= 2; j++)
   233                {  if (!('0' <= *s && *s <= '9'))
   234 err1:                error1(mpl, str, s, fmt, f, "time zone offset valu"
   235                         "e incomplete or invalid");
   236                   hh = 10 * hh + ((*s++) - '0');
   237                }
   238                if (hh > 23)
   239 err2:             error1(mpl, str, s, fmt, f, "time zone offset value o"
   240                      "ut of range");
   241                if (*s == ':')
   242                {  s++;
   243                   if (!('0' <= *s && *s <= '9')) goto err1;
   244                }
   245                mm = 0;
   246                if (!('0' <= *s && *s <= '9')) goto skip;
   247                for (j = 1; j <= 2; j++)
   248                {  if (!('0' <= *s && *s <= '9')) goto err1;
   249                   mm = 10 * mm + ((*s++) - '0');
   250                }
   251                if (mm > 59) goto err2;
   252 skip:          zone = z * (60 * hh + mm);
   253             }
   254             else if (*f == '%')
   255             {  /* literal % character */
   256                goto test;
   257             }
   258             else
   259                error1(mpl, str, s, fmt, f, "invalid conversion specifie"
   260                   "r");
   261          }
   262          else if (*f == ' ')
   263             ;
   264          else
   265 test:    {  /* check a matching character in the input string */
   266             if (*s != *f)
   267                error1(mpl, str, s, fmt, f, "character mismatch");
   268             s++;
   269          }
   270       }
   271       if (year < 0) year = 1970;
   272       if (month < 0) month = 1;
   273       if (day < 0) day = 1;
   274       if (hh < 0) hh = 0;
   275       if (mm < 0) mm = 0;
   276       if (ss < 0) ss = 0;
   277       if (zone == INT_MAX) zone = 0;
   278       j = jday(day, month, year);
   279       xassert(j >= 0);
   280       return (((double)(j - jday(1, 1, 1970)) * 24.0 + (double)hh) *
   281          60.0 + (double)mm) * 60.0 + (double)ss - 60.0 * (double)zone;
   282 }
   283 
   284 static void error2(MPL *mpl, const char *fmt, const char *f,
   285       const char *msg)
   286 {     xprintf("Format string passed to time2str:\n");
   287       xprintf("%s\n", fmt);
   288       xprintf("%*s\n", (f - fmt) + 1, "^");
   289       error(mpl, "%s", msg);
   290       /* no return */
   291 }
   292 
   293 static int weekday(int j)
   294 {     /* determine weekday number (1 = Mon, ..., 7 = Sun) */
   295       return (j + jday(1, 1, 1970)) % 7 + 1;
   296 }
   297 
   298 static int firstday(int year)
   299 {     /* determine the first day of the first week for a specified year
   300          according to ISO 8601 */
   301       int j;
   302       /* if 1 January is Monday, Tuesday, Wednesday or Thursday, it is
   303          in week 01; if 1 January is Friday, Saturday or Sunday, it is
   304          in week 52 or 53 of the previous year */
   305       j = jday(1, 1, year) - jday(1, 1, 1970);
   306       switch (weekday(j))
   307       {  case 1: /* 1 Jan is Mon */ j += 0; break;
   308          case 2: /* 1 Jan is Tue */ j -= 1; break;
   309          case 3: /* 1 Jan is Wed */ j -= 2; break;
   310          case 4: /* 1 Jan is Thu */ j -= 3; break;
   311          case 5: /* 1 Jan is Fri */ j += 3; break;
   312          case 6: /* 1 Jan is Sat */ j += 2; break;
   313          case 7: /* 1 Jan is Sun */ j += 1; break;
   314          default: xassert(j != j);
   315       }
   316       /* the first day of the week must be Monday */
   317       xassert(weekday(j) == 1);
   318       return j;
   319 }
   320 
   321 void fn_time2str(MPL *mpl, char *str, double t, const char *fmt)
   322 {     /* convert the calendar time to character string */
   323       int j, year, month, day, hh, mm, ss, len;
   324       double temp;
   325       const char *f;
   326       char buf[MAX_LENGTH+1];
   327       if (!(-62135596800.0 <= t && t <= 64092211199.0))
   328          error(mpl, "time2str(%.*g,...); argument out of range",
   329             DBL_DIG, t);
   330       t = floor(t + 0.5);
   331       temp = fabs(t) / 86400.0;
   332       j = (int)floor(temp);
   333       if (t < 0.0)
   334       {  if (temp == floor(temp))
   335             j = - j;
   336          else
   337             j = - (j + 1);
   338       }
   339       xassert(jdate(j + jday(1, 1, 1970), &day, &month, &year) == 0);
   340       ss = (int)(t - 86400.0 * (double)j);
   341       xassert(0 <= ss && ss < 86400);
   342       mm = ss / 60, ss %= 60;
   343       hh = mm / 60, mm %= 60;
   344       len = 0;
   345       for (f = fmt; *f != '\0'; f++)
   346       {  if (*f == '%')
   347          {  f++;
   348             if (*f == 'a')
   349             {  /* the abbreviated weekday name */
   350                memcpy(buf, week[weekday(j)-1], 3), buf[3] = '\0';
   351             }
   352             else if (*f == 'A')
   353             {  /* the full weekday name */
   354                strcpy(buf, week[weekday(j)-1]);
   355             }
   356             else if (*f == 'b' || *f == 'h')
   357             {  /* the abbreviated month name */
   358                memcpy(buf, moon[month-1], 3), buf[3] = '\0';
   359             }
   360             else if (*f == 'B')
   361             {  /* the full month name */
   362                strcpy(buf, moon[month-1]);
   363             }
   364             else if (*f == 'C')
   365             {  /* the century of the year */
   366                sprintf(buf, "%02d", year / 100);
   367             }
   368             else if (*f == 'd')
   369             {  /* the day of the month as a decimal number (01..31) */
   370                sprintf(buf, "%02d", day);
   371             }
   372             else if (*f == 'D')
   373             {  /* the date using the format %m/%d/%y */
   374                sprintf(buf, "%02d/%02d/%02d", month, day, year % 100);
   375             }
   376             else if (*f == 'e')
   377             {  /* the day of the month like with %d, but padded with
   378                   blank (1..31) */
   379                sprintf(buf, "%2d", day);
   380             }
   381             else if (*f == 'F')
   382             {  /* the date using the format %Y-%m-%d */
   383                sprintf(buf, "%04d-%02d-%02d", year, month, day);
   384             }
   385             else if (*f == 'g')
   386             {  /* the year corresponding to the ISO week number, but
   387                   without the century (range 00 through 99); this has
   388                   the same format and value as %y, except that if the
   389                   ISO week number (see %V) belongs to the previous or
   390                   next year, that year is used instead */
   391                int iso;
   392                if (j < firstday(year))
   393                   iso = year - 1;
   394                else if (j < firstday(year + 1))
   395                   iso = year;
   396                else
   397                   iso = year + 1;
   398                sprintf(buf, "%02d", iso % 100);
   399             }
   400             else if (*f == 'G')
   401             {  /* the year corresponding to the ISO week number; this
   402                   has the same format and value as %Y, excepth that if
   403                   the ISO week number (see %V) belongs to the previous
   404                   or next year, that year is used instead */
   405                int iso;
   406                if (j < firstday(year))
   407                   iso = year - 1;
   408                else if (j < firstday(year + 1))
   409                   iso = year;
   410                else
   411                   iso = year + 1;
   412                sprintf(buf, "%04d", iso);
   413             }
   414             else if (*f == 'H')
   415             {  /* the hour as a decimal number, using a 24-hour clock
   416                   (00..23) */
   417                sprintf(buf, "%02d", hh);
   418             }
   419             else if (*f == 'I')
   420             {  /* the hour as a decimal number, using a 12-hour clock
   421                   (01..12) */
   422                sprintf(buf, "%02d",
   423                   hh == 0 ? 12 : hh <= 12 ? hh : hh - 12);
   424             }
   425             else if (*f == 'j')
   426             {  /* the day of the year as a decimal number (001..366) */
   427                sprintf(buf, "%03d",
   428                   jday(day, month, year) - jday(1, 1, year) + 1);
   429             }
   430             else if (*f == 'k')
   431             {  /* the hour as a decimal number, using a 24-hour clock
   432                   like %H, but padded with blank (0..23) */
   433                sprintf(buf, "%2d", hh);
   434             }
   435             else if (*f == 'l')
   436             {  /* the hour as a decimal number, using a 12-hour clock
   437                   like %I, but padded with blank (1..12) */
   438                sprintf(buf, "%2d",
   439                   hh == 0 ? 12 : hh <= 12 ? hh : hh - 12);
   440             }
   441             else if (*f == 'm')
   442             {  /* the month as a decimal number (01..12) */
   443                sprintf(buf, "%02d", month);
   444             }
   445             else if (*f == 'M')
   446             {  /* the minute as a decimal number (00..59) */
   447                sprintf(buf, "%02d", mm);
   448             }
   449             else if (*f == 'p')
   450             {  /* either AM or PM, according to the given time value;
   451                   noon is treated as PM and midnight as AM */
   452                strcpy(buf, hh <= 11 ? "AM" : "PM");
   453             }
   454             else if (*f == 'P')
   455             {  /* either am or pm, according to the given time value;
   456                   noon is treated as pm and midnight as am */
   457                strcpy(buf, hh <= 11 ? "am" : "pm");
   458             }
   459             else if (*f == 'r')
   460             {  /* the calendar time using the format %I:%M:%S %p */
   461                sprintf(buf, "%02d:%02d:%02d %s",
   462                   hh == 0 ? 12 : hh <= 12 ? hh : hh - 12,
   463                   mm, ss, hh <= 11 ? "AM" : "PM");
   464             }
   465             else if (*f == 'R')
   466             {  /* the hour and minute using the format %H:%M */
   467                sprintf(buf, "%02d:%02d", hh, mm);
   468             }
   469             else if (*f == 'S')
   470             {  /* the second as a decimal number (00..59) */
   471                sprintf(buf, "%02d", ss);
   472             }
   473             else if (*f == 'T')
   474             {  /* the time of day using the format %H:%M:%S */
   475                sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
   476             }
   477             else if (*f == 'u')
   478             {  /* the day of the week as a decimal number (1..7),
   479                   Monday being 1 */
   480                sprintf(buf, "%d", weekday(j));
   481             }
   482             else if (*f == 'U')
   483             {  /* the week number of the current year as a decimal
   484                   number (range 00 through 53), starting with the first
   485                   Sunday as the first day of the first week; days
   486                   preceding the first Sunday in the year are considered
   487                   to be in week 00 */
   488 #if 1 /* 09/I-2009 */
   489 #undef sun
   490 /* causes compilation error in SunOS */
   491 #endif
   492                int sun;
   493                /* sun = the first Sunday of the year */
   494                sun = jday(1, 1, year) - jday(1, 1, 1970);
   495                sun += (7 - weekday(sun));
   496                sprintf(buf, "%02d", (j + 7 - sun) / 7);
   497             }
   498             else if (*f == 'V')
   499             {  /* the ISO week number as a decimal number (range 01
   500                   through 53); ISO weeks start with Monday and end with
   501                   Sunday; week 01 of a year is the first week which has
   502                   the majority of its days in that year; week 01 of
   503                   a year can contain days from the previous year; the
   504                   week before week 01 of a year is the last week (52 or
   505                   53) of the previous year even if it contains days
   506                   from the new year */
   507                int iso;
   508                if (j < firstday(year))
   509                   iso = j - firstday(year - 1);
   510                else if (j < firstday(year + 1))
   511                   iso = j - firstday(year);
   512                else
   513                   iso = j - firstday(year + 1);
   514                sprintf(buf, "%02d", iso / 7 + 1);
   515             }
   516             else if (*f == 'w')
   517             {  /* the day of the week as a decimal number (0..6),
   518                   Sunday being 0 */
   519                sprintf(buf, "%d", weekday(j) % 7);
   520             }
   521             else if (*f == 'W')
   522             {  /* the week number of the current year as a decimal
   523                   number (range 00 through 53), starting with the first
   524                   Monday as the first day of the first week; days
   525                   preceding the first Monday in the year are considered
   526                   to be in week 00 */
   527                int mon;
   528                /* mon = the first Monday of the year */
   529                mon = jday(1, 1, year) - jday(1, 1, 1970);
   530                mon += (8 - weekday(mon)) % 7;
   531                sprintf(buf, "%02d", (j + 7 - mon) / 7);
   532             }
   533             else if (*f == 'y')
   534             {  /* the year without a century as a decimal number
   535                   (00..99) */
   536                sprintf(buf, "%02d", year % 100);
   537             }
   538             else if (*f == 'Y')
   539             {  /* the year as a decimal number, using the Gregorian
   540                   calendar */
   541                sprintf(buf, "%04d", year);
   542             }
   543             else if (*f == '%')
   544             {  /* a literal % character */
   545                buf[0] = '%', buf[1] = '\0';
   546             }
   547             else
   548                error2(mpl, fmt, f, "invalid conversion specifier");
   549          }
   550          else
   551             buf[0] = *f, buf[1] = '\0';
   552          if (len + strlen(buf) > MAX_LENGTH)
   553             error(mpl, "time2str; output string length exceeds %d chara"
   554                "cters", MAX_LENGTH);
   555          memcpy(str+len, buf, strlen(buf));
   556          len += strlen(buf);
   557       }
   558       str[len] = '\0';
   559       return;
   560 }
   561 
   562 /* eof */