This question already has answers here:
How to get current time and date in Android
(42 answers)
Closed 6 years ago.
#include "stdafx.h"
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
time_t now = time(NULL);
int sec = now % 60;
int min = ((now - sec) / 60) % 60;
int hour = ((((now - sec) / 60) - min) / 60) % 24;
int day = ((((((now - sec) / 60) - min) / 60) - hour) / 24) % 30;
int month = ((((((((now - sec) / 60) - min) / 60) - hour) / 24) - day) / 30) % 12;
int year = ((((((((((now - sec) / 60) - min) / 60) - hour) / 24) - day) / 30) - month) / 12) + 1970;
cout << hour << ":" << min << ":"<< ":" << day << ":" << month << ":" << year<<endl;
}
I can't understand why month output 10 instead of 3 ???
(I know there is c_time function but i am learning and making my own one)
time() function returns a number of seconds since January 1st, 1970.
Converting this into a date is much more complex than you think:
It starts with calculating the seconds: taking the number %60 should in principle tell the seconds in the current minute. That seems obvious. But this is not exact: it doesn't take into account the 25 leap seconds that took place since 1970.
minutes, hours would be ok if you take into account this first correction (and eventually taking into account daylight saving time, if this code is not ran in winter)
your calculation of the day of the month does not take into account the fact that not all the months are 30 days long, and it doesn't take into account the leap years.
And basically, the calculation of the months and the year would be affected by the impact of the wrong estimation of month.
Do yourself a favour and convert the date using a standard function like gmtime() or localtime() and accessing the fields of the struct tm
Converting seconds to days/hours/minutes is trivial. Converting days since 1970 to current date is not. You either should use library functions if you can or you may use formula provided for Julian day, just pay attention that 1/1/1970 is not 0 for Julian day.
PS subtracting reminder is completely unnecessary:
int min = ((now - sec) / 60) % 60;
should be:
int min = now / 60 % 60;
or even better:
int sec = now % 60;
now /= 60;
int min = now % 60;
now /= 60;
and so on.
It is extremely daunting task of converting 'seconds since epoch' to day/month/year/etc.
This is why library got you covered. There are gmtime_r() and localtime()_r designed specifically for that. Unfortunately, those functions are not standard, and the only standard ones are thread unsafe gmtime() and localtime().
I saw examples for C#, Java, but for C++ i cant find solution to calculate how many days between two dates.
For example between 2012-01-24 and 2013-01-08
Thanks!
This is one way.
#include <iostream>
#include <ctime>
int main()
{
struct std::tm a = {0,0,0,24,5,104}; /* June 24, 2004 */
struct std::tm b = {0,0,0,5,6,104}; /* July 5, 2004 */
std::time_t x = std::mktime(&a);
std::time_t y = std::mktime(&b);
if ( x != (std::time_t)(-1) && y != (std::time_t)(-1) )
{
double difference = std::difftime(y, x) / (60 * 60 * 24);
std::cout << std::ctime(&x);
std::cout << std::ctime(&y);
std::cout << "difference = " << difference << " days" << std::endl;
}
return 0;
}
my output
Thu Jun 24 01:00:00 2004
Mon Jul 05 01:00:00 2004
difference = 11 days
Here is a ref to Original author post
Convert your dates to integer denoting the number of days since an epoch, then subtract. In this example i chosed Rata Die, an explanation of the algorithm can be found at <http://mysite.verizon.net/aesir_research/date/rata.htm>.
int
rdn(int y, int m, int d) { /* Rata Die day one is 0001-01-01 */
if (m < 3)
y--, m += 12;
return 365*y + y/4 - y/100 + y/400 + (153*m - 457)/5 + d - 306;
}
int days = rdn(2013, 1, 8) - rdn(2012, 1, 24);
Update for C++20:
#include <chrono>
#include <iostream>
int
main()
{
using namespace std::chrono;
using namespace std;
auto x = 2012y/1/24;
auto y = 2013y/1/8;
cout << x << '\n';
cout << y << '\n';
cout << "difference = " << sys_days{y} - sys_days{x} << 'n';
}
Output:
2012-01-24
2013-01-08
difference = 350d
If the {year, month, day} data exists in ints, then it just looks like:
int xy = 2012;
int xm = 1;
int xd = 24;
int yy = 2013;
int ym = 1;
int yd = 8;
auto x = year{xy}/xm/xd;
auto y = year{yy}/ym/yd;
// ...
The type of sys_days{y} - sys_days{x} is std::chrono::days which is a type alias for std::chrono::duration<signed integral type, std::ratio<86'400>>.
New answer for an old question:
Using this C++11/C++14 header-only date library, you can now write:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std;
auto x = 2012_y/1/24;
auto y = 2013_y/1/8;
cout << x << '\n';
cout << y << '\n';
cout << "difference = " << (sys_days{y} - sys_days{x}).count() << " days\n";
}
Which outputs:
2012-01-24
2013-01-08
difference = 350 days
If you don't want to depend on this library, you can write your own, using the same date algorithms that the above date library uses. They are found in this paper: chrono-Compatible Low-Level Date Algorithms. The algorithm from this paper that is being exercised in this example is this one:
// 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;
}
See chrono-Compatible Low-Level Date Algorithms for details about how this algorithm works, unit tests for it, and its range of validity.
This algorithm models the proleptic Gregorian calendar, which extends the Gregorian calendar indefinitely, both forwards and backwards. To model other calendars (such as the Julian calendar), you will need other algorithms, such as the ones shown here. Once you have other calendars set up, and synchronized to the same serial epoch (these algorithms use 1970-01-01 Gregorian, which is also the Unix time epoch), you can easily compute the number of days not only between any two dates, but also between any two calendars which you have modeled.
This gives you the freedom of not having to hard-code in a date for the switch from Julian to Gregorian. You just have to know which calendar your input data is referenced against.
Sometimes dates in historical documents that might otherwise be ambiguous are annotated with Old Style / New Style to indicate the Julian or Gregorian calendar respectively.
If you are also concerned about the time of day with your dates, this same date library seamlessly integrates with the <chrono> library for use of hours, minutes, seconds, milliseconds, microseconds and nanoseconds, and with system_clock::now() to get the current date and time.
If you are concerned about time zones, an additional (separate) timezone library is written on top of the date library to handle time zones using the IANA timezone database. If needed, the timezone library also has a facility for computations that include leap seconds.
you can try the boost date_time library
To avoid making your own function you can use date_time from Boost.
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;
}
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;
}
*/