Calculate number of days elapsed from epoch - formula based - c

I am trying to calculate number of days elapsed from a given GMT time.
Well, I am able to make it work with iterative approach of calculation (finding number of normal years
and leap years)
The function get_number_of_leap_years_from_base_year is iterating over all the years from 1970 till the given date and checking every year whether its a leap or not and finally add all days.
Is there any other way (formula) based to calculating number normal & leap years elapsed.
/* so-prg-2: Calculating number normal & leap years passed */
#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 get_number_of_leap_years_from_base_year(int start_year, int end_year);
int calculate_days_elapsed_from_epoch(struct tm tm);
int main()
{
int days = 0;
char gmt_time_fmt[] = "Dec 28 18:40:01 2020 GMT";
//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";
days = convert_gmt_date_time_to_tm_format(gmt_time_fmt);
printf("GMT = %s and days are %d\n", gmt_time_fmt, days);
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 calculate_days_elapsed_from_epoch(tm);
}
int calculate_days_elapsed_from_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;
int total_years_passed = current_year - BASE_YEAR;
/* -1, to skip the current year */
int nleap_years_passed = get_number_of_leap_years_from_base_year(BASE_YEAR, current_year-1);
int normal_years = total_years_passed - nleap_years_passed;
int total_days_passed = (normal_years*365 + nleap_years_passed*366 );
printf(" **Years total_days_passed =%d\n", total_days_passed);
total_days_passed += days_by_month[(current_year%4 == 0) - (current_year%100 == 0) + (current_year%400 == 0)][tm.tm_mon];
total_days_passed += tm.tm_mday - 1; /* to skip the current day */
printf(" **total_days_passed =%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( (year%4 == 0) - (year%100 == 0) + (year%400 == 0) )
leap_year_count++;
year++;
}
printf("leap_year_count = %d\n", leap_year_count);
return leap_year_count;
}
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 );
}

