how to compare 2 dates in c++ - c++

I have a string variable containing date in yyyy/mm/dd format. How to convert it into time_t type in C++? eg: string date_details = "2012/09/12"
Also, how to compare two variables containing date as to decide which is the earliest in C++? eg: string curr_date = "2012/09/13" string user_date = "2012/09/12"
Thanks.

To compare two dates in year-month-day format, strcmp is sufficient:
assert(strcmp("2012/09/13", "2012/09/12") > 0);
assert(strcmp("2012/10/13", "2013/01/12") < 0);

You can use strptime to parse dates in any format if you want, and then mktime to convert the tm structure to a time_t value which you can compare.

For what its worth since your example dates string curr_date = "2012/09/13" and string curr_date = "2012/09/13" appear to be in ISO8601 format (apart from the use of '/' as a separator). The joy of ISO8601 is that lexicographic order is the same as chronological order... that is sort the strings and you sort by time as well.
This is appealing becuase it provides a nice way to deal with many dates (as opposed to just 2)
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
int main()
{
vector<string> v = getDates(); //Made up function returning a vector of ISO dates
sort(v, v.begin(), v.end()); //Done
//Do whatever you have to
return 0;
}

My suggestion would be to use a functor. Doing so will allow you determine exactly how you want to compare those dates and also give you the ability to later define another functor which compares the dates a different way.
For example, you could define a functor as such:
struct DateLessThan
{
BOOL operator()(const std::string& lhs, const std::string& rhs)
{
// the following is pseudocode
split lhs and rhs into arrays of strings based on the delimiter "/"
convert the string arrays into integers and store them in vars (possibly lhs_day, lhs_month, etc.)
if (lhs_year < rhs_year)
return true;
else if (lhs_year == rhs_year)
{
if (lhs_month < rhs_month)
return true;
else if (lhs_month == rhs_month)
{
if (lhs_day < rhs_day)
return true;
}
}
return false;
}
};

