Find the difference between the dates and group it under some category - c++

I have a start date and an end date. I need to find the difference between these dates and group it under the following categories.
< 1 year, < 2 year and so on till X years.
I'm trying to write a unix C++ program for this problem.
I can easily find the unix time difference between start and end date and compare with the 1 year's time stamp (12 * 30 * 20 * 60 * 60) and so on.
Is there any C++ function that returns the difference in years given the start and end date? Also let's say, the difference is 8 years, I suppose I have to write conditions like this,
if((end_date - start_date) < 12 * 30 * 24 * 60 * 60)
group = " less than 1 year"
...
...
Until what point do I stop at, as I won't know what the maximum difference is between the dates?
Is there any easy way to compute this?
I know i'm confusing here, but i ve put all my efforts to explain the problem here. Thanks in advance.
Also note, this is not a assignment or anything.

Assuming "precise years" (in other words, all years are 365 days long) is not an issue, I would do something like this (counting the number of times each year happens in this case - since the original question doesn't really say WHAT to do with each year)
const int MAX_YEARS = 10;
const int YEAR_IN_SECONDS = 365 * 24 * 60 * 60;
std::array<int, MAX_YEARS+1> bins;
int years = static_cast<int>(difftime(end_date - start_date) / YEAR_IN_SECONDS);
// Outside of range, put it at the end of range...
// We could discard or do something else in this case.
if (years > MAX_YEARS)
{
years = MAX_YEARS;
}
bins[years]++; // Seen one more of "this year".
Obviously, what you do with "bins", and what/how you store data there really depends on what you actually are trying to achieve.
An alternative solution would be to use const double YEAR_IN_SECONDS = 365.25 * 24 * 60 * 60;, which would slightly better cover for leap-years. If you want to be precise about it, you'd have to find out if you are before or after each of the leapday in a particular year that is divisible by 4 (and keep in mind that there are special cases for years divisible by 100 and other rules at 400).

#include <chrono>
using years = std::chrono::duration<std::chrono::system_clock::rep, std::ratio<365 * 24 * 60 * 60, 1>>;
std::chrono::system_clock::time_point end_date = std::chrono::system_clock::now();
std::chrono::system_clock::time_point start_date = end_date - years(2);
years how_many = std::chrono::duration_cast<years>(end_date - start_date);
int how_many_as_int = how_many.count();
std::cout << how_many_as_int << std::endl;
std::unordered_map<int, std::list<whatever>> m;
m[how_many_as_int].push_back(...);

Related

non-integral difference in days

I'm not asking why the following differenceInDays is wrong.
After figuring that out I kind of kicked myself.
But I wonder what's the right fix.
/**
* differenceInDays - return the difference in days between 2 dates.
*
* Since `moment.duration.asDays` can return a non-integral value
* (i.e. 36 hours == 1.5 days) each date is first adjusted to the
* start of day before the difference is determined.
*/
const differenceInDays = (date1, date2, timezone) => {
const localStartOfDay1 = moment(date1)
.tz(timezone)
.startOf('day');
const localStartOfDay2 = moment(date2)
.tz(timezone)
.startOf('day');
return moment.duration(localStartOfDay2.diff(localStartOfDay1)).asDays();
};
expect(differenceInDays('2020-03-08T17:00:00Z',
'2020-03-09T17:00:00Z',
'America/New_York')).toEqual(1);
Expected value to equal:
1
Received:
0.9583333333333334
The result might not be integral if DST transition or a leap adjustment happens between date1 and date2.
One fix could be to just round the result.

Which boost class should I use to store a human age

I have to store an age (years, months, days....possibly hours, minutes, seconds) of the user. I'm working with C++ and boost.
I'm not sure wich class of boost::posix_time (or boost::date_time) I should use.
I tried boost::posix_time::time_duration, but it's not obvious because there is no constructor taking a year count, it's only hours, so I did:
boost::posix_time::time_duration age = boost::posix_time::hours(24*365*ageInYears);
But I'm not sure that's a good strategy because all years does not have 365 days ;-)
I also tried boost::gregorian::date, but that's tricky because this one does not allow to store a year before 1400 (and this stores a date, not a duration).
I don't want to store user date of birth because I need to store its age when my program ran (medical data).
I don't want to store a regular int because it's not accurate enough (24 years old + 11 months is almost 25).
I don't want to store a float because I don't want to reinvent the wheel with float to age conversion I would have to do...
Is there really no class making it easy to store a number of years and optionally a number of month and days in boost?
Ideally, for a guy of 30 years old and a half, I'd like to be able to create an object like that: boost::....... theAge( 30, 6, 0 ); and then:
Have a function to get age in years: theAge.years() returning 30 (ignoring months)
Possibly have a conversion to float that would give me 30.5 as an age
boost::posix_time::time_duration really is one way to do this properly. Another way (which I personally would prefer) is to store the birth date and the "as-of date" both, and subtract them when you need to find the age as-of that date.
In any case you don't need a constructor taking a number of years--you can simply subtract birth_date from today--if you do that using date_time objects, you'll get a time_duration.
There are indeed duration types in boost::gregorian, specifically:
boost::gregorian::date_duration (aka boost::gregorian::days) - a count of days
boost::gregorian::months - a count of calendar months
boost::gregorian::years - a count of calendar years
boost::gregorian::weeks - a count of 7 days
These would be ideal for storage i.e. store a tuple of (years, months, days).
Note though that arithmetic using in particular months and years can have unexpected results, as they provide a snap-to-end-of-month behavior:
months single(1); // 1 month duration
date(2005,Feb,28) + single; // => 2005-Mar-31
Edit from OP owner: There's actually a an existing boost struct to store year/month/day objects (boost::date_time::date_time::year_month_day_base).
Here is an implementation perfect to answer the OP:
class age : public date_time::year_month_day_base<years, months, days>
{
typedef date_time::year_month_day_base<years, months, days> baseClass;
public:
age( int yearsCount, int monthsCount = 0, int daysCount = 0 ) :
baseClass( boost::gregorian::years(yearsCount),
boost::gregorian::months(monthsCount),
boost::gregorian::days(daysCount) )
{
}
inline int years() const { return year.number_of_years().as_number(); }
inline int months() const { return month.number_of_months().as_number(); }
inline int days() const { return day.days(); }
float getAsFloat() const
{
float age = static_cast<float>(years());
age += months()/12.0f;
age += days()/365.25f;
return age;
}
};
Then, age(30).years() == 30 and age(30,6,8).getAsFloat() == 30.521902

