Converting days since Jan.1 1900 to today's date - c

typedef struct dbdatetime
{ // Internal representation of DATETIME data type
LONG dtdays; // No of days since Jan-1-1900 (maybe negative)
ULONG dttime; // No. of 300 hundredths of a second since midnight
} DBDATETIME;
I am trying to convert this struct into today's date. I don't suspect the time will give me much trouble but I am having problems with the logic of converting the total number of days to the proper month and day.
Ex. Friday November 7th is 41948 days.
You can divide by 365.2425+1900 to get the current year but how would you get the proper month / date.
Does C have anything built in to handle this? I am not a C programmer by trade.

There's nothing in the C standard directly to handle this, but if you are willing to write OS specific code, or can import libraries like boost::date_time, this is the best option. Don't attempt to handle it yourself unless you are okay with edge cases being wrong. Dates and times are notoriously difficult to get right.
Here are the docs for date_time which can do arithmetic on dates, including "add N days to 1/1/1900". http://www.boost.org/doc/libs/1_56_0/doc/html/date_time/gregorian.html#date_time.gregorian.date_duration
date d(1900, Jan, 1)
d += days(dtdays);
EDIT: OP can't use boost, but I'll leave this here in case a future visitor could use the info.

As you said, you can divide the number of days by 365.2425 to get a approx estimation of the year. Once you have that, use the number of years and leap years between 1900 and that year to calculate the number of days till that year.
You need to make sure that the logic for detecting leap years is sound. Once you know which years are leap years, you'll be fine. For example, 1900 is NOT a leap year, even though it is divisible by 4. Google leap year rules to find out why.
Once you have that, the remaining days will belong to the current year. Use the leap year logic again to determine whether you the current year is leap or not. Then on it's just a matter of counting days for each month.

C++ is out of the question, thanks for all the input guys.
I did find this algorithm that seems to work so far but am testing it now.
int l = nSerialDate + 68569 + 2415019;
int n = int(( 4 * l ) / 146097);
l = l - int(( 146097 * n + 3 ) / 4);
int i = int(( 4000 * ( l + 1 ) ) / 1461001);
l = l - int(( 1461 * i ) / 4) + 31;
int j = int(( 80 * l ) / 2447);
nDay = l - int(( 2447 * j ) / 80);
l = int(j / 11);
nMonth = j + 2 - ( 12 * l );
nYear = 100 * ( n - 49 ) + i + l;
Trying to find some theory behind it now.

Thanks for all of the input everyone, here is what I ended up doing.
Convert dtdays to seconds, minus the offset of the number of seconds between Jan-1-1900(Serial Date) and Jan-1-1970(UNIX Time). Which is 2208988800, this leaves us with UNIX time.
Convert the hundredths of seconds into seconds and add to the total.
Convert UNIX time using gmtime() to a tm struct.

Related

How do I calculate the difference in days, months and years between two dates with leap years?

