How to use bsearch correctly in c? - c

I want to search a date (which is a struct) in an array of dates to see if it is in it. This is the first time I am using bsearch and it always returns the same result, 0, whereas it should either return null or a pointer to date found. I am using the same comparing function I used to sort the array and the sorting works fine. I'm guessing if the function returns 0 it means it has found the date in the array. What have I done wrong? If the mistake is not obvious I can post the full code.
#define MIN_SIZE 0
#define MAX_SIZE 10000
#define MAX_MONTH_STR 9
#define SWAP 1
#define NO_SWAP -1
#define EQUAL 0
//Define a struct data type
typedef struct
{
//Declaration of struct members
char* month;
int day;
int year;
}date;
//Method to allocate memory for a list of date structures
date* allocateStruct(int size)
{
//Declaration of variables
date *array;
int i;
//Allocate memory for array to store 'size' many 'date' struct data types
array = malloc(size*sizeof(date));
//For-loop to allocate memory for each struct's members and initialize them to zero
for (i=0; i<size; i++)
{
array[i].month = calloc(MAX_MONTH_STR,sizeof(char));
array[i].day = (int) calloc(1,sizeof(int));
array[i].year = (int) calloc(1,sizeof(int));
}
return array;
}
//Method to free memory allocated
void freeStruct(date* array, int size)
{
//Declaration of variable
int i;
//For-loop to free up struct members
for (i=0; i<size; i++)
{
free(array[i].month);
free(&array[i].day);
free(&array[i].year);
}
//Free up structs
free(array);
}
//Method to compare two dates
int cmpDates (const void *a, const void *b)
{
//Declaration and dereference of variables
date first = *(date*)a;
date second = *(date*)b;
int y_result, m_result, d_result;
//Calculate results
y_result = second.year-first.year;
m_result = second.month-first.month;
d_result = second.day-first.day;
//If-statements to determine whether to swap dates based on year
//If-statement to determine whether both years are in 90s group
if (first.year>=90 && first.year<=99 && second.year>=90 && second.year<=99)
{
//If-statement to determine whether years are equal
if (y_result!=0)
{
return (y_result);
}
}
//Else-if-statement to determine whether both years are in 00-12 group
else if (first.year>=0 && first.year<=12 && second.year>=0 && second.year<=12)
{
//If-statement to determine whether years are equal
if (y_result!=0)
{
return (y_result);
}
}
//Otherwise the two years belong to different year groups
else
{
//If-statement to determine whether first year belongs to 00-12 group
if (first.year>=0 && first.year<=12)
{
return NO_SWAP;
}
else
{
return SWAP;
}
}
//If-statement to determine whether to swap dates based on months
if (m_result!=0)
{
return m_result;
}
//If-statement to determine whether to swap dates based on days
if (d_result!=0)
{
return d_result;
}
//If dates are exactly the same
return EQUAL;
}
enum months {
January=1,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
int main()
{
//Declaration of variables
int n; //number of dates in array
date* date_list; //array of dates
date *key_date; //date to search for
date *q_result; //result of search
//Read input
do
{
//printf("Enter number of dates you want to enter (between 1 and 10000):\n");
scanf("%d", &n);
}while(n<MIN_SIZE || n>MAX_SIZE);
//Allocate memory for an array of n dates
date_list = allocateStruct(n);
//For-loop to store values in 'date_list'
for (i=0; i<n; i++)
{
//printf("Enter the date (month day year) in the following format <text number number>:");
scanf("%s", date_list[i].month);
scanf("%d", &date_list[i].day);
scanf("%d", &date_list[i].year);
}
//Allocate memory for one date
key_date = allocateStruct(1);
//Read date for query
//printf("Enter date you want to query:");
scanf("%s", key_date->month);
scanf("%d", &key_date->day);
scanf("%d", &key_date->year);
//Sort the array with built-in function qsort
qsort(date_list, n, sizeof(date), cmpDates);
//Print list of sorted dates
for (i=0; i<n; i++)
{
//printf("Enter the date (month day year) in the following format: text number number");
printf("%s ", date_list[i].month);
printf("%d ", date_list[i].day);
printf("%02d\n", date_list[i].year); //need & ?
}
//Query with bsearch --> I TRIED BOTH OF THESE LINES BUT THE RESULT WAS THE SAME
q_result = (date*) bsearch(&key_date, date_list, n, sizeof(date), cmpDates);
// q_result = bsearch(&key_date, date_list, n, sizeof(date), cmpDates);
//Printing answer to query
if(q_result!=NULL)
{
printf("Yes in list");
}
else
{
printf("No not in list");
}
}

key_date is a pointer but you pass its address to bsearch so when cmpDates is called, it will try to cast(reinterpret) a void* which is a date** to a date* and then dereference it to a date. Obviously, the values retrieved from that date struct will be all wrong. I am surprised you didn't have a crash with the month string.
q_result = (date*) bsearch(&key_date, date_list, n, sizeof(date), cmpDates);
should be
q_result = (date*) bsearch(key_date, date_list, n, sizeof(date), cmpDates);
Obviously, you still need to sort out(no pun intended) the calloc issue
for (i=0; i<size; i++)
{
array[i].month = calloc(MAX_MONTH_STR,sizeof(char));
array[i].day = (int) calloc(1,sizeof(int));
array[i].year = (int) calloc(1,sizeof(int));
}
should be(if you really need to keep month as a string)
for (i=0; i<size; i++)
{
array[i].month = calloc(MAX_MONTH_STR,sizeof(char));
array[i].day = 0;
array[i].year = 0;
}
and then
for (i=0; i<size; i++)
{
free(array[i].month);
free(&array[i].day);
free(&array[i].year);
}
should be
for (i=0; i<size; i++)
{
free(array[i].month);
}
Regarding your month comparison, you need a function like this:
months GetMonth(char* m)
{
if (strcmp(m, "January") == 0)
return months.January;
else if (strcmp(m, "February") == 0)
return months.February;
....
}
I have left out case sensitiveness.
Then you store this enum in your date struct and the month comparison will at least make sense.

Here is code I assembled based on yours that seems to work. I've not stress tested it.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define MIN_SIZE 1
#define MAX_SIZE 10000
#define MAX_MONTH_STR 10
#define EQUAL 0
#define LESS_THAN -1
#define MORE_THAN +1
typedef struct
{
char *month;
int day;
int year;
} date;
static
date *allocateStruct(int size)
{
date *array = malloc(size * sizeof(date));
if (array == 0)
exit(1);
for (int i = 0; i < size; i++)
{
array[i].month = calloc(MAX_MONTH_STR, sizeof(char));
if (array[i].month == 0)
exit(1);
}
return array;
}
static
void freeStruct(date *array, int size)
{
for (int i = 0; i < size; i++)
{
free(array[i].month);
}
free(array);
}
static inline int normalize_year(int year)
{
return (year < 50) ? year + 2000 : year + 1900;
}
static inline int month_number(const char *name)
{
static const char *months[] =
{
"",
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December",
};
for (int i = 1; i <= 12; i++)
if (strcmp(name, months[i]) == 0)
return i;
fprintf(stderr, "Invalid (unrecognized) month name <<%s>>\n", name);
exit(1);
/*NOTREACHED*/
}
static
int cmpDates(const void *a, const void *b)
{
date first = *(date *)a;
date second = *(date *)b;
int y1 = normalize_year(first.year);
int y2 = normalize_year(second.year);
if (y1 < y2)
return LESS_THAN;
else if (y1 > y2)
return MORE_THAN;
int m1 = month_number(first.month);
int m2 = month_number(second.month);
if (m1 < m2)
return LESS_THAN;
else if (m1 > m2)
return MORE_THAN;
if (first.day < second.day)
return LESS_THAN;
else if (first.day > second.day)
return MORE_THAN;
else
return EQUAL;
}
int main(void)
{
int n;
date *date_list;
date *key_date;
date *q_result;
do
{
if (scanf("%d", &n) != 1)
exit(1);
} while (n < MIN_SIZE || n > MAX_SIZE);
date_list = allocateStruct(n);
printf("Input data:\n");
for (int i = 0; i < n; i++)
{
scanf("%s", date_list[i].month);
scanf("%d", &date_list[i].day);
scanf("%d", &date_list[i].year);
printf("%.2d: %.2d %s %.2d\n", i, date_list[i].day, date_list[i].month, date_list[i].year);
}
qsort(date_list, n, sizeof(date), cmpDates);
printf("Sorted:\n");
for (int i = 0; i < n; i++)
printf("%.2d: %.2d %s %.2d\n", i, date_list[i].day, date_list[i].month, date_list[i].year);
key_date = allocateStruct(1);
scanf("%s", key_date->month);
scanf("%d", &key_date->day);
scanf("%d", &key_date->year);
printf("Search: %.2d %s %.2d\n", key_date->day, key_date->month, key_date->year);
q_result = (date *) bsearch(key_date, date_list, n, sizeof(date), cmpDates);
if (q_result != NULL)
{
printf("Yes (%.2d %s %.2d) in list (%.2d %s %.2d)\n",
key_date->day, key_date->month, key_date->year,
q_result->day, q_result->month, q_result->year);
}
else
{
printf("No (%.2d %s %.2d) in list\n",
key_date->day, key_date->month, key_date->year);
}
freeStruct(date_list, n);
freeStruct(key_date, 1);
return 0;
}
Sample data:
16
November 26 00
February 18 12
January 19 08
March 22 11
October 08 05
December 22 10
November 08 01
May 21 10
July 10 92
October 06 91
November 30 93
April 25 90
March 21 90
September 18 97
June 23 97
July 19 98
November 29 93
The last line of the file is the date to search for. The November 29 93 is not in the list, but change 29 to 30 and the date is in the list. In both cases, the code reports this correctly.
Sample output:
Input data:
00: 26 November 00
01: 18 February 12
02: 19 January 08
03: 22 March 11
04: 08 October 05
05: 22 December 10
06: 08 November 01
07: 21 May 10
08: 10 July 92
09: 06 October 91
10: 30 November 93
11: 25 April 90
12: 21 March 90
13: 18 September 97
14: 23 June 97
15: 19 July 98
Sorted:
00: 21 March 90
01: 25 April 90
02: 06 October 91
03: 10 July 92
04: 30 November 93
05: 23 June 97
06: 18 September 97
07: 19 July 98
08: 26 November 00
09: 08 November 01
10: 08 October 05
11: 19 January 08
12: 21 May 10
13: 22 December 10
14: 22 March 11
15: 18 February 12
Search: 29 November 93
No (29 November 93) in list
I think it is a lousy idea to store the month name in the date structure. If you are going to store a pointer, you could store a pointer to one of a fixed array of strings representing the month names. You'd read the input name into an array, then find the canonical pointer from a list of the month names. You'd store the names so that the address of January is before the address for February, etc, so that you could compare pointers and come up with the correct answer. However, I've not implemented that. In fact, I think storing month names in the structure is a bad idea; I'd store a month number. I also think storing just two digits of the year is a lousy decision. Maybe you weren't aware of the Y2K bug fixing work that was necessary because people tried to cut corners and only stored two digits for years instead of four digits, but it was messy. It is far, far simpler just to store the year number. You aren't even saving any space since you're using 4 bytes (an int is usually 4 bytes these days) to store the 2-digit year. If you have year as a 4-digit number, and month and day as 2-digit numbers, then yyyy * 10000 + mm * 100 + dd gives you an 8-digit integer that can be sorted trivially. If you need to compute the number of days between two dates, then the broken-down format isn't as convenient as a count of the days since a reference date (such as 1900-01-01 was day 1). You can take the difference between two such date values to get the number of days between them.

Related

Having trouble with a function in C

In my program, I am asking the user to input a date (in just integers such as 12 31 2019 367) and the number of days they add to it. In one of my functions, this is precisely what I am doing.
The user inputs 12 31 2019 367, and the program is meant to print 1 1 2021, but instead prints 1 1 2020 (a year behind)...
What I did (sorry for a lot of code, I tried to keep it simple and clean):
int days_in_month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void add_days_to_date(int *mm, int *dd, int *yy, int days_left_to_add)
{
int days_left_in_month;
while(days_left_in_month > 0)
{
days_left_in_month = days_in_month[*mm] - *dd;
// days_left_in_month = days_in_month[*mm] - *dd;
if (days_in_month[2] && is_leap_year(*yy) == true)
{
days_left_in_month++;
}
} // end while
printf("after while\n");
if(days_left_to_add > days_left_in_month)
{
days_left_to_add -= days_left_in_month;
*dd = 1;
if(*mm == 12)
{
*mm = 1;
(*yy)++;
}
else
{
(*mm)++;
}
}
else
{
*dd += days_left_to_add;
days_left_to_add = 0;
}
}
int main()
{
int mm, dd, yy, days_left_to_add;
printf("Please enter a date between the years 1800 and 10000 in the format mm dd yy and provide the number of days to add to this date:\n");
scanf("%d %d %d %d", &mm, &dd, &yy, &days_left_to_add);
// printf("\nREAD\n");
//These are pointers, so they have to be at certain location i.e. int* mm = &mm
add_days_to_date(&mm, &dd, &yy, days_left_to_add);
printf("%d %d %d\n", mm, dd, yy);
}
What I got after inputs:
Inputs: 12 31 2019 367
Output: 1 1 2020 (meant to be 1 1 2021)
Your loop in the function needs to be while (days_left_to_add > 0), and should cover the whole of the function (and then needs some minor adjustments). As it is, you add just one month, which given that your input is 2019-12-31 lands you on 2020-01-01.
Your code should validate the input date.
This code works, at least for a number of test cases similar to your example.
/* SO 74925-5084 */
#include <stdio.h>
#include <stdbool.h>
static const int days_in_month[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static bool is_leap_year(int year)
{
if (year % 4 != 0)
return false;
if (year % 100 != 0)
return true;
if (year % 400 != 0)
return false;
return true;
}
static void add_days_to_date(int *mm, int *dd, int *yy, int days)
{
while (days > 0)
{
int days_left_in_month = days_in_month[*mm] - *dd + 1;
if (*mm == 2 && is_leap_year(*yy))
days_left_in_month++;
//printf("DAYS = %2d, DLIM = %2d, leap(%4d) = %s\n",
// days, days_left_in_month, *yy, is_leap_year(*yy) ? "true" : "false");
if (days >= days_left_in_month)
{
days -= days_left_in_month;
*dd = 1;
if (*mm == 12)
{
*mm = 1;
(*yy)++;
}
else
{
(*mm)++;
}
}
else
{
*dd += days;
days = 0;
}
// This is informative while debugging
printf("%.4d-%.2d-%.2d + %d\n", *yy, *mm, *dd, days);
}
}
int main(void)
{
int mm, dd, yy, days_left_to_add;
printf("Please enter a date between the years 1800 and 10000 in the format mm dd yyyy\n"
"and provide the positive number of days to add to this date:\n");
if (scanf("%d %d %d %d", &mm, &dd, &yy, &days_left_to_add) != 4)
{
fprintf(stderr, "Failed to read 4 numbers\n");
return 1;
}
printf("Input: %.4d-%.2d-%.2d + %d\n", yy, mm, dd, days_left_to_add);
/* Data validation */
if (days_left_to_add <= 0)
{
fprintf(stderr, "The number of days to add must be a positive number (unlike %d)\n",
days_left_to_add);
return 1;
}
if (yy < 1800 || yy > 10000)
{
fprintf(stderr, "Year %d is outside the range 1800..10000\n", yy);
return 1;
}
if (mm < 1 || mm > 12)
{
fprintf(stderr, "Month %d is outside the range 1..12\n", mm);
return 1;
}
int dim = days_in_month[mm];
if (mm == 2 && is_leap_year(yy))
dim++;
if (dd < 1 || dd > dim)
{
fprintf(stderr, "Day %d is outside the range 1..%d\n", dd, dim);
return 1;
}
add_days_to_date(&mm, &dd, &yy, days_left_to_add);
printf("%d %d %d\n", mm, dd, yy);
return 0;
}
Sample runs (program ad41 created from ad41.c):
$ ad41
Please enter a date between the years 1800 and 10000 in the format mm dd yyyy
and provide the positive number of days to add to this date:
12 31 2019 367
Input: 2019-12-31 + 367
2020-01-01 + 366
2020-02-01 + 335
2020-03-01 + 306
2020-04-01 + 275
2020-05-01 + 245
2020-06-01 + 214
2020-07-01 + 184
2020-08-01 + 153
2020-09-01 + 122
2020-10-01 + 92
2020-11-01 + 61
2020-12-01 + 31
2021-01-01 + 0
1 1 2021
$ ad41
Please enter a date between the years 1800 and 10000 in the format mm dd yyyy
and provide the positive number of days to add to this date:
12 31 2018 60
Input: 2018-12-31 + 60
2019-01-01 + 59
2019-02-01 + 28
2019-03-01 + 0
3 1 2019
$ ad41
Please enter a date between the years 1800 and 10000 in the format mm dd yyyy
and provide the positive number of days to add to this date:
12 31 2019 60
Input: 2019-12-31 + 60
2020-01-01 + 59
2020-02-01 + 28
2020-02-29 + 0
2 29 2020
$

2022 calendar in C determine starting day

I need to write a program in C that it prints a month chosen by the user from the year 2022.
If the user enter January (has 31 days) and the user enter to print 20 days, then should be printed from 1st January untill 20 January.
Same for every month.
I have this pice of code but I'm not happy with it and I googled everything.
void print_month(char *mo, int number_day_userInput){
int i,j; // as counters
int week = 7; // as 7 days in a week
int day=1;
int day_of_the_week=1;
printf("M\tT\tW\tT\tF\tS\tS");
//January
if(strcmp(mo, "January")== 0){
for(i=0;i<=5;i++){ // this 5 is as 5 weeks in January
printf("\n");
for(j=1;j<=week;j++){
if(day_of_the_week>=6){ // 1st day start printing
printf("%i",day);
day++;
if(day > number_day_userInput){
break;
}
}
day_of_the_week++;
printf("\t");
}
}
printf("\n\nJanuary has 31 days");
}
I can't figure it out, how to determine for each month where to start printing the first day. For example, January 1st starts on a Saturday, the 6th day. February starts on 2nd day. Also, I don't know how to determine how many weeks are in a month. Could you please share some ideas ? Not necessarily in C, but an advice will be welcomed. Thank you
Here I started with Sunday as day 0 (Gregorian calendar). It should print out correctly based on the month given. Core concept here is just using the modulus operator % to find remainders, and basic for-loop stuff :)
#include <stdio.h>
#include <string.h>
static int daysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static int start = 6;
void print_month(char *mo, int number_day_userInput)
{
int j = 0;
if(strcmp(mo, "February") == 0) { j = 1; }
else if(strcmp(mo, "March") == 0) { j = 2; }
else if(strcmp(mo, "April") == 0) { j = 3; } // and so on...
for(int i = 0; i < j; ++i)
{
start += daysOfMonth[i];
}
int startDay = start % 7;
printf("S\tM\tT\tW\tT\tF\tS\n");
for(int tab = 0; tab < startDay; ++tab)
{
printf("\t");
}
for(int day = 1; day <= number_day_userInput; ++day)
{
printf("%d\t", day);
if(++startDay % 7 == 0)
{
printf("\n");
}
}
printf("\n");
}
int main()
{
print_month("March", 20);
}
SAMPLE OUTPUT of print_month("January", 31):
S M T W T F S
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

Manually Calculate GMT to Epoch

I am new here, please bear with me for any blunders.
I am trying to convert the time in GMT format to Unix Epoch (starting from 1970).
The procedure i am following for this is to, iterate over the years starting from 1970 till the given date finding out number of leap years and normal years and multiplying the number of days accordingly.
Then adding the number of days passed in a month, add day value and hours, minutes seconds from tm structure, as shown in below code.
The code is not working as expected , and i seem to be lost in some calculations, and i don't understand what it is.
Followig are the inputs i am trying , expected and actual output is provided here.
Normal years:
Input: Jan 20 19:00:01 2019 GMT
Expected output: 1548010801
Actual output: 1548097201 (which is Jan 21 19:00:01 2019 GMT, 1 day difference)
Leap Years:
Input: Dec 27 14:52:30 2020 GMT
Expected output: 1609080750
Actual output: 1609253550 (which is Dec 29 14:52:30 2020 GMT, 2 days difference)
I request help in finding the problem.
I have two Open Questions
1)I am not sure whether i need to worry about DST or not because i am getting the time as input in GMT format.
2)Is there a better way to calculate the epoch using some formulas expressions , rather than iterating and manually
calculating.
There is already one existing post for same
but it is using mktime and difftime.
I want to know what is the problem in my code and any better way to do in formulas expressions.
#include <stdio.h>
#include <string.h>
#include <time.h>
#define BASE_YEAR 1970
void print_time_readable_format(struct tm tm);
int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt);
int check_year_is_leap_or_normal(int year);
int get_number_of_leap_years_from_base_year(int start_year, int end_year);
int convert_gmt_to_epoch(struct tm tm);
int main()
{
int epoch = 0;
//char gmt_time_fmt[] = "Jan 20 19:00:01 2019 GMT";
char gmt_time_fmt[] = "Dec 27 14:52:30 2020 GMT";
//char gmt_time_fmt[] = "Jan 01 00:00:01 1970 GMT";
epoch = convert_gmt_date_time_to_tm_format(gmt_time_fmt);
printf("time in GMT = %s and epoch is %d\n", gmt_time_fmt, epoch);
return 0;
}
int convert_gmt_date_time_to_tm_format(char* gmt_time_fmt)
{
struct tm tm;
char tm_time_fmt[255];
//set tm struture to 0
memset(&tm, 0, sizeof(struct tm));
// convert gmt_time_fmt to format required by 'tm' structure
strptime(gmt_time_fmt, "%B %d %H:%M:%S %Y GMT", &tm);
strftime(tm_time_fmt, sizeof(tm_time_fmt), "%s", &tm);
printf("tm_time_fmt = %s\n", tm_time_fmt);
print_time_readable_format(tm);
return convert_gmt_to_epoch(tm);
return 0;
}
int convert_gmt_to_epoch(struct tm tm)
{
int days_by_month [2][12] = {
/* normal years */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
/* leap years */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
};
int current_year = tm.tm_year+1900;
printf("current_year =%d\n", current_year);
int total_years_passed = current_year - BASE_YEAR;
printf("total_years_passed =%d\n", total_years_passed);
int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR, current_year);
int normal_years = total_years_passed - nleap_years_passed;
printf("normal_years =%d\n", normal_years);
int total_days_passed = (normal_years*365 + nleap_years_passed*366 );
printf("total_days_passed =%d\n", total_days_passed);
total_days_passed += days_by_month[check_year_is_leap_or_normal(current_year)][tm.tm_mon];
printf("total_days_passed after adding month =%d\n", total_days_passed);
total_days_passed += tm.tm_mday;
printf("total_days_passed after adding day =%d\n", total_days_passed);
total_days_passed = total_days_passed*24 + tm.tm_hour;
total_days_passed = total_days_passed*60 + tm.tm_min;
total_days_passed = total_days_passed*60 + tm.tm_sec;
printf("total_days_passed final =%d\n", total_days_passed);
return total_days_passed;
}
int get_number_of_leap_years_from_base_year(int start_year, int end_year)
{
int leap_year_count = 0;
int year = start_year;
while( year <= end_year)
{
if(check_year_is_leap_or_normal(year))
leap_year_count++;
year++;
}
printf("leap_year_count = %d\n", leap_year_count);
return leap_year_count;
}
int check_year_is_leap_or_normal(int year)
{
if( ( year%4 == 0 ) && ( ( year%400 == 0 ) || ( year%100 != 0)))
return 1;
else
return 0;
}
void print_time_readable_format(struct tm tm)
{
printf("tm.tm_year = %d ", tm.tm_year);
printf("tm.tm_mon = %d ", tm.tm_mon);
printf("tm.tm_mday = %d ",tm.tm_mday);
printf("tm.tm_hour = %d ", tm.tm_hour);
printf("tm.tm_min = %d ", tm.tm_min );
printf("tm.tm_sec = %d\n", tm.tm_sec );
}
Two calculation errors:
How leap year is counted, it must exclude the very last year. The correct one should be:
int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR, current_year - 1);
Total days passed must also exclude the end date. The revised formula:
total_days_passed += (tm.tm_mday - 1);
Above two formula has caused the epoch to be off by 1 day each.