Use mktime()
Since your code is allowed to use both Standard C strftime() and POSIX strptime(), there's no reason not to use Standard C mktime() either.
It gives you a time_t value which is the number of seconds since The Epoch.
int calculate_days_elapsed_from_epoch(struct tm tm)
{
time_t t = mktime(&tm);
return t / 86400; // 24 * 60 * 60 = 86400
}
But if the goal is to calculate the seconds since The Epoch, you have the answer immediately from mktime().
Note that mktime() is passed a struct tm pointer, and it accepts values that are 'out of range' and normalizes the result. See also the example code in the section 'Demonstrating mktime()'.
Calculating leap days
I have a function jl_dmy_conversion() lurking in my library which converts a combination of year, month, day to a number of days since 1899-12-31 (so in this system, day 1 was 1900-01-01). But it includes a calculation for number of leap days. This code is internal to a package where the parameters are already validated as valid within the date range 0001-01-01 .. 9999-12-31, so it does not do much to protect itself from invalid data. There is another function that invokes this that does the data validation. Some of the information shown here comes from a header, most from the source file containing the implementation.
typedef int Date;
enum { DATE_NULL = -2147483648 }; /* Informix NULL DATE */
#define LEAPYEAR(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
#define PRId_Date "d"
/*
** In 400 years, there are 97 leap years (because the three years
** divisible by 100 but not by 400 are not leap years). This also
** happens to be exactly 20871 weeks.
*/
#define DAYS_IN_400_YEARS (400*365+97)
#define DAYS_IN_2000_YEARS (5*DAYS_IN_400_YEARS)
enum
{
DAYS_IN_JANUARY = 31,
DAYS_IN_FEBRUARY = 28,
DAYS_IN_MARCH = 31,
DAYS_IN_APRIL = 30,
DAYS_IN_MAY = 31,
DAYS_IN_JUNE = 30,
DAYS_IN_JULY = 31,
DAYS_IN_AUGUST = 31,
DAYS_IN_SEPTEMBER = 30,
DAYS_IN_OCTOBER = 31,
DAYS_IN_NOVEMBER = 30,
DAYS_IN_DECEMBER = 31
};
static const int days_in_month[][2] =
{
{ 0, 0 },
{ DAYS_IN_JANUARY, DAYS_IN_JANUARY },
{ DAYS_IN_FEBRUARY, DAYS_IN_FEBRUARY+1 },
{ DAYS_IN_MARCH, DAYS_IN_MARCH },
{ DAYS_IN_APRIL, DAYS_IN_APRIL },
{ DAYS_IN_MAY, DAYS_IN_MAY },
{ DAYS_IN_JUNE, DAYS_IN_JUNE },
{ DAYS_IN_JULY, DAYS_IN_JULY },
{ DAYS_IN_AUGUST, DAYS_IN_AUGUST },
{ DAYS_IN_SEPTEMBER, DAYS_IN_SEPTEMBER },
{ DAYS_IN_OCTOBER, DAYS_IN_OCTOBER },
{ DAYS_IN_NOVEMBER, DAYS_IN_NOVEMBER },
{ DAYS_IN_DECEMBER, DAYS_IN_DECEMBER }
};
/* Return date as number of days since 31st December 1899 - no range check */
static Date jl_dmy_conversion(int d, int m, int y)
{
int leap;
int i;
Date daynum;
/* No need to assert here - calling functions have checked basics */
DB_TRACE(1, "[[-- jl_dmy_conversion (d = %2d, m = %2d, y = %4d) ", d, m, y);
leap = LEAPYEAR(y);
if (d > days_in_month[m][leap])
{
DB_TRACE(1, "<<-- NULL (invalid day of month)\n");
return(DATE_NULL);
}
/* Number of days so far this month */
daynum = d;
/* Days so far this year prior to this month */
for (i = 1; i < m; i++)
daynum += days_in_month[i][leap];
DB_TRACE(4, "YDAY = %3ld ", daynum);
/*
** Now compute number of days to 1st of January of this year. Add
** 2000 years (5 periods of 400 years) to ensure that numbers
** resulting from subtraction are positive, even when dates back to
** 0001-01-01 are allowed, and then remove the number of days found
** in 2000 years. This assumes int is 32-bit or longer.
**
** NB: Things begin to go haywire when (y - 1901) yields -4, which
** is when y == 1897. Things get worse before 1601. The result is
** usually, but not always, off by one. Adding 2000 years and then
** subtracting the appropriate number of days sidesteps the
** problems.
*/
y += 2000;
daynum += 365 * (y - 1900); /* Ignoring leap years */
DB_TRACE(4, "INC1 = %7d ", 365 * (y - 1900));
daynum += (y - 1901) / 4; /* Allowing for leap years */
DB_TRACE(4, "INC2 = %4d ", (y - 1901) / 4);
daynum -= (y - 1901) / 100; /* Allowing for non-leap years */
DB_TRACE(4, "INC3 = %3d ", -(y - 1901) / 100);
daynum += (y - 1601) / 400; /* Allowing for leap years */
DB_TRACE(4, "INC4 = %2d ", (y - 1601) / 400);
daynum -= DAYS_IN_2000_YEARS;
DB_TRACE(1, " (r = %7" PRId_Date ") --]]\n", daynum);
return(daynum);
}
The DB_TRACE macro is derived from the code shown in #define a macro for debug printing in C?. The DB_TRACE macro is available in my SOQ (Stack Overflow Questions) repository on GitHub as files debug.c and debug.h in the src/libsoq sub-directory.
The formatting gives a single line showing the calculation steps.
The code above compiles with the debug.h header and <stdio.h> included, and a minimal main(), plus linking with the code from debug.c:
int main(void)
{
int dd = 28;
int mm = 12;
int yyyy = 2020;
Date dt = jl_dmy_conversion(dd, mm, yyyy);
printf("%.4d-%.2d-%.2d = %d\n", yyyy, mm, dd, dt);
return 0;
}
Demonstrating mktime()
As mentioned above, mktime() is passed a struct tm pointer, and it is documented that it accepts values that are 'out of range' and normalizes the result — modifying the structure it is passed. It also sets the tm_wday and tm_yday fields — it ignores them as inputs.
If you have a struct tm value for 2020-12-28 08:20:26 and you want to know the time_t value for 6 days, 18 hours, 43 minutes, 32 seconds later, you can use code like this:
#include <stdio.h>
#include <time.h>
static void print_time(time_t t, const struct tm *tm)
{
char buffer[32];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S %A", tm);
printf("%lld: %s (%d)\n", (long long)t, buffer, tm->tm_yday);
}
int main(void)
{
struct tm tm = { .tm_year = 2020 - 1900, .tm_mon = 12 - 1, .tm_mday = 28,
.tm_hour = 8, .tm_min = 20, .tm_sec = 26 };
time_t t0 = mktime(&tm);
print_time(t0, &tm);
tm.tm_mday += 6;
tm.tm_hour += 18;
tm.tm_min += 43;
tm.tm_sec += 32;
time_t t1 = mktime(&tm);
print_time(t1, &tm);
return 0;
}
When run (in US/Mountain standard time zone — UTC-7), it produces:
1609168826: 2020-12-28 08:20:26 Monday (362)
1609754638: 2021-01-04 03:03:58 Monday (3)

