3 /***********************************************************************
4 * This code is part of GLPK (GNU Linear Programming Kit).
6 * Authors: Andrew Makhorin <mao@gnu.org>
7 * Heinrich Schuchardt <xypron.glpk@gmx.de>
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>.
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.
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.
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 ***********************************************************************/
32 double fn_gmtime(MPL *mpl)
33 { /* obtain the current calendar time (UTC) */
38 if (timer == (time_t)(-1))
39 err: error(mpl, "gmtime(); unable to obtain current calendar time");
41 if (tm == NULL) goto err;
42 j = jday(tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year);
44 return (((double)(j - jday(1, 1, 1970)) * 24.0 +
45 (double)tm->tm_hour) * 60.0 + (double)tm->tm_min) * 60.0 +
49 static char *week[] = { "Monday", "Tuesday", "Wednesday", "Thursday",
50 "Friday", "Saturday", "Sunday" };
52 static char *moon[] = { "January", "February", "March", "April", "May",
53 "June", "July", "August", "September", "October", "November",
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");
60 xprintf("%*s\n", (s - str) + 1, "^");
61 xprintf("Format string passed to str2time:\n");
63 xprintf("%*s\n", (f - fmt) + 1, "^");
64 error(mpl, "%s", msg);
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;
72 year = month = day = hh = mm = ss = -1, zone = INT_MAX;
74 for (f = fmt; *f != '\0'; f++)
77 if (*f == 'b' || *f == 'h')
78 { /* the abbreviated month name */
82 error1(mpl, str, s, fmt, f, "month multiply specified"
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;
92 for (k = 3; name[k] != '\0'; k++)
93 { if (toupper((unsigned char)*s) !=
94 toupper((unsigned char)name[k])) break;
101 error1(mpl, str, s, fmt, f, "abbreviated month name m"
102 "issing or invalid");
105 { /* the day of the month as a decimal number (01..31) */
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");
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");
118 { /* the hour as a decimal number, using a 24-hour clock
121 error1(mpl, str, s, fmt, f, "hour multiply specified")
123 while (*s == ' ') s++;
124 if (!('0' <= *s && *s <= '9'))
125 error1(mpl, str, s, fmt, f, "hour missing or invalid")
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");
134 { /* the month as a decimal number (01..12) */
136 error1(mpl, str, s, fmt, f, "month multiply specified"
138 while (*s == ' ') s++;
139 if (!('0' <= *s && *s <= '9'))
140 error1(mpl, str, s, fmt, f, "month missing or invalid"
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");
149 { /* the minute as a decimal number (00..59) */
151 error1(mpl, str, s, fmt, f, "minute multiply specifie"
153 while (*s == ' ') s++;
154 if (!('0' <= *s && *s <= '9'))
155 error1(mpl, str, s, fmt, f, "minute missing or invali"
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");
164 { /* the second as a decimal number (00..60) */
166 error1(mpl, str, s, fmt, f, "second multiply specifie"
168 while (*s == ' ') s++;
169 if (!('0' <= *s && *s <= '9'))
170 error1(mpl, str, s, fmt, f, "second missing or invali"
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");
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
184 error1(mpl, str, s, fmt, f, "year multiply specified")
186 while (*s == ' ') s++;
187 if (!('0' <= *s && *s <= '9'))
188 error1(mpl, str, s, fmt, f, "year missing or invalid")
191 if ('0' <= *s && *s <= '9')
192 year = 10 * year + ((*s++) - '0');
193 year += (year >= 69 ? 1900 : 2000);
196 { /* the year as a decimal number, using the Gregorian
199 error1(mpl, str, s, fmt, f, "year multiply specified")
201 while (*s == ' ') s++;
202 if (!('0' <= *s && *s <= '9'))
203 error1(mpl, str, s, fmt, f, "year missing or invalid")
206 for (j = 1; j <= 4; j++)
207 { if (!('0' <= *s && *s <= '9')) break;
208 year = 10 * year + ((*s++) - '0');
210 if (!(1 <= year && year <= 4000))
211 error1(mpl, str, s, fmt, f, "year out of range");
214 { /* time zone offset in the form zhhmm */
217 error1(mpl, str, s, fmt, f, "time zone offset multipl"
219 while (*s == ' ') s++;
221 { z = hh = mm = 0, s++;
229 error1(mpl, str, s, fmt, f, "time zone offset sign mi"
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');
239 err2: error1(mpl, str, s, fmt, f, "time zone offset value o"
243 if (!('0' <= *s && *s <= '9')) goto err1;
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');
251 if (mm > 59) goto err2;
252 skip: zone = z * (60 * hh + mm);
255 { /* literal % character */
259 error1(mpl, str, s, fmt, f, "invalid conversion specifie"
265 test: { /* check a matching character in the input string */
267 error1(mpl, str, s, fmt, f, "character mismatch");
271 if (year < 0) year = 1970;
272 if (month < 0) month = 1;
273 if (day < 0) day = 1;
277 if (zone == INT_MAX) zone = 0;
278 j = jday(day, month, year);
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;
284 static void error2(MPL *mpl, const char *fmt, const char *f,
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);
293 static int weekday(int j)
294 { /* determine weekday number (1 = Mon, ..., 7 = Sun) */
295 return (j + jday(1, 1, 1970)) % 7 + 1;
298 static int firstday(int year)
299 { /* determine the first day of the first week for a specified year
300 according to ISO 8601 */
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);
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);
316 /* the first day of the week must be Monday */
317 xassert(weekday(j) == 1);
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;
326 char buf[MAX_LENGTH+1];
327 if (!(-62135596800.0 <= t && t <= 64092211199.0))
328 error(mpl, "time2str(%.*g,...); argument out of range",
331 temp = fabs(t) / 86400.0;
332 j = (int)floor(temp);
334 { if (temp == floor(temp))
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;
345 for (f = fmt; *f != '\0'; f++)
349 { /* the abbreviated weekday name */
350 memcpy(buf, week[weekday(j)-1], 3), buf[3] = '\0';
353 { /* the full weekday name */
354 strcpy(buf, week[weekday(j)-1]);
356 else if (*f == 'b' || *f == 'h')
357 { /* the abbreviated month name */
358 memcpy(buf, moon[month-1], 3), buf[3] = '\0';
361 { /* the full month name */
362 strcpy(buf, moon[month-1]);
365 { /* the century of the year */
366 sprintf(buf, "%02d", year / 100);
369 { /* the day of the month as a decimal number (01..31) */
370 sprintf(buf, "%02d", day);
373 { /* the date using the format %m/%d/%y */
374 sprintf(buf, "%02d/%02d/%02d", month, day, year % 100);
377 { /* the day of the month like with %d, but padded with
379 sprintf(buf, "%2d", day);
382 { /* the date using the format %Y-%m-%d */
383 sprintf(buf, "%04d-%02d-%02d", year, month, day);
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 */
392 if (j < firstday(year))
394 else if (j < firstday(year + 1))
398 sprintf(buf, "%02d", iso % 100);
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 */
406 if (j < firstday(year))
408 else if (j < firstday(year + 1))
412 sprintf(buf, "%04d", iso);
415 { /* the hour as a decimal number, using a 24-hour clock
417 sprintf(buf, "%02d", hh);
420 { /* the hour as a decimal number, using a 12-hour clock
423 hh == 0 ? 12 : hh <= 12 ? hh : hh - 12);
426 { /* the day of the year as a decimal number (001..366) */
428 jday(day, month, year) - jday(1, 1, year) + 1);
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);
436 { /* the hour as a decimal number, using a 12-hour clock
437 like %I, but padded with blank (1..12) */
439 hh == 0 ? 12 : hh <= 12 ? hh : hh - 12);
442 { /* the month as a decimal number (01..12) */
443 sprintf(buf, "%02d", month);
446 { /* the minute as a decimal number (00..59) */
447 sprintf(buf, "%02d", mm);
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");
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");
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");
466 { /* the hour and minute using the format %H:%M */
467 sprintf(buf, "%02d:%02d", hh, mm);
470 { /* the second as a decimal number (00..59) */
471 sprintf(buf, "%02d", ss);
474 { /* the time of day using the format %H:%M:%S */
475 sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
478 { /* the day of the week as a decimal number (1..7),
480 sprintf(buf, "%d", weekday(j));
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
488 #if 1 /* 09/I-2009 */
490 /* causes compilation error in SunOS */
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);
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
508 if (j < firstday(year))
509 iso = j - firstday(year - 1);
510 else if (j < firstday(year + 1))
511 iso = j - firstday(year);
513 iso = j - firstday(year + 1);
514 sprintf(buf, "%02d", iso / 7 + 1);
517 { /* the day of the week as a decimal number (0..6),
519 sprintf(buf, "%d", weekday(j) % 7);
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
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);
534 { /* the year without a century as a decimal number
536 sprintf(buf, "%02d", year % 100);
539 { /* the year as a decimal number, using the Gregorian
541 sprintf(buf, "%04d", year);
544 { /* a literal % character */
545 buf[0] = '%', buf[1] = '\0';
548 error2(mpl, fmt, f, "invalid conversion specifier");
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));