using mktime()

J

John Hanley

I created a function that breaks down a date into broken down time, I
subtract a certain number of seconds from that, then use mktime() to
recompute the calendar time.

It works basically except every so often, I get the date 060207 (Feb 7,
2006) which is obviously not correct. When it does this it always gives me
this date.

I tracked it down to my mktime() call, when I get to a certain date, mktime
returns a number in 4000000000, when it should return a number in
1000000000. But I am not sure why. The variable in my tm struct all have
the correct date, but mktime returns this.

It is happening to me when the date I am changing is: 040727 but it has done
this on others as well.

I am thinking that something goes wrong in mktime() to cause it to return
some default number. Perhaps something wrong in one of my time_broken
members and then it gets past to mktime?

Suggestions? Thanks a bunch!

Here's my code:

int adjust_time()
{
struct tm time_broken; /* time in broken time */
struct tm tb; /* adjusted time */
time_t time_calendar=0; /* calendar time as a long int */
int sec,min,hour,mday,mon,year;
long int dt; /* temp variables */
int tm;


/* if CHG_TIME==0, no conversion necessary */
if(CHG_TIME==0)
{
return 1;
}

/* parse out time & date info */
sec=0;
min=TIME%100;
hour=TIME/100;
mday=DATE%100;
mon=((DATE%10000)/100)-1;

/* year must be represented as years since 1900 */
/* If the year is < 40, we assume it's in 2000's */
/* If the year is >= 40, we assume it's in 1900's */
year=(DATE/10000);
if(year<40)
year=year+100;


/* create the struct tm */
time_broken.tm_sec=sec;
time_broken.tm_min=min;
time_broken.tm_hour=hour;
time_broken.tm_mday=mday;
time_broken.tm_mon=mon;
time_broken.tm_year=year;

/* get the number of seconds since
* Jan 1, 1970 */
time_calendar=mktime(&time_broken);

/* subtract CHGTIME from calendar time_t date */
time_calendar=time_calendar-(CHG_TIME*60);
tb=*(gmtime(&time_calendar));

/* calculate new time */
tm=(tb.tm_hour*100)+tb.tm_min;

/* calculate new date */
dt=( ((tb.tm_year%100)*10000) + ((tb.tm_mon+1)*100) + (tb.tm_mday) );

/* return new time & date */
TIME=tm;
DATE=dt;

return 0;
}
 
M

Mike Wahler

John Hanley said:
I created a function that breaks down a date into broken down time, I
subtract a certain number of seconds from that, then use mktime() to
recompute the calendar time.

It works basically except every so often, I get the date 060207 (Feb 7,
2006) which is obviously not correct. When it does this it always gives me
this date.

I tracked it down to my mktime() call, when I get to a certain date, mktime
returns a number in 4000000000, when it should return a number in
1000000000. But I am not sure why. The variable in my tm struct all have
the correct date, but mktime returns this.

It is happening to me when the date I am changing is: 040727 but it has done
this on others as well.

I am thinking that something goes wrong in mktime() to cause it to return
some default number. Perhaps something wrong in one of my time_broken
members and then it gets past to mktime?

Suggestions? Thanks a bunch!

Here's my code:

You don't provide definitions for 'CHG_TIME', 'TIME', or
'DATE', so it's impossible to diagnose your problem. Try
posting a complete compilable example that demonstrates
the problem.

I don't know if it's an issue or not, but your reference to
dates above in the form of e.g. 040727 causes me to caution
you that if that's a literal numeric value, note that any
numeric literal whose first digit is zero is interpreted
by the compiler as an octal (base eight) representation.

-Mike
 
J

j

John Hanley said:
I created a function that breaks down a date into broken down time, I
subtract a certain number of seconds from that, then use mktime() to
recompute the calendar time.

It works basically except every so often, I get the date 060207 (Feb 7,
2006) which is obviously not correct. When it does this it always gives me
this date.

I tracked it down to my mktime() call, when I get to a certain date, mktime
returns a number in 4000000000, when it should return a number in
1000000000. But I am not sure why. The variable in my tm struct all have
the correct date, but mktime returns this.

It is happening to me when the date I am changing is: 040727 but it has done
this on others as well.

I am thinking that something goes wrong in mktime() to cause it to return
some default number. Perhaps something wrong in one of my time_broken
members and then it gets past to mktime?

