I need to create a basic date calculator in C that will have the user input a date in YYYY-MM-DD format. I have the basics of it down. Although I don't need to, I want to go the extra little bit and factor in for leap years. The program runs fine; however, it doesn't calculate leap years correctly. When I enter a date of 2016-02-26, I should have an expected result of 2016-03-04 but I get the result of 2016-03-03. I'd reckon that if I used an if else statement to the effect of the following using modulo.
if (month == 2 && year % 4) days = 29;
else days = 28;
Here's my full code...
//Does not require <stdlib.h>
#include <stdio.h>
// Set variables
int newDay, newMonth, newYear, daysInMonth, daysRemain;
// Set structure for day month year
struct date {
int day, month, year;
};
// set structure for date
struct date d1;
int main (void) {
//Intro
printf("Date calculation program by Keith A. Russell");
//Asks for user input
printf("\n\nPlease enter the year in four digit format (YYYY) ");
scanf("%i", &d1.year);
printf("\nEnter the month in two digit format (MM) ");
scanf("%i", &d1.month);
printf("\nEnter the day in two digit format (DD) ");
scanf("%i", &d1.day);
//Runs calculations to increase the date by a week
newDay = d1.day + 7;
newMonth = d1.month;
newYear = d1.year;
daysRemain = 0;
//For if the next week is going to be greater than the next month
if (newDay > 28)
checkMonth(); //Runs checkMonth Function
//Prints the dates
printf("\nThe new date is %i-%i-%i: \n", newYear, newMonth, newDay);
}
checkMonth() {
if (d1.month == 1 || 3 || 5 || 7 || 8 || 10 || 12)
daysInMonth = 31; //For months with 31 days
if (d1.month == 2 && d1.year % 4) //Attempt to calculate leap year
daysInMonth = 29;
else {
daysInMonth = 28; //All other years
}
if (d1.month == 4 || 6 || 9 || 11) //For months with 30 days
daysInMonth = 30;
//Sets up to advance the year if approaching the end of year
if (newDay > daysInMonth) {
daysRemain = newDay - daysInMonth;
newDay = daysRemain;
newMonth++;
checkYear();
}
}
//Runs function to advance to the next year
checkYear() {
if (d1.month == 12)
if (daysRemain > 0) {
newYear++;
newMonth = 1;
}
}
If there are more elegant ways of calculating the leap year and including it in this program, I'd appreciate that greatly. Thank you.
well for a start this is wrong
if (d1.month == 1 || 3 || 5 || 7 || 8 || 10 || 12)
It will always be true. You need
if (d1.month == 1 || d1.month == 3 || d1.month == 5 ....)
This doesn't do what you think it does:
if (d1.month == 1 || 3 || 5 || 7 || 8 || 10 || 12)
You can't compare one value to a list of values like this. What you're actually doing is this:
if ((d1.month == 1) || 3 || 5 || 7 || 8 || 10 || 12)
You're comparing d1.month against the value 1, but then you take that boolean result and do a logical OR with several other numbers. Since all of these numbers is non-zero, this expression will always evaluate to true.
The same goes for this:
if (d1.month == 4 || 6 || 9 || 11)
You need to explicitly compare against each value:
if ((d1.month == 1) || (d1.month == 3) || (d1.month == 5) ...
You could actually do this more cleanly with a switch with fallthrough cases:
switch (d1.month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
daysInMonth = 31;
break;
case 4:
case 6:
case 9:
case 11:
daysInMonth = 30;
break;
case 2:
// years divisible by 100 are not leap years, unless they are also divisible by 400
daysInMonth = (d1.year % 400 == 0) ? 29 :
(d1.year % 100 == 0) ? 28 :
(d1.year % 4 == 0) ? 29 : 28;
break;
}
There are multiple problems in your code:
The test for leap years is incorrect: (d1.month == 2 && d1.year % 4) indicates a normal year between 1901 and 2099 which is non-leap. The correct test is this:
if (d1.month == 2 && (d1.year % 4) == 0) //Attempt to calculate leap year
daysInMonth = 29;
Note however that according to the Gregorian reformed calendar, years multiples of 100 that are not also multiples of 400 are not leap years, so the complete test is this:
if (d1.month == 2) {
daysInMonth = (d1.year % 4 || (!(d1.year % 100) && (d1.year % 400)) ? 28 : 29;
}
Your tests for month values are incorrect: instead of if (d1.month == 1 || 3 || 5 || 7 || 8 || 10 || 12), you should write:
if (d1.month == 1 || d1.month == 3 || d1.month == 5 || d1.month == 7 ||
d1.month == 8 || d1.month == 10 || d1.month == 12)
To make the code more readable, you could use a switch statement. If you first check that d1.month is between 1 and 12, you can condense the above test into a more compact single test:
if ((1 << d1.month) & ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 7) | (1 << 8) | (1 << 10) | (1 << 12))) {
daysInMonth = 31;
}
Related
i'm doing a program in c that needs to have random dates that have to be valid and after the current date. For the generation there aren't any problems at all. The only thing i can't do is to accept only the up to date dates.
I've already tried to firstly check if the date is valid and then to check if is greater than current date. Then i've tried to do the opposite but any of these solutions didn't worked.
This is the function that checks the date:
short dateControl( const unsigned short day, const unsigned short month, const unsigned short year, const int min_year, const int max_year, struct tm curTime)
{
short correct = ZERO;
if( (year >= curTime.tm_year) && (year <= max_year) ){
if( (month >= 1) && (month <= 12) ){
if( (day >= 1 && day <= 31) && (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) ){
correct = ONE;
}else if( (day >= 1 && day <= 30) && (month == 4 || month == 6 || month == 9 || month == 11) ){
correct = ONE;
}else if( (day >= 1 && day <= 28) && (month == 2) ){
correct = ONE;
}else if( day == 29 && month == 2 && (year % 400 == 0 ||(year % 4 == 0 && year % 100 != 0)) ){
correct = ONE;
}else{
correct = ZERO;
}
} else{
correct = ZERO;
}
}else{
correct = ZERO;
}
if(correct == ONE){
if( year > curTime.tm_year ){
correct = ONE;
}else if( year == curTime.tm_year ){
if( month > curTime.tm_mon ){
correct = ONE;
}else if( month == curTime.tm_mon ){
if( day > curTime.tm_mday ){
correct = ONE;
}else if(day == curTime.tm_mday){
correct = ZERO;
}else if(day < curTime.tm_mday){
correct = ZERO;
}
}else if( month < curTime.tm_mon ){
correct = ZERO;
}
}
}
}
return correct;
}
This is the part where the date is generated
day = random_int(1, 31);
month = random_int(1, 12);
year = random_int(local->tm_year, local->tm_year + 1);
validDate = dateControl(tempTrip.dep_date.day, tempTrip.dep_date.month, tempTrip.dep_date.year, local->tm_year, (local->tm_year + 1), *local);
while(validDate == ZERO){
tempDriver.sub_date.day = random_int(1, 31);
tempDriver.sub_date.month = random_int(1, 12);
tempDriver.sub_date.year = random_int(local->tm_year, (local->tm_year + 1));
validDate = dateControl(tempTrip.dep_date.day, tempTrip.dep_date.month, tempTrip.dep_date.year, local->tm_year, (local->tm_year + 1), *local);
}
}
I expect that if the generated date is 2/7/2019 and today is the 18/8/2019, the generated date needs to be discarded and another needs to be generated.
i'm doing a program in c that needs to have random dates that have to be valid and after the current date. For the generation there aren't any problems at all. The only thing i can't do is to accept only the up to date dates.
The fact that you need to determine if the date is/isn't valid indicates that the way you're generating random dates is a problem.
Specifically, if you get the current time (using time()) and add a random positive value to it (while being careful to avoid overflow), and then convert the result into a date (e.g. using gmtime()); then you can guarantee that the random dates are valid and in the future without doing any checking and you can delete all of your dateControl() code.
Hi I have just started learning C language at University. Now I am faced with a problem I just don't know how to solve. So far we have only used
library and nothing else, also scanf is a topic we are going to
learn next week. For now we just use printf for everything.
I have learned how to say if a year is a leap year or not, however, my task is the next one: Need to create a program that reads in a date and prints out the next day date with output like this:
Enter a date in the form day month year: 17 5 2010
The date of the next day is: 18/5/2010
My dilemma is that I have no idea on what operations to use or how to set the code to make sure that leap year is considered and for example if today's date is 28 2 2010 the next date needs to be 1 3 2010 because it wasn't leap year.
The only library used is and there is no scanf yet (with scanf yet)
So far I got this:
#include <stdio.h>
int day, month, year, ndays, leapdays;
bool leapyear;
int main () {
day = 28;
month = 2;
year = 2010;
ndays = day + 1;
leapdays = 31;
leapyear = false;
if (leapyear % 4 == 0) {
leapyear = true;
}
if (leapyear % 100 == 0) {
leapyear = false;
}
if (leapyear % 400 == 0) {
leapyear = true;
}
if ((leapyear) && (month == 12 || month == 1 || month == 3 || month == 5
|| month == 7 || month == 8 || month == 10 )) {
leapdays = 31;
}
if ((leapyear) && (month == 4 || month == 6 || month == 9 || month == 11
)) {
leapdays = 30;
}
if ((leapyear) && (month == 2 )) {
leapdays = 29;
} else if ((leapyear == false) && (month == 2)) {
leapdays = 28;
}
printf ("Enter a date in the form day month year: %d %d %d \n", day,
month, year);
printf ("The date of the next day is: %d/%d/%d", ndays, month, year);
}
Consider a different flow. First find the days per month first, then test if at end of month, and end of year.
int year, month, day;
// set year, month, day in some fashion
day++; // tomorrow
int days_per_month = 31;
if (month == 4 || month == 6 || month == 9 || month == 11) {
days_per_month = 30;
} else if (month == 2) {
days_per_month = 28;
if (year % 4 == 0) {
days_per_month = 29;
if (year % 100 == 0) {
days_per_month = 28;
if (year % 400 == 0) {
days_per_month = 29;
}
}
}
}
if (day > days_per_month) {
day = 1;
month++;
if (month > 12) {
month = 1;
year++;
}
}
Other improvement would use helper functions, enumerated types and various defines. Yet this code seems to reflect OP's level.
Short and simpler. Only check for leap year when needed.
int main()
{
int Iyear, Imonth, Iday;
Iyear = 2016;
Imonth = 4;
Iday = 24;
printf ("Enter a date in the form day month year: %d %d %d \n", Iday, Imonth, Iyear);
NextDate(&Iyear, &Imonth, &Iday);
printf ("The date of the next day is: %d/%d/%d", Iday, Imonth, Iyear);
}
void NextDate(int *year, int *month, int *day)
{
int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
*day = *day +1;
if (( *month == 2 ) && (*day == 29))
{
// Leap year checking, if yes, Feb is 29 days.
if(*year % 400 == 0 || (*year % 100 != 0 && *year % 4 == 0))
{
daysInMonth[1] = 29;
}
}
if (*day > daysInMonth[*month -1])
{
*day = 1;
*month = *month +1;
if (*month > 12)
{
*month = 1;
*year = *year +1;
}
}
}
After you add 1 to the day, check to see if that value is greater than the number of days in that month. If so, set the day to 1 and add 1 to the month. Then check if month is greater than 12, and if so set the month to 1 and add 1 to the year.
As for determining the number of days in a month, all months except February have the same number of days whether or not the year is a leap year. Right now you're checking if the year is a leap year for those other months. You can leave that check out and just check the month number.
if (month == 12 || month == 1 || month == 3 || month == 5
|| month == 7 || month == 8 || month == 10 ) {
leapdays = 31;
}
if (month == 4 || month == 6 || month == 9 || month == 11) {
leapdays = 30;
}
Got it right :) thanks for all the help
#include <stdio.h>
int day, month, year, ndays, leapdays;
bool leapyear;
int main () {
day = 31;
month = 12;
year = 2010;
ndays = day + 1;
leapyear = false;
printf ("Enter a date in the form day month year: %d %d %d \n", day, month, year);
if (year % 4 == 0) {
leapyear = true;
}
if (year % 100 == 0) {
leapyear = false;
}
if (year % 400 == 0) {
leapyear = true;
}
if ((leapyear) && (month == 12 || month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 )) {
leapdays = 31;
}
if ((leapyear) && (month == 4 || month == 6 || month == 9 || month == 11 )) {
leapdays = 30;
}
if ((leapyear) && (month == 2 )) {
leapdays = 29;
} else if (leapyear == false) {
leapdays = 28;
}
if ((leapdays == 31) && (day == 31)) {
ndays = 1;
month = month + 1;
}else if ((leapdays == 30) && (day == 30)) {
ndays = 1;
month = month + 1;
}else if ((leapdays == 29) && (day == 29)) {
ndays = 1;
month = month + 1;
}else if ((leapdays == 28) && (day == 28)) {
ndays = 1;
month = month + 1;
}else if ((month == 12) && (day == 31)) {
ndays = 1;
month = 1;
year = year + 1;
}
printf ("The date of the next day is: %d/%d/%d", ndays, month, year);
}
I have a little problem with my code. I need to check if user's given date is valid or not. I want to check it using 2 rules:
1. Date is valid date and found in a calendar (for example 29.2.2015 is invalid because there's only 28 days in February 2015) 2. Day and month can only be max 2 numbers (for example day 10 and 02 are valid but 010 and 002 are not). All help is appreciated!
This is my code this far:
void dateValidator(const char *date1) {
struct tm date = {0};
int day1;
int month1;
int year1;
int vday;
int vmonth;
int vyear;
sscanf(date1, "%3d.%3d.%d",&day1,&month1,&year1);
/**How do I check that if sscanf reads more than 2 characters on month and day, date is
invalid?**/
date.tm_year = year1 - 1900;
date.tm_mon = month1 - 1;
date.tm_mday = day1;
date.tm_isdst = -1;
vday = date.tm_mday;
vmonth = date.tm_mon;
vyear = date.tm_year;
mktime(&pvm);
if ((vday != date.tm_mday) || (vmonth != date.tm_mon) || (vyear != date.tm_year)) {
/**This doesnt seem to work**/
printf("Invalid date");
}
}
I have deleted my previous answer and posting another one. Actually your code is fine, You just have to send the correct parameter to mktime and to check its return value. I.e. change:
mktime(&pvm);
if ((vday != date.tm_mday) || (vmonth != date.tm_mon) || (vyear != date.tm_year)) {
/**This doesnt seem to work**/
printf("Invalid date");
}
to something like:
r = mktime(&date);
if (r == -1 || (vday != date.tm_mday) || (vmonth != date.tm_mon) || (vyear != date.tm_year)) {
printf("Invalid date");
}
and declare the variable r as time_t r;.
int IsValidDate(int year, int month, int day)
{
unsigned int leap;
unsigned char mon_day[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/* check the rang of the year */
if ((year < 1) || (year >= 3200))
{
return 0;
}
if ((month < 1) || (month > 12))
{
return 0;
}
/* if it's leep year */
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
leap = 1;
}
else
{
leap = 0;
}
mon_day[1] += leap;
if ((day > mon_day[month - 1]) || (day < 1))
{
return 0;
}
return 1;
}
For ruler 1, I have wrote this code for check the date before.
And I hope this code can help you.
The ruler 2 can easily checked, you can check it by format the valid date and compare it with the input string.
How do I check that if sscanf reads more than 2 characters on month and day, date is invalid?
Day and month can only be max 2 numbers digits (for example day 10 and 02 are valid but 010 and 002 are not).
An easy check: Record the offset at the end of the scan and tests 1) that it is not the original 0 (scanning got that far) and 2) this is the end of the string so as to detect extra junk in data1[]. Change 3d to 2d.
int n = 0;
sscanf(date1, "%2d.%2d.%d %n",&day1,&month1,&year1, &n);
if (n > 0 && data1[n] == '\0') Success();
else Fail();
Pedantic code would check with "%[]" to disallow leading spaces and '+'.
int n = 0;
sscanf(date1, "%*2[0-9].%*2[0-9].%*4[0-9] %n", &n);
if (n == 0 || data1[n]) Fail();
else {
sscanf(date1, "%2d.%2d.%d",&day1,&month1,&year1);
...
How to check if date is valid using mktime?
#Marian posted a good solution which adds a test of the return value of mktime().
if (-1 == mktime(&data)) Fail();
if (vday != date.tm_mday) || (vmonth != date.tm_mon) || (vyear != date.tm_year) Fail();
I am writing a program that involves checking the validity of a date. I have set the conditions for the months which have 31 days in them, and I have set the conditions for the months which have 30 days in them. I left out February because there are issues with leap year.
When I run the program, it is able to print "SUCCESS" on the screen for all valid dates. However, when I try to enter an invalid date, the program fails to write "FAILURE" on the screen, as I have it set to do [at least I believe].
What is wrong with the code here?
#include <stdio.h>
int main ()
{
int month = 1;
int day = 32;
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8
|| month == 10 || month == 12)
{
if (0 < day && day < 32)
{
return printf("%s","SUCCESS");
}
}
else if (month == 4 || month == 6 || month == 9 || month == 11)
{
if (0 < day && day < 31 )
{
return printf("%s","SUCCESS");
}
}
else
{
return printf("%s","FAILURE");
}
return 1;
}
Your code have a wrong flow and #JoshStir is right about it.
I may know when you enter in a conditional statement, the other conditionals in the same level are ignored.
You test guide your code to enter in your first condicional.
int month = 1;
int day = 32;
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8
|| month == 10 || month == 12)
{
//--> Flow pass through here ( cause month == 1 )
if (0 < day && day < 32)
{
//Flow DON'T pass through here ( cause day == 32)
return printf("%s","SUCCESS");
}
}
else if (month == 4 || month == 6 || month == 9 || month == 11)
{
//--> Flow DON'T pass through here ( cause flow ENTERER FIRST IF STATEMENT BEFORE)
if (0 < day && day < 31 )
{
return printf("%s","SUCCESS");
}
}
else
{
//--> Flow DON'T pass through here ( cause flow ENTERED FIRST IF STATEMENT BEFORE)
return printf("%s","FAILURE");
}
I advice you to review your code.
Try rethink and considerate what did #JoshStir advice you too.
Maybe using months and days in same comparation operation a good idea.
We were all new once, so I'll try to answer your question as clearly as I can. Given your test data:
int month = 1;
int day = 32;
your code will enter the first if statement (as month == 1)
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8
|| month == 10 || month == 12)
It will then check against the inner if statement (is day > 0 and < 32?)
if (0 < day && day < 32)
day == 32, so it will not enter the if and print "SUCCESS", it will exit the if and then exit the application. If you want the data above to enter the failure condition, you would want something somewhat like the following:
if ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8|| month == 10 || month == 12) && (day > 0 && day < 32))
The program is supposed to return what day of the week it is for the entered date. One of the dates that doesn't work is 01012000. Nothing is returned at all. But on some other leap years the first day of March can be calculated. Also sometimes seemingly random dates don't work. I'm not sure how to fix this. Also I'm supposed to write the part that calculates "daynumber" and then call on it later so I'm not sure if I'm doing that right.
Sorry for the beginner questions, this is my first ever C program.
#include<stdio.h>
#include<math.h>
int main()
{
int day, month, year, lastday, dayname, daynumber, input, d;
//Determine the last day of user specified month
printf("Enter date: ddmmyyyy:\n");
scanf("%d", &input);
day = input/1000000;
month = (input/10000) % 100;
year = input % 10000;
if (month == 1 || month == 3 || month == 5 || month == 7 ||
month == 8 || month == 10 || month == 12)
lastday = 31;
else if (month == 4 || month == 6 || month == 9 || month == 11)
lastday = 30;
else if ((year%4 == 0 && year%100 !=0) || year%400 == 0)
lastday = 29;
else
lastday = 28;
//Verify the date
if (year < 0)
return 1;
if (month < 1 || month > 12)
return 2;
if (day < 1 || day > lastday)
return 3;
//Algorithm
{
int m, d, y, c, daynumber;
if (month > 3)
m = month - 2;
else
m = month + 10;
if (m == 11 || m == 12)
year = year - 1;
else
year = year;
d = day;
y = year % 100;
c = year / 100;
daynumber = (((13*m - 1)/5) + d + y + (y/4) + (c/4) - 2*c) % 7;
if (daynumber == 0)
printf("Sunday\n");
if (daynumber == 1)
printf("Monday\n");
if (daynumber == 2)
printf("Tuesday\n");
if (daynumber == 3)
printf("Wednesday\n");
if (daynumber == 4)
printf("Thursday\n");
if (daynumber == 5)
printf("Friday\n");
if (daynumber == 6)
printf("Saturday\n");
}
}
I can't see your bug, but there is no need to scan the whole number and divide, use:
scanf("%2d%2d%4d", &day, &month, &year);
Using Sakamoto's algorithm you can do the same in few lines:
#include <stdio.h>
static int wday(int d, int m, int y)
{
static int offset[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
y -= m < 3;
return (y + y / 4 - y / 100 + y / 400 + offset[m - 1] + d) % 7;
}
int main(void)
{
const char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
int d, m, y;
printf("Enter date [ddmmyyyy]: ");
scanf("%2d%2d%4d", &d, &m, &y);
printf("%s\n", days[wday(d, m, y)]);
return 0;
}
This
(((13*m - 1)/5) + d + y + (y/4) + (c/4) - 2*c)
can probably be negative. The result of % 7 would then still be negative and nothing is printed.
Just add daynumber = (daynumber + 7) % 7; after the line
daynumber = (((13*m - 1)/5) + d + y + (y/4) + (c/4) - 2*c) % 7;
You are using:
printf("Enter date: ddmmyyyy:\n");
scanf("%d", &input);
Here you are storing the input as a int. If sizeof(int) is 2 byte, its range would be between -32,768 to 32,767 and your input would be out of the range. For this you should use long int.