How to compare two dates in C++ - c++

I need to compare two dates.
The current date with the user input one in the format dd/mm/yyyy.
So if expiry_date > current_date
display....
I have tried difftime() but that didnt work well since it uses a time parameter
time_t now;
struct tm newyear;
double seconds;
time(&now); /* get current time; same as: now = time(NULL) */
newyear = *localtime(&now);
newyear.tm_hour = 0; newyear.tm_min = 0; newyear.tm_sec = 0;
newyear.tm_mon = 0; newyear.tm_mday = 1;
seconds = difftime(now,mktime(&newyear));
printf ("%.f seconds diff", seconds);
system("pause");
heres a sample code i found

If this is really a fixed format, you can do it with simple C string comparison
int date_cmp(const char *d1, const char *d2)
{
int rc;
// compare years
rc = strncmp(d1 + 6, d2 + 6, 4);
if (rc != 0)
return rc;
// compare months
rc = strncmp(d1 + 3, d2 + 3, 2);
if (rc != 0)
return rc;
// compare days
return strncmp(d1, d2, 2);
}

Try strftime to parse the string into a tm*, then use mktime() and difftime().

Related

Converting Char array to TimeStamp in C++

I have char start_time[40] = "2020-04-01 12:00:00"; How can I convert the char array to timestamp in C++ without using strptime?
You can try this:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
int main(void) {
const char T[] = "2020-04-01 12:00:00";
time_t result = 0;
int year = 0, month = 0, day = 0, hour = 0, min = 0,sec=0;
if (sscanf(T, "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min,&sec) == 6) {
struct tm test_time = {0};
test_time.tm_year = year - 1900; /* years since 1900 */
test_time.tm_mon = month - 1;
test_time.tm_mday = day;
test_time.tm_hour = hour;
test_time.tm_min = min;
test_time.tm_sec = sec;
if ((result = mktime(&test_time)) == (time_t)-1) {
fprintf(stderr, "Cannot convert time to time_t\n");
return EXIT_FAILURE;
}
std::cout << result << '\n' ;
puts(ctime(&result));
struct tm *t_start = localtime(&result);
char date_time[30];
strftime( date_time, sizeof(date_time), "%Y-%m-%d %H:%M:%S", t_start );
std::cout << date_time << '\n' ;
return EXIT_SUCCESS;
}
else {
fprintf(stderr, "The input was not a valid time format\n");
return EXIT_FAILURE;
}
}

Get a time_t from universal time string?

If I have this string:
2011-10-08T07:07:09Z
is it possible to get a time_t from it? If so, how can this be done?
Yes, it is. First, convert it to a broken down time with strptime(3). This gives you a struct tm, which is the structure type for a broken down time.
From there, you can convert to a time_t with mktime(3).
Here's an example:
#define _XOPEN_SOURCE
#include <time.h>
#include <stdio.h>
#include <string.h>
int main(void) {
const char *date_example = "2011-10-08T07:07:09Z";
struct tm broken_down;
memset(&broken_down, 0, sizeof(broken_down));
strptime(date_example, "%Y-%m-%dT%H:%M:%SZ", &broken_down);
broken_down.tm_isdst = 0; // Indicates that DST is not in effect
time_t epoch_time = mktime(&broken_down);
// Note: this is platform dependent
printf("Epoch time: %lld\n", (long long) epoch_time);
return 0;
}
Use sscanf() to tear apart the time. The trick is somehow determine the difference between local and universal time so code may call mktime() - which uses assumes struct tm is local time..
#include <time.h>
#include <stdio.h>
int Get_TZ_delta(const struct tm *tmptr) {
// Make local copy
struct tm tm = *tmptr;
time_t t = mktime(&tm);
struct tm utc_tm = *gmtime(&t);
time_t t2 = mktime(&utc_tm);
return (int) difftime(t, t2);
}
time_t UniversalTimeStamp_to_time_t(const char *ts) {
struct tm tm = { 0 };
// Use a sentinel to catch extra garbage
char sentinel;
if (sscanf(ts, "%d-%2d-%2dT%2d:%2d:%2dZ%c", &tm.tm_year, &tm.tm_mon,
&tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &sentinel) != 6) {
return -1;
}
// struct tm uses offset from 1900 and January is month 0
tm.tm_year -= 1900;
tm.tm_mon--;
// Convert tm from UCT to local standard time
tm.tm_isdst = 0;
tm.tm_sec += Get_TZ_delta(&tm);
time_t t = mktime(&tm); // mktime() assumes tm is local
// test code
{
printf("UTC `%s`\n", ts);
char buf[100];
strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S %Z", &tm);
printf("Local %s\n", buf);
printf("Unix %lld\n\n", (long long) mktime(&tm));
}
return t;
}
int main(void) {
UniversalTimeStamp_to_time_t("2015-06-18T22:07:52Z");
UniversalTimeStamp_to_time_t("2011-10-08T07:07:09Z");
UniversalTimeStamp_to_time_t("1970-01-01T00:00:00Z");
return 0;
}
Output
UTC `2015-06-18T22:07:52Z`
Local 2015-06-18T17:07:52 CDT
Unix 1434665272
UTC `2011-10-08T07:07:09Z`
Local 2011-10-08T02:07:09 CDT
Unix 1318057629
UTC `1970-01-01T00:00:00Z`
Local 1969-12-31T18:00:00 CST
Unix 0
Another approach works should code know that time_t is the number of seconds since Jan 1, 1970 0:00:00. Uses sscanf() to parse the string, calculate the number of days, and then return the number of seconds.
#include <time.h>
#include <stdio.h>
#define MARCH 3
#define DaysPer400Years (400*365LL + 97)
#define DaysPer100Years (100*365LL + 24)
#define DaysPer4Years (4*365LL + 1)
#define DaysPer1Year 365LL
#define DayNumber1970Jan1 719469LL
long long DayNumber(int year, int Month, int Day, long epoch) {
long long dn = Day;
long long y = year;
y += Month / 12;
Month %= 12;
while (Month < MARCH) {
Month += 12;
y--;
}
// And then a miracle occurs.
dn += ((Month - MARCH) * (7832 / 4) + (140 / 4)) >> (8 - 2);
dn += (y / 400) * DaysPer400Years;
y %= 400;
dn += (y / 100) * DaysPer100Years;
y %= 100;
dn += (y / 4) * DaysPer4Years;
y %= 4;
dn += y * DaysPer1Year;
return dn - epoch;
}
time_t UniversalTimeStamp_to_time_t(const char *ts) {
int y,m,d,H,M,S;
// Use a sentinel to catch extra garbage
char sentinel;
if (sscanf(ts, "%d-%2d-%2dT%2d:%2d:%2dZ%c", &y, &m,
&d, &H, &M, &S, &sentinel) != 6) {
return -1;
}
long long t = DayNumber(y, m, d, DayNumber1970Jan1);
t = t*24L*60*60 + 3600L*H + 60*M + S;
// test code
{
printf("UTC `%s`\n", ts);
time_t tt = t;
struct tm tm = *gmtime(&tt);
char buf[100];
strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S %Z", &tm);
printf("Local %s\n", buf);
printf("Unix %lld\n\n", t);
}
return t;
}
int main(void) {
UniversalTimeStamp_to_time_t("2015-06-18T22:07:52Z");
UniversalTimeStamp_to_time_t("2011-10-08T07:07:09Z");
UniversalTimeStamp_to_time_t("1970-01-01T00:00:00Z");
return 0;
}
Output
UTC `2015-06-18T22:07:52Z`
Local 2015-06-18T22:07:52
Unix 1434665272
UTC `2011-10-08T07:07:09Z`
Local 2011-10-08T07:07:09
Unix 1318057629
UTC `1970-01-01T00:00:00Z`
Local 1970-01-01T00:00:00
Unix 0

Including headers correctly

First question. If I include a header of a class to the header file of a second class, do I have to include the header of the first class to the .cpp file of the second class?
At the .cpp file of the second class I include its header which includes the .h file of the first class. So isn't it correct or do I have to include the first's class header to the .cpp file of the second class also?
Second problem. I have two classes. The first has static variables and functions, so I can call it without making objects. At the first class I have a static object of the second class. Now at the second class I want to pass the returns of some functions of the first class as operands to functions of the first class. I get no errors at the second's class, but I get an error at the declaration of the static object I mention before at the first class. Why is this happening?
The code:
Header files:
/*
* NTPlib.h
*
* Created on: 13 Feb 2013
* Author : Platonas
*/
#ifndef NTP_H_
#define NTP_H_
#include "Arduino.h"
#include "SPI.h"
#include "IPAddress.h"
#include "EthernetUdp.h"
#include "Ethernet.h"
#include "DayNumber.h"
class NTP {
private:
public:
static EthernetUDP Udp;
static DayNumber DN;
static const int Gmt = 2;
static const unsigned int localPort = 8888;
static const int NTP_PACKET_SIZE = 48;
static byte packetBuffer[NTP_PACKET_SIZE ];
static unsigned long secsSince1900;
static unsigned long UnixTime;
static int utchour;
static int lcthour;
static int min;
static int sec;
static int year;
static int month;
static int date;
static int dayOfWeek;
static bool timeSet;
NTP();
static NTP getTime();
static bool testNtpServer();
static void startEthernetAndUdp();
static unsigned long sendNTPpacket(IPAddress& address);
static int getYear();
static int getMonth();
static int getDate();
static int getDayOfWeek();
static int getUTChour();
static int getLCThour();
static int getMin();
static int getSec();
static void serialPrinting();
virtual ~NTP();
};
#endif /* NTPLIB_H_ */
and
/*
DayNumber.h - Library for calculation of day's number on 1 and 4 years loop.
Created by Pavlidis Kyriakos, December 20, 2012.
Released into the public domain.
*/
#ifndef DayNumber_H_
#define DayNumber_H_
#include "NTP.h"
class DayNumber {
private:
int _day1YearLoop[];
int _day4YearLoop[];
public:
int Days1YearLoop;
int Days4YearLoop;
DayNumber();
void dayNumberCalc();
virtual ~DayNumber();
bool checkLeapYear(int setYear);
};
#endif
.cpp files:
/*
NTP.cpp - Library for NTP server.
Created by Pavlidis Kyriakos, Feb 13, 2013.
Released into the public domain.
*/
#include "NTP.h"
#include "DayNumber.h"
unsigned long NTP::UnixTime = 0;
unsigned long NTP::secsSince1900 = 0;
int NTP::utchour = 99;
int NTP::lcthour = 99;
int NTP::min = 99;
int NTP::sec = 99;
int NTP::year = 99;
int NTP::month = 99;
int NTP::date = 99;
int NTP::dayOfWeek = 99;
byte NTP::packetBuffer[NTP_PACKET_SIZE ];
bool NTP::timeSet = false;
DayNumber NTP::DN = DayNumber();
EthernetUDP NTP::Udp = EthernetUDP();
NTP::NTP() {
// TODO Auto-generated constructor stub
}
NTP NTP::getTime() {
if (testNtpServer()) {
timeSet = true;
// We've received a packet, read the data from it
Udp.read((unsigned char*)packetBuffer,NTP_PACKET_SIZE); // Read the packet into the buffer
// The timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, extract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// Combine the four bytes (two words) into a long integer.
// This is NTP time (seconds since Jan 1 1900):
secsSince1900 = highWord << 16 | lowWord;
// Now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// Subtract seventy years:
UnixTime = secsSince1900 - seventyYears;
sec = UnixTime % 60;
//Calc min
min = (UnixTime/60)%60;
//Calc hour
utchour = (UnixTime/3600)%24;
lcthour = utchour + Gmt;
//Day of the week
dayOfWeek = (((UnixTime/86400UL) + 3) % 7) + 1; //Setting first day Sunday = 1
//Calculating years
unsigned long UnixTimeToDays = UnixTime/86400UL;
//Serial.println(UnixTimeToDays);
unsigned long calcDaysInYears = 0;
int calcYear = 1970;
while((calcDaysInYears += (DN.checkLeapYear(calcYear)? 366:365)) <= UnixTimeToDays) {
calcYear++;
}
year = calcYear;
//Calculating days in this year
calcDaysInYears -= (DN.checkLeapYear(calcYear)? 366:365);
int daysPassedInYear = UnixTimeToDays - calcDaysInYears;
//Set DayNumber one year loop
DN.Days1YearLoop = daysPassedInYear + 1;
//calculating date and month
static const uint8_t monthDays[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int calcMonth;
int monthLength;
for (calcMonth = 0; calcMonth<12; calcMonth++) {
if (DN.checkLeapYear(year)) {
monthLength = (calcMonth == 1) ? 29: 28;
}
else {
monthLength = monthDays[calcMonth];
}
if ( daysPassedInYear > monthLength) {
daysPassedInYear -= monthLength;
}
else {
break;
}
}
month = ++calcMonth;
date = ++daysPassedInYear;
serialPrinting();
return NTP();
}
else {
//Error me tous ntp diavazoume wra apo DS1307
Serial.println("pame gia RTC");
return NTP();
}
}
unsigned long NTP::sendNTPpacket(IPAddress& address) {
// Set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// Eight bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// All NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer,NTP_PACKET_SIZE);
Udp.endPacket();
}
void NTP::startEthernetAndUdp() {
//Declaration of the mac address of ethernet shield
byte mac[] = {0x00,0xAA,0xBB,0xCC,0xDE,0x02};
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// No point in carrying on, so do nothing forevermore:
//for(;;)
// ;
//Prepei na diorthwthei na kanei bypass to DHCP kai na paei sto RTC an den exei internet
}
Udp.begin(localPort);
}
bool NTP::testNtpServer() {
//(193,93,167,241 ); //GR time server on athens ntp.asda.gr
//(129,215,160,240 ); //UK extntp0.inf.ed.ac.uk School of Informatics, University of Edinburgh, Scotland, UK
//(138,195,130,71 ); //FR ntp.via.ecp.fr VIA, Ecole Centrale Paris, France
//(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov NTP server
//(193,93,167,239); //GR time server on athens ChronosAsdaGr
//(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov NTP server
//(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov NTP server
byte serverslist[4][4] = {
193,93,167,241,
129,215,160,240,
138,195,130,71,
132,163,4,101
};
IPAddress ntpServers(serverslist[0]);
sendNTPpacket(ntpServers);
int x = 0;
delay(1000);
//Checking different NTP server if someone is down
while(!Udp.parsePacket() && x <= 3) {
//Have to check parsePacket return.
x++;
IPAddress ntpServers(serverslist[x]);
sendNTPpacket(ntpServers);
delay(1000);
}
switch (x) {
case 0:
Serial.println("1st NTPServer working");
return true;
break;
case 1:
Serial.println("2st NTPServer working");
return true;
break;
case 2:
Serial.println("3st NTPServer working");
return true;
break;
case 3:
Serial.println("4st NTPServer working");
return true;
break;
default:
Serial.println("All NTP Servers are Down");
return false;
}
}
int NTP::getYear() {
do {
getTime();
} while(timeSet == false);
return year;
}
int NTP::getMonth() {
do {
getTime();
}while(timeSet == false);
return month;
}
int NTP::getDate() {
do {
getTime();
} while(timeSet == false);
return date;
}
int NTP::getDayOfWeek() {
do {
getTime();
} while(timeSet == false);
return dayOfWeek;
}
int NTP::getUTChour() {
do {
getTime();
} while(timeSet == false);
return utchour;
}
int NTP::getLCThour() {
do {
getTime();
} while(timeSet == false);
return lcthour;
}
int NTP::getMin() {
do {
getTime();
} while(timeSet == false);
return min;
}
int NTP::getSec() {
do {
getTime();
} while(timeSet == false);
return sec;
}
void NTP::serialPrinting() {
//Serial.PRINTS
//print seconds since 1900
Serial.print("Seconds since Jan 1 1900 = " );
Serial.println(secsSince1900);
// print Unix time:
Serial.print("Unix time = ");
Serial.println(UnixTime);
//print year
Serial.print("the year is :");
Serial.println(year);
//print month
Serial.print("Month is : ");
Serial.print(month);
//print date
Serial.print(" Date is: ");
Serial.println(date);
//print dayOfWeek
Serial.print("the day is : ");
Serial.println(dayOfWeek);
//printnumber of days that passed in this year (this day counts DayNumber object)
Serial.print("This day is the number:");
Serial.println(DN.Days1YearLoop);
//print Local Time Hour
Serial.print("The LTC time is ");
Serial.println(lcthour);
// Print the hour, minute and second:
Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT)
Serial.print(utchour); // Print the hour (86400 equals secs per day)
Serial.print(':');
if ( min < 10 ) {
// In the first 10 minutes of each hour, we'll want a leading '0'.
Serial.print('0');
}
Serial.print(min); // Print the minute (3600 equals secs per minute)
Serial.print(':');
if ( sec < 10 ) {
// In the first 10 seconds of each minute, we'll want a leading '0'.
Serial.print('0');
}
Serial.println(sec); // Print the seconds
}
NTP::~NTP() {
// TODO Auto-generated destructor stub
}
and
/*
DayNumber.cpp - Library for Calculation of Day's Number on 1 and 4 years loop.
Created by Pavlidis Kyriakos, December 20, 2012.
Released into the public domain.
*/
#include "DayNumber.h"
DayNumber::DayNumber() {
}
void DayNumber::dayNumberCalc() {
int setYear = NTP::getYear();
int setMonth = NTP::getMonth();
int setDay = NTP::getDate();
//Days that passed from the begging of the year for the 1st Day each Month
int _day1YearLoop[] = {0,31,59,90,120,151,181,212,243,273,304,334};
//i = _day1YearLoop;
//Days that passed from the beginning of the second Year since the for the 1st Day of the running Year in 4 years loop.
int _day4YearLoop[] = {366,731,1096};
if (checkLeapYear(setYear)) {
if (setMonth>2) { //Diorthwsi gia ton mina flebari
Days1YearLoop = *(_day1YearLoop+(setMonth-1)) + setDay + 1;
Days4YearLoop = Days1YearLoop;
}
else {
Days1YearLoop = *(_day1YearLoop+(setMonth-1)) + setDay;
Days4YearLoop = Days1YearLoop;
}
}
else {
Days1YearLoop = *(_day1YearLoop + (setMonth-1)) + setDay;
switch (setYear%4) {
case 1:
Days4YearLoop = *(_day4YearLoop) + Days1YearLoop;
break;
case 2:
Days4YearLoop = *(_day4YearLoop+1) + Days1YearLoop;
break;
case 3:
Days4YearLoop = *(_day4YearLoop+2) + Days1YearLoop;
break;
Default:;
break;
}
}
}
DayNumber::~DayNumber() {
}
bool DayNumber::checkLeapYear(int setYear) {
if (setYear%4 == 0) {
return true;
}
else {
return false;
}
}
The error is at the first header of NTP.h that says
Description Resource Path Location Type
'DayNumber' does not name a type NTP.h /NTP/lib line 24 C/C++ Problem
It does not understand the declaration of the object.
It is always good practice to include ALL files that the current file depends on. Let's say you change your "DayNumber.h" such that it no longer needs "NTP.h", now your code won't compile.
Assuming your files are local, and your machine isn't extremely low on memory, it will only make a marginal difference. There was a question on a similar subject recently, and I measured the difference between including some header or or not, compiling 30 or so files (actually, the SAME file). And the margin of error between a "good" and "bad" run on my machine was quite a bit larger than the difference between including headers and not including it.
As for your second question, it is actually caused by including "NTP.h" which isn't needed in "DayNumber.h" - so you should just "not include that". But if you have a real situation where you have one class that needs to know of another class, you need to use a forward declaration, e.g. class NTP; - now, you can use NTP * or NTP & to pass/store pointers/references to the NTP class. You can still not use what is inside the NTP class until the NTP class has been fully defined.