Printing calendar for specific year and month

I am trying to print out a calendar for a specific year and month but keep getting the same calendar for every month. I tried to also add a statement to see if the year is a leap year to add to the number of days but it made no difference. I am new to c.Please help with any suggestions. Thanks in advance.
void print_month_calendar(int year, int month)
{
int day;
int daycode = ddaycode(year);
int days_in_month[]={0,31,28,31,30,31,30,31,31,30,31,30,31};
char *months[]=
{
" ",
"\n\n\nJanuary",
"\n\n\nFebruary",
"\n\n\nMarch",
"\n\n\nApril",
"\n\n\nMay",
"\n\n\nJune",
"\n\n\nJuly",
"\n\n\nAugust",
"\n\n\nSeptember",
"\n\n\nOctober",
"\n\n\nNovember",
"\n\n\nDecember"
};
printf("%s", months[month]);
printf("\n\nSun Mon Tue Wed Thu Fri Sat\n");
if(( year%4==0 && year%100 !=0) || year%400==0)
days_in_month[2] = 29;
for (day = 1; day <= 1 + daycode * 5; day++)
{
printf(" ");
}
//Print all the dates for the month
for (day = 1; day <= days_in_month[month]; day++)
{
printf("%2d", day);
if ((day + daycode) % 7 > 0)
printf(" ");
else
printf("\n ");
}
}
int ddaycode(int year)
{
int daycode;
int d1, d2, d3;
d1 = (year - 1.)/ 4.0;
d2 = (year - 1.)/ 100.;
d3 = (year - 1.)/ 400.;
daycode = (year + d1 - d2 + d3) %7;
return daycode;
}
You have to know the first weekday in the month. You can use localtime to go to specific date, use mktime to get struct tm data which contains weekday. In addition, you can use strftime to get the month name. Example:
void print_month_calendar(int year, int month)
{
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo = localtime(&rawtime);
timeinfo->tm_year = year - 1900;
timeinfo->tm_mon = month - 1;
timeinfo->tm_mday = 1;
mktime(timeinfo);
//sunday is 1 ... saturday is 7
int weekday = 1 + timeinfo->tm_wday;
int days_in_month[] = { 0, 31,28,31,30,31,30,31,31,30,31,30,31 };
if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0))
days_in_month[2]++;
char monthname[30];
strftime(monthname, sizeof(monthname), "%B", timeinfo);
printf(" %s %d\n", monthname, year);
printf(" Su Mo Tu We Th Fr Sa\n");
int day = 1;
for (int i = 1; i <= 40; i++)
{
if (i < weekday)
{
printf(" ");
}
else
{
printf("%3d ", day);
if (day == days_in_month[month])
break;
day++;
if ((i % 7) == 0)
printf("\n");
}
}
printf("\n");
}
int main()
{
print_month_calendar(2016, 10);
return 0;
}
ideone example
October 2016
Su Mo Tu We Th Fr Sa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