I'm requesting the user to enter two dates (day, month and year).
Using the time.h header, I'm storing it in a struct tm variable like following (let's say date1):
date1.tm_mday = day;
date1.tm_mon = month;
date1.tm_year = year - 1900;
date1.tm_hour = 0;
date1.tm_min = 0;
date1.tm_sec = 0;
date1.tm_isdst = 0;
The second date (date2) is the same above but with "date2" instead of "date1" (two structures).
Next, I did the mktime of date1 and date2 to calculate the seconds of both dates:
date1TimeT = mktime(&date1);
date2TimeT = mktime(&date2);
Following, I checked whether the date1 is bigger than date2, so I placed:
if(date1TimeT > date2TimeT)
finalDateTimeT = date1TimeT - date2TimeT;
else
finalDateTimeT = date2TimeT - date1TimeT;
Now to calculate the days, months and years that have passed through this two dates:
totalDays = (int) finalDateTimeT / 86400;
remainingYears = totalDays / 365;
remainingMonths = (totalDays - remainingYears * 365) / 30;
remainingDays = (totalDays - remainingYears * 365 - remainingMonths * 30);
However, this is not 100% accurate, as this does not take care of the leap years (those who are divisible by 4).
Is there a safer way to calculate this? Keeping in mind the leap years.
How do I calculate the difference in days, months and years between two dates?
There are two totally different ways to do this. The more popular one is the way you've already tried: First convert both dates to some common, monotonic timebase, such as a Unix-style time_t value. Then subtract. This gives you a simple difference in, in this case, seconds. You can divide by 86400 to get days, but it's hard to see how to convert to months or years.
The reason it's hard is that our familiar year/month/day/hour/minute/second system of date/timekeeping is a mixed base scheme, and it's worse than that, in that different months have different numbers of days. There's no single constant days-per-month value you can divide by, the way there is for, say, inches-per-foot (or even hours-per-day).
And then time zones, daylight saving time, leap years, and leap seconds can make things even more complicated.
But in fact, those complications are one reason that the convert-to-monotonic-timebase technique is so popular, because it cuts through most of that complexity rather nicely. Assuming that library functions like mktime() and localtime() have been implemented properly, they take care of all the nasty details of time zones, daylight saving time, and leap years. Just about the only thing left for you to do is to make sure you use them correctly.
Using them correctly involves:
making sure to encode the tm_year and tm_mon fields of struct tm correctly (this is easy to get wrong, and in fact your posted code makes one of the common mistakes, forgetting that tm_mon is 0-based)
making sure to set tm_isdst to -1 if you want mktime to figure out whether DST applied for the date/time in question or not
setting tm_hour to 12 (not 0) if you're only interested in dates, not times (this helps to avoid certain anomalies that can crop up near DST transitions, although this probably wouldn't matter for your problem), and
when converting to days, remembering to round, not truncate, when dividing by 86400, since you may see an effective 23- or 25-hour day length if you're straddling a DST transition.
Leap years are generally not a problem when using this technique, and I'm not sure what you meant when you said that it was "not 100% accurate" because it "does not take care of the leap years". mktime and localtime understand leap years perfectly, and will allow you to compute a proper difference (in seconds) between two dates which straddle the February/March transition, in both leap and non-leap years. It's true that you won't get an accurate number of years if you just divide by the constant 365, but that's no worse than the way you'll often get the wrong number of months if you divide by a constant 30.
So if you've converted to a monotonic number of seconds, the answer to the question of how you convert to years, months, and days is: you don't. It's an impossible, ill-defined problem. You'll have to settle for presenting an answer in terms of just days, or perhaps divide by 7 to get weeks and days. (Or you can present a decidedly approximate answer after dividing by an average number of days per month, like 30.43.)
Now, just about everyone would agree that, informally at least, the difference between, say, February 5 and March 7 is "one month and two days". Notice that this is equally true in leap and non-leap years. If you want to implement this more "informal" or "human-centric" algorithm in a C program, you certainly can, although it's not going to involve mktime or localtime. Instead, it's going to be an implementation of mixed-base arithmetic, with a complication due to the fact that the number of days per month is not constant.
Suppose we want to find the difference between April 5, 2021 and June 10, 2022. That will look like
year month day
2022 6 10
- 2021 4 5
---- -- --
1 2 5
The answer is "One year, two months, and five days". This problem was simple, because we didn't have to borrow.
Here's a harder one: what's the difference between January 25,
2020 and March 5, 2020? That looks like
year month day
2020 3 5
- 2020 1 25
---- -- --
???? ?? ??
What are we supposed to do with this? The first date is clearly greater than the second, but of course 5 is less than 20. Informally, from January 25 to February 25 is one month, and then it's a few more days until March 5, but that "few more days" hits the worst case, because it straddles the end of February, so we have to worry about whether it's a leap year or not.
The way to work the problem is probably like this:
year month day
2020 2 34
- 2020 1 25
---- -- --
0 1 9
Here I have borrowed 1 from the month's column, and added it in to the day's column. Since 2020 was a leap year, there were 29 days in February, so the adjusted days number after the borrow is 5+29=34.
You might have to implement a similar borrow from the year's to the month's column, although that's easier, because the number of months per year is always 12. (That is, I'm pretty sure you will not have to worry about leap years at that point in the algorithm.)
I'm out of time for this too-long answer, so I'm not going to write any C code to do this mixed-base arithmetic, but with this prose outline I think you should be able to implement it straightforwardly enough.
Footnote 1: The algorithm I've presented isn't quite complete. It will not handle, for example, the interval from January 31 to March 1 properly. It's not necessarily obvious how to handle that rather problematic case, but the answer is probably to add the possibility of looping and borrowing twice, as #chux shows in his answer (see comment "Might loop twice").
Footnote 2: Remember that in our Gregorian calendar, the leap-year determination is not a simple test for divisibility by 4.
Footnote 3: For completeness and for anyone who might be curious, I'll say a little more about leap seconds. Unlike leap years, leap seconds are not just automatically taken care of for you by mktime and localtime. In fact, handling leap seconds properly is more or less impossible under the conventional mktime/localtime scheme, for the simple reason that the Unix time_t representation denies the existence of leap seconds altogether. Due to the presence of leap seconds, UTC is not the uniform, continuous, monotonic time scale that time_t assumes. Handling leap seconds properly requires thinking pretty carefully about how you want them to affect your addition and subtraction algorithms, and it requires using some data type other than time_t.
Footnote 4. Best not to worry at all about the anomalies that happened in 1752 (or whenever your country changed over to the Gregorian calendar). :-)
The idea to go through the delta days is a bad one. Once you pass the month boundary you are in trouble. Each month has 28..31 days. In your original post you assume every month to have 30 days.
In order not to loose relationship with the month's lengths we have to work directly with day/month/year. We can combine month+year in one variable as every year has 12 months, like the expression month+year*12. Here my code:
#include <time.h>
#include <stdio.h>
void PrintDelta (int StartYear, int StartMonth, int StartDay, int EndYear, int EndMonth, int EndDay)
{
//Month and Day are 1-based
int Start_MonthYear = StartYear * 12 + StartMonth-1;
int End_MonthYear = EndYear * 12 + EndMonth-1;
int Delta_MonthYear = End_MonthYear-Start_MonthYear;
int Delta_Days = EndDay-StartDay;
if (Delta_Days < 0)
{
//Like Jan 31st --> Feb 1st
//remove one month from Delta_MonthYear and add one month to Delta_Days
if (0)
{ //old buggy code used month before end date
static const int MonthDays[2][12]=
{
//MonthDays[][0] is the number of days in the month BEFORE January!!
//12 01 02 03 04 05 06 07 08 09 10 11 <--- month
{ 31,31,28,31,30,31,30,31,31,30,31,30}, //non-leap year
{ 31,31,29,31,30,31,30,31,31,30,31,30} //leap year
};
Delta_Days += MonthDays[(EndYear & 3) ? 0 : 1][EndMonth-1];
}
else
{ //better code uses month from start date
static const int MonthDays[2][12]=
{
{ 31,28,31,30,31,30,31,31,30,31,30,31}, //non-leap year
{ 31,29,31,30,31,30,31,31,30,31,30,31} //leap year
};
Delta_Days += MonthDays[(StartYear & 3) ? 0 : 1][StartMonth-1];
}
Delta_MonthYear--;
}
printf("Timespan from %d-%02d-%02d to %d-%02d-%02d is %d year(s), %d month(s), %d day(s)\n",
StartYear, StartMonth, StartDay,
EndYear, EndMonth, EndDay,
Delta_MonthYear/12, Delta_MonthYear%12, Delta_Days
);
}
int main(int argc, char **argv)
{
PrintDelta(2000,2,28, 2001,3,1); //1yr 1day, including Feb 29th
PrintDelta(2001,2,28, 2002,3,1); //1yr 1day, no Feb 29th
PrintDelta(2022,1,11, 2022,1,21); //10 days
PrintDelta(2022,1,31, 2022,2,1); //one day
PrintDelta(2022,1,31, 2022,3,1); //1 month 1 day
}
Subtract days, then months, then years. The only challenging issue is how to borrow from months
Some illustrative sample code below -simplifications exist.
#include <stdio.h>
int is_leap_year(int year) {
// Leave this for OP
}
// year: Gregorian calender
// mont: 0 = Dec, 1 = Jan, ... 12 = Dec
static int days_per_month(int year, int month) {
static const char dpm[] = {31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month == 2 && is_leap_year(year)) {
return 29;
}
return dpm[month];
}
typedef struct {
int year, month, day;
} ymd_type;
// *a - *b
// Assume: a and b elements are already in their preferred ranges and a >= b.
ymd_type date_subtract(const struct tm *a, const struct tm *b) {
ymd_type ymd = {a->tm_year + 1900, a->tm_mon + 1, a->tm_mday};
ymd.day -= b->tm_mday;
while (ymd.day < 0) { // Might loop twice
ymd.month--;
ymd.day += days_per_month(ymd.year, ymd.month);
}
ymd.month -= b->tm_mon + 1;
if (ymd.month < 0) {
ymd.year--;
ymd.month += 12; // 12 months per year
}
ymd.year -= b->tm_year + 1900;
return ymd;
}
int test_ymd(int y1, int m1, int d1, int y0, int m0, int d0) {
const struct tm t1 = {.tm_year = y1 - 1900, .tm_mon = m1 - 1, .tm_mday = d1};
const struct tm t0 = {.tm_year = y0 - 1900, .tm_mon = m0 - 1, .tm_mday = d0};
ymd_type ymd = date_subtract(&t1, &t0);
printf(" %4d/%2d/%2d - %4d/%2d/%2d = %2dy %2dm %2dd\n", y1, m1, d1, y0, m0,
d0, ymd.year, ymd.month, ymd.day);
return 0;
}
Sample
int main() {
test_ymd(2022, 2, 17, 2020, 1, 1);
test_ymd(2022, 3, 1, 2020, 12, 31);
test_ymd(2022, 3, 1, 2022, 1, 31);
test_ymd(2020, 3, 1, 2020, 1, 31);
return 0;
}
Output
2022/ 2/17 - 2020/ 1/ 1 = 2y 1m 16d
2022/ 3/ 1 - 2020/12/31 = 1y 1m 29d
2022/ 3/ 1 - 2022/ 1/31 = 0y 0m 29d
2020/ 3/ 1 - 2020/ 1/31 = 0y 0m 30d
To Do:
Handle cases when dates are in the opposite order.
Handle cases when the date is not in the primary rage.
Handle extreme case like when year is near INT_MAX.