The conditions for leap year are summarized as:
leap year if perfectly visible by 400
not a leap year if visible by 100 but not divisible by 400
leap year if not divisible by 100 but divisible by 4
all other years are not leap year
So the logic could be expressed as:
if (((year%4 == 0) && (year%100 != 0)) || (year%400 == 0))
{
leap_year_count++;
}
year++;
But not sure if re-factoring your existing logic will add any speed advantage.

Related

Add 7 days to date added by user

I need to get input from user in string (for example 20.10.2020) and add 7 days to it. So far, I have something like that but do not know how what to do next.
This is whatI have so far:
#include <stdio.h>
static int days_in_month[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day, month, year;
unsigned short day_counter;
int is_leap(int y) {
return ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0);
}
void next_day()
{
day += 1; day_counter++;
if (day > days_in_month[month]) {
day = 1;
month += 1;
if (month > 12) {
month = 1;
year += 1;
if (is_leap(year)) {
days_in_month[2] = 29;
} else {
days_in_month[2] = 28;
}
}
}
}
void set_date(int d, int m, int y)
{
m < 1 ? m = 1 : 0;
m > 12 ? m = 12 : 0;
d < 1 ? d = 1 : 0;
d > days_in_month[m] ? d = days_in_month[m] : 0;
if (is_leap(y)){
days_in_month[2] = 29;
} else {
days_in_month[2] = 28;
}
day = d;
month = m;
year = y;
}
void skip_days(int x)
{
int i;
for (i=0;i<x;i++) next_day();
}
void print_date()
{
printf (" %d.%d.%d\n", day, month, year);
}
int main(int argc, char **argv)
{
int i;
set_date(5, 2, 1980);
skip_days(7);
day_counter = 0;
print_date();
return 0;
}
You needn't reinvent the wheel. A very good suggestion in the comments is to use the standard functions mktime and strftime, although the given snippet might leave some members of the tm structure uninitialized and distort the result, and although it is for your purpose not necessary to bother with the epoch. When we use
struct tm
{
…
int tm_mday; // day of the month — [1, 31]
int tm_mon; // months since January — [0, 11]
int tm_year; // years since 1900
…
}
we can take advantage of the circumstance that the original values of these components are not restricted to the ranges indicated above. On successful completion, the values are forced to the ranges indicated above. This means mktime takes care of overflows into the next month or year. So, your whole program can be reduced to:
#include <stdio.h>
#include <time.h>
int main(int argc, char **argv)
{
int d, m, y;
// get date input from user on the command line
if (argc < 2 || sscanf(argv[1], "%d.%d.%d", &d, &m, &y) < 3) return 1;
// put that into the time structure, adding 7 days
struct tm date = { .tm_mday = d+7, .tm_mon = m-1, .tm_year = y-1900 };
// force to the ranges
mktime(&date);
char s[11];
strftime(s, sizeof s, "%d.%m.%Y", &date);
puts(s);
}