Printing a Julian Calendar in C using month and year input?

I'm trying to output a calendar in C and I think I've got the format down, but I'm having trouble with:
Validating user input (I've commented out the potential solution, but it hasn't compiled correctly yet. It always prints the "invalid year/month selected" and the break statement doesn't work.)
Starting the calendar days on the correct day of the week (May of 2018 starts on Tuesday, not on Sunday)
My current output:
Enter month: 5
Enter year: 2018
Su M Tu W Th F Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
My desired output:
Enter month: 5
Enter year: 2018
Su M Tu W Th F Sa
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Here's my program:
#include <stdio.h>
#include <stdlib.h>
/* #defines */
#define BEGIN 1900
#define DAYS_IN_WEEK 7
/* function prototypes */
void getMonthYear(int *month, int *year);
int toJulian(int month, int day, int year);
int daysInMonth(int month, int year);
int leapYear(int year);
long yearsToDays(int year);
void printCalendar(int startDay, int numDays);
void printHeader();
/* Calendar.c: Prints monthly calendar. Lab 4 / Week 9 */
void main(void)
{
int month, year;
int startDay; // what day is first day of month. 1/1/1900 was Monday, so...
// day 1 is Mon, day 2 is Tue, ... , day 0 is Sun
getMonthYear(&month, &year);
startDay = (toJulian(month, 1, year) + yearsToDays(year)) % DAYS_IN_WEEK;
printCalendar(startDay, daysInMonth(month, year));
}
void getMonthYear(int *month, int *year) {
printf("Enter month: ");
scanf("%d", month);
/*if (month < 1 || month > 12) {
printf("Invalid month selected");
//break;
}*/
printf("Enter year: ");
scanf("%d", year);
/* if (year < BEGIN) {
printf("Invalid year selected");
//break;
}*/
printf("\n");
}
int toJulian(int month, int day, int year) {
int count;
for(count = 1; count < month; ++count) {
day += daysInMonth(month, year);
}
return day;
}
int daysInMonth(int month, int year) {
int numDays;
switch (month) {
case 1: numDays = 31;
break;
case 2: numDays = 28;
break;
case 3: numDays = 31;
break;
case 4: numDays = 30;
break;
case 5: numDays = 31;
break;
case 6: numDays = 30;
break;
case 7: numDays = 31;
break;
case 8: numDays = 31;
break;
case 9: numDays = 30;
break;
case 10: numDays = 31;
break;
case 11: numDays = 30;
break;
case 12: numDays = 31;
break;
}
return numDays;
}
int leapYear(int year) {
if (year % 400 == 0 && (year % 100 != 0 || year % 400 == 0)) {
return 1;
}
else {
return 0;
}
}
long yearsToDays(int year) {
int count;
long days;
for (count = BEGIN; count < year; ++count) {
days = 365 + leapYear(year);
}
return days;
}
void printCalendar(int startDay, int numDays) {
int dayid;
printHeader();
// Shifts position for the first date... sort of
for ( dayid = 0; dayid < startDay; dayid++ ) {
printf(" ");
}
// Supposedly prints all the dates for one month
for ( dayid = 1; dayid <= numDays; dayid++ ) {
printf("%3d", dayid );
// If the day is not before Saturday, start next line on Sun
if ( ( dayid + startDay ) % DAYS_IN_WEEK > 0 ) {
printf(" ");
}
else {
printf("\n" );
}
}
}
void printHeader() {
printf(" Su M Tu W Th F Sa\n");
}
break is used for exiting loops or switch statements. It can only be used inside a loop inside the current function scope.
So even if getMonthYear() were called inside a loop in main() (which it isn't), a break inside it will not exit that loop.
As it is, you have no loop at all, neither in getMonthYear(), nor in the calling sequence leading to it, so the break would be a no-op.
You have to ask yourself "what do I actually want to do when they make an invalid entry?"
One possibility would be to return the validity from getMonthYear()
bool valid_input = 0;
while (!valid_input) {
valid_input = getMonthYear(&month, &year);
}
To deal with the problem of getting the day correct, you probably need to do a little more debugging yourself: put print statements in all the places where there are calculations, print out the intermediate results, and find out where you have an error that way.
You have errors in the following functions:
toJulian
int toJulian(int month, int day, int year) {
int count;
for(count = 1; count < month; ++count) {
day += daysInMonth(month, year);
}
return day;
}
You are using the month in every call. You need to use count.
You don't need the input argument day. You can have a local variable that is initialized to 0 before the loop starts.
The updated version:
int toJulian(int month, int year) {
int count;
int days = 0;
for(count = 1; count < month; ++count) {
days += daysInMonth(count, year);
}
return days;
}
leapYear.
int leapYear(int year) {
if (year % 400 == 0 && (year % 100 != 0 || year % 400 == 0)) {
return 1;
}
else {
return 0;
}
}
The logic in the if statement is not correct. It needs to be:
(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
You can make the function to be more readable and less error prone by using:
int leapYear(int year) {
int ret = 0;
if ( year % 4 != 0 )
{
ret = 0;
}
else
{
if ( year % 100 != 0 )
{
ret = 1;
}
else
{
ret = (year % 400 == 0);
}
}
return ret;
}
yearsToDays
long yearsToDays(int year) {
int count;
long days;
for (count = BEGIN; count < year; ++count) {
days = 365 + leapYear(year);
}
return days;
}
You have not initialized days.
You are not accumulating the number of days. You are just assigning a value. The last returned value gets returned instead of the accumulated number of days.
You are using year in the loop instead of count in the argument to leapYear.
Here's the updated version:
long yearsToDays(int year) {
int count;
long days = 0;
for (count = BEGIN; count < year; ++count) {
days += 365;
days += leapYear(count);
}
return days;
}
printCalendar
void printCalendar(int startDay, int numDays) {
int dayid;
printHeader();
// Shifts position for the first date... sort of
for ( dayid = 0; dayid < startDay; dayid++ ) {
printf(" ");
}
// Supposedly prints all the dates for one month
for ( dayid = 1; dayid <= numDays; dayid++ ) {
printf("%3d", dayid );
// If the day is not before Saturday, start next line on Sun
if ( ( dayid + startDay ) % DAYS_IN_WEEK > 0 ) {
printf(" ");
}
else {
printf("\n" );
}
}
}
You are using up four spaces when printing the days of the month - "%3d" and " ". You need to write four spaces for each day up to startDay in the first for loop. Otherwise, the first row of output won't be properly aligned.
You need to use:
for ( dayid = 0; dayid < startDay; dayid++ ) {
printf(" ");
}
main
The line
startDay = (toJulian(month, 1, year) + yearsToDays(year)) % DAYS_IN_WEEK;
should be
startDay = (1 + toJulian(month, year) + yearsToDays(year)) % DAYS_IN_WEEK;
to account for (1) the fact that 1900-Jan-01 falls on a Monday and (2) change to the interface of toJulian.

Resources