Suggestions? Thanks a bunch!

Here's my code:

int adjust_time()
{
struct tm time_broken; /* time in broken time */
struct tm tb; /* adjusted time */
time_t time_calendar=0; /* calendar time as a long int */
int sec,min,hour,mday,mon,year;
long int dt; /* temp variables */
int tm;


/* if CHG_TIME==0, no conversion necessary */
if(CHG_TIME==0)
{
return 1;
}

/* parse out time & date info */
sec=0;
min=TIME%100;
hour=TIME/100;
mday=DATE%100;
mon=((DATE%10000)/100)-1;

/* year must be represented as years since 1900 */
/* If the year is < 40, we assume it's in 2000's */
/* If the year is >= 40, we assume it's in 1900's */
year=(DATE/10000);
if(year<40)
year=year+100;


/* create the struct tm */
time_broken.tm_sec=sec;
time_broken.tm_min=min;
time_broken.tm_hour=hour;
time_broken.tm_mday=mday;
time_broken.tm_mon=mon;
time_broken.tm_year=year;

/* get the number of seconds since
* Jan 1, 1970 */
time_calendar=mktime(&time_broken);

You never assign a meaningful value to member ``tm_isdst''.
mktime thus uses an indeterminate value and the consequence
is undefined behaviour.
 
A

Al Bowers

John said:
I created a function that breaks down a date into broken down time, I
subtract a certain number of seconds from that, then use mktime() to
recompute the calendar time.

It works basically except every so often, I get the date 060207 (Feb 7,
2006) which is obviously not correct. When it does this it always gives me
this date.

I tracked it down to my mktime() call, when I get to a certain date, mktime
returns a number in 4000000000, when it should return a number in
1000000000. But I am not sure why. The variable in my tm struct all have
the correct date, but mktime returns this.

It is happening to me when the date I am changing is: 040727 but it has done
this on others as well.

I am thinking that something goes wrong in mktime() to cause it to return
some default number. Perhaps something wrong in one of my time_broken
members and then it gets past to mktime?

Suggestions? Thanks a bunch!

Here's my code:

The code is not complete and it is not easy to follow the logic.
But I see you are making some errors. Apparently, you are
assuming that type time_t is type long representing seconds.
Standard C does not specify this to be fact. The Standard
only specifies that time_t be an arithmetic time capable of
representing time. And, it does not specify anything on
its instrumentality. So, to be portable, the code must not
assume the type to be type long and the values representing
seconds. To get around this, Standard C provides functions
that will allow you to manipulate time. So, to correct
your function adjust_time, you will need to convert the
time_t value to broken down time and the adjust the struct
member tm_sec in the number of seconds. Then call function
mktime to generate a new time_t value.


Another problem: check your return values.
You did not check the return value
of your mktime function. time_t's range of dates is limited.
Function mktime will return (time_t)-1 should it be uncapable
of representing that date. I can't be sure, but the values
you are getting in the range of 4000000000 may be result of
function mktime returning a (time_t)-1 value.

An example:

#include <stdio.h>
#include <time.h>
#include <limits.h>

time_t AdjustTime(time_t tvalue, int secs)
{
struct tm *tp;
time_t ret;

if((ret = (tvalue != (time_t)-1)))
{
tp = localtime(&tvalue);
if(secs > INT_MAX - tp->tm_sec)
ret = (time_t)-1;
else
{
tp->tm_sec+=secs;
tp->tm_isdst = -1;
ret = mktime(tp);
}
}
return ret;
}

int main(void)
{
time_t date;
struct tm t;

/* make a time_t value for 25DEC2005 12:00:00 */
t.tm_year = 2005-1900;
t.tm_mon = 11;
t.tm_mday = 25;
t.tm_hour = 12;
t.tm_min = t.tm_sec = t.tm_isdst = 0;

if((date = mktime(&t)) == (time_t)-1)
puts("Time is not available");
else
{
printf("date represents %s"
"Attemping to subtract 60 secs\n",ctime(&date));
if((date = AdjustTime(date, -60)) != (time_t)-1)
printf("The new date is %s",ctime(&date));
else puts("Time for the new date is unavailable");
}
return 0;
}
 
J

John Hanley

The code is not complete and it is not easy to follow the logic.
But I see you are making some errors. Apparently, you are
assuming that type time_t is type long representing seconds.

you're right, that was what I was assuming.
Standard C does not specify this to be fact. The Standard
only specifies that time_t be an arithmetic time capable of
representing time. And, it does not specify anything on
its instrumentality. So, to be portable, the code must not
assume the type to be type long and the values representing
seconds. To get around this, Standard C provides functions
that will allow you to manipulate time. So, to correct
your function adjust_time, you will need to convert the
time_t value to broken down time and the adjust the struct
member tm_sec in the number of seconds. Then call function
mktime to generate a new time_t value.

I see what you mean. My problem is that I am actually trying to adjust
minutes (from 1 to 59) and subtract the CHG_TIME number from the number of
minutes. However, if my date is Jan 1, 2000 at 00:00, by changing the
minutes, the hour, day, month, & year all have to change as well. That's
why I thought if I could get the time_t value as calendar time, subtract
from it number of minutes*60 (the correct number of seconds), and use
mktime(), I would get the adjusted date (the date-CHG_TIME).

I was under the assumption that the calendar time is the number of seconds
since Jan 1, 1970, and my subtracting so many seconds from that value and
calling mktime(), I would get an earlier date.

So if I were to adjust the broken time instead of the calendar time, how
would I handle the change in minutes, hour, date, month year without having
to do it manually?

Thanks so much for the help! I really appreciate it!

John
 
J

John Hanley

j said:
You never assign a meaningful value to member ``tm_isdst''.
mktime thus uses an indeterminate value and the consequence
is undefined behaviour.

Very good eye. I set tm_isdst to -1 each time (as I have no idea of DST on
any of these dates) and it seemed to work great.

Now, I was looking at the next reply from Al Bowers' and he mentioned that
my assumption of time_t calendar time as being actual seconds, my be an
incorrect assumption.

What I have been doing is getting the calendar time from broken time,
subtracting so many seconds from it (which is actually equivalent to 1 to 59
minutes) and converting the calendar time back to broken time. The reason I
do it this way is so that if it's Jan 1, 2000 at 00:00, subtracting 45
minutes (45*60 seconds) from this gets me an entirely new date, not just a
change in minutes.

Is this approach ok, or will I run into any future undefined behaviour?

Thanks so much for the help! I very much appreciate it!

Best regards,
John
 
J

John Hanley

I see what you mean. My problem is that I am actually trying to adjust
minutes (from 1 to 59) and subtract the CHG_TIME number from the number of
minutes. However, if my date is Jan 1, 2000 at 00:00, by changing the
minutes, the hour, day, month, & year all have to change as well. That's
why I thought if I could get the time_t value as calendar time, subtract
from it number of minutes*60 (the correct number of seconds), and use
mktime(), I would get the adjusted date (the date-CHG_TIME).

sorry, that should read "and use gmtime(), I would get the adjusted date..."
I was under the assumption that the calendar time is the number of seconds
since Jan 1, 1970, and my subtracting so many seconds from that value and
calling mktime(), I would get an earlier date.

here too. "...and calling gmtime(), i would get an earlier date".

sorry for the confusion.
 
A

Al Bowers

John said:
you're right, that was what I was assuming.




I see what you mean. My problem is that I am actually trying to adjust
minutes (from 1 to 59) and subtract the CHG_TIME number from the number of
minutes. However, if my date is Jan 1, 2000 at 00:00, by changing the
minutes, the hour, day, month, & year all have to change as well. That's
why I thought if I could get the time_t value as calendar time, subtract
from it number of minutes*60 (the correct number of seconds), and use
mktime(), I would get the adjusted date (the date-CHG_TIME).

I was under the assumption that the calendar time is the number of seconds
since Jan 1, 1970, and my subtracting so many seconds from that value and
calling mktime(), I would get an earlier date.

You should not make this assumption if you are writing portable Standard
C code.
So if I were to adjust the broken time instead of the calendar time, how
would I handle the change in minutes, hour, date, month year without having
to do it manually?
You will need to update the members in the struct tm. When you call
function mktime all values will be normalized and put in range. For
example you can substract 2 min by just modifying the tm_sec -= 120
or you can substract from tm_min -= 2. Both are valid.

Example:

#include <stdio.h>
#include <time.h>

int main(void)
{
time_t date;
struct tm t;

/* make a time_t value for 25DEC2005 12:00:00 */
t.tm_year = 2005-1900;
t.tm_mon = 11;
t.tm_mday = 25;
t.tm_hour = 12;
t.tm_min = t.tm_sec = t.tm_isdst = 0;

/* substract 120 sec (2 minutes) */
if((date = mktime(&t)) != (time_t)-1)
{
printf("The Date is: %s",ctime(&date));
t.tm_sec -= 120;
t.tm_isdst = -1;
if((date = mktime(&t)) != (time_t)-1)
{
printf("Subst. 2min: %s",ctime(&date));
t = *gmtime(&date);
t.tm_isdst = -1;
if((date = mktime(&t)) != (time_t)-1)
printf("GMT date is: %s",ctime(&date));
}
else puts("Time unavailable");
}
else puts("Time unavailable");
return 0;
}
 
C

Chris Croughton

Very good eye. I set tm_isdst to -1 each time (as I have no idea of DST on
any of these dates) and it seemed to work great.

If you want it to assume local time, that is the correct thing to do.
To assume GMT (UT) set that field to zero (if you are manipulating dates
it is easier in GMT, convert to and from local time only when doing I/O).
Now, I was looking at the next reply from Al Bowers' and he mentioned that
my assumption of time_t calendar time as being actual seconds, my be an
incorrect assumption.

Correct. It could be a floating point number of nanofortnights since the
Big Bang for all you know. The only thing guaranteed is that (time_t)-1
is an error value. It might not even be a linear representation, it
could use bit fields for years, months, days, hours, minutes, seconds
etc. (like the MSDOS filetimes did). The function difftime() will do
whatever magic is needed to return the difference of two time_t values
as a double number of seconds, localtime() and gmtime() will break it
down into the structure, and mktime() will create a time_t from the
structure.
What I have been doing is getting the calendar time from broken time,
subtracting so many seconds from it (which is actually equivalent to 1 to 59
minutes) and converting the calendar time back to broken time. The reason I
do it this way is so that if it's Jan 1, 2000 at 00:00, subtracting 45
minutes (45*60 seconds) from this gets me an entirely new date, not just a
change in minutes.

Is this approach ok, or will I run into any future undefined behaviour?

It will probably work until something changes. It is, however undefined
in the C standard (IIRC POSIX.1 defines it for systems which comply with
that standard).

The correct way to manipulate times is to use the broken-down structure,
mess about with the fields and make that back into a time_t using
mktime(). For instance, to get the time 2:23:45 from now use:

time_t now = time(NULL);
time_t then;
struct tm tt = *gmtime(&now);
tt.tm_hour += 2;;
tt.tm_min += 23;
tt.tm_sec += 45;
then = mktime(&tt);

(that's a snippet, not a complete program -- some of the pedants will
complain about not including headers and the like if I don't say that).

Unfortunately, the C standard doesn't say anything about the allowable
ranges of the fields, except that they are of type int and the normal
ranges are what you would expect. In particular, it doesn't say whether
negative values have the correct effect, so while adding to times is no
problem reducing them could be undefined...

Chris C
 
A

Al Bowers

Chris said:
It will probably work until something changes. It is, however undefined
in the C standard (IIRC POSIX.1 defines it for systems which comply with
that standard).

The correct way to manipulate times is to use the broken-down structure,
mess about with the fields and make that back into a time_t using
mktime(). For instance, to get the time 2:23:45 from now use:

time_t now = time(NULL);
time_t then;
struct tm tt = *gmtime(&now);
tt.tm_hour += 2;;
tt.tm_min += 23;
tt.tm_sec += 45;
then = mktime(&tt);

(that's a snippet, not a complete program -- some of the pedants will
complain about not including headers and the like if I don't say that).

Unfortunately, the C standard doesn't say anything about the allowable
ranges of the fields, except that they are of type int and the normal
ranges are what you would expect. In particular, it doesn't say whether
negative values have the correct effect, so while adding to times is no
problem reducing them could be undefined...

No. What the Standard says is that function mktime will bring
all values into range. For example the range for struct tm member
tm_sec is 0-59. If tm_sec has the value of say -69 the function
mktime will bring tm_sec into range by subtracting 1 from tm_min.
And on up the ladder, if necessary, until finally tm_mon and
tm_year are determined. Then tm_wday and tm_yday components of the
struct are set appropriately. I would think that a Standard C
that would allow you to add to a time but make reducing it undefined
would be unwise.
 
L

Lawrence Kirby

sorry, that should read "and use gmtime(), I would get the adjusted date..."

Normally you would use localtime(). Note that mktime() works from local
time, not UTC.

Lawrence
 
J

John Hanley

Al Bowers said:
The code is not complete and it is not easy to follow the logic.
But I see you are making some errors. Apparently, you are
assuming that type time_t is type long representing seconds.
Standard C does not specify this to be fact. The Standard
only specifies that time_t be an arithmetic time capable of
representing time. And, it does not specify anything on
its instrumentality. So, to be portable, the code must not
assume the type to be type long and the values representing
seconds. To get around this, Standard C provides functions
that will allow you to manipulate time. So, to correct
your function adjust_time, you will need to convert the
time_t value to broken down time and the adjust the struct
member tm_sec in the number of seconds. Then call function
mktime to generate a new time_t value.

Ok. So I tried this and after calling mktime, I then call gmtime to convert
the (normalized) calendar time back into broken time. I need the broken
time because I need the values of month, day, year, etc each separately. So
I created a test program as follows. Here I am subtracting minutes (as my
program needs to subtract 0-59 minutes from the date):

eg:

struct tm time_broken; /* time in broken time */
time_t time_calendar=0; /* calendar time as a long int */

/* create the struct tm */
time_broken.tm_sec=0;
time_broken.tm_min=0;
time_broken.tm_hour=0;
time_broken.tm_mday=1;
time_broken.tm_mon=0;
time_broken.tm_year=104; /* years since 1900 */
time_broken.tm_isdst=-1;

time_broken.tm_min=time_broken.tm_min-45;

time_calendar=mktime(&time_broken);

time_broken=*(gmtime(&time_calendar));

printf("%s\n",asctime(&time_broken));

and I get the correct date.

Am I making any incorrect assumptions here? Will this work ok?

Thanks a bunch for the help!

John
 
A

Al Bowers

John said:
Ok. So I tried this and after calling mktime, I then call gmtime to convert
the (normalized) calendar time back into broken time. I need the broken
time because I need the values of month, day, year, etc each separately. So
I created a test program as follows. Here I am subtracting minutes (as my
program needs to subtract 0-59 minutes from the date):

eg:

struct tm time_broken; /* time in broken time */
time_t time_calendar=0; /* calendar time as a long int */

/* create the struct tm */
time_broken.tm_sec=0;
time_broken.tm_min=0;
time_broken.tm_hour=0;
time_broken.tm_mday=1;
time_broken.tm_mon=0;
time_broken.tm_year=104; /* years since 1900 */
time_broken.tm_isdst=-1;

time_broken.tm_min=time_broken.tm_min-45;

time_calendar=mktime(&time_broken);

Just a reminder that the return value of mktime should
be checked. The range of times on many implementations
is limited. Therefore, it is not unusual for you to
encounter the (time_t)-1 return value indicating time
is not available for the arguments you supplied.
 
D

Dave Thompson

Chris Croughton wrote:
The correct way to [offset] times is to use the broken-down structure,
mess about with the fields and make that back into a time_t using
mktime(). For instance, to get the time 2:23:45 from now use:

time_t now = time(NULL);
time_t then;
struct tm tt = *gmtime(&now);
tt.tm_hour += 2;;
tt.tm_min += 23;
tt.tm_sec += 45;
then = mktime(&tt);

(that's a snippet, not a complete program -- some of the pedants will
complain about not including headers and the like if I don't say that).

Unfortunately, the C standard doesn't say anything about the allowable
ranges of the fields, except that they are of type int and the normal
ranges are what you would expect. In particular, it doesn't say whether
negative values have the correct effect, so while adding to times is no
problem reducing them could be undefined...

No. What the Standard says is that function mktime will bring
all values into range. For example the range for struct tm member

If the call is successful, yes. It (definitely) won't be if the
requested time is not representable in time_t, and it's not clear if
mktime() is allowed to fail in other cases that the implementor
decides are "too hard" -- the comments in the Olson public-domain
implementation imply to me that this might have happened.
tm_sec is 0-59. If tm_sec has the value of say -69 the function
mktime will bring tm_sec into range by subtracting 1 from tm_min.

Actually tm_sec is 0-60 to allow for (positive) leap seconds, which
are rarely if ever implemented. That is, leap seconds actually happen
(for now, there has been discussion of eliminating them) but (most?) C
implementations (and systems) just treat them as transient errors.
The only people I've heard of actually using them are the ones for
whom they were designed -- astronomers and space navigators, and their
only contact to most ordinary people, GPS.

Presumably you meant -60sec = -1min. -69sec = -2min leaving 51sec.
And on up the ladder, if necessary, until finally tm_mon and
tm_year are determined. Then tm_wday and tm_yday components of the
struct are set appropriately. I would think that a Standard C
that would allow you to add to a time but make reducing it undefined
would be unwise.

- David.Thompson1 at worldnet.att.net
 
I

infobahn

Actually tm_sec is 0-60 to allow for (positive) leap seconds, which
are rarely if ever implemented. That is, leap seconds actually happen
(for now, there has been discussion of eliminating them) but (most?) C
implementations (and systems) just treat them as transient errors.
The only people I've heard of actually using them are the ones for
whom they were designed -- astronomers and space navigators, and their
only contact to most ordinary people, GPS.

Is it your claim that astronomers don't have telephones, or that
they don't know how to use them?
 
A

Al Bowers

Dave said:
If the call is successful, yes. It (definitely) won't be if the
requested time is not representable in time_t, and it's not clear if
mktime() is allowed to fail in other cases that the implementor
decides are "too hard" -- the comments in the Olson public-domain
implementation imply to me that this might have happened.
Yes, the Standard does not specify anthing on the resulting values of
the struct tm
members should and and when an implement of function mktime decides that
time is
not representable. The values may be changed or unchanged, in part or
in total.
Actually tm_sec is 0-60 to allow for (positive) leap seconds, which
are rarely if ever implemented. That is, leap seconds actually happen
(for now, there has been discussion of eliminating them) but (most?) C
implementations (and systems) just treat them as transient errors.
The only people I've heard of actually using them are the ones for
whom they were designed -- astronomers and space navigators, and their
only contact to most ordinary people, GPS.
I believe the Standard specifies the representable range as 0-59. I do
not recall any mention
of leap seconds in the Standard. Perhaps you are referring to an
implement that extends
the Standard. Unfortumately, I will be away from headquarters for a
week, and not have
access to the Standard document. Please correct me if I am wrong.
Presumably you meant -60sec = -1min. -69sec = -2min leaving 51sec.
Yes, the result to bring tm_sec into range would be a decrease of 2 in
tm_min and an
increase of 120 in tm_sec to bring it's value in range at 51 (120-69).

Al Bowers
 
M

Michael Mair

Al said:
Yes, the Standard does not specify anthing on the resulting values of
the struct tm
members should and and when an implement of function mktime decides that
time is
not representable. The values may be changed or unchanged, in part or
in total.

I believe the Standard specifies the representable range as 0-59. I do
not recall any mention
of leap seconds in the Standard. Perhaps you are referring to an
implement that extends
the Standard. Unfortumately, I will be away from headquarters for a
week, and not have
access to the Standard document. Please correct me if I am wrong.

Read it just this weekend in C Unleashed, so I am sure the range
is 0..60; however we can have the look at the standard:

"7.23.1 Components of time
.....
4 The range and precision of times representable in clock_t and time_t
are implementation-defined. The tm structure shall contain at least the
following members, in any order. The semantics of the members and their
normal ranges are expressed in the comments.265)
int tm_sec; // seconds after the minute [0, 60]
int tm_min; // minutes after the hour [0, 59]
int tm_hour; // hours since midnight [0, 23]
int tm_mday; // day of the month [1, 31]
int tm_mon; // months since January [0, 11]
int tm_year; // years since 1900
int tm_wday; // days since Sunday [0, 6]
int tm_yday; // days since January 1 [0, 365]
int tm_isdst; // Daylight Saving Time flag

________________________________________________________________________
265) The range [0, 60] for tm_sec allows for a positive leap second.
"

Cheers
Michael
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top