Calculate the end date from start date and number of days in C

for the past few hours I've been trying to figure out how to write a programme in C to calculate the end date based on the start date and number of days. (I haven't found the forum for this exact problem, yet).
So let's say you input 27/1/2021 as the starting date and then 380 days. The programme should now calculate and show you the end date 11/2/2022.
I don't know how to move forward, the help would be appreciated.
#include <stdio.h>
#include <stdlib.h>
int day, month, year, numberDays;
int leapYear(int year) {
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
}
int monthYear[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int main(void) {
printf("Enter starting date: ");
scanf("%d %d %d", &day, &month, &year);
printf("Enter number of days: ");
scanf("%d", &numberDays);
leapYear(year);
int wholeYears, rest;
if (leapYear(year)) {
wholeYears = numberDays / 366;
rest = numberDays % 366;
}
else {
wholeYears = numberDays / 365;
rest = numberDays % 365;
}
int resultYears = year + wholeYears;
int midDays = day + rest;
int resultMonths;
return 0;
}
I can't move any further. I'd need help.
So let's say you input 27/1/2021 as the starting date and then 380 days. The programme should now calculate and show you the end date 11/2/2022.
An easy approach is to use mktime() to bring a date into its standard range.
#include <stdio.h>
#include <time.h>
int main(void) {
struct tm start = {.tm_year = 2021 - 1900, .tm_mon = 1 - 1, .tm_mday = 27,
.tm_hour = 12}; // Midday to avoid DST issues.
start.tm_mday += 380;
printf("%d/%d/%d\n", start.tm_mday, start.tm_mon + 1, start.tm_year + 1900);
time_t t = mktime(&start);
printf("%d/%d/%d %s\n", start.tm_mday, start.tm_mon + 1, start.tm_year + 1900,
t == -1 ? "failed" : "OK");
}
Output
407/1/2021
11/2/2022 OK
Otherwise with discrete code, one is effectively re-writing the year/month/day portion of mktime().
I don't know how to move forward, the help would be appreciated.
Add the 380 to d of y, m, d. Maybe add some value to m in other cases. Then perform basic range reductions. There are 12 months to every year. Month range [1...12]. There are 365*400 + 97 days every 400 years. Day of the month is at least 1.
Last step is to handle a day outside the month range.
A simply, though inefficient, approach: test if the data is more than the days in that month. If so subtract the days in that month and advance to the next month.
I left out some code details as OP's goal is some ideas to move forward.
#define DAYSPER400YEARS (365 * 400 + 97)
#define JANUARY 1
#define FEBRUARY 2
#define DECEMBER 12
#define MONTHPERYEAR 12
static int is_leap_year(long long year) {
// If not divisible by 4 ...
// If not divisible by 100 ...
// If not divisible by 400 ...
}
static int days_per_month(long long year, int month) {
static const signed char dpm[] = { 0, //
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month != FEBRUARY) {
return ....;
}
return .... + is_leap_year(year);
}
/*
* Bring a date into it primary range.
* https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
* The year, month, day may be any value INT_MIN ... INT_MAX.
* Return error flag.
*/
bool ymd_to_primary_range(int *year, int *month, int *day) {
long long y = *year;
y += *month / 12;
*month %= 12; // month now in -11 to 11 range
while (*month < JANUARY) {
*month += MONTHPERYEAR;
year--;
}
y += (*day / DAYSPER400YEARS) * 400;
*day %= DAYSPER400YEARS;
while (*day < 1) {
*day += DAYSPER400YEARS;
y -= 400;
}
int dpm;
while (*day > (dpm = days_per_month(y, *month))) {
*day -= dpm;
(*month)++;
if (*month > ...) {
*month -= ....;
y++;
}
}
if (y < INT_MIN) {
*year = INT_MIN;
return true;
}
if (y > INT_MAX) {
*year = INT_MAX;
return true;
}
*year = (int) y;
return false;
}
Smaple
int main(void) {
int y = 2021;
int m = 1;
int d = 27;
d += 380;
printf("Error: %d\n", ymd_to_primary_range(&y, &m, &d));
printf("Date (dmy): %2d/%02d/%04d\n", d, m, y);
}
Output
Error: 0
Date (dmy): 11/02/2022

If inside a for loop (day difference for common and leap year) not working

I'm new to C-programming and I'm trying to make a code to count the day difference between a date and a birthday.
int year, birth_year;
int year_common;
int year_leap;
for(int i = year; i <= birth_year; i--){
year = i;
if (year % 400 != 0 || (year % 100 == 0 && year % 400 != 0)){
year_common++;
}
if(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)){
year_leap++;
}
}
int date_pass = (year_common * 365) + (year_leap * 366);
I wanted the loop to decrement the 'i', add 1 into the year_common integer when it's a common year, and add 1 into the year_leap integer when it's a leap year until it is the same as the birth_year.
For now, I'm still trying to check out the years, but no matter how many year difference I made, they always give out 5856 days.
e.g. :
birth date : 05/11/2005
checked date : 05/11/ 2007
give out '5856 days'
And I don't know where that number comes from.
I tried initializing it with 0, but it gave out 0 days instead.
I tried this code:
int year_pass = year - birth_year;
int year_leap = 0;
for(int i = year; i <= birth_year; i--){
if(i % 400 == 0 || (i % 100 != 0 && i % 4 == 0)){
year_leap++;
}
}
int date_pass = (year_pass * 365) + (year_leap * 1);
And it missed 1 day for the leap year.
Is there something wrong with the loop?
My prof wants the code to be as standard as possible with loops and conditions.
You have declared the year common and year leap as in variables but haven't assigned any value. Since youre adding one unit by ++;, you need to pre define year_leap and year_common =0
int year, birth_year;
int year_common=0;
int year_leap=0;
Dates are hard to handle, with all UTC leap seconds and local timezones - leap years are only the tip of the iceberg. Don't reinvent the wheel. Whenever you want to do something with dates, first thing to do is to convert everything to seconds since epoch. Then you do what you want.
#include <time.h>
#include <stdio.h>
#include <stdint.h>
int main() {
// 05/11/2005
struct tm birthday = {
.tm_year = 2005 - 1900,
.tm_mon = 11,
.tm_mday = 5,
.tm_hour = 12,
};
// 05/11/2007
struct tm end = {
.tm_year = 2007 - 1900,
.tm_mon = 11,
.tm_mday = 5,
.tm_hour = 12,
};
time_t birth_s = mktime(&birthday);
time_t end_s = mktime(&end);
time_t diff_s = end_s - birth_s;
time_t diff_day = diff_s / 3600 / 24;
printf("%ju\n", (uintmax_t)diff_day);
}