count number of weekdays between two date strings in C99

I have two date strings in the form yyyy-mm-dd , just like
const char* date_start = "2015-09-30";
const char* date_end = "2015-10-03";
How do I calculate the number of weekdays (number of days which are neither Saturday nor Sunday) between the two dates? Dates where start and end day are equal can exists (the day count should be equal to 1 (workday) or 0 (weekend) then). All input dates are guaranteed to be valid (e.g. no 30th of February).
The solution need to work with C99 on OS X as well as Windows and independent of the system locale settings.
I would prefer to use as little external code (i.e. libraries or frameworks) as possible.
Pseudo Code
Form time structures
struct tm start = {0};
start.tm_year = 2105-1900;
start.tm_mon = 9-1;
start.tm_mday = 30;
start.tm_isdst = 0;
struct tm end = ...
Form time number and set tm_wday field
time_t tstart = mktime(&start);
time_t tend = mktime(&end);
Find day difference
double day_diff = difftime(&tend, &tstart)/(24.0*60*60);
Some magic per weekday (left for OP)
numweekdays = ((long)day_diff/7)*5 + foo(start->tm_wday, end->tm_wday);
convert date-strings to something more handable, like "long date since 1.1.1970", if that suits your use case; or "struct tm"
calculate the difference in days (end minus start plus one)
for each complete week inside the difference (> 7) add 5 weekdays/2 weekends
calculate the weekday-status for the rest (at least 6) days and add them accordinly