Find the date given the year, the month and the "nth" occurrance of day within the month C/C++

In order that a device (with limited memory) is able to manage its own timezone and daylight savings, I'm trying to calculate daylight savings triggers for 85 time zones based on a simplified description of each timezone. I have access to minimal C and C++ libraries within the device. The format of the timezone (inc. DST) description for each time zone is as follows:
UTC - the base time and date from system clock
GMTOffsetMinutes - offset from GMT with DST inactive
DSTDeltaMinutes - modifier to above with DST active (as applicable to TZ)
DSTStartMonth - month in which DST becomes active
DSTStartNthOccurranceOfDay - the nth occurrence of the day name in month
DSTDayOfWeek - Sun = 0 through to Sat = 6
DSTStartHour - hour at which DST becomes active
DSTStartMinute - minute at which DST becomes active
and corresponding EndMonth, EndNth..., EndHour, EndMinute
I have found numerous examples going the other way, i.e. starting with the date, but they involve using the modulus, keeping the remainder and dropping the quotient hence I have been unable to transpose the formula to suit my needs.
I also tried to reuse the standard "Jan = 6, Feb = 2, Mar = 2, Apr = 5, May = 0, etc. modifier table and year modifiers from the "tell me what day the 25th of June, 2067 is?" party trick and developed the following algorithm.
Date = DayOfWeek + ((NthOccuranceOfDay - 1) x 7 ) - MonthCode - YearCode
This worked for the first 6 random test dates I selected but then I started to see dates for which it failed. Is it possible that the basic algorithm is sound but I'm missing a further modifier or maybe that I'm applying the modifiers incorrectly?
Is there another solution I could utilize?
Using this open source, cross platform date library, one can write:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
year_month_day us_daylight_starts = sys_days(sun[2]/mar/2015);
year_month_day us_daylight_ends = sys_days(sun[1]/nov/2015);
std::cout << us_daylight_starts << '\n';
std::cout << us_daylight_ends << '\n';
}
which will output:
2015-03-08
2015-11-01
The formulas this library is based on are in the public domain and documented here.
The algorithms paper has very complete unit tests validating the date algorithms over a range of millions of years (a far larger range than is necessary).
Sometimes daylight savings rules are written in terms of the last weekday of a month. That is just as easily handled:
year_month_day ymd = sys_days(sun[last]/nov/2015);
std::cout << ymd << '\n'; // 2015-11-29
That formula will be off by one week (or even two weeks) if MonthCode + YearCode is greater than or equal to DayOfWeek, because in that case you will be counting NthOccurenceOfDay from a negative date.
As an alternative, with no tables, you can compute the day of week of the first of the month using, for example, Zeller's algorithm:
int NthOccurrence(int year, int month, int n, int dayOfWeek) {
// year is the current year (eg. 2015)
// month is the target month (January == 1...December == 12)
// Finds the date of the nth dayOfWeek (Sun == 0...Sat == 6)
// Adjust month and year
if (month < 3) { --year, month += 12; }
// The gregorian calendar is a 400-year cycle
year = year % 400;
// There are no leap years in years 100, 200 and 300 of the cycle.
int century = year / 100;
int leaps = year / 4 - century;
// A normal year is 52 weeks and 1 day, so the calendar advances one day.
// In a leap year, it advances two days.
int advances = year + leaps;
// This is either magic or carefully contrived,
// depending on how you look at it:
int month_offset = (13 * (month + 1)) / 5;
// From which, we can compute the day of week of the first of the month:
int first = (month_offset + advances) % 7;
// If the dayOfWeek we're looking for is at least the day we just
// computed, we just add the difference. Otherwise, we need to add 7.
// Then we just add the desired number of weeks.
int offset = dayOfWeek - first;
if (offset < 0) offset += 7;
return 1 + offset + (n - 1) * 7;
}