strptime() is what you're looking for. Unfortunately its not easy to get it to work on windows.
Although after much googling I finally found a implementation linked on ffmpeg.org which will do the job. The implementation is found on plibc.sourceforge.net (A POSIX compliant libc for Windows) here.
You're going to have to remove the line numbers if you're going to copy it from doxygen with this regex 00([0-9])([0-9])([0-9]). Also theres a error # line 115 enum locale_status { not, loc, raw }; has to be changed to enum locale_status { nott, loc, raw };.
Alternatively if you're too lazy for all that heres the version I used:
/* Convert a string representation of time to a time value.
Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper#cygnus.com>, 1996.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
The GNU C Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
void get_locale_strings(void);
/* XXX This version of the implementation is not really complete.
Some of the fields cannot add information alone. But if seeing
some of them in the same format (such as year, week and weekday)
this is enough information for determining the date. */
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
#ifndef Macintosh
#if defined __GNUC__ && __GNUC__ >= 2
# define match_string(cs1, s2) \
({ size_t len = strlen (cs1); \
int result = strncasecmp ((cs1), (s2), len) == 0; \
if (result) (s2) += len; \
result; })
#else
/* Oh come on. Get a reasonable compiler. */
# define match_string(cs1, s2) \
(strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
#endif
#else
# define match_string(cs1, s2) \
(strncmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
#endif /* mac */
/* We intentionally do not use isdigit() for testing because this will
lead to problems with the wide character version. */
#define get_number(from, to, n) \
do { \
int __n = n; \
val = 0; \
while (*rp == ' ') \
++rp; \
if (*rp < '0' || *rp > '9') \
return NULL; \
do { \
val *= 10; \
val += *rp++ - '0'; \
} while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
if (val < from || val > to) \
return NULL; \
} while (0)
# define get_alt_number(from, to, n) \
/* We don't have the alternate representation. */ \
get_number(from, to, n)
#define recursive(new_fmt) \
(*(new_fmt) != '\0' \
&& (rp = strptime_internal (rp, (new_fmt), tm, decided)) != NULL)
/* This version: may overwrite these with versions for the locale */
static char weekday_name[][20] =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
static char ab_weekday_name[][10] =
{
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static char month_name[][20] =
{
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
static char ab_month_name[][10] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static char am_pm[][4] = {"AM", "PM"};
# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
# define HERE_D_FMT "%y/%m/%d"
# define HERE_T_FMT_AMPM "%I:%M:%S %p"
# define HERE_T_FMT "%H:%M:%S"
static const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
/* Status of lookup: do we use the locale data or the raw data? */
enum locale_status { nott, loc, raw };
# define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
/* Compute the day of the week. */
void
day_of_the_week (struct tm *tm)
{
/* We know that January 1st 1970 was a Thursday (= 4). Compute the
the difference between this data in the one on TM and so determine
the weekday. */
int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
int wday = (-473
+ (365 * (tm->tm_year - 70))
+ (corr_year / 4)
- ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
+ (((corr_year / 4) / 25) / 4)
+ __mon_yday[0][tm->tm_mon]
+ tm->tm_mday - 1);
tm->tm_wday = ((wday % 7) + 7) % 7;
}
/* Compute the day of the year. */
void
day_of_the_year (struct tm *tm)
{
tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
+ (tm->tm_mday - 1));
}
char *
strptime_internal (const char *rp, const char *fmt, struct tm *tm,
enum locale_status *decided)
{
const char *rp_backup;
int cnt;
size_t val;
int have_I, is_pm;
int century, want_century;
int have_wday, want_xday;
int have_yday;
int have_mon, have_mday;
have_I = is_pm = 0;
century = -1;
want_century = 0;
have_wday = want_xday = have_yday = have_mon = have_mday = 0;
while (*fmt != '\0')
{
/* A white space in the format string matches 0 more or white
space in the input string. */
if (isspace (*fmt))
{
while (isspace (*rp))
++rp;
++fmt;
continue;
}
/* Any character but `%' must be matched by the same character
in the iput string. */
if (*fmt != '%')
{
match_char (*fmt++, *rp++);
continue;
}
++fmt;
/* We need this for handling the `E' modifier. */
start_over:
/* Make back up of current processing pointer. */
rp_backup = rp;
switch (*fmt++)
{
case '%':
/* Match the `%' character itself. */
match_char ('%', *rp++);
break;
case 'a':
case 'A':
/* Match day of week. */
for (cnt = 0; cnt < 7; ++cnt)
{
if (*decided != loc
&& (match_string (weekday_name[cnt], rp)
|| match_string (ab_weekday_name[cnt], rp)))
{
*decided = raw;
break;
}
}
if (cnt == 7)
/* Does not match a weekday name. */
return NULL;
tm->tm_wday = cnt;
have_wday = 1;
break;
case 'b':
case 'B':
case 'h':
/* Match month name. */
for (cnt = 0; cnt < 12; ++cnt)
{
if (match_string (month_name[cnt], rp)
|| match_string (ab_month_name[cnt], rp))
{
*decided = raw;
break;
}
}
if (cnt == 12)
/* Does not match a month name. */
return NULL;
tm->tm_mon = cnt;
want_xday = 1;
break;
case 'c':
/* Match locale's date and time format. */
if (!recursive (HERE_T_FMT_AMPM))
return NULL;
break;
case 'C':
/* Match century number. */
get_number (0, 99, 2);
century = val;
want_xday = 1;
break;
case 'd':
case 'e':
/* Match day of month. */
get_number (1, 31, 2);
tm->tm_mday = val;
have_mday = 1;
want_xday = 1;
break;
case 'F':
if (!recursive ("%Y-%m-%d"))
return NULL;
want_xday = 1;
break;
case 'x':
/* Fall through. */
case 'D':
/* Match standard day format. */
if (!recursive (HERE_D_FMT))
return NULL;
want_xday = 1;
break;
case 'k':
case 'H':
/* Match hour in 24-hour clock. */
get_number (0, 23, 2);
tm->tm_hour = val;
have_I = 0;
break;
case 'I':
/* Match hour in 12-hour clock. */
get_number (1, 12, 2);
tm->tm_hour = val % 12;
have_I = 1;
break;
case 'j':
/* Match day number of year. */
get_number (1, 366, 3);
tm->tm_yday = val - 1;
have_yday = 1;
break;
case 'm':
/* Match number of month. */
get_number (1, 12, 2);
tm->tm_mon = val - 1;
have_mon = 1;
want_xday = 1;
break;
case 'M':
/* Match minute. */
get_number (0, 59, 2);
tm->tm_min = val;
break;
case 'n':
case 't':
/* Match any white space. */
while (isspace (*rp))
++rp;
break;
case 'p':
/* Match locale's equivalent of AM/PM. */
if (!match_string (am_pm[0], rp))
if (match_string (am_pm[1], rp))
is_pm = 1;
else
return NULL;
break;
case 'r':
if (!recursive (HERE_T_FMT_AMPM))
return NULL;
break;
case 'R':
if (!recursive ("%H:%M"))
return NULL;
break;
case 's':
{
/* The number of seconds may be very high so we cannot use
the `get_number' macro. Instead read the number
character for character and construct the result while
doing this. */
time_t secs = 0;
if (*rp < '0' || *rp > '9')
/* We need at least one digit. */
return NULL;
do
{
secs *= 10;
secs += *rp++ - '0';
}
while (*rp >= '0' && *rp <= '9');
if ((tm = localtime (&secs)) == NULL)
/* Error in function. */
return NULL;
}
break;
case 'S':
get_number (0, 61, 2);
tm->tm_sec = val;
break;
case 'X':
/* Fall through. */
case 'T':
if (!recursive (HERE_T_FMT))
return NULL;
break;
case 'u':
get_number (1, 7, 1);
tm->tm_wday = val % 7;
have_wday = 1;
break;
case 'g':
get_number (0, 99, 2);
/* XXX This cannot determine any field in TM. */
break;
case 'G':
if (*rp < '0' || *rp > '9')
return NULL;
/* XXX Ignore the number since we would need some more
information to compute a real date. */
do
++rp;
while (*rp >= '0' && *rp <= '9');
break;
case 'U':
case 'V':
case 'W':
get_number (0, 53, 2);
/* XXX This cannot determine any field in TM without some
information. */
break;
case 'w':
/* Match number of weekday. */
get_number (0, 6, 1);
tm->tm_wday = val;
have_wday = 1;
break;
case 'y':
/* Match year within century. */
get_number (0, 99, 2);
/* The "Year 2000: The Millennium Rollover" paper suggests that
values in the range 69-99 refer to the twentieth century. */
tm->tm_year = val >= 69 ? val : val + 100;
/* Indicate that we want to use the century, if specified. */
want_century = 1;
want_xday = 1;
break;
case 'Y':
/* Match year including century number. */
get_number (0, 9999, 4);
tm->tm_year = val - 1900;
want_century = 0;
want_xday = 1;
break;
case 'Z':
/* XXX How to handle this? */
break;
case 'E':
/* We have no information about the era format. Just use
the normal format. */
if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
&& *fmt != 'x' && *fmt != 'X')
/* This is an invalid format. */
return NULL;
goto start_over;
case 'O':
switch (*fmt++)
{
case 'd':
case 'e':
/* Match day of month using alternate numeric symbols. */
get_alt_number (1, 31, 2);
tm->tm_mday = val;
have_mday = 1;
want_xday = 1;
break;
case 'H':
/* Match hour in 24-hour clock using alternate numeric
symbols. */
get_alt_number (0, 23, 2);
tm->tm_hour = val;
have_I = 0;
break;
case 'I':
/* Match hour in 12-hour clock using alternate numeric
symbols. */
get_alt_number (1, 12, 2);
tm->tm_hour = val - 1;
have_I = 1;
break;
case 'm':
/* Match month using alternate numeric symbols. */
get_alt_number (1, 12, 2);
tm->tm_mon = val - 1;
have_mon = 1;
want_xday = 1;
break;
case 'M':
/* Match minutes using alternate numeric symbols. */
get_alt_number (0, 59, 2);
tm->tm_min = val;
break;
case 'S':
/* Match seconds using alternate numeric symbols. */
get_alt_number (0, 61, 2);
tm->tm_sec = val;
break;
case 'U':
case 'V':
case 'W':
get_alt_number (0, 53, 2);
/* XXX This cannot determine any field in TM without
further information. */
break;
case 'w':
/* Match number of weekday using alternate numeric symbols. */
get_alt_number (0, 6, 1);
tm->tm_wday = val;
have_wday = 1;
break;
case 'y':
/* Match year within century using alternate numeric symbols. */
get_alt_number (0, 99, 2);
tm->tm_year = val >= 69 ? val : val + 100;
want_xday = 1;
break;
default:
return NULL;
}
break;
default:
return NULL;
}
}
if (have_I && is_pm)
tm->tm_hour += 12;
if (century != -1)
{
if (want_century)
tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
else
/* Only the century, but not the year. Strange, but so be it. */
tm->tm_year = (century - 19) * 100;
}
if (want_xday && !have_wday) {
if ( !(have_mon && have_mday) && have_yday) {
/* we don't have tm_mon and/or tm_mday, compute them */
int t_mon = 0;
while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
t_mon++;
if (!have_mon)
tm->tm_mon = t_mon - 1;
if (!have_mday)
tm->tm_mday = tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1;
}
day_of_the_week (tm);
}
if (want_xday && !have_yday)
day_of_the_year (tm);
return (char *) rp;
}
char *
strptime (const char *buf, const char *format, struct tm *tm)
{
enum locale_status decided;
#ifdef HAVE_LOCALE_H
if(!have_used_strptime) {
get_locale_strings();
/* have_used_strptime = 1; might change locale during session */
}
#endif
decided = raw;
return strptime_internal (buf, format, tm, &decided);
}
#ifdef HAVE_LOCALE_H
void get_locale_strings(void)
{
int i;
struct tm tm;
char buff[4];
tm.tm_sec = tm.tm_min = tm.tm_hour = tm.tm_mday = tm.tm_mon
= tm.tm_isdst = 0;
tm.tm_year = 30;
for(i = 0; i < 12; i++) {
tm.tm_mon = i;
strftime(ab_month_name[i], 10, "%b", &tm);
strftime(month_name[i], 20, "%B", &tm);
}
tm.tm_mon = 0;
for(i = 0; i < 7; i++) {
tm.tm_mday = tm.tm_yday = i+1; /* 2000-1-2 was a Sunday */
tm.tm_wday = i;
strftime(ab_weekday_name[i], 10, "%a", &tm);
strftime(weekday_name[i], 20, "%A", &tm);
}
tm.tm_hour = 1;
/* in locales where these are unused, they may be empty: better
not to reset them then */
strftime(buff, 4, "%p", &tm);
if(strlen(buff)) strcpy(am_pm[0], buff);
tm.tm_hour = 13;
strftime(buff, 4, "%p", &tm);
if(strlen(buff)) strcpy(am_pm[1], buff);
}
#endif
And heres a working exemple:
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <time.h>
#include <stdio.h>
#include "strptime.h"
using namespace std;
int main()
{
struct tm tm1, tm2;
time_t t1, t2;
memset(&tm1, 0, sizeof(struct tm));
memset(&tm2, 0, sizeof(struct tm));
strptime("12 February 2010", "%d %b %Y", &tm1);
strptime("11 February 2010", "%d %b %Y", &tm2);
t1 = mktime(&tm1);
t2 = mktime(&tm2);
cout << t1 << endl;
cout << t2 << endl;
if (t1 > t2)
{
cout << "t1 > t2" << endl;
}
else if (t1 == t2)
{
cout << "t1 == t2" << endl;
}
else if (t1 < t2)
{
cout << "t1 < t2" << endl;
}
cout << (t1 - t2) << endl;
return 0;
}
Outputs:
1265925600
1265839200
t1 > t2
86400

Related

C++ sscanf seams not to working as intended

I'm currently working on project for school with an Arduino Uno and came across an issue with C sscanf function. The code bellow is parsing a char array sent over the serial interface to the Arduino. Each data block is separated with a blankspace. I also checked similar question on Stackoverflow but found nothing helpful.
char testInput[] = "t 1 3 65 1";
int registerIndex;
int locoAddr;
int locoSpeed;
int locoDirection;
if(sscanf(testInput, "t %d %d %d %d", &registerIndex, &locoAddr, &locoSpeed, &locoDirection) != 4) {
return;
}
// print for demo:
Serial.print(registerIndex);
Serial.print(": ");
Serial.print("loco:");
Serial.print(locoAddr);
Serial.print(" speed:");
Serial.print(locoSpeed);
Serial.print(" direction:");
Serial.println(locoDirection);
// expected output: "1: loco:3 speed:65 direction:1"
// output: "-18248: loco:-18248 speed:-18248 direction:-18248"
the sscanf function returns the expected integer 4 but doesn't fill the variables with the values so only the default values are printed.
I'm out of ideas what the issue could be. Maybe someone on the internet has a solution.
Edit 1: For better understanding of the complexity of the sketch this might help to understand the code a bit better:
loop in .ino file calls static Commander::process().
Commander::_readLine(...); get called and reads the data from serial.
some more validation is done but should not matter in this case
Commander::parseCommand(command); get called with the command.
registers->setThrottle(command); gets called when the command starts with a "t". registers is volatile so I'm able to call it from the interupt.
in PackageRegisterList::setThrottle is the sscanf method located.
I usually use this version stolen from the Linux kernel:
int ASCII_vsscanf(const char * buf, const char * fmt, va_list args)
{
const char *str = buf;
char *next;
char digit;
int num = 0;
int qualifier;
int base;
int field_width;
int is_sign = 0;
while(*fmt && *str) {
/* skip any white space in format */
/* white space in format matchs any amount of
* white space, including none, in the input.
*/
if (isspace(*fmt)) {
while (isspace(*fmt))
++fmt;
while (isspace(*str))
++str;
}
/* anything that is not a conversion must match exactly */
if (*fmt != '%' && *fmt) {
if (*fmt++ != *str++)
break;
continue;
}
if (!*fmt)
break;
++fmt;
/* skip this conversion.
* advance both strings to next white space
*/
if (*fmt == '*') {
while (!isspace(*fmt) && *fmt)
fmt++;
while (!isspace(*str) && *str)
str++;
continue;
}
/* get field width */
field_width = -1;
if (isdigit(*fmt))
field_width = skip_atoi(&fmt);
/* get conversion qualifier */
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
*fmt == 'Z' || *fmt == 'z') {
qualifier = *fmt++;
if (unlikely(qualifier == *fmt)) {
if (qualifier == 'h') {
qualifier = 'H';
fmt++;
} else if (qualifier == 'l') {
qualifier = 'L';
fmt++;
}
}
}
base = 10;
is_sign = 0;
if (!*fmt || !*str)
break;
switch(*fmt++) {
case 'c':
{
char *s = (char *) va_arg(args,char*);
if (field_width == -1)
field_width = 1;
do {
*s++ = *str++;
} while (--field_width > 0 && *str);
num++;
}
continue;
case 's':
{
char *s = (char *) va_arg(args, char *);
if(field_width == -1)
field_width = INT_MAX;
/* first, skip leading white space in buffer */
while (isspace(*str))
str++;
/* now copy until next white space */
while (*str && !isspace(*str) && field_width--) {
*s++ = *str++;
}
*s = '\0';
num++;
}
continue;
case 'n':
/* return number of characters read so far */
{
int *i = (int *)va_arg(args,int*);
*i = str - buf;
}
continue;
case 'o':
base = 8;
break;
case 'x':
case 'X':
base = 16;
break;
case 'i':
base = 0;
case 'd':
is_sign = 1;
case 'u':
break;
case '%':
/* looking for '%' in str */
if (*str++ != '%')
return num;
continue;
default:
/* invalid format; stop here */
return num;
}
/* have some sort of integer conversion.
* first, skip white space in buffer.
*/
while (isspace(*str))
str++;
digit = *str;
if (is_sign && digit == '-')
digit = *(str + 1);
if (!digit
|| (base == 16 && !isxdigit(digit))
|| (base == 10 && !isdigit(digit))
|| (base == 8 && (!isdigit(digit) || digit > '7'))
|| (base == 0 && !isdigit(digit)))
break;
switch(qualifier) {
case 'H': /* that's 'hh' in format */
if (is_sign) {
signed char *s = (signed char *) va_arg(args,signed char *);
*s = (signed char) ASCII_simple_strtol(str,&next,base);
} else {
unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
*s = (unsigned char) ASCII_simple_strtoul(str, &next, base);
}
break;
case 'h':
if (is_sign) {
short *s = (short *) va_arg(args,short *);
*s = (short) ASCII_simple_strtol(str,&next,base);
} else {
unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
*s = (unsigned short) ASCII_simple_strtoul(str, &next, base);
}
break;
case 'l':
if (is_sign) {
long *l = (long *) va_arg(args,long *);
*l = ASCII_simple_strtol(str,&next,base);
} else {
unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
*l = ASCII_simple_strtoul(str,&next,base);
}
break;
case 'L':
if (is_sign) {
long long *l = (long long*) va_arg(args,long long *);
*l = ASCII_simple_strtoll(str,&next,base);
} else {
unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
*l = ASCII_simple_strtoull(str,&next,base);
}
break;
case 'Z':
case 'z':
{
size_t *s = (size_t*) va_arg(args,size_t*);
*s = (size_t) ASCII_simple_strtoul(str,&next,base);
}
break;
default:
if (is_sign) {
int *i = (int *) va_arg(args, int*);
*i = (int) ASCII_simple_strtol(str,&next,base);
} else {
unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
*i = (unsigned int) ASCII_simple_strtoul(str,&next,base);
}
break;
}
num++;
if (!next)
break;
str = next;
}
return num;
}
Limited but small and good enough for me.

How to get value of optarg without using command line?

I used getopt() function to get value of optarg parameters in Visual Studio. However, following to document of GNU, getopt() is a function that used to parse command-line options of the Unix/POSIX style and I can not use commandline in Visual Studio. So, how can I get the optarg in VS without using command line or getopt().
Here is my code:
int ch;
extern char* optarg;/*
extern int optind, opterr;*/
char* testfn;
bool testflag = false;
double prd = 60 * 24; // minutes of a day
int maxstep = 100; // # em steps
int Z = 2; // # mixtures
double initscale = 0.25 * prd; // 6 hours
while (optind < argc) {
if ((ch = getopt(argc, argv, "t:m:z:s:")) != -1) {
switch (ch) {
case 't':
testflag = true;
testfn = optarg;
break;
case 'm':
maxstep = atoi(optarg);
break;
case 'z':
Z = atoi(optarg);
break;
case 's':
initscale = atof(optarg);
break;
default:
usage();
}
}
else {
// Regular argument
//code to handle the argument
optind++; // Skip to the next argument
}
}

(Arduino) Can I skip multiple characters in a parseInt()?

Pretty much what the title says. From the arduino website:
Syntax
Serial.parseInt()
Serial.parseInt(char skipChar)
Parameters
skipChar: used to skip the indicated char in the search. Used for example to skip thousands divider.
Am I able to use a charmap or something similar to skip multiple characters?
Maybe you could read from Serial, but take digits only? And then parse string? Dirty example:
val = 0;
while (Serial.available() > 0) {
int c = Serial.read();
if (isDigit(c)) {
val = val * 10 + c; // Thanks to #Danny_ds for this
}
}
// Print *val*
I ended up using a very different method to get it working by using char arrays and not relying on timing. So far it has worked perfectly. I was using this to get my arduino to work as a temp monitor.
How it communicates the temperatures
PC > OpenHardwaremonitor > WMI > Batch Script (shown below) > COM Port > Arduino > LCD
This was the only way that I could get cpu temps correctly because it's so old
Batch code:
#echo off
mode COM3: baud=9600 data=8 >nul
wmic /namespace:\\root\openhardwaremonitor path sensor where "Identifier='/intelcpu/0/temperature/0' or Identifier='/nvidiagpu/0/temperature/0'" get Value /every:2|findstr [0-9]>COM3
Arduino code:
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
bool sounded = false;
int cpu = 0;
int gpu = 0;
char invalids[3] = {10, 32, '\0'}; // line feed, space
boolean newData = false;
const byte numChars = 8;
char charMap[numChars];
char tempChars[numChars];
void setup() {
pinMode(6, OUTPUT); // character intensity
pinMode(8, OUTPUT); // buzzer
pinMode(10, OUTPUT); // backlight
lcd.begin(16, 2);
Serial.begin(9600);
analogWrite(6, 100); // set intensity without POT
analogWrite(10, 168); // ~3.3v
analogReference(EXTERNAL);
lcd.print("CPU: ");
lcd.print((char)223);
lcd.print("C AIR:");
lcd.setCursor(0, 1);
lcd.print("GPU: ");
lcd.print((char)223);
lcd.print("C ");
lcd.print((char)223);
lcd.print("F");
}
void loop() {
recvWithoutWhitespace();
if (newData == true) {
parseData();
lcd.setCursor(4, 0);
lcd.print(cpu);
lcd.setCursor(4, 1);
lcd.print(gpu);
int reading = analogRead(A0);
float degreesF = (((reading * 3.3 / 1024 - 0.5) * 100) * 1.8) + 32.0;
lcd.setCursor(11, 1);
lcd.print((int)(degreesF+0.5));
if(!sounded && (cpu > 75 || gpu > 85)) { // used for buzzer alarm
tone(8, 500);
delay(250);
noTone(8);
delay(250);
tone(8, 500);
delay(250);
noTone(8);
delay(250);
tone(8, 500);
delay(250);
noTone(8);
sounded = true;
} else if(sounded && (cpu <= 75 && gpu <= 85)) {
sounded = false;
}
newData = false;
}
}
void recvWithoutWhitespace() {
static byte ndx = 0;
static byte control = 0; // switch control variable
char endMarker = 13; // ASCII code for carriage return
char rc;
char * check;
while (Serial.available() > 0 && newData == false) {
rc = Serial.read();
check=strchr(invalids,rc); //checks if any spaces or line feeds get in
if (check==NULL){
if (rc != endMarker) {
charMap[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
switch(control) { // expect 4 CRs in format: (num)CRCR(num)CRCR
case 0:
control = 1; // skip first of 2 CRs
break;
case 1:
charMap[ndx] = 44; // put comma in place of CR between numbers as delimeter
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
control = 2;
break;
case 2:
control = 3; // skip first of 2 CRs
break;
case 3:
charMap[ndx] = '\0'; // string terminator in place of last CR
ndx = 0;
control = 0;
newData = true;
break;
}
}
}
}
}
void parseData() {
strcpy(tempChars, charMap); //strtok is destructive so copy string temporarily
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars, ",");
cpu = atoi(strtokIndx); // convert cpu to an integer
strtokIndx = strtok(NULL, ",");
gpu = atoi(strtokIndx); // convert gpu to an integer
}

How to input sample images in libsvm train code

I have downloaded the libsvm code for object detection. I am having problems in using the train svm code. I can't input the sample files properly. Anyone please help me how to input positive and negative images.Here is the train code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "svm.h"
#define Malloc(type,n) (type *)malloc((n)*sizeof(type))
void print_null(const char *s) {}
void exit_with_help()
{
printf(
"Usage: svm-train [options] training_set_file [model_file]\n"
"options:\n"
"-s svm_type : set type of SVM (default 0)\n"
" 0 -- C-SVC (multi-class classification)\n"
" 1 -- nu-SVC (multi-class classification)\n"
" 2 -- one-class SVM\n"
" 3 -- epsilon-SVR (regression)\n"
" 4 -- nu-SVR (regression)\n"
"-t kernel_type : set type of kernel function (default 2)\n"
" 0 -- linear: u'*v\n"
" 1 -- polynomial: (gamma*u'*v + coef0)^degree\n"
" 2 -- radial basis function: exp(-gamma*|u-v|^2)\n"
" 3 -- sigmoid: tanh(gamma*u'*v + coef0)\n"
" 4 -- precomputed kernel (kernel values in training_set_file)\n"
"-d degree : set degree in kernel function (default 3)\n"
"-g gamma : set gamma in kernel function (default 1/num_features)\n"
"-r coef0 : set coef0 in kernel function (default 0)\n"
"-c cost : set the parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1)\n"
"-n nu : set the parameter nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5)\n"
"-p epsilon : set the epsilon in loss function of epsilon-SVR (default 0.1)\n"
"-m cachesize : set cache memory size in MB (default 100)\n"
"-e epsilon : set tolerance of termination criterion (default 0.001)\n"
"-h shrinking : whether to use the shrinking heuristics, 0 or 1 (default 1)\n"
"-b probability_estimates : whether to train a SVC or SVR model for probability estimates, 0 or 1 (default 0)\n"
"-wi weight : set the parameter C of class i to weight*C, for C-SVC (default 1)\n"
"-v n: n-fold cross validation mode\n"
"-q : quiet mode (no outputs)\n"
);
exit(1);
}
void exit_input_error(int line_num)
{
fprintf(stderr,"Wrong input format at line %d\n", line_num);
exit(1);
}
void parse_command_line(int argc, char **argv, char *input_file_name, char *model_file_name);
void read_problem(const char *filename);
void do_cross_validation();
struct svm_parameter param; // set by parse_command_line
struct svm_problem prob; // set by read_problem
struct svm_model *model;
struct svm_node *x_space;
int cross_validation;
int nr_fold;
static char *line = NULL;
static int max_line_len;
static char* readline(FILE *input)
{
int len;
if(fgets(line,max_line_len,input) == NULL)
return NULL;
while(strrchr(line,'\n') == NULL)
{
max_line_len *= 2;
line = (char *) realloc(line,max_line_len);
len = (int) strlen(line);
if(fgets(line+len,max_line_len-len,input) == NULL)
break;
}
return line;
}
int main(int argc, char **argv)
{
char input_file_name[1024];
char model_file_name[1024];
const char *error_msg;
parse_command_line(argc, argv, input_file_name, model_file_name);
read_problem(input_file_name);
error_msg = svm_check_parameter(&prob,&param);
if(error_msg)
{
fprintf(stderr,"ERROR: %s\n",error_msg);
exit(1);
}
if(cross_validation)
{
do_cross_validation();
}
else
{
model = svm_train(&prob,&param);
if(svm_save_model(model_file_name,model))
{
fprintf(stderr, "can't save model to file %s\n", model_file_name);
exit(1);
}
svm_free_and_destroy_model(&model);
}
svm_destroy_param(&param);
free(prob.y);
free(prob.x);
free(x_space);
free(line);
return 0;
}
void do_cross_validation()
{
int i;
int total_correct = 0;
double total_error = 0;
double sumv = 0, sumy = 0, sumvv = 0, sumyy = 0, sumvy = 0;
double *target = Malloc(double,prob.l);
svm_cross_validation(&prob,&param,nr_fold,target);
if(param.svm_type == EPSILON_SVR ||
param.svm_type == NU_SVR)
{
for(i=0;i<prob.l;i++)
{
double y = prob.y[i];
double v = target[i];
total_error += (v-y)*(v-y);
sumv += v;
sumy += y;
sumvv += v*v;
sumyy += y*y;
sumvy += v*y;
}
printf("Cross Validation Mean squared error = %g\n",total_error/prob.l);
printf("Cross Validation Squared correlation coefficient = %g\n",
((prob.l*sumvy-sumv*sumy)*(prob.l*sumvy-sumv*sumy))/
((prob.l*sumvv-sumv*sumv)*(prob.l*sumyy-sumy*sumy))
);
}
else
{
for(i=0;i<prob.l;i++)
if(target[i] == prob.y[i])
++total_correct;
printf("Cross Validation Accuracy = %g%%\n",100.0*total_correct/prob.l);
}
free(target);
}
void parse_command_line(int argc, char **argv, char *input_file_name, char *model_file_name)
{
int i;
void (*print_func)(const char*) = NULL; // default printing to stdout
// default values
param.svm_type = C_SVC;
param.kernel_type = RBF;
param.degree = 3;
param.gamma = 0; // 1/num_features
param.coef0 = 0;
param.nu = 0.5;
param.cache_size = 100;
param.C = 1;
param.eps = 1e-3;
param.p = 0.1;
param.shrinking = 1;
param.probability = 0;
param.nr_weight = 0;
param.weight_label = NULL;
param.weight = NULL;
cross_validation = 0;
// parse options
for(i=1;i<argc;i++)
{
if(argv[i][0] != '-') break;
if(++i>=argc)
exit_with_help();
switch(argv[i-1][1])
{
case 's':
param.svm_type = atoi(argv[i]);
break;
case 't':
param.kernel_type = atoi(argv[i]);
break;
case 'd':
param.degree = atoi(argv[i]);
break;
case 'g':
param.gamma = atof(argv[i]);
break;
case 'r':
param.coef0 = atof(argv[i]);
break;
case 'n':
param.nu = atof(argv[i]);
break;
case 'm':
param.cache_size = atof(argv[i]);
break;
case 'c':
param.C = atof(argv[i]);
break;
case 'e':
param.eps = atof(argv[i]);
break;
case 'p':
param.p = atof(argv[i]);
break;
case 'h':
param.shrinking = atoi(argv[i]);
break;
case 'b':
param.probability = atoi(argv[i]);
break;
case 'q':
print_func = &print_null;
i--;
break;
case 'v':
cross_validation = 1;
nr_fold = atoi(argv[i]);
if(nr_fold < 2)
{
fprintf(stderr,"n-fold cross validation: n must >= 2\n");
exit_with_help();
}
break;
case 'w':
++param.nr_weight;
param.weight_label = (int *)realloc(param.weight_label,sizeof(int)*param.nr_weight);
param.weight = (double *)realloc(param.weight,sizeof(double)*param.nr_weight);
param.weight_label[param.nr_weight-1] = atoi(&argv[i-1][2]);
param.weight[param.nr_weight-1] = atof(argv[i]);
break;
default:
fprintf(stderr,"Unknown option: -%c\n", argv[i-1][1]);
exit_with_help();
}
}
svm_set_print_string_function(print_func);
// determine filenames
if(i>=argc)
exit_with_help();
strcpy(input_file_name, argv[i]);
if(i<argc-1)
strcpy(model_file_name,argv[i+1]);
else
{
char *p = strrchr(argv[i],'/');
if(p==NULL)
p = argv[i];
else
++p;
sprintf(model_file_name,"%s.model",p);
}
}
// read in a problem (in svmlight format)
void read_problem(const char *filename)
{
int max_index, inst_max_index, i;
size_t elements, j;
FILE *fp = fopen(filename,"r");
char *endptr;
char *idx, *val, *label;
if(fp == NULL)
{
fprintf(stderr,"can't open input file %s\n",filename);
exit(1);
}
prob.l = 0;
elements = 0;
max_line_len = 1024;
line = Malloc(char,max_line_len);
while(readline(fp)!=NULL)
{
char *p = strtok(line," \t"); // label
// features
while(1)
{
p = strtok(NULL," \t");
if(p == NULL || *p == '\n') // check '\n' as ' ' may be after the last feature
break;
++elements;
}
++elements;
++prob.l;
}
rewind(fp);
prob.y = Malloc(double,prob.l);
prob.x = Malloc(struct svm_node *,prob.l);
x_space = Malloc(struct svm_node,elements);
max_index = 0;
j=0;
for(i=0;i<prob.l;i++)
{
inst_max_index = -1; // strtol gives 0 if wrong format, and precomputed kernel has <index> start from 0
readline(fp);
prob.x[i] = &x_space[j];
label = strtok(line," \t\n");
if(label == NULL) // empty line
exit_input_error(i+1);
prob.y[i] = strtod(label,&endptr);
if(endptr == label || *endptr != '\0')
exit_input_error(i+1);
while(1)
{
idx = strtok(NULL,":");
val = strtok(NULL," \t");
if(val == NULL)
break;
errno = 0;
x_space[j].index = (int) strtol(idx,&endptr,10);
if(endptr == idx || errno != 0 || *endptr != '\0' || x_space[j].index <= inst_max_index)
exit_input_error(i+1);
else
inst_max_index = x_space[j].index;
errno = 0;
x_space[j].value = strtod(val,&endptr);
if(endptr == val || errno != 0 || (*endptr != '\0' && !isspace(*endptr)))
exit_input_error(i+1);
++j;
}
if(inst_max_index > max_index)
max_index = inst_max_index;
x_space[j++].index = -1;
}
if(param.gamma == 0 && max_index > 0)
param.gamma = 1.0/max_index;
if(param.kernel_type == PRECOMPUTED)
for(i=0;i<prob.l;i++)
{
if (prob.x[i][0].index != 0)
{
fprintf(stderr,"Wrong input format: first column must be 0:sample_serial_number\n");
exit(1);
}
if ((int)prob.x[i][0].value <= 0 || (int)prob.x[i][0].value > max_index)
{
fprintf(stderr,"Wrong input format: sample_serial_number out of range\n");
exit(1);
}
}
fclose(fp);
}
UPDATE
can i convert to numerical representation using this code?
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <cv.h>
#include <highgui.h>
#include <cvaux.h>
#include <iostream>
#include <vector>
#include<string.h>
using namespace std;
using namespace cv;
int main ( int argc, char** argv )
{
cout << "OpenCV Training SVM Automatic Number Plate Recognition\n";
cout << "\n";
char* path_Plates;
char* path_NoPlates;
int numPlates;
int numNoPlates;
int imageWidth=150;
int imageHeight=150;
//Check if user specify image to process
if(1)
{
numPlates= 12;
numNoPlates= 67 ;
path_Plates= "/home/kaushik/opencv_work/Manas6/Pics/Positive_Images/";
path_NoPlates= "/home/kaushik/opencv_work/Manas6/Pics/Negative_Images/i";
}else{
cout << "Usage:\n" << argv[0] << " <num Plate Files> <num Non Plate Files> <path to plate folder files> <path to non plate files> \n";
return 0;
}
Mat classes;//(numPlates+numNoPlates, 1, CV_32FC1);
Mat trainingData;//(numPlates+numNoPlates, imageWidth*imageHeight, CV_32FC1 );
Mat trainingImages;
vector<int> trainingLabels;
for(int i=1; i<= numPlates; i++)
{
stringstream ss(stringstream::in | stringstream::out);
ss<<path_Plates<<i<<".jpg";
try{
const char* a = ss.str().c_str();
printf("\n%s\n",a);
Mat img = imread(ss.str(), CV_LOAD_IMAGE_UNCHANGED);
img= img.clone().reshape(1, 1);
//imshow("Window",img);
//cout<<ss.str();
trainingImages.push_back(img);
trainingLabels.push_back(1);
}
catch(Exception e){;}
}
for(int i=0; i< numNoPlates; i++)
{
stringstream ss(stringstream::in | stringstream::out);
ss << path_NoPlates<<i << ".jpg";
try
{
const char* a = ss.str().c_str();
printf("\n%s\n",a);
Mat img=imread(ss.str(),CV_LOAD_IMAGE_UNCHANGED);
//imshow("Win",img);
img= img.clone().reshape(1, 1);
trainingImages.push_back(img);
trainingLabels.push_back(0);
//cout<<ss.str();
}
catch(Exception e){;}
}
Mat(trainingImages).copyTo(trainingData);
//trainingData = trainingData.reshape(1,trainingData.rows);
trainingData.convertTo(trainingData, CV_32FC1);
Mat(trainingLabels).copyTo(classes);
FileStorage fs("SVM.xml", FileStorage::WRITE);
fs << "TrainingData" << trainingData;
fs << "classes" << classes;
fs.release();
return 0;
}
What I can see from your code is, that you are mixing OpenCV and LIBSVM.
Basically you can follow one of the following ways. Personally I would suggest to use OpenCV only.
OpenCV
OpenCV is a very powerfull library for working with images. Hence they implement their own machine learning algorithms including SVMs.
As described in a very good way here it is very easy to perform classification with images via OpenCV since the algorithms use a common interface for this purpose.
LIBSVM
LIBSVM a standalone library for SVM classification in various form (e.g. multiclass, two-class, with probability estimates, etc). If you go this way, you have to perform the following steps in order to do successful classification:
Think about how many different classes you want to differentiate (e.g. + / -)
Maybe preprocess your images (filters, ...)
Extract so called "features" rom your images using a feature selection method (for example: Mutual Information). Those methods will tell you, which points are significant for your given classes since we follow the basic assumption, that not every singel pixel in an image is important.
According to your extracted features you transform your images to an vectorial representation.
Write it into an file according to the LIBSVM data format:
label feature_id1:feature_value1 feature_id2:feature_value2
+1 1:0.53265 2:0.5232
-1 1:0.78543 2:0.64326
Proceed with "svm_train" according to its description. Classification would be a combination of 2.) 4.) 5.) and a run of "svm_predict".