How can I find out all the dates of a given day in this month?

I have to write a program in C which, given the name of a day, returns all of the dates in this month that the day will be on.
For example, if the input is "Sunday" the output should be: 5,12,19,26 (which are the days Sunday will be on this month.)
Does any one have any idea how to do this? I have tried a lot.
You can use the time() function to get the current time.
Then use the localtime() function to get a struct (struct tm) with the information (year, month, day, ...) of the current time.
From the "struct tm" get the tm_mday and the tm_wday. Use these fields to determine the next or previous sunday. e.g. if tm_mday is 12, and tm_wday is 3 (wednesday), then we now that the 9th of this month is a sunday (12-3 = 9). From that number simply add or subtract 7 to get all other sundays.
You need to know it for a given year too? Or is this for only this year? If you need to know it for any given year, you can do a "days per month" enum, having one for the leap years and one for the non-leap years.
You just need to know in which day of week started a year (i.e: "Monday", "Tuesday", etc)
You will, at least, have 5 dates for any given month, so, you can have a fixed length array of ints.
You know that the gregorian calendar repeats itself each 400 years, and that if a year X started with day "Y", then, year X + 1 will start with day ("Y" + 1) % 7 if x is not a leap year, if it is a leap year, it will start with day ("Y" + 2).
that could give you the first date of any year, and knowing how many days have all the months for any given year, you can easily get what date that month starts in ("Monday", etc).
Then, all you have to do, is something like
int offset = 0;
int i;
while (myDate + offset != monthStartingDate) {
offset++;
}
i = offset + monthStartingDate;
(myDate is the number of day of week, and monthStartingDate is the number of day of week for the first day of that month)
when you go out of that loop, you will have the first ocurrence, then, you just add 7 until i is out of month bounds.
you can add each i into the array..
int res[5] = {0,0,0,0,0}
for ( ; i < daysOfMonth(month, year); i += 7) {
int res[i / 7] = i;
}
then you just return res.
Oh, I dind't know that you were able to use date functions :P I think the idea of the excercise was practicing C :P
1) Take a string (weekday name) from the input (use scanf or gets)
2) Convert it to a number (find it's index in a table of weekdays using loop and strcmp), assign 0 to Sunday, 1 for Monday ...
3) Get current time with time function and convert it to tm struct with localtime function
4) From tm struct calculate the first day in current month of a given weekday
first_mday_of_given_wday = (tm_struct.tm_mday + given_wday - tm_struct.tm_wday + 6) % 7 + 1
5) Find out how many days is in current month. To do this:
put 1 into tm_mday and 0 into tm_isdst of your tm struct
duplicate the struct
increase by 1 tm_mon in the duplicate (watch for the last month! in that case increase tm_year by 1 and set tm_mon to 1)
convert booth strutcs to time_t with mktime function and calculate difference (just subtract these time_t values), convert result from seconds to days (divide by 60*60*24)
6) Run a loop though calculated range:
for (i = first_mday_of_given_wday; i <= num_days_in_month; i += 7) printf("%d, ", i)
5th step can be omitted in a certain situations. We know that a month can have from 28 to 31 days. If any of hypothetical days 29,30,31 of current month cannot be a given weekday we can assume that current month has 28 days.
So simply - assume we have 28 days in current month if first_mday_of_given_wday is more then 3, otherwise calculate the number like shown in 5th step.

