blob: b7253ac918966a6b5d61eb44984b66314319abb1 [file] [log] [blame]
/*****************************************************************************\
* src/common/parse_time.c - time parsing utility functions
* $Id$
*****************************************************************************
* Copyright (C) 2005-2006 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Morris Jette <jette1@llnl.gov>.
* UCRL-CODE-226842.
*
* This file is part of SLURM, a resource management program.
* For details, see <http://www.llnl.gov/linux/slurm/>.
*
* SLURM is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with SLURM; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include <stdio.h>
#include <time.h>
#include <strings.h>
#ifndef __USE_ISOC99
# define __USE_ISOC99 /* isblank() */
#endif
#include <ctype.h>
#include <slurm/slurm.h>
#define _RUN_STAND_ALONE 0
time_t time_now;
struct tm *time_now_tm;
/* convert time differential string into a number of seconds
* time_str (in): string to parse
* pos (in/out): position of parse start/end
* delta (out): delta in seconds
* RET: -1 on error, 0 otherwise
*/
static int _get_delta(char *time_str, int *pos, long *delta)
{
int offset;
long cnt = 0;
offset = (*pos) + 1;
for ( ; ((time_str[offset]!='\0')&&(time_str[offset]!='\n')); offset++) {
if (isspace(time_str[offset]))
continue;
if (strncasecmp(time_str+offset, "minutes", 7) == 0) {
cnt *= 60;
offset +=7;
break;
}
if (strncasecmp(time_str+offset, "hours", 5) == 0) {
cnt *= (60 * 60);
offset += 5;
break;
}
if (strncasecmp(time_str+offset, "days", 4) == 0) {
cnt *= (24 * 60 * 60);
offset += 4;
break;
}
if (strncasecmp(time_str+offset, "weeks", 5) == 0) {
cnt *= (7 * 24 * 60 * 60);
offset += 5;
break;
}
if ((time_str[offset] >= '0') && (time_str[offset] <= '9')) {
cnt = (cnt * 10) + (time_str[offset] - '0');
continue;
}
goto prob;
}
*pos = offset - 1;
*delta = cnt;
return 0;
prob: *pos = offset - 1;
return -1;
}
/* convert "HH:MM[:SS] [AM|PM]" string to numeric values
* time_str (in): string to parse
* pos (in/out): position of parse start/end
* hour, minute, second (out): numberic values
* RET: -1 on error, 0 otherwise
*/
static int
_get_time(char *time_str, int *pos, int *hour, int *minute, int * second)
{
int hr, min, sec;
int offset = *pos;
/* get hour */
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
hr = time_str[offset++] - '0';
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
hr = (hr * 10) + time_str[offset++] - '0';
if (hr > 23) {
offset -= 2;
goto prob;
}
if (time_str[offset] != ':')
goto prob;
offset++;
/* get minute */
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
min = time_str[offset++] - '0';
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
min = (min * 10) + time_str[offset++] - '0';
if (min > 59) {
offset -= 2;
goto prob;
}
/* get optional second */
if (time_str[offset] == ':') {
offset++;
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
sec = time_str[offset++] - '0';
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
sec = (sec * 10) + time_str[offset++] - '0';
if (sec > 59) {
offset -= 2;
goto prob;
}
} else
sec = 0;
while (isspace(time_str[offset])) {
offset++;
}
if (strncasecmp(time_str+offset, "pm", 2)== 0) {
hr += 12;
if (hr > 23) {
if (hr == 24)
hr = 12;
else
goto prob;
}
offset += 2;
} else if (strncasecmp(time_str+offset, "am", 2) == 0) {
if (hr > 11) {
if (hr == 12)
hr = 0;
else
goto prob;
}
offset += 2;
}
*pos = offset - 1;
*hour = hr;
*minute = min;
*second = sec;
return 0;
prob: *pos = offset;
return -1;
}
/* convert "MMDDYY" "MM.DD.YY" or "MM/DD/YY" string to numeric values
* time_str (in): string to parse
* pos (in/out): position of parse start/end
* month, mday, year (out): numberic values
* RET: -1 on error, 0 otherwise
*/
static int _get_date(char *time_str, int *pos, int *month, int *mday, int *year)
{
int mon, day, yr;
int offset = *pos;
/* get month */
mon = time_str[offset++] - '0';
if ((time_str[offset] >= '0') && (time_str[offset] <= '9'))
mon = (mon * 10) + time_str[offset++] - '0';
if ((mon < 1) || (mon > 12)) {
offset -= 2;
goto prob;
}
if ((time_str[offset] == '.') || (time_str[offset] == '/'))
offset++;
/* get day */
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
day = time_str[offset++] - '0';
if ((time_str[offset] >= '0') && (time_str[offset] <= '9'))
day = (day * 10) + time_str[offset++] - '0';
if ((day < 1) || (day > 31)) {
offset -= 2;
goto prob;
}
if ((time_str[offset] == '.') || (time_str[offset] == '/'))
offset++;
/* get optional year */
if ((time_str[offset] >= '0') && (time_str[offset] <= '9')) {
yr = time_str[offset++] - '0';
if ((time_str[offset] < '0') || (time_str[offset] > '9'))
goto prob;
yr = (yr * 10) + time_str[offset++] - '0';
} else
yr = 0;
*pos = offset - 1;
*month = mon - 1; /* zero origin */
*mday = day;
if (yr)
*year = yr + 100; /* 1900 == "00" */
return 0;
prob: *pos = offset;
return -1;
}
/* Convert string to equivalent time value
* input formats:
* today or tomorrow
* midnight, noon, teatime (4PM)
* HH:MM[:SS] [AM|PM]
* MMDD[YY] or MM/DD[/YY] or MM.DD[.YY]
* now + count [minutes | hours | days | weeks]
*
* Invalid input results in message to stderr and return value of zero
* NOTE: not thread safe
*/
extern time_t parse_time(char *time_str)
{
int hour = -1, minute = -1, second = 0;
int month = -1, mday = -1, year = -1;
int pos = 0;
struct tm res_tm;
time_now = time(NULL);
time_now_tm = localtime(&time_now);
for (pos=0; ((time_str[pos] != '\0')&&(time_str[pos] != '\n')); pos++) {
if (isblank(time_str[pos]) || (time_str[pos] == '-'))
continue;
if (strncasecmp(time_str+pos, "today", 5) == 0) {
month = time_now_tm->tm_mon;
mday = time_now_tm->tm_mday;
year = time_now_tm->tm_year;
pos += 4;
continue;
}
if (strncasecmp(time_str+pos, "tomorrow", 8) == 0) {
time_t later = time_now + (24 * 60 * 60);
struct tm *later_tm = localtime(&later);
month = later_tm->tm_mon;
mday = later_tm->tm_mday;
year = later_tm->tm_year;
pos += 7;
continue;
}
if (strncasecmp(time_str+pos, "midnight", 8) == 0) {
hour = 0;
minute = 0;
second = 0;
pos += 7;
continue;
}
if (strncasecmp(time_str+pos, "noon", 4) == 0) {
hour = 12;
minute = 0;
pos += 3;
continue;
}
if (strncasecmp(time_str+pos, "teatime", 7) == 0) {
hour = 16;
minute = 0;
pos += 6;
continue;
}
if (strncasecmp(time_str+pos, "now", 3) == 0) {
int i;
long delta = 0;
time_t later;
struct tm *later_tm;
for (i=(pos+3); ; i++) {
if (time_str[i] == '+') {
pos += i;
if (_get_delta(time_str, &pos, &delta))
goto prob;
break;
}
if (isblank(time_str[i]))
continue;
if ((time_str[i] == '\0') || (time_str[i] == '\n')) {
pos += (i-1);
break;
}
pos += i;
goto prob;
}
later = time_now + delta;
later_tm = localtime(&later);
month = later_tm->tm_mon;
mday = later_tm->tm_mday;
year = later_tm->tm_year;
hour = later_tm->tm_hour;
minute = later_tm->tm_min;
second = later_tm->tm_sec;
continue;
}
if ((time_str[pos] < '0') || (time_str[pos] > '9')) /* invalid */
goto prob;
/* We have some numeric value to process */
if (time_str[pos+2] == ':') { /* time */
if (_get_time(time_str, &pos, &hour, &minute, &second))
goto prob;
continue;
}
if (_get_date(time_str, &pos, &month, &mday, &year))
goto prob;
}
/* printf("%d/%d/%d %d:%d\n",month+1,mday,year+1900,hour+1,minute); */
if ((hour == -1) && (month == -1)) /* nothing specified, time=0 */
return (time_t) 0;
else if ((hour == -1) && (month != -1)) { /* date, no time implies 00:00 */
hour = 0;
minute = 0;
}
else if ((hour != -1) && (month == -1)) { /* time, no date implies soonest day */
if ((hour > time_now_tm->tm_hour)
|| ((hour == time_now_tm->tm_hour) && (minute > time_now_tm->tm_min))) {
/* today */
month = time_now_tm->tm_mon;
mday = time_now_tm->tm_mday;
year = time_now_tm->tm_year;
} else {/* tomorrow */
time_t later = time_now + (24 * 60 * 60);
struct tm *later_tm = localtime(&later);
month = later_tm->tm_mon;
mday = later_tm->tm_mday;
year = later_tm->tm_year;
}
}
if (year == -1) {
if ((month > time_now_tm->tm_mon)
|| ((month == time_now_tm->tm_mon) && (mday > time_now_tm->tm_mday))
|| ((month == time_now_tm->tm_mon) && (mday == time_now_tm->tm_mday)
&& (hour > time_now_tm->tm_hour))
|| ((month == time_now_tm->tm_mon) && (mday == time_now_tm->tm_mday)
&& (hour == time_now_tm->tm_hour) && (minute > time_now_tm->tm_min))) {
/* this year */
year = time_now_tm->tm_year;
} else {
year = time_now_tm->tm_year + 1;
}
}
/* convert the time into time_t format */
bzero(&res_tm, sizeof(res_tm));
res_tm.tm_sec = second;
res_tm.tm_min = minute;
res_tm.tm_hour = hour;
res_tm.tm_mday = mday;
res_tm.tm_mon = month;
res_tm.tm_year = year;
res_tm.tm_isdst = -1;
return mktime(&res_tm);
prob: fprintf(stderr, "Invalid time specification (pos=%d): %s\n", pos, time_str);
return (time_t) 0;
}
#if _RUN_STAND_ALONE
int main(int argc, char *argv[])
{
char in_line[128];
time_t when;
while (1) {
printf("time> ");
if ((fgets(in_line, sizeof(in_line), stdin) == NULL)
|| (in_line[0] == '\n'))
break;
when = parse_time(in_line);
if (when)
printf("%s", asctime(localtime(&when)));
}
}
#endif
/*
* slurm_make_time_str - convert time_t to string with a format of
* "month/date hour:min:sec" for use in user command output
*
* IN time - a time stamp
* OUT string - pointer user defined buffer
* IN size - length of string buffer, we recommend a size of 32 bytes to
* easily support different site-specific formats
*/
extern void
slurm_make_time_str (time_t *time, char *string, int size)
{
struct tm time_tm;
localtime_r(time, &time_tm);
if ( *time == (time_t) 0 ) {
snprintf(string, size, "Unknown");
} else {
#ifdef ISO8601
/* Format YYYY-MM-DDTHH:MM:SS, ISO8601 standard format,
* NOTE: This is expected to break Maui, Moab and LSF
* schedulers management of SLURM. */
snprintf(string, size,
"%4.4u-%2.2u-%2.2uT%2.2u:%2.2u:%2.2u",
(time_tm.tm_year + 1900), (time_tm.tm_mon+1),
time_tm.tm_mday, time_tm.tm_hour, time_tm.tm_min,
time_tm.tm_sec);
#else
/* Format MM/DD-HH:MM:SS */
snprintf(string, size,
"%2.2u/%2.2u-%2.2u:%2.2u:%2.2u",
(time_tm.tm_mon+1), time_tm.tm_mday,
time_tm.tm_hour, time_tm.tm_min, time_tm.tm_sec);
#endif
}
}
/* Convert a string to an equivalent time value
* input formats:
* min
* min:sec
* hr:min:sec
* days-hr:min:sec
* days-hr
* output:
* minutes (or -1 on error) (or INFINITE value defined in slurm.h
* if unlimited is the value of string)
*/
extern int time_str2mins(char *string)
{
int days = -1, hr = -1, min = -1, sec = -1;
int i, tmp = 0, res = 0;
if ((string == NULL) || (string[0] == '\0'))
return -1; /* invalid input */
if (!strcasecmp(string, "UNLIMITED")) {
return INFINITE;
}
for (i=0; ; i++) {
if ((string[i] >= '0') && (string[i] <= '9')) {
tmp = (tmp * 10) + (string[i] - '0');
} else if (string[i] == '-') {
if (days != -1)
return -1; /* invalid input */
days = tmp;
tmp = 0;
} else if ((string[i] == ':') || (string[i] == '\0')) {
if (min == -1) {
min = tmp;
} else if (sec == -1) {
sec = tmp;
} else if (hr == -1) {
hr = min;
min = sec;
sec = tmp;
} else
return -1; /* invalid input */
tmp = 0;
} else
return -1; /* invalid input */
if (string[i] == '\0')
break;
}
if ((days != -1) && (hr == -1) && (min != 0)) {
/* format was "days-hr" or "days-hr:min" */
hr = min;
min = sec;
sec = 0;
}
if (days == -1)
days = 0;
if (hr == -1)
hr = 0;
if (min == -1)
min = 0;
if (sec == -1)
sec = 0;
res = (((days * 24) + hr) * 60) + min;
if (sec > 0)
res++; /* round up */
return res;
}
extern void secs2time_str(time_t time, char *string, int size)
{
if (time == INFINITE) {
snprintf(string, size, "UNLIMITED");
} else {
long days, hours, minutes, seconds;
seconds = time % 60;
minutes = (time / 60) % 60;
hours = (time / 3600) % 24;
days = time / 86400;
if (days)
snprintf(string, size,
"%ld-%2.2ld:%2.2ld:%2.2ld",
days, hours, minutes, seconds);
else
snprintf(string, size,
"%2.2ld:%2.2ld:%2.2ld",
hours, minutes, seconds);
}
}