strftime/strptime problem

I've written a function to convert datetimes from one format to another -
int convertDateTime(char* source_fmt,char* dest_fmt,char* source,char* dest)
{
struct tm tmpptr;
if (strptime(source,source_fmt,&tmpptr) == NULL)
{
strcpy(dest,"");
return -1;
}
strftime(dest,100,dest_fmt,&tmpptr);
return 0;
}
It works fine for most formats, But when I use format = "%y%j", all I get is 10001; the julian day does not work.
I'm using gcc on solaris 10. Any idea what i need to change?
Check the expected behaviour of strptime, in some implementations it is greedy and will consume as many digits as it can.
This has been a problem for me on MacOS, the implementation changed in 10.5 with the UNIX standards compliance efforts. Before this the following call worked fine.
strptime("20071124", "%Y%m%d")
As of 10.5 you have to do the following to get it to work.
#define _NONSTD_SOURCE
Your compiler libraries and OS may differ though.
I can tell you what the problem is. When you use strptime() with the %j format, it only populates the tm_yday field on your struct tm. This isn't limited to Solaris by the way, CygWin gcc is doing the same thing.
Because your strftime() is most likely using other fields (tm_mon and tm_mday), and these are still set to zero, that's why you're getting the wrong day of the year.
The following code illustrates this:
#include <time.h>
#include <stdio.h>
#include <string.h>
#define dump() \
printf ("DEBUG tm_sec = %d, tm_min = %d, tm_hour = %d, tm_mday = %d, " \
"tm_mon = %d, tm_year = %d, tm_wday = %d, tm_yday = %d, " \
"tm_isdst = %d\n", \
tmpptr.tm_sec, tmpptr.tm_min, tmpptr.tm_hour, tmpptr.tm_mday, \
tmpptr.tm_mon, tmpptr.tm_year, tmpptr.tm_wday, tmpptr.tm_yday, \
tmpptr.tm_isdst)
int convertDateTime(char* source_fmt,char* dest_fmt,char* source,char* dest) {
struct tm tmpptr;
memset (&tmpptr,0,sizeof(tmpptr));
dump();
if (strptime(source,source_fmt,&tmpptr) == NULL) {
strcpy(dest,"");
return -1;
}
dump();
strftime(dest,100,dest_fmt,&tmpptr);
return 0;
}
int main (int argc, char *argv[]) {
char dest[1000];
printf ("1: [%s]\n", argv[1]);
printf ("2: [%s]\n", argv[2]);
printf ("3: [%s]\n", argv[3]);
printf ("retval = %d\n", convertDateTime (argv[1],argv[2],argv[3],dest));
printf ("=: [%s]\n", dest);
return 0;
}
When you run it thus:
pax> date ; ./tetsprog %y%j %Y-%m-%d 10162
you get:
Tue Aug 3 12:46:13 WAST 2010
1: [%y%j]
2: [%Y-%m-%d]
3: [10162]
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0,
tm_mday = 0, tm_mon = 0, tm_year = 0,
tm_wday = 0, tm_yday = 0, tm_isdst = 0
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0,
tm_mday = 0, tm_mon = 0, tm_year = 110,
tm_wday = 0, tm_yday = 161, tm_isdst = 0
retval = 0
=: [2010-01-00]
Fixing it is tricky, I'm not aware of any standard time function that will rebuild tm_mday and tm_mon from tm_yday.
But, if you're stuck for a solution, try this out:
static void fixIt (struct tm *t) {
static int monthDaysN[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static int monthDaysL[] = {31,29,31,30,31,30,31,31,30,31,30,31};
int *monthDays = monthDaysN;
int base = 0;
int i;
if (((t->tm_year + 1900) % 4) == 0) monthDays = monthDaysL;
if (((t->tm_year + 1900) % 100) == 0) monthDays = monthDaysN;
if (((t->tm_year + 1900) % 400) == 0) monthDays = monthDaysL;
// Leap years irrelevant for January dates.
if (t->tm_yday < 31) monthDays = monthDaysN;
for (i = 0; i < 12; i++) {
if (t->tm_yday - base < monthDays[i]) {
t->tm_mday = t->tm_yday - base + 1;
t->tm_mon = i;
return;
}
base += monthDays[i];
}
}
It will set those two fields based on tm_year and tm_yday and it's a bit of a kludge but will get you going at least (and maybe you'll find a better way).
I would insert a call to this in your convert function and only call it under specific circumstances so as not to overwrite values that are already set:
int convertDateTime(char* source_fmt,char* dest_fmt,char* source,char* dest) {
struct tm tmpptr;
memset (&tmpptr,0,sizeof(tmpptr));
dump();
if (strptime(source,source_fmt,&tmpptr) == NULL) {
strcpy(dest,"");
return -1;
}
if ((tmpptr.tm_yday != 0) && (tmpptr.tm_mday == 0))
fixIt (&tmpptr);
dump();
strftime(dest,100,dest_fmt,&tmpptr);
return 0;
}
which gives:
pax> date ; testprog %y%j %Y-%m-%d 10162
Tue Aug 3 13:34:36 WAST 2010
1: [%y%j]
2: [%Y-%m-%d]
3: [10162]
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0,
tm_mday = 0, tm_mon = 0, tm_year = 0,
tm_wday = 0, tm_yday = 0, tm_isdst = 0
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0,
tm_mday = 11, tm_mon = 5, tm_year = 110,
tm_wday = 0, tm_yday = 161, tm_isdst = 0
retval = 0
=: [2010-06-11]
And, like all code here, you should test it thoroughly. I'm pretty certain I got all the edge cases but, since you haven't paid me cold hard cash for my services, you should assume this is general advice only, not a specific solution :-)
Update
The original answer (below) presumed that the "%y%j" format was used on the output (strftime) not the input (strptime). The mktime function will compute yday from valid info, but it doesn't work the other way.
If you want to decode something like that you need to do it manually. Some example code follows. It has had minimal testing and almost certainly has bugs. You may need to alter ir, depending in the input format you want.
#include <string>
#include <ctime>
#include <cassert>
#include <iostream>
int GetCurrentYear()
{
time_t tNow(::time(NULL));
struct tm tmBuff = *::localtime(&tNow);
return tmBuff.tm_year;
}
bool IsLeapYear(int nYear)
{
if (0 == (nYear%1000)) return true;
if (0 == (nYear%100)) return false;
if (0 == (nYear%4)) return true;
return false;
}
// nMonth = 0 (Jan) to 11 (Dec)
int DaysPerMonth(int nMonth, bool bLeapYear)
{
// J F M A M J J A S O N D
int nDays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
assert(nMonth>=0 && nMonth<12);
int nRet = nDays[nMonth];
if (bLeapYear && nMonth==1)
nRet++;
return nRet;
}
// sDate is in the format YYDDD where YY is the last 2 digits of the year
// and YYY is the day of the year (1/1 = 1, 31/12 = 365 for non-leap year)
bool DecodeDate(const std::string &sDate, struct tm &tmBuff)
{
if (sDate.length() != 5 ||
!isdigit(sDate[0]) ||
!isdigit(sDate[1]) ||
!isdigit(sDate[2]) ||
!isdigit(sDate[3]) ||
!isdigit(sDate[4]))
{
return false;
}
::memset(&tmBuff, 0, sizeof(struct tm));
tmBuff.tm_year = GetCurrentYear();
// drop last 2 digits
tmBuff.tm_year -= tmBuff.tm_year%100;
// replace last 2 digits
tmBuff.tm_year += ::atoi(sDate.substr(0, 2).c_str());
tmBuff.tm_yday = ::atoi(sDate.substr(2).c_str());
int nDays(tmBuff.tm_yday);
bool bLeapYear(IsLeapYear(1900 + tmBuff.tm_year));
int nTmp = DaysPerMonth(0, bLeapYear);
while (nTmp < nDays)
{
nDays -= nTmp;
tmBuff.tm_mon++;
nTmp = DaysPerMonth(tmBuff.tm_mon, bLeapYear);
}
tmBuff.tm_mday = nDays;
::mktime(&tmBuff);
return true;
}
int main(int argc, char *argv[])
{
for (int i=1; i<argc; i++)
{
struct tm tmBuff;
DecodeDate(argv[i], tmBuff);
const size_t nSize(128);
char szBuff[nSize];
strftime(szBuff, nSize, "%A, %d %B %Y", &tmBuff);
std::cout << argv[i] << '\t' << szBuff << std::endl;
}
return 0;
}
================================================
C:\Dvl\Tmp>Test.exe 07123 08123 08124
07123 Thursday, 03 May 2007
08123 Friday, 02 May 2008
08124 Saturday, 03 May 2008
End Update
After you call strptime(), call mktime() which will populate any missing members of the struct. Also, you should zero out the struct before beginning.
#include <string>
#include <ctime>
int convertDateTime(const std::string &sSourceFmt,
const std::string &sDestFmt,
const std::string &sSource,
std::string &sDest)
{
struct tm tmbuff = { 0 };
if (::strptime(sSource.c_str(), sSourceFmt.c_str(), &tmbuff) != NULL)
{
::mktime(&tmbuff);
const size_t nSize(256);
char szBuff[nSize+1] = "";
if (::strftime(szBuff, nSize, sDestFmt.c_str(), &tmbuff))
{
sDest = szBuff;
return 0;
}
}
sDest.clear();
return -1;
}