(c/c++) dd/mm/yyyy hh:ii:ss to unix timestamp

given a date string dd/mm/yyyy hh:ii:ss, we wish to convert it to a unix timestamp (10 digits) through a function.
long foo(int yyyy, int mm = 0, int dd = 0, int hh = 0, int ii = 0, int ss = 0) { }
i couldn't figure out the exact formula that gives accurate results, ones that match unixtimestamp.com.
Unix time is a system for describing instants in time, defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds.
As mentioned in the comments, strptime can do the parsing for you to create a struct tm. Then you can call mktime to get a time_t:
#include <stdio.h>
#include <stdlib.h>
#define _XOPEN_SOURCE
#include <time.h>
int main(void)
{
struct tm mytm;
time_t t;
strptime("31/10/2015 08:33:00","%d/%m/%Y %H:%M:%S",&mytm);
t = mktime(&mytm);
printf("t=%ld\n",t);
return 0;
}
Result:
t=1446294780
You can create a tm struct (from < time.h>/< ctime>) and passing those parameters to it, then you can simple call the mktime() funtion or the timegm() funtion, which depends on you want to take the input as local time or GMT time.
One thing you should carefully remember is that the actual year stored in tm struct is (year-1990), and month should be (month-1). Don't forget to set the tm's tm_isdst flag. You can set is as -1 which can automatically detect whether daylight saving time is applied.
For example, you want to convert GMT 11/02/1990 00:16:50.
tm* t;
t->tm_sec = 50;
t->tm_min = 16;
t->tm_hour = 0;
t->tm_mday = 11;
t->tm_mon = 1; // 2-1, not 2!
t->tm_year = 90; // 1990-1900, not 1990!
t->tm_isdst = -1;
Then you can call timegm(t) which will give you the result 634695410.
on arduino using the time library github.com/PaulStoffregen/Time
#include <Time.h>
struct unix {
long get(int y, int m = 0, int d = 0, int h = 0, int i = 0, int s = 0) {
setTime(h, i, s, d, m, y);
adjustTime(-10800); // +3
return now();
}
} unix;
void setup() {
Serial.begin(19200);
Serial.setTimeout(0);
Serial.println("..");
//Serial.println(unix.get(2015));
//Serial.println(unix.get(2015, 10));
//Serial.println(unix.get(2015, 10, 31));
//Serial.println(unix.get(2015, 10, 31, 18));
//Serial.println(unix.get(2015, 10, 31, 18, 41));
//Serial.println(unix.get(2015, 10, 31, 18, 41, 0));
Serial.println("Enter the date (dd/mm/yyyy hh:ii:ss am/pm). Example: \"31/10/2015 7:27 pm\".");
}
void loop() {
while (Serial.available() > 0) {
int dd = Serial.readStringUntil('/').toInt();
int mm = Serial.readStringUntil('/').toInt();
int yyyy = Serial.readStringUntil(' ').toInt();
int hh = Serial.readStringUntil(':').toInt();
int ii = Serial.readStringUntil(':').toInt();
int ss = Serial.readStringUntil(' ').toInt();
String pm = Serial.readStringUntil('\n');
if (pm == 0) pm = "am";
Serial.print(dd); Serial.print("/");
Serial.print(mm); Serial.print("/");
Serial.print(yyyy); Serial.print(" ");
Serial.print(hh); Serial.print(":");
Serial.print(ii); Serial.print(":");
Serial.print(ss); Serial.print(" ");
Serial.print(pm); Serial.print(" = ");
if (pm == "pm") hh += 12;
Serial.println(unix.get(yyyy, mm, dd, hh, ii, ss));
Serial.println();
Serial.println("Enter the date (dd/mm/yyyy hh:ii:ss am/pm).");
}
delay(50);
}