References for implementing calendar functionality in an embedded system?

I have an embedded system that currently keeps track of seconds until an event is supposed to occur using a real-time clock driven by a watch crystal.
Now it needs to keep track of the actual date and time. So, I need to be able to calculate the day, month, year, hour, minute and second from a start date/time and offset in seconds.
Could anyone point me in the right direction for taking into account leap years, daylight savings time (DST) and other complications?
Hardware solutions are not an option as this feature is being added to an existing product. An RTC peripheral is integrated into the MCU chosen for the next generation device.
The C Snippets archive has some date and time functions. Update: unfortunately, the C snippets archive is now defunct. I have updated the link to point to the web archive of the page.
See also "Julian day", Wikipedia, which includes formulas for Julian date calculation.
A "julian date calculation" Google search should uncover more if you want to search further.
Calendar code can be a bit complex - if the C runtime library you're using doesn't have such support built-in (and some way to integrate your clock counter to it) you might consider looking at the code in P.J. Plauger's "The Standard C Library" and adapting it to your needs.
I'm bored, couldn't resist trying a solution. Here's a prototype in ruby - should be clear enough to translate to C.
Given offset and a start date stored as: Baseyear, Baseday, Basesec where day 0 = Jan1,
you can calculate the date as
#initialize outputs
year= Baseyear
day = Baseday
sec = Basesec+offset
#days & seconds remaining in the current year
is_leap = is_leap_year(year)
days_remaining = 365+(is_leap ? 1 : 0) - day
secs_remaining = SEC_PER_DAY*days_remaining
#advance by year
while (sec>=secs_remaining)
sec-=secs_remaining
year+=1
is_leap = is_leap_year(year)
days_remaining = 365+(is_leap ? 1 : 0)
secs_remaining = SEC_PER_DAY*days_remaining
day=0
end
#sec holds seconds into the current year, split into days+seconds
day += sec / SEC_PER_DAY
day = day.to_i #cast to int
sec %= SEC_PER_DAY
#lookup month
for i in (0..11)
dpm = DAYS_PER_MONTH[i] # =[31,28,31,30,...]
if (i==1 && is_leap)
dpm+=1
end
if day < dpm
month = i
break
else
day-=dpm
end
end
day+=1 #1-based
hour = sec/3600
min = (sec%3600)/60
sec = sec%60
puts "%s %d, %d # %02d:%02d:%02d" % [MONTHNAME[month],day,year, hour, min, sec]
It should be easy to add a check that the day is between the begin and end days for DST in the current locale, and adjust the hour accordingly.
The following function determines whether a given year is a leap year:
bool is_leap_year(int year)
{
return ((0 == year % 400) || ((0 == year % 4) && (0 != year % 100)));
}