Converting Gregorian date to Julian Date and then back again (with time)

I'm writing a program that has to convert the current gregorian date and time to a Julian Date and then back to Gregorian gate. Eventually I will need to add the functionality of being able to add years, months, days, hours, minutes and seconds, but I need to get this part out of the way first.
Right now I have the conversion from Gregorian Date to Julian Date, so logically I feel like I should simply be able to reverse the equation somehow and that that would be fairly simple. However I'm doing a two step process where I first convert the Gregorian Date to a Julian Day Number, and then to a Julian Date (difference being day number doesn't include time). So converting it back should just mean that I have to get the hours, minutes and seconds back from the equation, and then do the seperate conversion for Julian Day Number back to Gregorian date. I would like to think it's simple process of dividing and moding 3 times for hours, minutes and seconds, and normally I'm pretty good with math and thinking these things through logically, but my brain is simply not functioning on this one.
jdn_t gregorian_to_jd(year_t year, month_t month, day_t day, hour_t hour, minute_t minute, second_t second)
{
//implement the conversion from gregorian to jdn
long long a = (14 - month)/12;
long long y = year + 4800 - a;
long long m = month + 12*a - 3;
jdn_t jdn = day + (153 * m + 2)/5 + 365*y + y/4 - y/100 + y/400 - 32045 - 0.5;
jdnt_t jdnt = jdn + (hour-12)/24 + minute/1440 + second/86400;
}
void jdn_to_gregorianTime(jdnt_t jdnt,year_t & year, month_t & month, day_t & day, hour_t & hour, minute_t & minute, second_t & second)
{
long long j = static_cast<long long>(jdnt + 0.5) + 32044;
long long g = j / 146097;
long long dg = j % 146097;
long long c = (dg / 36524 + 1) * 3 / 4;
long long dc = dg - c * 36524;
long long b = dc / 1461;
long long db = dc % 1461;
long long a = (db / 365 + 1) *3 / 4;
long long da = db - a * 365;
long long y = g * 400 + c * 100 + b * 4 + a;
long long m = (da * 5 + 308) / 153 - 2;
long long d = da - (m+4) * 153 / 5 + 122;
year = y - 4800 + (m + 2) / 12;
month = (m + 2) % 12 + 1;
day = static_cast<day_t>(d + 1);
The bottom half there are the calculations I'll need once I've been able to get out my hours, minutes and seconds. All they do Is put the Julian Day Number back to Gregorian Date.
The wiki page explains the whole julian date thing for those who aren't farmiliar: http://en.wikipedia.org/wiki/Julian_day
I hope I've explained what I need well enough! Thanks for any help you guys can offer!
This free, open source C++11/14 date/time library uses the <chrono> foundation to facilitate conversions between any two calendars by setting up conversions from all calendars to and from Unix Time.
It happens to have a Julian calendar as well as two variants of the Gregorian calendar ({year, month, day} and {year, month, weekday, index}), the ISO week-based calendar, and an (imperfect) Islamic calendar. Calendars are relatively easily added, and once added a calendar is interoperable with all other calendars, and <chrono>'s system_clock::time_point at any precision.
Example code:
#include "date.h"
#include "julian.h"
#include <iostream>
int
main()
{
using namespace date::literals;
auto ymd = 2016_y/oct/11;
auto jymd = julian::year_month_day{ymd};
auto ymd2 = date::year_month_day{jymd};
std::cout << ymd << '\n';
std::cout << jymd << '\n';
auto ymd2 = date::year_month_weekday{jymd};
}
which outputs:
2016-10-11
2016-09-28
2016/Oct/Tue[2]
If you would like more details about the underlying algorithms, they are discussed (and proven) here:
http://howardhinnant.github.io/date_algorithms.html
You could just use this library.
http://www.iausofa.org/current_C.html
Or gain some insight by just looking it over and using the concepts.
I've used it before and it's pretty straight forward. Lots of pointers though so be prepared.
The one I do know about is cal2jd and the other is jd2cal.
Those get you the dates. There are more for time and formatting. It has some examples in the docs.
And if you are so inclines to want to C++ then there is
http://www.naughter.com/aa.html
which has functions for astronomical calculations.
Good luck!
Some other resources...
http://129.79.46.40/~foxd/cdrom/musings/formulas/formulas.htm
http://robm.fastmail.fm/articles/date_class.html
https://www.autoitscript.com/forum/topic/182372-standalone-moon-phase-calculation/
What are the default values taken (say 1721119) to calculate the Gregorian Year, Month, Day from Julian Day
http://www.projectpluto.com/source.htm
This is one solution where the full time is returned as hhmmss format but you get them sperately. See the end of the function at
//hours: secs/3600 % 24, min: secs/60 % 60, secs secs % 60
unsigned JulianToTime(double julianDate)
{
double remainder = julianDate - (unsigned)julianDate;
const unsigned long long base = 1000000;
const unsigned long long halfbase = 500000;
const unsigned secsPerDay = 86400;
// "rounded" remainder after adding half a day
unsigned long long rndRemainder = (unsigned long long)(remainder * base + halfbase) % base;
rndRemainder *= secsPerDay;
// "rounded" number of seconds
unsigned long long nsecs = (rndRemainder + halfbase) / base;
//hours: secs/3600 % 24, min: secs/60 % 60, secs secs % 60
unsigned rtn = (nsecs/3600 % 24) * 10000 + (nsecs/60 % 60) * 100 + (nsecs % 60);
return rtn;
}

Math to convert seconds since 1970 into date and vice versa

I have seconds since Jan 1 1970 00:00 as an int64 in nanoseconds and I'm trying to convert it into month/day/year/day of week.
It's easy to do this iteratively, I have that working but I want to do it formulaically. I'm looking for the actual math.
New answer for old question:
Rationale for this new answer: The existing answers either do not show the algorithms for the conversion from nanoseconds to year/month/day (e.g. they use libraries with the source hidden), or they use iteration in the algorithms they do show.
This answer has no iteration whatsoever.
The algorithms are here, and explained in excruciating detail. They are also unit tested for correctness over a span of +/- a million years (way more than you need).
The algorithms don't count leap seconds. If you need that, it can be done, but requires a table lookup, and that table grows with time.
The date algorithms deal only with units of days, and not nanoseconds. To convert days to nanoseconds, multiply by 86400*1000000000 (taking care to ensure you're using 64 bit arithmetic). To convert nanoseconds to days, divide by the same amount. Or better yet, use the C++11 <chrono> library.
There are three date algorithms from this paper that are needed to answer this question.
1. days_from_civil:
// Returns number of days since civil 1970-01-01. Negative values indicate
// days prior to 1970-01-01.
// Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
// m is in [1, 12]
// d is in [1, last_day_of_month(y, m)]
// y is "approximately" in
// [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
// Exact range of validity is:
// [civil_from_days(numeric_limits<Int>::min()),
// civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
y -= m <= 2;
const Int era = (y >= 0 ? y : y-399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1; // [0, 365]
const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
return era * 146097 + static_cast<Int>(doe) - 719468;
}
2. civil_from_days:
// Returns year/month/day triple in civil calendar
// Preconditions: z is number of days since 1970-01-01 and is in the range:
// [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept
{
static_assert(std::numeric_limits<unsigned>::digits >= 18,
"This algorithm has not been ported to a 16 bit unsigned integer");
static_assert(std::numeric_limits<Int>::digits >= 20,
"This algorithm has not been ported to a 16 bit signed integer");
z += 719468;
const Int era = (z >= 0 ? z : z - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
const Int y = static_cast<Int>(yoe) + era * 400;
const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
const unsigned mp = (5*doy + 2)/153; // [0, 11]
const unsigned d = doy - (153*mp+2)/5 + 1; // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
return std::tuple<Int, unsigned, unsigned>(y + (m <= 2), m, d);
}
3. weekday_from_days:
// Returns day of week in civil calendar [0, 6] -> [Sun, Sat]
// Preconditions: z is number of days since 1970-01-01 and is in the range:
// [numeric_limits<Int>::min(), numeric_limits<Int>::max()-4].
template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}
These algorithms are written for C++14. If you have C++11, remove the constexpr. If you have C++98/03, remove the constexpr, the noexcept, and the static_asserts.
Note the lack of iteration in any of these three algorithms.
They can be used like this:
#include <iostream>
int
main()
{
int64_t z = days_from_civil(2015LL, 8, 22);
int64_t ns = z*86400*1000000000;
std::cout << ns << '\n';
const char* weekdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
unsigned wd = weekday_from_days(z);
int64_t y;
unsigned m, d;
std::tie(y, m, d) = civil_from_days(ns/86400/1000000000);
std::cout << y << '-' << m << '-' << d << ' ' << weekdays[wd] << '\n';
}
which outputs:
1440201600000000000
2015-8-22 Sat
The algorithms are in the public domain. Use them however you want. The date algorithms paper has several more useful date algorithms if needed (e.g. weekday_difference is both remarkably simple and remarkably useful).
These algorithms are wrapped up in an open source, cross platform, type-safe date library if needed.
If timezone or leap second support is needed, there exists a timezone library built on top of the date library.
Update: Different local zones in same app
See how to convert among different time zones.
Update: Are there any pitfalls to ignoring leap seconds when doing date calculations in this manner?
This is a good question from the comments below.
Answer: There are some pitfalls. And there are some benefits. It is good to know what they both are.
Almost every source of time from an OS is based on Unix Time. Unix Time is a count of time since 1970-01-01 excluding leap seconds. This includes functions like the C time(nullptr) and the C++ std::chrono::system_clock::now(), as well as the POSIX gettimeofday and clock_gettime. This is not a fact specified by the standard (except it is specified by POSIX), but it is the de facto standard.
So if your source of seconds (nanoseconds, whatever) neglects leap seconds, it is exactly correct to ignore leap seconds when converting to field types such as {year, month, day, hours, minutes, seconds, nanoseconds}. In fact to take leap seconds into account in such a context would actually introduce errors.
So it is good to know your source of time, and especially to know if it also neglects leap seconds as Unix Time does.
If your source of time does not neglect leap seconds, you can still get the correct answer down to the second. You just need to know the set of leap seconds that have been inserted. Here is the current list.
For example if you get a count of seconds since 1970-01-01 00:00:00 UTC which includes leap seconds and you know that this represents "now" (which is currently 2016-09-26), the current number of leap seconds inserted between now and 1970-01-01 is 26. So you could subtract 26 from your count, and then follow these algorithms, getting the exact result.
This library can automate leap-second-aware computations for you. For example to get the number of seconds between 2016-09-26 00:00:00 UTC and 1970-01-01 00:00:00 UTC including leap seconds, you could do this:
#include "date/tz.h"
#include <iostream>
int
main()
{
using namespace date;
auto now = clock_cast<utc_clock>(sys_days{2016_y/September/26});
auto then = clock_cast<utc_clock>(sys_days{1970_y/January/1});
std::cout << now - then << '\n';
}
which outputs:
1474848026s
Neglecting leap seconds (Unix Time) looks like:
#include "date/date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono_literals;
auto now = sys_days{2016_y/September/26} + 0s;
auto then = sys_days{1970_y/January/1};
std::cout << now - then << '\n';
}
which outputs:
1474848000s
For a difference of 26s.
This upcoming New Years (2017-01-01) we will insert the 27th leap second.
Between 1958-01-01 and 1970-01-01 10 "leap seconds" were inserted, but in units smaller than a second, and not just at the end of Dec or Jun. Documentation on exactly how much time was inserted and exactly when is sketchy, and I have not been able to track down a reliable source.
Atomic time keeping services began experimentally in 1955, and the first atomic-based international time standard TAI has an epoch of 1958-01-01 00:00:00 GMT (what is now UTC). Prior to that the best we had was quartz-based clocks which were not accurate enough to worry about leap seconds.
The Single Unix Specification gives a formula for Seconds since the Epoch:
A value that approximates the number of seconds that have elapsed
since the Epoch. A Coordinated Universal Time name (specified in terms
of seconds (tm_sec), minutes (tm_min), hours (tm_hour), days since
January 1 of the year (tm_yday), and calendar year minus 1900
(tm_year)) is related to a time represented as seconds since the
Epoch, according to the expression below.
If the year is <1970 or the value is negative, the relationship is
undefined. If the year is >=1970 and the value is non-negative, the
value is related to a Coordinated Universal Time name according to the
C-language expression, where tm_sec, tm_min, tm_hour, tm_yday, and
tm_year are all integer types:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
The relationship between the actual time of day and the current value
for seconds since the Epoch is unspecified.
How any changes to the value of seconds since the Epoch are made to
align to a desired relationship with the current actual time is
implementation-defined. As represented in seconds since the Epoch,
each and every day shall be accounted for by exactly 86400 seconds.
Note:
The last three terms of the expression add in a day for each year that follows a leap year starting with the first leap year since the
Epoch. The first term adds a day every 4 years starting in 1973, the
second subtracts a day back out every 100 years starting in 2001, and
the third adds a day back in every 400 years starting in 2001. The
divisions in the formula are integer divisions; that is, the remainder
is discarded leaving only the integer quotient.
You'll need to convert month and day of month to tm_yday to use this formula and that too should be done taking into account leap years. The rest in the formula is trivial.
Try to figure out from this how to get back date and time from seconds.
EDIT:
I've implemented a convertor in integer arithmetic in this answer.
See a test run at ideone.
Depends on which time you want gmtime or localtime then just read the struct_tm
This code works...
Usage:
uint32_t getSecsSinceEpoch(1970, month, day, years_since_epoch, hour, minute, second);
Example:
timestamp = getSecsSinceEpoch(1970, 6, 12, (2014 - 1970), 15, 29, 0)
Returns: 1402586940
You can verify at www.epochconverter.com.
Took about 20 mins to write it and most of that was spent arguing with a friend as to whether I should include leap-seconds, nano-seconds, etc. Blech.
Have fun...
Dr. Bryan Wilcutt
#define DAYSPERWEEK (7)
#define DAYSPERNORMYEAR (365U)
#define DAYSPERLEAPYEAR (366U)
#define SECSPERDAY (86400UL) /* == ( 24 * 60 * 60) */
#define SECSPERHOUR (3600UL) /* == ( 60 * 60) */
#define SECSPERMIN (60UL) /* == ( 60) */
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
const int _ytab[2][12] = {
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
/****************************************************
* Class:Function : getSecsSomceEpoch
* Input : uint16_t epoch date (ie, 1970)
* Input : uint8 ptr to returned month
* Input : uint8 ptr to returned day
* Input : uint8 ptr to returned years since Epoch
* Input : uint8 ptr to returned hour
* Input : uint8 ptr to returned minute
* Input : uint8 ptr to returned seconds
* Output : uint32_t Seconds between Epoch year and timestamp
* Behavior :
*
* Converts MM/DD/YY HH:MM:SS to actual seconds since epoch.
* Epoch year is assumed at Jan 1, 00:00:01am.
****************************************************/
uint32_t getSecsSinceEpoch(uint16_t epoch, uint8_t month, uint8_t day, uint8_t years, uint8_t hour, uint8_t minute, uint8_t second)
{
unsigned long secs = 0;
int countleap = 0;
int i;
int dayspermonth;
secs = years * (SECSPERDAY * 365);
for (i = 0; i < (years - 1); i++)
{
if (LEAPYEAR((epoch + i)))
countleap++;
}
secs += (countleap * SECSPERDAY);
secs += second;
secs += (hour * SECSPERHOUR);
secs += (minute * SECSPERMIN);
secs += ((day - 1) * SECSPERDAY);
if (month > 1)
{
dayspermonth = 0;
if (LEAPYEAR((epoch + years))) // Only counts when we're on leap day or past it
{
if (month > 2)
{
dayspermonth = 1;
} else if (month == 2 && day >= 29) {
dayspermonth = 1;
}
}
for (i = 0; i < month - 1; i++)
{
secs += (_ytab[dayspermonth][i] * SECSPERDAY);
}
}
return secs;
}
bool FloatToTime(float seconds_since_epoch, bool local_time, struct tm *timest)
{
struct tm *ret;
time_t t=(time_t) seconds_since_epoch;
if (local_time) ret=localtime(&t);
else ret=gmtime(&t);
if(ret==NULL) return false;
memcpy(timest, ret, sizeof(struct tm));
return true;
}
Pass it the seconds as the first parameter. The second parameter should be true for local time, false for GMT. The third parameter is a pointer to a structure to hold the response.
The return structures are (from the man page):
tm_sec: The number of seconds after the minute, normally in the range 0 to
59, but can be up to 60 to allow for leap seconds.
tm_min: The number of minutes after the hour, in the range 0 to 59.
tm_hour: The number of hours past midnight, in the range 0 to 23.
tm_mday: The day of the month, in the range 1 to 31.
tm_mon: The number of months since January, in the range 0 to 11.
tm_year: The number of years since 1900.
tm_wday: The number of days since Sunday, in the range 0 to 6.
tm_yday: The number of days since January 1, in the range 0 to 365.
tm_isdst: A flag that indicates whether daylight saving time is in effect
at the time described. The value is positive if daylight saving
time is in effect, zero if it is not, and negative if the
information is not available.
First of all, do not store your seconds as a float. If you need micro/nanoseconds, store them separately. You're going to need integers to do these calculations.
It depends on your time zone (DST rules, leap years, leap seconds), but I would say first get the number of days by integer dividing by 86400. Then find out what's left over, by modulo dividing by 86400. Now you can figure out how many years have passed by first integer dividing the number of days by 365, and then subtracting the number of leap days from the remaining days (calculated by modulo dividing the number of days by 365). You'll also want to subtract the number of leap seconds from the number of remaining seconds (already calculated). If that subtraction drives those numbers below zero, then subtract from the next biggest denomination. Then you can calculate the day of month using explicit logic for your calendar. Make sure to add an hour (or whatever the DST offset is) if you land in DST.
Personally, I would just use Boost.Date_Time, since it does all this and more (probably with fewer mistakes than you or I would make in the first few iterations), but I figured I'd take a shot at your question...
BEFORE
for (i = 0; i < (years - 1); i++)
{
if (LEAPYEAR((epoch + i)))
countleap++;
}
LATER:
for (i = 0; i < years; i++)
{
if (LEAPYEAR((epoch + i)))
countleap++;
}
After the correction the code worked for me.
I needed to implement conversion to Unix time at a low-end 8-bit MCU without HW multiplier. Below is the C# code that requires only a general 8-bit multiplication and a division by constant values 4 and 100. Both on 32-bit (long) operand. The C# code can be easily ported to the final framework. It gives the same result as DateTimeOffset.ToUnixTimeSeconds() from .NET.
static long UnixTime ( int sec, int min, int hour, int day, int month, int year )
{
// Cumulative days for each previous month of the year
int[] mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
// Year is to be relative to the epoch start
year -= 1970;
// Compensation of the non-leap years
int minusYear = 0;
// Detect potential lead day (February 29th) in this year?
if ( month >= 3 )
{
// Then add this year into "sum of leap days" computation
year++;
// Compute one year less in the non-leap years sum
minusYear = 1;
}
return
// + Seconds from computed minutes
60 * (
// + Minutes from computed hours
60 * (
// + Hours from computed days
24 * (
// + Day (zero index)
day - 1
// + days in previous months (leap day not included)
+ mdays[month - 1]
// + days for each year divisible by 4 (starting from 1973)
+ ( ( year + 1 ) / 4 )
// - days for each year divisible by 100 (starting from 2001)
- ( ( year + 69 ) / 100 )
// + days for each year divisible by 400 (starting from 2001)
+ ( ( year + 369 ) / 100 / 4 )
// + days for each year (as all are non-leap years) from 1970 (minus this year if potential leap day taken into account)
+ ( 5 * 73 /*=365*/ ) * ( year - minusYear )
// + Hours
) + hour
// + Minutes
) + min
// + Seconds
) + sec;
}
Hope it helps.
Edited:
Below is the optimized code for 8-bit PIC MCU and CC5X compiler.
uns32 unixTime;
...
// Test data returning 0xFfFfFfFf UnixTime
uns8 year = 2106 - 1970;
uns8 month = 2;
uns8 day = 7;
uns8 hour = 6;
uns8 min = 28;
uns8 sec = 15;
// See original C# code below
//### Compute days
// ( 5 * 73 /*=365*/ ) * year
unixTime = year;
mulUnixTime( 5 );
mulUnixTime( 73 );
// if ( month >= 3 ) year++;
if ( month > 3 )
year++;
// if ( year > 130 ) => minus 1 total days ( year-=4 makes a result of the next division by 4 less by 1)
if ( year > 130 )
year -= 4;
// + ( ( year + 1 ) / 4 )
addUnixTime( ( year + 1 ) / 4 );
// + mdays[month - 1]
addUnixTime( daysInMonths( month ) );
// + day - 1
addUnixTime( day - 1 );
//### Compute hours
// Hours from computed days
mulUnixTime( 24 );
// + Hours
addUnixTime( hour );
//### Compute minutes
// Minutes from computed hours
mulUnixTime( 60 );
// + Minutes
addUnixTime( min );
//### Compute seconds
// Seconds from computed minutes
mulUnixTime( 60 );
// + Seconds
addUnixTime( sec );
...
void mulUnixTime( uns8 mul )
{
unixTime *= mul;
}
void addUnixTime( uns8 add )
{
unixTime += add;
}
uns8 daysInMonths( uns8 month # W )
{
skip( month );
#pragma computedGoto 1
return 0xFF;// Dummy value for month 0
return 0; // January
return 31; // February
return 59; // ...
return 90;
return 120;
return 151;
return 181;
return 212;
return 243;
return 273;
return 304; // ...
return 334; // December
#pragma computedGoto 0
}
/*
static long UnixTime ( int sec, int min, int hour, int day, int month, int year )
{
// Cumulative days for each previous month of the year
int[] mdays = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
// Year is to be relative to the epoch start
year -= 1970;
// Compensation of the non-leap years
int minusYear = 0;
// Detect potential lead day (February 29th) in this year?
if ( month >= 3 )
{
// Then add this year into "sum of leap days" computation
year++;
// Compute one year less in the non-leap years sum
minusYear = 1;
}
return
// + Seconds from computed minutes
60 * (
// + Minutes from computed hours
60 * (
// + Hours from computed days
24L * (
// + Day (zero index)
day - 1
// + days in previous months (leap day not included)
+ mdays[month - 1]
// + days for each year divisible by 4 (starting from 1973)
+ ( ( year + 1 ) / 4 )
// - days after year 2000
- ( ( year > 130 ) ? 1 : 0 )
// + days for each year (as all are non-leap years) from 1970 (minus this year if potential leap day taken into account)
+ ( 5 * 73 ) * ( year - minusYear )
// + Hours
) + hour
// + Minutes
) + min
// + Seconds
) + sec;
}
*/