calculate a date given day of year and year

I am trying to write a program in C given these two data sets from a user. Year:[Range 1901-2099] and Day in year: [range 1-366] I need a formula to calculate the date in MM/DD/YYYY format. One more thing. No IF/ELSE statements. No AND/OR or GREATER THAN or LESS THAN are allowed.
You can use nested switch case to avoid using if-else.
Find out whether it is a leap year or not.
Create four buckets of months (1-99), (100-199).. etc. Bucket numbers will be used as case numbers.
Now check the left most bit of the day, and write a switch case for assigning it into the right bucket.
Each bucket could be divided into 4 more buckets too (the leap year information will be used).
Repeat step 3 for the middle bit and based on the result (case) switch to appropriate bucket.
Briefly, logic could be as follows:
isLeapYear = year % 4
Switch(isLeapYear)
Case 0: {
first_bucket = days/4
Switch(first_bucket)
{
Case 0: {
days_left = days % 100
second_bucket = days / 50;
// ...
// ...
}
Case 1, 2, 3: {
// Similar logic for non-leap year
// ...
}
You can do it without any kind of flow control statements (if, switch) and without doing leap year calculation yourself. Get the timestamp corresponding to January 1st of the desired year. Then, you can use a single addition on the timestamp to get to the correct day, and convert the timestamp to whatever format you want.
I would provide code, specific function names and explain what you need to add to the timestamp, but since this is obviously a homework question, I won't. If anyone reminds me in two weeks (i.e. when the deadline for the assignment is most likely over) I'll happily post example code.
In case library functions were allowed to be used (which I doubt) I'd do it this (lazy lego) way:
#define _XOPEN_SOURCE /* glibc2 needs this for strptime */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
int to_date(
char * date,
const size_t size,
const char * fmt,
const short unsigned int day_of_year,
const short unsigned int year)
{
char buffer[16] = "";
sprintf(buffer, "%hu %hu", day_of_year, year);
{
struct tm t = {0};
char * presult = strptime(buffer, "%j %Y", &t);
if ((NULL == presult) || ('\0' != *presult))
{
errno = EINVAL;
return -1;
}
strftime(date, size, fmt, &t);
}
return 0;
}
int main(int argc, char ** argv)
{
if (2 > argc)
{
fprintf(stderr, "Missing arguments. Usage: %s day-of-year year\n", argv[0]);
return EXIT_FAILURE;
}
short unsigned int day_of_year = atoi(argv[1]);
short unsigned int year = atoi(argv[2]);
char date[16] = "";
if (-1 == to_date(date, sizeof(date), "%m/%d/%Y", day_of_year, year))
{
perror("to_date() failed");
return EXIT_FAILURE;
}
printf("Result: day %d of year %d is '%s'.\n", day_of_year, year, date);
return EXIT_SUCCESS;
}
Call it like this
$ ./main 2 2000
to get
Result: day 2 of year 2000 is '01/02/2000'.
For years 1901 to 2099, can easily be done using time functions.
void Makedate(int year, int day, struct tm *dest) {
struct tm tm1 = { 0 };
// Leap year every 4 years, lets do calc referencing 2000-2003
tm1.tm_year = 2000 - 1900 + year % 4;
tm1.tm_mday = day; // Make the Jan 1st to Jan 366th. OK to be out of range.
// Avoid day changes due to DST by using Noon. BTW I doubt this is needed (CYA)
tm1.tm_hour = 12;
// mktime adjusts our fields for us, setting the month, mday, dow, etc.
mktime(&tm1);
tm1.tm_year = year - 1900; // set to selected year
*dest = tm1;
}
int main() {
struct tm tm0;
Makedate(2013, 1, & tm0); printf("y:%4d m:%2d d:%2d\n", tm0.tm_year + 1900, tm0.tm_mon + 1, tm0.tm_mday);
Makedate(2013, 365, & tm0); printf("y:%4d m:%2d d:%2d\n", tm0.tm_year + 1900, tm0.tm_mon + 1, tm0.tm_mday);
Makedate(2020, 1, & tm0); printf("y:%4d m:%2d d:%2d\n", tm0.tm_year + 1900, tm0.tm_mon + 1, tm0.tm_mday);
Makedate(2020, 366, & tm0); printf("y:%4d m:%2d d:%2d\n", tm0.tm_year + 1900, tm0.tm_mon + 1, tm0.tm_mday);
return 0;
}
y:2013 m: 1 d: 1
y:2013 m:12 d:31
y:2020 m: 1 d: 1
y:2020 m:12 d:31

Resources