|  | /*****************************************************************************\ | 
|  | *  src/common/parse_time.c - time parsing utility functions | 
|  | ***************************************************************************** | 
|  | *  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>. | 
|  | *  CODE-OCEC-09-009. All rights reserved. | 
|  | * | 
|  | *  This file is part of Slurm, a resource management program. | 
|  | *  For details, see <https://slurm.schedmd.com/>. | 
|  | *  Please also read the included file: DISCLAIMER. | 
|  | * | 
|  | *  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 "config.h" | 
|  |  | 
|  | #include <limits.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <time.h> | 
|  |  | 
|  | #include <ctype.h> | 
|  |  | 
|  | #include "slurm/slurm.h" | 
|  | #include "src/common/macros.h" | 
|  | #include "src/common/slurm_time.h" | 
|  | #include "src/common/strlcpy.h" | 
|  | #include "src/common/xstring.h" | 
|  | #include "src/common/parse_time.h" | 
|  |  | 
|  | /* | 
|  | ** Define slurm-specific aliases for use by plugins, see slurm_xlator.h | 
|  | ** for details. | 
|  | */ | 
|  | strong_alias(parse_time, slurm_parse_time); | 
|  | strong_alias(parse_time_make_str_utc, slurm_parse_time_make_str_utc); | 
|  | //slurm_make_time_str is already exported | 
|  | strong_alias(time_str2mins, slurm_time_str2mins); | 
|  | strong_alias(time_str2secs, slurm_time_str2secs); | 
|  | strong_alias(secs2time_str, slurm_secs2time_str); | 
|  | strong_alias(mins2time_str, slurm_mins2time_str); | 
|  |  | 
|  | typedef struct unit_names { | 
|  | char *name; | 
|  | int name_len; | 
|  | int multiplier; | 
|  | } unit_names_t; | 
|  | static unit_names_t un[] = { | 
|  | {"seconds",	7,	1}, | 
|  | {"second",	6,	1}, | 
|  | {"minutes",	7,	60}, | 
|  | {"minute",	6,	60}, | 
|  | {"hours",	5,	(60*60)}, | 
|  | {"hour",	4,	(60*60)}, | 
|  | {"days",	4,	(24*60*60)}, | 
|  | {"day",		3,	(24*60*60)}, | 
|  | {"weeks",	5,	(7*24*60*60)}, | 
|  | {"week",	4,	(7*24*60*60)}, | 
|  | {NULL,		0,	0} | 
|  | }; | 
|  |  | 
|  | /* _is_valid_timespec() | 
|  | * | 
|  | * Validate that time format follows | 
|  | * is supported. | 
|  | */ | 
|  | static bool | 
|  | _is_valid_timespec(const char *s) | 
|  | { | 
|  | int digit; | 
|  | int dash; | 
|  | int colon; | 
|  | bool already_digit = false; | 
|  | digit = dash = colon = 0; | 
|  |  | 
|  | while (*s) { | 
|  | if (*s >= '0' && *s <= '9') { | 
|  | if (!already_digit) { | 
|  | ++digit; | 
|  | already_digit = true; | 
|  | } | 
|  | } else if (*s == '-') { | 
|  | already_digit = false; | 
|  | ++dash; | 
|  | if (colon) | 
|  | return false; | 
|  | } else if (*s == ':') { | 
|  | already_digit = false; | 
|  | ++colon; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | ++s; | 
|  | } | 
|  |  | 
|  | if (!digit) | 
|  | return false; | 
|  |  | 
|  | if (dash > 1 | 
|  | || colon > 2) | 
|  | return false; | 
|  |  | 
|  | if (dash) { | 
|  | if (colon == 1 | 
|  | && digit < 3) | 
|  | return false; | 
|  | if (colon == 2 | 
|  | && digit < 4) | 
|  | return false; | 
|  | } else { | 
|  | if (colon == 1 | 
|  | && digit < 2) | 
|  | return false; | 
|  | if (colon == 2 | 
|  | && digit < 3) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* 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(const char *time_str, int *pos, long *delta) | 
|  | { | 
|  | int i, offset; | 
|  | long cnt = 0; | 
|  | int digit = 0; | 
|  |  | 
|  | for (offset = (*pos) + 1; | 
|  | ((time_str[offset] != '\0') && (time_str[offset] != '\n')); | 
|  | offset++) { | 
|  | if (isspace((int)time_str[offset])) | 
|  | continue; | 
|  | for (i=0; un[i].name; i++) { | 
|  | if (!xstrncasecmp((time_str + offset), | 
|  | un[i].name, un[i].name_len)) { | 
|  | offset += un[i].name_len; | 
|  | cnt    *= un[i].multiplier; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (un[i].name) | 
|  | break;	/* processed unit name */ | 
|  | if ((time_str[offset] >= '0') && (time_str[offset] <= '9')) { | 
|  | cnt = (cnt * 10) + (time_str[offset] - '0'); | 
|  | digit++; | 
|  | continue; | 
|  | } | 
|  | goto prob; | 
|  | } | 
|  |  | 
|  | if (!digit)	/* No numbers after the '=' */ | 
|  | return -1; | 
|  |  | 
|  | *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): numeric values | 
|  | * RET: -1 on error, 0 otherwise | 
|  | */ | 
|  | static int _get_time(const 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] != ':') { | 
|  | 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((int)time_str[offset])) { | 
|  | offset++; | 
|  | } | 
|  |  | 
|  | if (!xstrncasecmp(time_str + offset, "pm", 2)) { | 
|  | hr += 12; | 
|  | if (hr > 23) { | 
|  | if (hr == 24) | 
|  | hr = 12; | 
|  | else | 
|  | goto prob; | 
|  | } | 
|  | offset += 2; | 
|  | } else if (!xstrncasecmp(time_str + offset, "am", 2)) { | 
|  | 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 | 
|  | * or "YYYY-MM-DD string to numeric values | 
|  | * time_str (in): string to parse | 
|  | * pos (in/out): position of parse start/end | 
|  | * month, mday, year (out): numeric values | 
|  | * RET: -1 on error, 0 otherwise | 
|  | */ | 
|  | static int _get_date(const char *time_str, int *pos, int *month, int *mday, | 
|  | int *year) | 
|  | { | 
|  | int mon, day, yr; | 
|  | int offset = *pos; | 
|  | int len; | 
|  |  | 
|  | if (!time_str) | 
|  | goto prob; | 
|  |  | 
|  | len = strlen(time_str); | 
|  |  | 
|  | if ((len >= (offset+7)) && (time_str[offset+4] == '-') | 
|  | && (time_str[offset+7] == '-')) { | 
|  | /* get year */ | 
|  | if ((time_str[offset] < '0') || (time_str[offset] > '9')) | 
|  | goto prob; | 
|  | yr = time_str[offset++] - '0'; | 
|  |  | 
|  | if ((time_str[offset] < '0') || (time_str[offset] > '9')) | 
|  | goto prob; | 
|  | yr = (yr * 10) + time_str[offset++] - '0'; | 
|  |  | 
|  | if ((time_str[offset] < '0') || (time_str[offset] > '9')) | 
|  | goto prob; | 
|  | yr = (yr * 10) + time_str[offset++] - '0'; | 
|  |  | 
|  | if ((time_str[offset] < '0') || (time_str[offset] > '9')) | 
|  | goto prob; | 
|  | yr = (yr * 10) + time_str[offset++] - '0'; | 
|  |  | 
|  | offset++; // for the - | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | offset++; // for the - | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | *pos = offset - 1; | 
|  | *month = mon - 1;	/* zero origin */ | 
|  | *mday  = day; | 
|  | *year  = yr - 1900;     /* need to make it slurm_mktime | 
|  | happy 1900 == "00" */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* 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, elevenses (11 AM), fika (3 PM), teatime (4 PM) | 
|  | *   HH:MM[:SS] [AM|PM] | 
|  | *   MMDD[YY] or MM/DD[/YY] or MM.DD[.YY] | 
|  | *   MM/DD[/YY]-HH:MM[:SS] | 
|  | *   YYYY-MM-DD[THH:MM[:SS]] | 
|  | *   now[{+|-}count[seconds(default)|minutes|hours|days|weeks]] | 
|  | * | 
|  | * Invalid input results in message to stderr and return value of zero | 
|  | * NOTE: not thread safe | 
|  | * NOTE: by default this will look into the future for the next time. | 
|  | * if you want to look in the past set the past flag. | 
|  | */ | 
|  | extern time_t parse_time(const char *time_str, int past) | 
|  | { | 
|  | time_t time_now; | 
|  | struct tm time_now_tm; | 
|  | int    hour = -1, minute = -1, second = 0; | 
|  | int    month = -1, mday = -1, year = -1; | 
|  | int    pos = 0; | 
|  | struct tm res_tm; | 
|  | time_t ret_time; | 
|  |  | 
|  | if (!xstrncasecmp(time_str, "uts", 3)) { | 
|  | char *last = NULL; | 
|  | long uts = strtol(time_str+3, &last, 10); | 
|  | if ((uts < 1000000) || (uts == LONG_MAX) || | 
|  | (last == NULL) || (last[0] != '\0')) | 
|  | goto prob; | 
|  | return (time_t) uts; | 
|  | } | 
|  |  | 
|  | time_now = time(NULL); | 
|  | localtime_r(&time_now, &time_now_tm); | 
|  |  | 
|  | for (pos=0; ((time_str[pos] != '\0') && (time_str[pos] != '\n')); | 
|  | pos++) { | 
|  | if (isblank((int)time_str[pos]) || | 
|  | (time_str[pos] == '-') || (time_str[pos] == 'T')) | 
|  | continue; | 
|  | if (!xstrncasecmp(time_str + pos, "today", 5)) { | 
|  | month = time_now_tm.tm_mon; | 
|  | mday = time_now_tm.tm_mday; | 
|  | year = time_now_tm.tm_year; | 
|  | pos += 4; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str + pos, "tomorrow", 8)) { | 
|  | time_t later = time_now + (24 * 60 * 60); | 
|  | struct tm later_tm; | 
|  | localtime_r(&later, &later_tm); | 
|  | month = later_tm.tm_mon; | 
|  | mday = later_tm.tm_mday; | 
|  | year = later_tm.tm_year; | 
|  | pos += 7; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str + pos, "midnight", 8)) { | 
|  | hour   = 0; | 
|  | minute = 0; | 
|  | second = 0; | 
|  | pos += 7; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str + pos, "noon", 4)) { | 
|  | hour   = 12; | 
|  | minute = 0; | 
|  | second = 0; | 
|  | pos += 3; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str + pos, "elevenses", 9)) { | 
|  | hour   = 11; | 
|  | minute = 0; | 
|  | second = 0; | 
|  | pos += 8; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str + pos, "fika", 4)) { | 
|  | hour   = 15; | 
|  | minute = 0; | 
|  | second = 0; | 
|  | pos += 3; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str + pos, "teatime", 7)) { | 
|  | hour   = 16; | 
|  | minute = 0; | 
|  | second = 0; | 
|  | pos += 6; | 
|  | continue; | 
|  | } | 
|  | if (!xstrncasecmp(time_str+pos, "now", 3)) { | 
|  | 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 (time_str[i] == '-') { | 
|  | pos += i; | 
|  | if (_get_delta(time_str, &pos, &delta)) | 
|  | goto prob; | 
|  | delta = -delta; | 
|  | break; | 
|  | } | 
|  | if (isblank((int)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; | 
|  | localtime_r(&later, &later_tm); | 
|  | 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+1] == ':') || (time_str[pos+2] == ':')) { | 
|  | /* Parse the time stamp */ | 
|  | 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,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 (past || (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_r(&later, &later_tm); | 
|  | month = later_tm.tm_mon; | 
|  | mday = later_tm.tm_mday; | 
|  | year = later_tm.tm_year; | 
|  | } | 
|  | } | 
|  | if (year == -1) { | 
|  | if (past) { | 
|  | if (month > time_now_tm.tm_mon) { | 
|  | /* last year */ | 
|  | year = time_now_tm.tm_year - 1; | 
|  | } else  { | 
|  | /* this year */ | 
|  | year = time_now_tm.tm_year; | 
|  | } | 
|  | } else 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 { | 
|  | /* next year */ | 
|  | year = time_now_tm.tm_year + 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* convert the time into time_t format */ | 
|  | memset(&res_tm, 0, 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; | 
|  |  | 
|  | /* 	printf("%d/%d/%d %d:%d\n",month+1,mday,year,hour,minute); */ | 
|  | if ((ret_time = slurm_mktime(&res_tm)) != -1) | 
|  | return ret_time; | 
|  |  | 
|  | prob:	fprintf(stderr, "Invalid time specification (pos=%d): %s\n", pos, time_str); | 
|  | errno = ESLURM_INVALID_TIME_VALUE; | 
|  | return (time_t) 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Smart date for @epoch, relative to current date. | 
|  | * Maximum output length: 12 characters + '\0' | 
|  | *      19 Jan 2003	(distant past or future) | 
|  | *     Ystday 20:13 | 
|  | *         12:26:38	(today) | 
|  | *     Tomorr 03:22 | 
|  | *        Sat 02:17	(next Saturday) | 
|  | *     18 Jun 13:14	(non-close past or future) | 
|  | *     012345678901 | 
|  | * Uses base-10 YYYYddd numbers to compute date distances. | 
|  | */ | 
|  | static char *_relative_date_fmt(const struct tm *when) | 
|  | { | 
|  | static int todays_date; | 
|  | int distance = 1000 * (when->tm_year + 1900) + when->tm_yday; | 
|  |  | 
|  | if (!todays_date) { | 
|  | time_t now = time(NULL); | 
|  | struct tm tm; | 
|  |  | 
|  | localtime_r(&now, &tm); | 
|  | todays_date = 1000 * (tm.tm_year + 1900) + tm.tm_yday; | 
|  | } | 
|  |  | 
|  | distance -= todays_date; | 
|  | if (distance == -1)			/* yesterday */ | 
|  | return "Ystday %H:%M"; | 
|  | if (distance == 0)			/* same day */ | 
|  | return "%H:%M:%S"; | 
|  | if (distance == 1)			/* tomorrow */ | 
|  | return "Tomorr %H:%M"; | 
|  | if (distance < -365 || distance > 365)	/* far distance */ | 
|  | return "%-d %b %Y"; | 
|  | if (distance < -1 || distance > 6)	/* medium distance */ | 
|  | return "%-d %b %H:%M"; | 
|  | return "%a %H:%M";			/* near distance */ | 
|  | } | 
|  |  | 
|  | static void _make_time_str_internal(time_t *time, bool utc, char *string, | 
|  | int size) | 
|  | { | 
|  | struct tm time_tm; | 
|  |  | 
|  | if (utc) | 
|  | gmtime_r(time, &time_tm); | 
|  | else | 
|  | localtime_r(time, &time_tm); | 
|  | if ((*time == (time_t) 0) || (*time == (time_t) INFINITE)) { | 
|  | snprintf(string, size, "Unknown"); | 
|  | } else if (*time == (time_t) NO_VAL) { | 
|  | snprintf(string, size, "None"); | 
|  | } else { | 
|  | static char fmt_buf[32]; | 
|  | static const char *display_fmt = "%FT%T"; | 
|  |  | 
|  | if (!utc) { | 
|  | char *fmt = getenv("SLURM_TIME_FORMAT"); | 
|  |  | 
|  | if ((!fmt) || (!*fmt) || (!xstrcmp(fmt, "standard"))) { | 
|  | ; | 
|  | } else if (!xstrcmp(fmt, "relative")) { | 
|  | display_fmt = _relative_date_fmt(&time_tm); | 
|  | } else if ((strchr(fmt, '%')  == NULL) || | 
|  | (strlen(fmt) >= sizeof(fmt_buf))) { | 
|  | error("invalid SLURM_TIME_FORMAT = '%s'", fmt); | 
|  | } else { | 
|  | strlcpy(fmt_buf, fmt, sizeof(fmt_buf)); | 
|  | display_fmt = fmt_buf; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The result from strftime() is undefined if the buffer is | 
|  | * too small. Fill it with "#######..." instead. | 
|  | */ | 
|  | if (!strftime(string, size, display_fmt, &time_tm)) { | 
|  | memset(string, '#', size); | 
|  | string[size - 1] = 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * slurm_make_time_str - convert time_t to formatted string for user output | 
|  | * | 
|  | * The format depends on the environment variable SLURM_TIME_FORMAT, which may | 
|  | * be set to 'standard' (fallback, same as if not set), 'relative' (format is | 
|  | * relative to today's date and optimized for space), or a strftime(3) string. | 
|  | * | 
|  | * 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) | 
|  | { | 
|  | _make_time_str_internal(time, false, string, size); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Convert time_t to fixed "%FT%T" formatted string expressed in UTC. | 
|  | * | 
|  | * IN time - a timestamp | 
|  | * OUT string - pointer user defined buffer | 
|  | * IN size - length of string buffer (recommend 32 bytes) | 
|  | */ | 
|  | extern void parse_time_make_str_utc(time_t *time, char *string, int size) | 
|  | { | 
|  | _make_time_str_internal(time, true, string, size); | 
|  | } | 
|  |  | 
|  | /* Convert a string to an equivalent time value | 
|  | * input formats: | 
|  | *   min | 
|  | *   min:sec | 
|  | *   hr:min:sec | 
|  | *   days-hr:min:sec | 
|  | *   days-hr | 
|  | * output: | 
|  | *   minutes for time_str2mins | 
|  | *   seconds for time_str2secs | 
|  | *   NO_VAL on error | 
|  | *   INFINITE for "infinite" or "unlimited" | 
|  | */ | 
|  | extern int time_str2secs(const char *string) | 
|  | { | 
|  | int d = 0, h = 0, m = 0, s = 0; | 
|  |  | 
|  | if ((string == NULL) || (string[0] == '\0')) | 
|  | return NO_VAL;	/* invalid input */ | 
|  |  | 
|  | if ((!xstrcasecmp(string, "-1")) | 
|  | || (!xstrcasecmp(string, "INFINITE")) | 
|  | || (!xstrcasecmp(string, "UNLIMITED"))) { | 
|  | return INFINITE; | 
|  | } | 
|  |  | 
|  | if (! _is_valid_timespec(string)) | 
|  | return NO_VAL; | 
|  |  | 
|  | if (xstrchr(string, '-')) { | 
|  | /* days-[hours[:minutes[:seconds]]] */ | 
|  | sscanf(string, "%d-%d:%d:%d", &d, &h, &m, &s); | 
|  | d *= 86400; | 
|  | h *= 3600; | 
|  | m *= 60; | 
|  | } else { | 
|  | if (sscanf(string, "%d:%d:%d", &h, &m, &s) == 3) { | 
|  | /* hours:minutes:seconds */ | 
|  | h *= 3600; | 
|  | m *= 60; | 
|  | } else { | 
|  | /* | 
|  | * minutes[:seconds] | 
|  | * h is minutes here and m is seconds due | 
|  | * to sscanf parsing left to right | 
|  | */ | 
|  | s = m; | 
|  | m = h * 60; | 
|  | h = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | return (d + h + m + s); | 
|  | } | 
|  |  | 
|  | extern int time_str2mins(const char *string) | 
|  | { | 
|  | int i = time_str2secs(string); | 
|  | if ((i != INFINITE) &&  (i != NO_VAL)) | 
|  | i = ROUNDUP(i, 60);	/* round up */ | 
|  | return i; | 
|  | } | 
|  |  | 
|  | 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 < 0) || (hours < 0) || (minutes < 0) || | 
|  | (seconds < 0)) { | 
|  | snprintf(string, size, "INVALID"); | 
|  | } else 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); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | extern void mins2time_str(uint32_t time, char *string, int size) | 
|  | { | 
|  | if (time == INFINITE) { | 
|  | snprintf(string, size, "UNLIMITED"); | 
|  | } else { | 
|  | long days, hours, minutes, seconds; | 
|  | seconds = 0; | 
|  | minutes = time % 60; | 
|  | hours = time / 60 % 24; | 
|  | days = time / 1440; | 
|  |  | 
|  | if ((days < 0) || (hours < 0) || (minutes < 0) || | 
|  | (seconds < 0)) { | 
|  | snprintf(string, size, "INVALID"); | 
|  | } else 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); | 
|  | } | 
|  | } | 
|  | } |