How to convert a string variable containing time to time_t type in c++?

I have a string variable containing time in hh:mm:ss format. How to convert it into time_t type? eg: string time_details = "16:35:12"
Also, how to compare two variables containing time so as to decide which is the earliest?
eg : string curr_time = "18:35:21"
string user_time = "22:45:31"
With C++11 you can now do
struct std::tm tm;
std::istringstream ss("16:35:12");
ss >> std::get_time(&tm, "%H:%M:%S"); // or just %T in this case
std::time_t time = mktime(&tm);
see std::get_time and strftime for reference
You can use strptime(3) to parse the time, and then mktime(3) to convert it to a time_t:
const char *time_details = "16:35:12";
struct tm tm;
strptime(time_details, "%H:%M:%S", &tm);
time_t t = mktime(&tm); // t is now your desired time_t
This should work:
int hh, mm, ss;
struct tm when = {0};
sscanf_s(date, "%d:%d:%d", &hh, &mm, &ss);
when.tm_hour = hh;
when.tm_min = mm;
when.tm_sec = ss;
time_t converted;
converted = mktime(&when);
Modify as needed.
Here's the complete C implementation with date & time.
enum DateTimeFormat {
YearMonthDayDash, // "YYYY-MM-DD hh:mm::ss"
MonthDayYearDash, // "MM-DD-YYYY hh:mm::ss"
DayMonthYearDash // "DD-MM-YYYY hh:mm::ss"
};
//Uses specific datetime format and returns the Linux epoch time.
//Returns 0 on error
static time_t ParseUnixTimeFromDateTimeString(const std::wstring& date, DateTimeFormat dateTimeFormat)
{
int YY, MM, DD, hh, mm, ss;
struct tm when = { 0 };
int res;
if (dateTimeFormat == DateTimeFormat::YearMonthDayDash) {
res = swscanf_s(date.c_str(), L"%d-%d-%d %d:%d:%d", &YY, &MM, &DD, &hh, &mm, &ss);
}
else if (dateTimeFormat == DateTimeFormat::MonthDayYearDash) {
res = swscanf_s(date.c_str(), L"%d-%d-%d %d:%d:%d", &MM, &DD, &YY, &hh, &mm, &ss);
}
else if (dateTimeFormat == DateTimeFormat::DayMonthYearDash) {
res = swscanf_s(date.c_str(), L"%d-%d-%d %d:%d:%d", &DD, &MM, &YY, &hh, &mm, &ss);
}
//In case datetime was in bad format, returns 0.
if (res == EOF || res == 0) {
return 0;
}
when.tm_year = YY - 1900; //Years from 1900
when.tm_mon = MM - 1; //0-based
when.tm_mday = DD; //1 based
when.tm_hour = hh;
when.tm_min = mm;
when.tm_sec = ss;
//Make sure the daylight savings is same as current timezone.
time_t now = time(0);
when.tm_isdst = std::localtime(&now)->tm_isdst;
//Convert the tm struct to the Linux epoch
time_t converted;
converted = mktime(&when);
return converted;
}
use strptime.
struct tm tm;
memset(&tm, 0, sizeof(tm));
char *res = strptime(strtime.c_str(), format.c_str(), &tm);
if (res == nullptr) {
// err process
}
ti = mktime(&tm);
Must init tm, and check the return value.

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;
}