Converting unix timestamp to YYYY-MM-DD HH:MM:SS

I have a Unix timestamp that I need to get the individual year, month, day, hour, minute and second values from. I never was very good in math class so I was wondering if you guys could help me out a little :)
I have to do everything myself (no time.h functions). The language is C.
Disclaimer: The following code does not account for leap years or leap seconds [Unix time does not account for leap seconds. They're overrated, anyway. -Ed]. Also, I did not test it, so there may be bugs. It may kick your cat and insult your mother. Have a nice day.
Let's try a little psuedocode (Python, really):
# Define some constants here...
# You'll have to figure these out. Don't forget about February (leap years)...
secondsPerMonth = [ SECONDS_IN_JANUARY, SECONDS_IN_FEBRUARY, ... ]
def formatTime(secondsSinceEpoch):
# / is integer division in this case.
# Account for leap years when you get around to it :)
year = 1970 + secondsSinceEpoch / SECONDS_IN_YEAR
acc = secondsSinceEpoch - year * SECONDS_IN_YEAR
for month in range(12):
if secondsPerMonth[month] < acc:
acc -= month
month += 1
month += 1
# Again, / is integer division.
days = acc / SECONDS_PER_DAY
acc -= days * SECONDS_PER_DAY
hours = acc / SECONDS_PER_HOUR
acc -= hours * SECONDS_PER_HOUR
minutes = acc / SECONDS_PER_MINUTE
acc -= minutes * SECONDS_PER_MINUTE
seconds = acc
return "%d-%d-%d %d:%d%d" % (year, month, day, hours, minutes, seconds)
If I goofed up, please let me know. Doing this in C shouldn't be too much harder.
You really do not want to do this by hand. You could write up some simple code that assumes years, months, days, hours, minutes and seconds are all the same lengths (12 months; 28, 30, or 31 days; 24 hours; 60 minutes; and 60 seconds) and come up with the wrong answer.
To get the right answer, you have to handle leap years and leap seconds, and convert to the local time zone (with the right DST mode). (Unless you choose to only display in UTC time.)
I suggest that you have a look at the code of glibc and see how strftime works.
Edit: UNIX time does not use the leap second.
That format is often called ISO 8601, and searching for that might help you out.
You should have done a search - I posted a complete answer to this question over here just the other day (for UTC, at least - to adjust for other timezones just add or subtract the timezone offset in seconds from the unixtime before you call the function).
You don't have to do math. This can be easily handled in C like this,
char *ISO_Time (
time_t time
)
{
static char mStr[128];
struct tm *gmt;
gmt = gmtime (&time);
strftime (mStr, sizeof(mStr), "%Y-%m-%dT%H:%M:%SZ", gmt);
return mStr;
}
Since yours is not exactly ISO time, you just need to change one line,
strftime (mStr, sizeof(mStr), "%Y-%m-%d %H:%M:%S", gmt);

Resources