Laboratory Overview
What is new in this laboratory.
In the previous laboratory, you used struct POINT and split code into separate
files. In this laboratory, you will apply the same ideas to a more realistic problem:
a structure representing a date and a function that returns the number of days between two dates.
structure β validation β day counting β separate compilation β testing
The main challenge is not only writing a function, but also thinking carefully about calendar rules, leap years, date ordering, and the historical switch from the Julian calendar to the Gregorian calendar.
Learning Outcomes
- work in the correct laboratory directory on the AGH UNIX server,
- define and use a structure representing a date,
- write declarations in a custom header file and definitions in a source file,
- use
gcc -cfor separate compilation, - implement a function that returns the number of days between two dates,
- handle leap years correctly,
- take into account the JulianβGregorian calendar transition in 1582,
- write a small test program for a multi-file solution,
- write readable and properly indented code without global variables.
Task 0 β Reminder and preparation
Step 1 β Log in to the AGH UNIX server
ssh your_login@student.agh.edu.pl
Step 2 β Go to the course directory
cd ~/I2PL
Step 3 β Create the directory for Laboratory 6
mkdir lab6
cd lab6
Step 4 β General rules
- Work only in
~/I2PL/lab6. - Keep all files for this exercise in this directory.
- Do not use global variables.
- Keep the code readable and properly indented.
- Compile with warnings enabled.
gcc -Wall program.c -o program
student.agh.edu.pl,
use nano, compile with -Wall, and run programs with ./program.
Task 1 β Define a structure for dates
Step 1 β Create the header file
nano days.h
Step 2 β Add include guards
#ifndef DAYS_H
#define DAYS_H
/* content */
#endif
Step 3 β Define the structure
Define a structure named DAY that stores one calendar date.
A practical version is:
typedef struct {
int day;
int month;
int year;
} DAY;
Step 4 β Add the function declaration
int days(DAY a, DAY b);
How struct, typedef, and initialization work in C
In C, structures can be introduced in several different ways.
The form used in this laboratory is based on typedef, which creates a type alias.
1. Structure type with a name
struct DAY {
int day;
int month;
int year;
};
struct DAY d1, d2;
Here, struct DAY is the type name.
When using this form, the keyword struct is part of the type name.
2. Structure definition with variables
struct DAY {
int day;
int month;
int year;
} d1, d2;
This defines the structure type and declares variables of this type at the same time.
3. Anonymous structure with variables
struct {
int day;
int month;
int year;
} d1, d2;
This creates variables, but the structure type has no name. You cannot later declare another variable using this anonymous structure type.
4. typedef β creating an alias
typedef struct {
int day;
int month;
int year;
} DAY;
DAY d1, d2;
The keyword typedef creates an alias.
In this example, DAY becomes a shorter name for this structure type.
DAY instead of struct DAY.
5. typedef with a named structure
typedef struct DAY {
int day;
int month;
int year;
} DAY;
This defines both a structure tag (struct DAY) and a type alias (DAY).
Both names refer to the same structure type.
6. Initialization of structure variables
You can initialize structure variables in several ways.
Direct initialization
DAY d1 = {1, 1, 2000};
Values are assigned in the order of the fields in the structure definition:
day, month, year.
Designated initializers
DAY d2 = {
.day = 31,
.month = 12,
.year = 1999
};
Partial initialization
DAY d3 = {15, 5};
Missing fields are initialized to zero. In this example, year becomes 0.
Assignment after declaration
DAY d4;
d4.day = 10;
d4.month = 6;
d4.year = 2024;
Copying structures
DAY a = {1, 1, 2000};
DAY b = a; /* copy all fields */
Task 2 β Create the implementation file
Step 1 β Create the file
nano days.c
Step 2 β Include your header
#include "days.h"
Step 3 β Implement the function
Define the function days in days.c.
Its job is to return the number of days between two dates.
Step 4 β Keep helper functions private to this file
If you need helper functions such as date validation, leap-year checking,
or conversion of a date into an absolute day number, place them in days.c.
static.
Minimal working version β stub implementation
Before implementing the full algorithm, first make sure the program compiles and links correctly.
This approach lets you test the structure of the program before writing the actual calendar logic.
Step 1 β Temporary implementation in days.c
#include "days.h"
int days(DAY a, DAY b) {
return -1; /* temporary placeholder */
}
Step 2 β Minimal ex19.c
#include <stdio.h>
#include "days.h"
int main(void) {
DAY d1 = {1, 1, 2000};
DAY d2 = {2, 1, 2000};
printf("days = %d\n", days(d1, d2));
return 0;
}
Step 3 β Compile and run
gcc -Wall -c days.c
gcc -Wall -c ex19.c
gcc ex19.o days.o -o ex19
./ex19
-1, the build process is correct.
You can now replace the stub with the real implementation.
Task 3 β Write a test program
Step 1 β Create the file
nano ex19.c
Step 2 β Include the header
#include <stdio.h>
#include "days.h"
Step 3 β Create sample dates
In main, define a few test dates as variables of type DAY
and print the results of calling days.
Step 4 β Test more than one case
Check at least:
- two dates in the same year,
- a case involving a leap year,
- a case where the earlier and later date are entered in reversed order,
- a case around the year
1582.
Task 4 β Use separate compilation
Step 1 β Recommended workflow
Recompile days.c only when you change the implementation of the module.
Recompile ex19.c when you change the test program.
gcc -Wall -c days.c # recompile only if days.c changed
gcc -Wall -c ex19.c
gcc ex19.o days.o -o ex19
./ex19
ex19.c changes, you do not need to recompile days.c.
This is the main advantage of separate compilation.
.o) are reused unless their source file changes.
Step 2 β Simplified linking with an already compiled module
If days.o already exists and is up to date, you may compile and link the test program in one command:
gcc -Wall ex19.c days.o -o ex19
./ex19
Step 3 β What -c means
The option -c tells gcc to compile a source file into an object file,
but not to link the final executable yet.
days.c β days.o
ex19.c β ex19.o
days.o + ex19.o β ex19
Task 5 β Handle leap years correctly
Step 1 β Distinguish Julian and Gregorian rules
In the Julian calendar, a leap year occurs every year divisible by 4.
In the Gregorian calendar, the rule is more precise:
divisible by 4 β leap year
except divisible by 100
unless divisible by 400
Step 2 β Apply the correct rule depending on the date
Since this exercise explicitly mentions the calendar change in 1582,
your program should not blindly use only one leap-year rule for all centuries.
Task 6 β Take into account the calendar change in 1582
The exercise notes that the Julian calendar was superseded by the Gregorian calendar in 1582.
In practice, this means that some dates were skipped during the reform.
04.10.1582 the next date is 15.10.1582.
04.10.1582 comes 15.10.1582.
Dates from 05.10.1582 to 14.10.1582 do not exist.
Step 1 β Decide how to represent the missing days
A sensible approach is to convert each date into an absolute day count and make sure that the skipped dates in October 1582 are not counted as existing calendar dates.
Step 2 β Reject or avoid invalid dates from the missing interval
Dates from 05.10.1582 to 14.10.1582 should not be treated as normal valid dates
in this model.
01.01.1582 β 31.12.1582 β 354 days
shows that the missing days must affect the calculation.
Task 7 β Make the function independent of input order
Step 1 β Compare the two dates
Before counting, determine which date is earlier and which is later.
Step 2 β Return a non-negative difference
A convenient interpretation for this exercise is that the function returns the absolute number of days between the two dates.
Definition used in this laboratory
Clarify what the function should return.
The function days returns the
absolute number of days between two dates.
Examples:
01.01.2024 β 02.01.2024 β 1 day
02.01.2024 β 01.01.2024 β 1 day
01.01.2024 β 01.01.2024 β 0 days
- from one day to the next day the result is 1,
- the result is always non-negative,
- the order of arguments does not change the result.
Suggested Test Cases
Sample cases inspired by the original exercise.
01.01.2024 β 01.01.2024 β 0 days
01.01.2024 β 02.01.2024 β 1 day
02.01.2024 β 01.01.2024 β 1 day
12.12.1234 β 20.01.1410 β 63958 days
12.12.1234 β 31.10.1972 β 269500 days
31.10.1972 β 20.01.1410 β 205542 days
01.01.1582 β 31.12.1582 β 354 days
Implementation Strategy
There are several correct ways to solve the problem. A clean and reliable approach is:
- validate the input date,
- determine whether the date belongs to the Julian or Gregorian part of the calendar,
- convert the date to an absolute day number,
- subtract the two day numbers and take the absolute value.
-1).
Mini Theory β Why gcc -c matters
In this laboratory, the -c option is important because the program is split into separate files.
gcc -Wall -c days.c β days.o
gcc -Wall -c ex19.c β ex19.o
gcc ex19.o days.o -o ex19
Separate compilation allows each source file to be compiled independently. Only after that does the linker combine the object files into one executable program.
Checklist before submission
- defining and using a structure representing a date,
- putting declarations in
days.hand definitions indays.c, - using
gcc -ccorrectly for separate compilation, - correct handling of leap years,
- taking into account the calendar transition in 1582,
- obtaining the same result regardless of argument order,
- writing readable and properly indented code.