From dce08deb34939eaed349d315777214c3181c1a8d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 19 Sep 2012 17:35:52 -0700 Subject: [PATCH 1/4] 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 --- Makefile | 5 ++- dive.h | 13 ++++--- divelist.c | 56 +++++++++++++++--------------- file.c | 4 +-- info.c | 21 ++++++----- parse-xml.c | 44 ++++------------------- print.c | 24 ++++++------- save-xml.c | 16 +++++---- statistics.c | 28 +++++++-------- time.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 195 insertions(+), 114 deletions(-) create mode 100644 time.c diff --git a/Makefile b/Makefile index 8c7717b34..94e23909a 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/dive.h b/dive.h index e443ce965..0953e4d8c 100644 --- a/dive.h +++ b/dive.h @@ -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); diff --git a/divelist.c b/divelist.c index f032e6243..ea2dcfb5b 100644 --- a/divelist.c +++ b/divelist.c @@ -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; diff --git a/file.c b/file.c index ca39c8dd6..943be6f09 100644 --- a/file.c +++ b/file.c @@ -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++) { diff --git a/info.c b/info.c index ad482f11d..16149cb02 100644 --- a/info.c +++ b/info.c @@ -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; diff --git a/parse-xml.c b/parse-xml.c index 2a10792f8..f83deb705 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -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; diff --git a/print.c b/print.c index 12504b13d..5adba94cd 100644 --- a/print.c +++ b/print.c @@ -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); diff --git a/save-xml.c b/save-xml.c index 7ce0afbc1..55740ce92 100644 --- a/save-xml.c +++ b/save-xml.c @@ -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, "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("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); diff --git a/statistics.c b/statistics.c index 5e1c8804a..35b9cecb9 100644 --- a/statistics.c +++ b/statistics.c @@ -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); diff --git a/time.c b/time.c new file mode 100644 index 000000000..ed8222a0f --- /dev/null +++ b/time.c @@ -0,0 +1,98 @@ +#include +#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; +} + + From d66d376d204f07ccce36994c43f16365cfa1a5e5 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 19 Sep 2012 21:44:54 -0700 Subject: [PATCH 2/4] Fix the incorrect data type for DIVE_DATE accesses This is the same bugfix that Lubomir did in the master branch, but now on top of the new 64-bit timestamp_t model. So now we also remove the comment about the year 2038 problem, because it's not true any more. We do all the date handling in a 64-bit integer. Signed-off-by: Linus Torvalds --- divelist.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/divelist.c b/divelist.c index ea2dcfb5b..849a8bd2d 100644 --- a/divelist.c +++ b/divelist.c @@ -378,15 +378,13 @@ static void date_data_func(GtkTreeViewColumn *col, GtkTreeIter *iter, gpointer data) { - int val, idx, nr; + int idx, nr; struct tm tm; timestamp_t when; char buffer[40]; - gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &val, -1); + gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); nr = gtk_tree_model_iter_n_children(model, iter); - /* 2038 problem */ - when = val; utc_mkdate(when, &tm); if (idx < 0) { From 4f920b71aae7ecb1470888b95c472c1f794486bc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Sep 2012 10:53:45 -0700 Subject: [PATCH 3/4] dive-time widget: fix incorrect use of timestamp_t I did a global search-and-replace to make all "time_t" users use the internal subsurface 64-bit "timestamp_t" type instead, but we have one case that still uses the system time functions: the use of "localtime()" in the dive_time_widget(). Everywhere else we always just use UTC for all our time handling, and we don't really ever care about the local timezone etc. However, for the dive time widget, we initialize the calendar widget to the current time, which obviously does want to take the local timezone into account, so there we end up using the whole system time handling code. So that one should continue to use time_t, even if it might have the year-2038 problem. We also don't care about the fact that it's not thread-safe, since this is just initializing the widget which definitely doesn't happen threaded. Signed-off-by: Linus Torvalds --- info.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/info.c b/info.c index 16149cb02..40e0af278 100644 --- a/info.c +++ b/info.c @@ -727,7 +727,7 @@ static timestamp_t dive_time_widget(struct dive *dive) utc_mkdate(when, &tm); time = &tm; } else { - timestamp_t now; + time_t now; struct timeval tv; gettimeofday(&tv, NULL); now = tv.tv_sec; From 6d16a15196857eb4fe2eb4ca3cf363f1221afe60 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 20 Sep 2012 11:08:15 -0700 Subject: [PATCH 4/4] FIND_TRIP: don't cast a timestamp to a pointer The pointer size may not be large enough to contain a timestamp, so make FIND_TRIP() just pass the pointer to the timestamp instead. And use an inline function instead of macros with casts. That gets us proper type safety while at it, so that we get a warning if somebody doesn't pass the expected "timestamp_t *". Plus the code actually looks simpler and way more straightforward. Signed-off-by: Linus Torvalds --- dive.h | 14 ++++++++++---- divelist.c | 10 +++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/dive.h b/dive.h index 0953e4d8c..a0b019f96 100644 --- a/dive.h +++ b/dive.h @@ -289,11 +289,17 @@ 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 != (timestamp_t) _time; +static inline int dive_when_find(gconstpointer _listentry, gconstpointer _when) +{ + const struct dive *dive = _listentry; + const timestamp_t *tsp = _when; + return dive->when != *tsp; } -#define FIND_TRIP(_when) g_list_find_custom(dive_trip_list, (gconstpointer)(_when), dive_when_find) +static inline GList *FIND_TRIP(timestamp_t *when) +{ + return g_list_find_custom(dive_trip_list, when, dive_when_find); +} #ifdef DEBUG_TRIP static void dump_trip_list(void) @@ -316,7 +322,7 @@ static void dump_trip_list(void) static void inline insert_trip(struct dive **trip) { struct dive *dive_trip = *trip; - GList *result = FIND_TRIP(dive_trip->when); + GList *result = FIND_TRIP(&dive_trip->when); if (result) { if (! DIVE_TRIP(result)->location) DIVE_TRIP(result)->location = dive_trip->location; diff --git a/divelist.c b/divelist.c index 849a8bd2d..9a3493c99 100644 --- a/divelist.c +++ b/divelist.c @@ -1058,7 +1058,7 @@ static void fill_dive_list(void) parent_ptr = NULL; dive_trip = create_and_hookup_trip_from_dive(dive); dive_trip->tripflag = IN_TRIP; - trip = FIND_TRIP(dive_trip->when); + trip = FIND_TRIP(&dive_trip->when); } if (trip) dive_trip = DIVE_TRIP(trip); @@ -1265,7 +1265,7 @@ void edit_trip_cb(GtkWidget *menuitem, GtkTreePath *path) gtk_tree_model_get_iter(MODEL(dive_list), &iter, path); gtk_tree_model_get(MODEL(dive_list), &iter, DIVE_DATE, &when, -1); - trip = FIND_TRIP(when); + trip = FIND_TRIP(&when); dive_trip = DIVE_TRIP(trip); if (edit_trip(dive_trip)) gtk_tree_store_set(STORE(dive_list), &iter, DIVE_LOCATION, dive_trip->location, -1); @@ -1564,7 +1564,7 @@ static void remove_from_trip(GtkTreePath *path) /* if this was the last dive on the trip, remove the trip */ if (! gtk_tree_model_iter_has_child(MODEL(dive_list), &parent)) { gtk_tree_store_remove(STORE(dive_list), &parent); - delete_trip(FIND_TRIP(dive->divetrip->when)); + delete_trip(FIND_TRIP(&dive->divetrip->when)); free(dive->divetrip); } /* mark the dive as intentionally at the top level */ @@ -1672,7 +1672,7 @@ void remove_trip(GtkTreePath *trippath, gboolean force_no_trip) } /* finally, remove the trip */ gtk_tree_store_remove(STORE(dive_list), &parent); - delete_trip(FIND_TRIP(dive_trip->when)); + delete_trip(FIND_TRIP(&dive_trip->when)); free(dive_trip); #ifdef DEBUG_TRIP dump_trip_list(); @@ -2097,7 +2097,7 @@ void remove_autogen_trips() while(gtk_tree_model_get_iter(TREEMODEL(dive_list), &iter, path)) { gtk_tree_model_get(TREEMODEL(dive_list), &iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1); if (idx < 0) { - trip = FIND_TRIP(when); + trip = FIND_TRIP(&when); if (DIVE_TRIP(trip)->tripflag == IN_TRIP) { /* this was autogen */ remove_trip(path, FALSE); continue;