Use a 64-bit 'timestamp_t' for all timestamps, rather than 'time_t'

This makes the time type unambiguous, and we can use G_TYPE_INT64 for it
in the divelist too.

It also implements a portable (and thread-safe) "utc_mkdate()" function
that acts kind of like gmtime_r(), but using the 64-bit timestamp_t.  It
matches our original "utc_mktime()".

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Linus Torvalds 2012-09-19 17:35:52 -07:00
parent d14932058f
commit dce08deb34
10 changed files with 195 additions and 114 deletions

View file

@ -118,7 +118,7 @@ endif
LIBS = $(LIBXML2) $(LIBXSLT) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm
OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \
OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o \
parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o \
gtk-gui.o statistics.o file.o cochran.o $(OSSUPPORT).o $(RESFILE)
@ -167,6 +167,9 @@ save-xml.o: save-xml.c dive.h
dive.o: dive.c dive.h
$(CC) $(CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c dive.c
time.o: time.c dive.h
$(CC) $(CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) -c time.c
main.o: main.c dive.h display.h divelist.h
$(CC) $(CFLAGS) $(GLIB2CFLAGS) $(GCONF2CFLAGS) $(XML2CFLAGS) -c main.c

13
dive.h
View file

@ -236,6 +236,7 @@ struct event {
#define W_IDX_PRIMARY 0
#define W_IDX_SECONDARY 1
typedef gint64 timestamp_t;
typedef enum { TF_NONE, NO_TRIP, IN_TRIP, NUM_TRIPFLAGS } tripflag_t;
extern const char *tripflag_names[NUM_TRIPFLAGS];
@ -244,7 +245,7 @@ struct dive {
tripflag_t tripflag;
struct dive *divetrip;
int selected;
time_t when;
timestamp_t when;
char *location;
char *notes;
char *divemaster, *buddy;
@ -289,7 +290,7 @@ static inline int dive_date_cmp(gconstpointer _a, gconstpointer _b) {
/* returns 0 if the dive happened exactly at time */
static inline int dive_when_find(gconstpointer _dive, gconstpointer _time) {
return ((struct dive *)_dive)->when != (time_t) _time;
return ((struct dive *)_dive)->when != (timestamp_t) _time;
}
#define FIND_TRIP(_when) g_list_find_custom(dive_trip_list, (gconstpointer)(_when), dive_when_find)
@ -300,9 +301,10 @@ static void dump_trip_list(void)
GList *p = NULL;
int i=0;
while ((p = NEXT_TRIP(p))) {
struct tm *tm = gmtime(&DIVE_TRIP(p)->when);
struct tm tm;
utc_mkdate(DIVE_TRIP(p)->when, &tm);
printf("trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u\n", ++i, DIVE_TRIP(p)->location,
tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
tm.tm_year + 1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
printf("-----\n");
}
@ -412,7 +414,8 @@ static inline unsigned int dive_size(int samples)
return sizeof(struct dive) + samples*sizeof(struct sample);
}
extern time_t utc_mktime(struct tm *tm);
extern timestamp_t utc_mktime(struct tm *tm);
extern void utc_mkdate(timestamp_t, struct tm *tm);
extern struct dive *alloc_dive(void);
extern void record_dive(struct dive *dive);

View file

@ -50,7 +50,7 @@ const char *tripflag_names[NUM_TRIPFLAGS] = { "TF_NONE", "NOTRIP", "INTRIP" };
enum {
DIVE_INDEX = 0,
DIVE_NR, /* int: dive->nr */
DIVE_DATE, /* time_t: dive->when */
DIVE_DATE, /* timestamp_t: dive->when */
DIVE_RATING, /* int: 0-5 stars */
DIVE_DEPTH, /* int: dive->maxdepth in mm */
DIVE_DURATION, /* int: in seconds */
@ -75,16 +75,16 @@ static gboolean dump_model_entry(GtkTreeModel *model, GtkTreePath *path,
char *location;
int idx, nr, duration;
struct dive *dive;
time_t when;
struct tm *tm;
timestamp_t when;
struct tm tm;
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_NR, &nr, DIVE_DATE, &when,
DIVE_DURATION, &duration, DIVE_LOCATION, &location, -1);
tm = gmtime(&when);
utc_mkdate(when, &tm);
printf("iter %x:%x entry #%d : nr %d @ %04d-%02d-%02d %02d:%02d:%02d duration %d location %s ",
iter->stamp, iter->user_data, idx, nr,
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
duration, location);
dive = get_dive(idx);
if (dive)
@ -379,8 +379,8 @@ static void date_data_func(GtkTreeViewColumn *col,
gpointer data)
{
int val, idx, nr;
struct tm *tm;
time_t when;
struct tm tm;
timestamp_t when;
char buffer[40];
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1);
@ -388,21 +388,21 @@ static void date_data_func(GtkTreeViewColumn *col,
/* 2038 problem */
when = val;
tm = gmtime(&when);
utc_mkdate(when, &tm);
if (idx < 0) {
snprintf(buffer, sizeof(buffer),
"Trip %s, %s %d, %d (%d dive%s)",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
weekday(tm.tm_wday),
monthname(tm.tm_mon),
tm.tm_mday, tm.tm_year + 1900,
nr, nr > 1 ? "s" : "");
} else {
snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d %02d:%02d",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
tm->tm_hour, tm->tm_min);
weekday(tm.tm_wday),
monthname(tm.tm_mon),
tm.tm_mday, tm.tm_year + 1900,
tm.tm_hour, tm.tm_min);
}
g_object_set(renderer, "text", buffer, NULL);
}
@ -944,7 +944,7 @@ void update_dive_list_col_visibility(void)
return;
}
static GList *find_matching_trip(time_t when)
static GList *find_matching_trip(timestamp_t when)
{
GList *trip = dive_trip_list;
if (!trip || DIVE_TRIP(trip)->when > when)
@ -977,7 +977,7 @@ static gboolean dive_can_be_in_trip(int idx, struct dive *dive_trip)
{
struct dive *dive, *pdive;
int i = idx;
time_t when = dive_trip->when;
timestamp_t when = dive_trip->when;
dive = get_dive(idx);
/* if the dive is before the trip start but within the threshold
@ -1261,7 +1261,7 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data)
void edit_trip_cb(GtkWidget *menuitem, GtkTreePath *path)
{
GtkTreeIter iter;
time_t when;
timestamp_t when;
struct dive *dive_trip;
GList *trip;
@ -1336,13 +1336,13 @@ static int copy_tree_node(GtkTreeIter *a, GtkTreeIter *b)
}
/* to avoid complicated special cases based on ordering or number of children,
we always take the first and last child and pick the smaller time_t (which
we always take the first and last child and pick the smaller timestamp_t (which
works regardless of ordering and also with just one child) */
static void update_trip_timestamp(GtkTreeIter *parent, struct dive *divetrip)
{
GtkTreeIter first_child, last_child;
int nr;
time_t t1, t2, tnew;
timestamp_t t1, t2, tnew;
if (gtk_tree_store_iter_depth(STORE(dive_list), parent) != 0 ||
gtk_tree_model_iter_n_children(MODEL(dive_list), parent) == 0)
@ -1367,7 +1367,7 @@ static GtkTreeIter *move_dive_between_trips(GtkTreeIter *dive_iter, GtkTreeIter
GtkTreeIter *sibling, gboolean before)
{
int idx;
time_t old_when, new_when;
timestamp_t old_when, new_when;
struct dive *dive, *old_divetrip, *new_divetrip;
GtkTreeIter *new_iter = malloc(sizeof(GtkTreeIter));
@ -1442,7 +1442,7 @@ static void turn_dive_into_trip(GtkTreePath *path)
{
GtkTreeIter iter, *newiter, newparent;
GtkTreePath *treepath;
time_t when;
timestamp_t when;
char *location;
int idx;
struct dive *dive;
@ -1484,7 +1484,7 @@ static void insert_trip_before(GtkTreePath *path)
copy_tree_node(&parent, &newparent);
gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_INDEX, &idx, -1);
dive = get_dive(idx);
/* make sure that the time_t of the previous divetrip is correct before
/* make sure that the timestamp_t of the previous divetrip is correct before
* inserting a new one */
if (dive->when < prev_dive->when)
if (prev_dive->divetrip && prev_dive->divetrip->when < prev_dive->when)
@ -1693,7 +1693,7 @@ void merge_trips_cb(GtkWidget *menuitem, GtkTreePath *trippath)
GtkTreeIter thistripiter, prevtripiter, newiter, iter;
GtkTreeModel *tm = MODEL(dive_list);
GList *trip, *prevtrip;
time_t when;
timestamp_t when;
/* this only gets called when we are on a trip and there is another trip right before */
prevpath = gtk_tree_path_copy(trippath);
@ -1995,7 +1995,7 @@ GtkWidget *dive_list_create(void)
dive_list.listmodel = gtk_tree_store_new(DIVELIST_COLUMNS,
G_TYPE_INT, /* index */
G_TYPE_INT, /* nr */
G_TYPE_LONG, /* Date */
G_TYPE_INT64, /* Date */
G_TYPE_INT, /* Star rating */
G_TYPE_INT, /* Depth */
G_TYPE_INT, /* Duration */
@ -2011,7 +2011,7 @@ GtkWidget *dive_list_create(void)
dive_list.treemodel = gtk_tree_store_new(DIVELIST_COLUMNS,
G_TYPE_INT, /* index */
G_TYPE_INT, /* nr */
G_TYPE_LONG, /* Date */
G_TYPE_INT64, /* Date */
G_TYPE_INT, /* Star rating */
G_TYPE_INT, /* Depth */
G_TYPE_INT, /* Duration */
@ -2090,7 +2090,7 @@ void remove_autogen_trips()
{
GtkTreeIter iter;
GtkTreePath *path;
time_t when;
timestamp_t when;
int idx;
GList *trip;

4
file.c
View file

@ -99,7 +99,7 @@ static int try_to_open_suunto(const char *filename, struct memblock *mem, GError
return success;
}
static time_t parse_date(const char *date)
static timestamp_t parse_date(const char *date)
{
int hour, min, sec;
struct tm tm;
@ -171,7 +171,7 @@ static int try_to_open_csv(const char *filename, struct memblock *mem, enum csv_
char *p = mem->buffer;
char *header[8];
int i, time;
time_t date;
timestamp_t date;
struct dive *dive;
for (i = 0; i < 8; i++) {

21
info.c
View file

@ -99,13 +99,15 @@ static char *get_combo_box_entry_text(GtkComboBoxEntry *combo_box, char **textp,
static int divename(char *buf, size_t size, struct dive *dive)
{
struct tm *tm = gmtime(&dive->when);
struct tm tm;
utc_mkdate(dive->when, &tm);
return snprintf(buf, size, "Dive #%d - %s %02d/%02d/%04d at %d:%02d",
dive->number,
weekday(tm->tm_wday),
tm->tm_mon+1, tm->tm_mday,
tm->tm_year+1900,
tm->tm_hour, tm->tm_min);
weekday(tm.tm_wday),
tm.tm_mon+1, tm.tm_mday,
tm.tm_year+1900,
tm.tm_hour, tm.tm_min);
}
void show_dive_info(struct dive *dive)
@ -681,7 +683,7 @@ static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...)
}
/* Fixme - should do at least depths too - a dive without a depth is kind of pointless */
static time_t dive_time_widget(struct dive *dive)
static timestamp_t dive_time_widget(struct dive *dive)
{
GtkWidget *dialog;
GtkWidget *cal, *hbox, *vbox, *box;
@ -719,12 +721,13 @@ static time_t dive_time_widget(struct dive *dive)
* we'll just take the current time.
*/
if (amount_selected == 1) {
time_t when = current_dive->when;
timestamp_t when = current_dive->when;
when += current_dive->duration.seconds;
when += 60*60;
time = gmtime(&when);
utc_mkdate(when, &tm);
time = &tm;
} else {
time_t now;
timestamp_t now;
struct timeval tv;
gettimeofday(&tv, NULL);
now = tv.tv_sec;

View file

@ -175,40 +175,10 @@ static enum import_source {
UDDF,
} import_source;
time_t utc_mktime(struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
int year = tm->tm_year;
int month = tm->tm_mon;
int day = tm->tm_mday;
/* First normalize relative to 1900 */
if (year < 70)
year += 100;
else if (year > 1900)
year -= 1900;
/* Normalized to Jan 1, 1970: unix time */
year -= 70;
if (year < 0 || year > 129) /* algo only works for 1970-2099 */
return -1;
if (month < 0 || month > 11) /* array bounds */
return -1;
if (month < 2 || (year + 2) % 4)
day--;
if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
return -1;
return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
}
static void divedate(char *buffer, void *_when)
{
int d,m,y;
time_t *when = _when;
timestamp_t *when = _when;
int success = 0;
success = cur_tm.tm_sec | cur_tm.tm_min | cur_tm.tm_hour;
@ -234,7 +204,7 @@ static void divedate(char *buffer, void *_when)
static void divetime(char *buffer, void *_when)
{
int h,m,s = 0;
time_t *when = _when;
timestamp_t *when = _when;
if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
cur_tm.tm_hour = h;
@ -251,7 +221,7 @@ static void divedatetime(char *buffer, void *_when)
{
int y,m,d;
int hr,min,sec;
time_t *when = _when;
timestamp_t *when = _when;
if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
&y, &m, &d, &hr, &min, &sec) == 6) {
@ -822,7 +792,7 @@ static void uemis_date_unit(char *buffer, void *_unused)
/* Modified julian day, yay! */
static void uemis_date_time(char *buffer, void *_when)
{
time_t *when = _when;
timestamp_t *when = _when;
union int_or_float val;
switch (integer_or_float(buffer, &val)) {
@ -847,7 +817,7 @@ static void uemis_time_zone(char *buffer, void *_when)
#if 0 /* seems like this is only used to display it correctly
* the stored time appears to be UTC */
time_t *when = _when;
timestamp_t *when = _when;
signed char tz = atoi(buffer);
*when += tz * 3600;
@ -857,7 +827,7 @@ static void uemis_time_zone(char *buffer, void *_when)
static void uemis_ts(char *buffer, void *_when)
{
struct tm tm;
time_t *when = _when;
timestamp_t *when = _when;
memset(&tm, 0, sizeof(tm));
sscanf(buffer,"%d-%d-%dT%d:%d:%d",
@ -983,7 +953,7 @@ static void uddf_datetime(char *buffer, void *_when)
{
char c;
int y,m,d,hh,mm,ss;
time_t *when = _when;
timestamp_t *when = _when;
struct tm tm = { 0 };
int i;

24
print.c
View file

@ -51,7 +51,7 @@ static void show_dive_text(struct dive *dive, cairo_t *cr, double w,
const char *unit;
int len, decimals, width, height, maxwidth, maxheight;
PangoLayout *layout;
struct tm *tm;
struct tm tm;
char buffer[80], divenr[20], *people;
maxwidth = w * PANGO_SCALE;
@ -65,14 +65,14 @@ static void show_dive_text(struct dive *dive, cairo_t *cr, double w,
if (dive->number)
snprintf(divenr, sizeof(divenr), "Dive #%d - ", dive->number);
tm = gmtime(&dive->when);
utc_mkdate(dive->when, &tm);
len = snprintf(buffer, sizeof(buffer),
"%s%s, %s %d, %d %d:%02d",
divenr,
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
tm->tm_hour, tm->tm_min);
weekday(tm.tm_wday),
monthname(tm.tm_mon),
tm.tm_mday, tm.tm_year + 1900,
tm.tm_hour, tm.tm_min);
set_font(layout, font, FONT_LARGE, PANGO_ALIGN_LEFT);
pango_layout_set_text(layout, buffer, len);
@ -194,7 +194,7 @@ static void show_dive_table(struct dive *dive, cairo_t *cr, double w,
int len, decimals;
double maxwidth, maxheight, colwidth, curwidth;
PangoLayout *layout;
struct tm *tm;
struct tm tm;
char buffer[160], divenr[20];
maxwidth = w * PANGO_SCALE;
@ -223,13 +223,13 @@ static void show_dive_table(struct dive *dive, cairo_t *cr, double w,
// Col 2: Date #
pango_layout_set_width(layout, colwidth);
tm = gmtime(&dive->when);
utc_mkdate(dive->when, &tm);
len = snprintf(buffer, sizeof(buffer),
"%s, %s %d, %d %dh%02d",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
tm->tm_hour, tm->tm_min
weekday(tm.tm_wday),
monthname(tm.tm_mon),
tm.tm_mday, tm.tm_year + 1900,
tm.tm_hour, tm.tm_min
);
cairo_move_to(cr, curwidth / PANGO_SCALE, 0);
pango_layout_set_text(layout, buffer, len);

View file

@ -284,13 +284,15 @@ static void save_events(FILE *f, struct event *ev)
static void save_trip(FILE *f, struct dive *trip)
{
struct tm *tm = gmtime(&trip->when);
struct tm tm;
utc_mkdate(trip->when, &tm);
fprintf(f, "<trip");
fprintf(f, " date='%04u-%02u-%02u'",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
fprintf(f, " time='%02u:%02u:%02u'",
tm->tm_hour, tm->tm_min, tm->tm_sec);
tm.tm_hour, tm.tm_min, tm.tm_sec);
if (trip->location)
show_utf8(f, trip->location, " location=\'","\'", 1);
fprintf(f, " />\n");
@ -299,7 +301,9 @@ static void save_trip(FILE *f, struct dive *trip)
static void save_dive(FILE *f, struct dive *dive)
{
int i;
struct tm *tm = gmtime(&dive->when);
struct tm tm;
utc_mkdate(dive->when, &tm);
fputs("<dive", f);
if (dive->number)
@ -309,9 +313,9 @@ static void save_dive(FILE *f, struct dive *dive)
if (dive->rating)
fprintf(f, " rating='%d'", dive->rating);
fprintf(f, " date='%04u-%02u-%02u'",
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday);
tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
fprintf(f, " time='%02u:%02u:%02u'",
tm->tm_hour, tm->tm_min, tm->tm_sec);
tm.tm_hour, tm.tm_min, tm.tm_sec);
fprintf(f, " duration='%u:%02u min'>\n",
FRACTION(dive->duration.seconds, 60));
save_overview(f, dive);

View file

@ -364,7 +364,7 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive)
{
int idx;
struct dive *dp;
struct tm *tm;
struct tm tm;
int current_year = 0;
int current_month = 0;
int year_iter = 0;
@ -408,12 +408,12 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive)
process_dive(dp, &stats);
/* yearly statistics */
tm = gmtime(&dp->when);
utc_mkdate(dp->when, &tm);
if (current_year == 0)
current_year = tm->tm_year + 1900;
current_year = tm.tm_year + 1900;
if (current_year != tm->tm_year + 1900) {
current_year = tm->tm_year + 1900;
if (current_year != tm.tm_year) {
current_year = tm.tm_year + 1900;
process_dive(dp, &(stats_yearly[++year_iter]));
} else
process_dive(dp, &(stats_yearly[year_iter]));
@ -423,10 +423,10 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive)
/* monthly statistics */
if (current_month == 0) {
current_month = tm->tm_mon + 1;
current_month = tm.tm_mon + 1;
} else {
if (current_month != tm->tm_mon + 1)
current_month = tm->tm_mon + 1;
if (current_month != tm.tm_mon + 1)
current_month = tm.tm_mon + 1;
if (prev_month != current_month || prev_year != current_year)
month_iter++;
}
@ -495,17 +495,17 @@ static void show_single_dive_stats(struct dive *dive)
const char *unit;
int idx, offset, gas_used;
struct dive *prev_dive;
struct tm *tm;
struct tm tm;
process_all_dives(dive, &prev_dive);
tm = gmtime(&dive->when);
utc_mkdate(dive->when, &tm);
snprintf(buf, sizeof(buf),
"%s, %s %d, %d %2d:%02d",
weekday(tm->tm_wday),
monthname(tm->tm_mon),
tm->tm_mday, tm->tm_year + 1900,
tm->tm_hour, tm->tm_min);
weekday(tm.tm_wday),
monthname(tm.tm_mon),
tm.tm_mday, tm.tm_year + 1900,
tm.tm_hour, tm.tm_min);
set_label(single_w.date, buf);
set_label(single_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60);

98
time.c Normal file
View file

@ -0,0 +1,98 @@
#include <string.h>
#include "dive.h"
/*
* Convert 64-bit timestamp to 'struct tm' in UTC.
*
* On 32-bit machines, only do 64-bit arithmetic for the seconds
* part, after that we do everything in 'long'. 64-bit divides
* are unnecessary once you're counting minutes (32-bit minutes:
* 8000+ years).
*/
void utc_mkdate(timestamp_t timestamp, struct tm *tm)
{
static const int mdays[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};
static const int mdays_leap[] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};
unsigned long val;
unsigned int leapyears;
int m;
const int *mp;
memset(tm, 0, sizeof(*tm));
/* seconds since 1970 -> minutes since 1970 */
tm->tm_sec = timestamp % 60;
val = timestamp /= 60;
/* Do the simple stuff */
tm->tm_min = val % 60; val /= 60;
tm->tm_hour = val % 24; val /= 24;
/* Jan 1, 1970 was a Thursday (tm_wday=4) */
tm->tm_wday = (val+4) % 7;
/*
* Now we're in "days since Jan 1, 1970". To make things easier,
* let's make it "days since Jan 1, 1968", since that's a leap-year
*/
val += 365+366;
/* This only works up until 2099 (2100 isn't a leap-year) */
leapyears = val / (365*4+1);
val %= (365*4+1);
tm->tm_year = 68 + leapyears * 4;
/* Handle the leap-year itself */
mp = mdays_leap;
if (val > 365) {
tm->tm_year++;
val -= 366;
tm->tm_year += val / 365;
val %= 365;
mp = mdays;
}
for (m = 0; m < 12; m++) {
if (val < *mp)
break;
val -= *mp++;
}
tm->tm_mday = val+1;
tm->tm_mon = m;
}
timestamp_t utc_mktime(struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
int year = tm->tm_year;
int month = tm->tm_mon;
int day = tm->tm_mday;
/* First normalize relative to 1900 */
if (year < 70)
year += 100;
else if (year > 1900)
year -= 1900;
/* Normalized to Jan 1, 1970: unix time */
year -= 70;
if (year < 0 || year > 129) /* algo only works for 1970-2099 */
return -1;
if (month < 0 || month > 11) /* array bounds */
return -1;
if (month < 2 || (year + 2) % 4)
day--;
if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
return -1;
return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
}