summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--dive.c5
-rw-r--r--dive.h76
-rw-r--r--divelist.c262
-rw-r--r--file.c4
-rw-r--r--info.c27
-rw-r--r--parse-xml.c67
-rw-r--r--print.c24
-rw-r--r--save-xml.c16
-rw-r--r--statistics.c28
-rw-r--r--time.c98
11 files changed, 398 insertions, 214 deletions
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.c b/dive.c
index 52ec64517..b1739ad80 100644
--- a/dive.c
+++ b/dive.c
@@ -710,6 +710,11 @@ struct dive *try_to_merge(struct dive *a, struct dive *b)
res = alloc_dive();
res->when = a->when;
+ /* the larger tripflag is more relevant */
+ if(a->tripflag > b->tripflag)
+ res->tripflag = a->tripflag;
+ else
+ res->tripflag = b->tripflag;
MERGE_NONZERO(res, a, b, latitude);
MERGE_NONZERO(res, a, b, longitude);
MERGE_TXT(res, a, b, location);
diff --git a/dive.h b/dive.h
index e443ce965..cab6eec29 100644
--- a/dive.h
+++ b/dive.h
@@ -236,15 +236,24 @@ struct event {
#define W_IDX_PRIMARY 0
#define W_IDX_SECONDARY 1
-typedef enum { TF_NONE, NO_TRIP, IN_TRIP, NUM_TRIPFLAGS } tripflag_t;
+typedef gint64 timestamp_t;
+typedef enum { TF_NONE, NO_TRIP, IN_TRIP, ASSIGNED_TRIP, AUTOGEN_TRIP, NUM_TRIPFLAGS } tripflag_t;
extern const char *tripflag_names[NUM_TRIPFLAGS];
+typedef struct dive_trip {
+ tripflag_t tripflag;
+ timestamp_t when;
+ timestamp_t when_from_file;
+ char *location;
+ char *notes;
+} dive_trip_t;
+
struct dive {
int number;
tripflag_t tripflag;
- struct dive *divetrip;
+ dive_trip_t *divetrip;
int selected;
- time_t when;
+ timestamp_t when;
char *location;
char *notes;
char *divemaster, *buddy;
@@ -275,65 +284,15 @@ extern gboolean autogroup;
#define TRIP_THRESHOLD 3600*24*3
#define UNGROUPED_DIVE(_dive) ((_dive)->tripflag == NO_TRIP)
-#define DIVE_IN_TRIP(_dive) ((_dive)->tripflag == IN_TRIP)
+#define DIVE_IN_TRIP(_dive) ((_dive)->tripflag == IN_TRIP || (_dive)->tripflag == ASSIGNED_TRIP)
#define DIVE_NEEDS_TRIP(_dive) ((_dive)->tripflag == TF_NONE)
#define NEXT_TRIP(_entry) ((_entry) ? g_list_next(_entry) : (dive_trip_list))
#define PREV_TRIP(_entry) ((_entry) ? g_list_previous(_entry) : g_list_last(dive_trip_list))
-#define DIVE_TRIP(_trip) ((struct dive *)(_trip)->data)
+#define DIVE_TRIP(_trip) ((dive_trip_t *)(_trip)->data)
#define DIVE_FITS_TRIP(_dive, _dive_trip) ((_dive_trip)->when - TRIP_THRESHOLD <= (_dive)->when)
-/* compare two dives by when they happened */
-static inline int dive_date_cmp(gconstpointer _a, gconstpointer _b) {
- return ((struct dive *)_a)->when - ((struct dive *)_b)->when;
-}
-
-/* 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;
-}
-
-#define FIND_TRIP(_when) g_list_find_custom(dive_trip_list, (gconstpointer)(_when), dive_when_find)
+extern void insert_trip(dive_trip_t **trip);
-#ifdef DEBUG_TRIP
-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);
- 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);
- }
- printf("-----\n");
-}
-#endif
-
-/* insert the trip into the dive_trip_list - but ensure you don't have
- * two trips for the same date; but if you have, make sure you don't
- * keep the one with less information */
-static void inline insert_trip(struct dive **trip)
-{
- struct dive *dive_trip = *trip;
- GList *result = FIND_TRIP(dive_trip->when);
- if (result) {
- if (! DIVE_TRIP(result)->location)
- DIVE_TRIP(result)->location = dive_trip->location;
- *trip = DIVE_TRIP(result);
- } else {
- dive_trip_list = g_list_insert_sorted(dive_trip_list, dive_trip, dive_date_cmp);
- }
-#ifdef DEBUG_TRIP
- dump_trip_list();
-#endif
-}
-
-static inline void delete_trip(GList *trip)
-{
- dive_trip_list = g_list_delete_link(dive_trip_list, trip);
-#ifdef DEBUG_TRIP
- dump_trip_list();
-#endif
-}
/*
* We keep our internal data in well-specified units, but
* the input and output may come in some random format. This
@@ -412,7 +371,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);
@@ -447,7 +407,7 @@ extern void remember_event(const char *eventname);
extern void evn_foreach(void (*callback)(const char *, int *, void *), void *data);
extern int add_new_dive(struct dive *dive);
-extern gboolean edit_trip(struct dive *trip);
+extern gboolean edit_trip(dive_trip_t *trip);
extern int edit_dive_info(struct dive *dive);
extern int edit_multi_dive_info(struct dive *single_dive);
extern void dive_list_update_dives(void);
diff --git a/divelist.c b/divelist.c
index 590748691..b95dc86c3 100644
--- a/divelist.c
+++ b/divelist.c
@@ -41,7 +41,10 @@ static struct DiveList dive_list;
GList *dive_trip_list;
gboolean autogroup = FALSE;
-const char *tripflag_names[NUM_TRIPFLAGS] = { "TF_NONE", "NOTRIP", "INTRIP" };
+/* this duplicate assignment of "INTRIP" causes the save_xml code
+ * to convert an ASSIGNED_TRIP (which is temporary in memory) to
+ * a statically assigned trip (INTRIP) in file */
+const char *tripflag_names[NUM_TRIPFLAGS] = { "TF_NONE", "NOTRIP", "INTRIP", "INTRIP", "AUTOGEN_TRIP" };
/*
* The dive list has the dive data in both string format (for showing)
@@ -50,7 +53,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 +78,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,31 +382,28 @@ static void date_data_func(GtkTreeViewColumn *col,
gpointer data)
{
int idx, nr;
- long date;
- 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, &date, -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 = date;
- 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);
}
@@ -945,19 +945,114 @@ void update_dive_list_col_visibility(void)
return;
}
-static GList *find_matching_trip(time_t when)
+/*
+ * helper functions for dive_trip handling
+ */
+
+#ifdef DEBUG_TRIP
+static void dump_trip_list(void)
+{
+ GList *p = NULL;
+ int i=0;
+ timestamp_t last_time = 0;
+ while ((p = NEXT_TRIP(p))) {
+ dive_trip_t *dive_trip = DIVE_TRIP(p);
+ struct tm tm;
+ utc_mkdate(dive_trip->when, &tm);
+ if (dive_trip->when < last_time)
+ printf("\n\ndive_trip_list OUT OF ORDER!!!\n\n\n");
+ printf("%s trip %d to \"%s\" on %04u-%02u-%02u %02u:%02u:%02u\n",
+ dive_trip->tripflag == AUTOGEN_TRIP ? "autogen " : "",
+ ++i, dive_trip->location,
+ tm.tm_year + 1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ if (dive_trip->when_from_file && dive_trip->when != dive_trip->when_from_file) {
+ utc_mkdate(dive_trip->when_from_file, &tm);
+ printf("originally on %04u-%02u-%02u %02u:%02u:%02u\n", tm.tm_year + 1900,
+ tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+ last_time = dive_trip->when;
+ }
+ printf("-----\n");
+}
+#endif
+
+/* this finds a trip that starts at precisely the time given */
+static GList *find_trip(timestamp_t when)
{
GList *trip = dive_trip_list;
- if (!trip || DIVE_TRIP(trip)->when > when)
+ while (trip && DIVE_TRIP(trip)->when < when)
+ trip = trip->next;
+ if (DIVE_TRIP(trip)->when == when) {
+#ifdef DEBUG_TRIP
+ struct tm tm;
+ utc_mkdate(DIVE_TRIP(trip)->when, &tm);
+ printf("found trip @ %04d-%02d-%02d %02d:%02d:%02d\n",
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+#endif
+ return trip;
+ }
+#ifdef DEBUG_TRIP
+ printf("no matching trip\n");
+#endif
+ return NULL;
+}
+
+/* this finds the last trip that at or before the time given */
+static GList *find_matching_trip(timestamp_t when)
+{
+ GList *trip = dive_trip_list;
+ if (!trip || DIVE_TRIP(trip)->when > when) {
+#ifdef DEBUG_TRIP
+ printf("no matching trip\n");
+#endif
return NULL;
+ }
while (trip->next && DIVE_TRIP(trip->next)->when <= when)
trip = trip->next;
+#ifdef DEBUG_TRIP
+ {
+ struct tm tm;
+ utc_mkdate(DIVE_TRIP(trip)->when, &tm);
+ printf("found trip @ %04d-%02d-%02d %02d:%02d:%02d\n",
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+#endif
return trip;
}
-static struct dive *create_and_hookup_trip_from_dive(struct dive *dive)
+/* insert the trip into the dive_trip_list - but ensure you don't have
+ * two trips for the same date; but if you have, make sure you don't
+ * keep the one with less information */
+void insert_trip(dive_trip_t **dive_trip_p)
{
- struct dive *dive_trip = alloc_dive();
+ dive_trip_t *dive_trip = *dive_trip_p;
+ GList *trip = dive_trip_list;
+ while (trip && DIVE_TRIP(trip)->when < dive_trip->when)
+ trip = trip->next;
+ if (trip && DIVE_TRIP(trip)->when == dive_trip->when) {
+ if (! DIVE_TRIP(trip)->location)
+ DIVE_TRIP(trip)->location = dive_trip->location;
+ *dive_trip_p = DIVE_TRIP(trip);
+ } else {
+ dive_trip_list = g_list_insert_before(dive_trip_list, trip, *dive_trip_p);
+ }
+#ifdef DEBUG_TRIP
+ dump_trip_list();
+#endif
+}
+
+static inline void delete_trip(GList *trip)
+{
+ dive_trip_list = g_list_delete_link(dive_trip_list, trip);
+#ifdef DEBUG_TRIP
+ dump_trip_list();
+#endif
+}
+static dive_trip_t *create_and_hookup_trip_from_dive(struct dive *dive)
+{
+ dive_trip_t *dive_trip = calloc(sizeof(dive_trip_t),1);
dive_trip->when = dive->when;
if (dive->location)
dive_trip->location = strdup(dive->location);
@@ -974,11 +1069,11 @@ static struct dive *create_and_hookup_trip_from_dive(struct dive *dive)
* that starts at when and check on the way that there is no ungrouped
* dive and no break beyond the 3 day threshold between dives that
* haven't already been assigned to this trip */
-static gboolean dive_can_be_in_trip(int idx, struct dive *dive_trip)
+static gboolean dive_can_be_in_trip(int idx, dive_trip_t *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
@@ -1015,9 +1110,9 @@ static void fill_dive_list(void)
int i;
GtkTreeIter iter, parent_iter, *parent_ptr = NULL;
GtkTreeStore *liststore, *treestore;
- struct dive *last_trip = NULL;
+ dive_trip_t *last_trip = NULL;
GList *trip;
- struct dive *dive_trip = NULL;
+ dive_trip_t *dive_trip = NULL;
/* if we have pre-existing trips, start on the last one */
trip = g_list_last(dive_trip_list);
@@ -1050,25 +1145,42 @@ static void fill_dive_list(void)
dive_trip = NULL;
} else if (autogroup && DIVE_NEEDS_TRIP(dive)){
/* if we already have existing trips there are up to two trips that this
- * could potentially be part of. Let's try the one we are on, first */
- if (! (dive_trip && dive_can_be_in_trip(i, dive_trip))) {
- /* there could be a better trip in our list already */
- trip = find_matching_trip(dive->when);
- if (! (trip && dive_can_be_in_trip(i, DIVE_TRIP(trip)))) {
+ * could potentially be part of.
+ * Let's see if there is a matching one already */
+ GList *matching_trip;
+ matching_trip = find_matching_trip(dive->when);
+ if (matching_trip && dive_can_be_in_trip(i, DIVE_TRIP(matching_trip))) {
+ trip = matching_trip;
+ } else {
+ /* is there a trip we can extend ? */
+ if (! matching_trip && dive_trip) {
+ /* careful - this is before the first trip
+ yet we have a trip we're looking at; make
+ sure that is indeed the first trip */
+ dive_trip = DIVE_TRIP(dive_trip_list);
+ trip = dive_trip_list;
+ }
+ /* maybe we can extend the current trip */
+ if (! (dive_trip && dive_can_be_in_trip(i, dive_trip))) {
/* seems like neither of these trips work, so create
* a new one; all fields default to 0 and get filled
* in further down */
parent_ptr = NULL;
dive_trip = create_and_hookup_trip_from_dive(dive);
- dive_trip->tripflag = IN_TRIP;
- trip = FIND_TRIP(dive_trip->when);
+ dive_trip->tripflag = AUTOGEN_TRIP;
+ trip = find_trip(dive_trip->when);
}
- if (trip)
- dive_trip = DIVE_TRIP(trip);
}
+ if (trip)
+ dive_trip = DIVE_TRIP(trip);
} else if (DIVE_IN_TRIP(dive)) {
trip = find_matching_trip(dive->when);
- dive_trip = DIVE_TRIP(trip);
+ if (trip)
+ dive_trip = DIVE_TRIP(trip);
+ else
+ printf ("data seems inconsistent - dive claims to be in dive trip "
+ "yet there appears to be no matching trip\n"
+ "Trying to recover\n");
} else {
/* dive is not in a trip and we aren't autogrouping */
dive_trip = NULL;
@@ -1077,7 +1189,8 @@ static void fill_dive_list(void)
/* update dive as part of dive_trip and
* (if necessary) update dive_trip time and location */
if (dive_trip) {
- dive->tripflag = IN_TRIP;
+ if(DIVE_NEEDS_TRIP(dive))
+ dive->tripflag = ASSIGNED_TRIP;
dive->divetrip = dive_trip;
if (dive_trip->when > dive->when)
dive_trip->when = dive->when;
@@ -1126,6 +1239,9 @@ static void fill_dive_list(void)
DIVE_SUIT, dive->suit,
DIVE_SAC, 0,
-1);
+#ifdef DEBUG_TRIP
+ dump_trip_list();
+#endif
}
/* make sure we display the first date of the trip in previous summary */
@@ -1262,13 +1378,13 @@ void add_dive_cb(GtkWidget *menuitem, gpointer data)
void edit_trip_cb(GtkWidget *menuitem, GtkTreePath *path)
{
GtkTreeIter iter;
- time_t when;
- struct dive *dive_trip;
+ timestamp_t when;
+ dive_trip_t *dive_trip;
GList *trip;
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);
@@ -1337,13 +1453,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)
+static void update_trip_timestamp(GtkTreeIter *parent, dive_trip_t *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)
@@ -1368,8 +1484,9 @@ static GtkTreeIter *move_dive_between_trips(GtkTreeIter *dive_iter, GtkTreeIter
GtkTreeIter *sibling, gboolean before)
{
int idx;
- time_t old_when, new_when;
- struct dive *dive, *old_divetrip, *new_divetrip;
+ timestamp_t old_when, new_when;
+ struct dive *dive;
+ dive_trip_t *old_divetrip, *new_divetrip;
GtkTreeIter *new_iter = malloc(sizeof(GtkTreeIter));
if (before)
@@ -1443,10 +1560,11 @@ 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;
+ dive_trip_t *dive_trip;
/* this is a dive on the top level, insert trip AFTER it, populate its date / location, and
* then move the dive below that trip */
@@ -1461,8 +1579,10 @@ static void turn_dive_into_trip(GtkTreePath *path)
treepath = gtk_tree_model_get_path(MODEL(dive_list), newiter);
gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), treepath);
dive = get_dive(idx);
- /* we don't need the return value - everything is hooked up in the function */
- (void)create_and_hookup_trip_from_dive(dive);
+ /* this trip is intentionally created so it should be treated as if
+ * it was read from a file */
+ dive_trip = create_and_hookup_trip_from_dive(dive);
+ dive_trip->when_from_file = dive_trip->when;
}
/* we know that path is pointing at a dive in a trip and are asked to split this trip into two */
@@ -1470,7 +1590,8 @@ static void insert_trip_before(GtkTreePath *path)
{
GtkTreeIter iter, prev_iter, parent, newparent, nextsibling;
GtkTreePath *treepath, *prev_path;
- struct dive *dive, *prev_dive, *new_divetrip;
+ struct dive *dive, *prev_dive;
+ dive_trip_t *new_divetrip;
int idx, nr, i;
gtk_tree_model_get_iter(MODEL(dive_list), &iter, path);
@@ -1485,7 +1606,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)
@@ -1509,6 +1630,8 @@ static void insert_trip_before(GtkTreePath *path)
/* we copied the dive we were called with; we are done */
break;
}
+ /* treat this divetrip as if it had been read from a file */
+ new_divetrip->when_from_file = new_divetrip->when;
treepath = gtk_tree_model_get_path(MODEL(dive_list), &newparent);
gtk_tree_view_expand_to_path(GTK_TREE_VIEW(dive_list.tree_view), treepath);
#ifdef DEBUG_TRIP
@@ -1567,7 +1690,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 */
@@ -1642,7 +1765,8 @@ static void remove_from_trip_cb(GtkWidget *menuitem, GtkTreePath *path)
void remove_trip(GtkTreePath *trippath, gboolean force_no_trip)
{
GtkTreeIter newiter, parent, child, *lastiter = &parent;
- struct dive *dive, *dive_trip = NULL;
+ struct dive *dive;
+ dive_trip_t *dive_trip = NULL;
int idx;
GtkTreePath *childpath;
GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dive_list.tree_view));
@@ -1675,7 +1799,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();
@@ -1694,7 +1818,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);
@@ -1996,7 +2120,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 */
@@ -2012,7 +2136,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 */
@@ -2091,7 +2215,7 @@ void remove_autogen_trips()
{
GtkTreeIter iter;
GtkTreePath *path;
- time_t when;
+ timestamp_t when;
int idx;
GList *trip;
@@ -2100,12 +2224,26 @@ 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);
- if (DIVE_TRIP(trip)->tripflag == IN_TRIP) { /* this was autogen */
+ trip = find_trip(when);
+ if (trip && DIVE_TRIP(trip)->tripflag == AUTOGEN_TRIP) { /* this was autogen */
remove_trip(path, FALSE);
continue;
}
}
gtk_tree_path_next(path);
}
+ /* now walk the remaining trips in the dive_trip_list and restore
+ * their original time stamp; we don't do this in the loop above
+ * to ensure that the list stays in chronological order */
+ trip = NULL;
+ while(NEXT_TRIP(trip)) {
+ trip = NEXT_TRIP(trip);
+ DIVE_TRIP(trip)->when = DIVE_TRIP(trip)->when_from_file;
+ }
+ /* finally walk the dives and remove the 'ASSIGNED_TRIP' designator */
+ for (idx = 0; idx < dive_table.nr; idx++) {
+ struct dive *dive = get_dive(idx);
+ if (dive->tripflag == ASSIGNED_TRIP)
+ dive->tripflag = TF_NONE;
+ }
}
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..96e270459 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)
@@ -443,7 +445,7 @@ static void save_dive_info_changes(struct dive *dive, struct dive *master, struc
}
}
-static void dive_trip_widget(GtkWidget *box, struct dive *trip, struct dive_info *info)
+static void dive_trip_widget(GtkWidget *box, dive_trip_t *trip, struct dive_info *info)
{
GtkWidget *hbox, *label;
char buffer[80] = "Edit trip summary";
@@ -535,7 +537,7 @@ void update_equipment_data(struct dive *dive, struct dive *master)
memcpy(dive->weightsystem, master->weightsystem, WS_BYTES);
}
-gboolean edit_trip(struct dive *trip)
+gboolean edit_trip(dive_trip_t *trip)
{
GtkWidget *dialog, *vbox;
int success;
@@ -569,10 +571,8 @@ gboolean edit_trip(struct dive *trip)
if (old_text)
g_free(old_text);
}
- if (changed) {
+ if (changed)
mark_divelist_changed(TRUE);
- flush_divelist(trip);
- }
}
gtk_widget_destroy(dialog);
return changed;
@@ -681,7 +681,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,10 +719,11 @@ 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;
struct timeval tv;
diff --git a/parse-xml.c b/parse-xml.c
index 2a10792f8..da150d27e 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -156,7 +156,8 @@ const struct units IMPERIAL_units = {
/*
* Dive info as it is being built up..
*/
-static struct dive *cur_dive, *cur_trip = NULL;
+static struct dive *cur_dive;
+static dive_trip_t *cur_trip = NULL;
static struct sample *cur_sample;
static struct {
int active;
@@ -175,40 +176,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 +205,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 +222,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) {
@@ -542,8 +513,10 @@ static void get_tripflag(char *buffer, void *_tf)
*tf = TF_NONE;
for (i = NO_TRIP; i < NUM_TRIPFLAGS; i++)
- if(! strcmp(buffer, tripflag_names[i]))
+ if(! strcmp(buffer, tripflag_names[i])) {
*tf = i;
+ break;
+ }
}
static void centibar(char *buffer, void *_pressure)
@@ -822,7 +795,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 +820,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 +830,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 +956,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;
@@ -1152,21 +1125,23 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf)
}
/* We're in the top-level trip xml. Try to convert whatever value to a trip value */
-static void try_to_fill_trip(struct dive **divep, const char *name, char *buf)
+static void try_to_fill_trip(dive_trip_t **dive_trip_p, const char *name, char *buf)
{
int len = strlen(name);
start_match("trip", name, buf);
- struct dive *dive = *divep;
+ dive_trip_t *dive_trip = *dive_trip_p;
- if (MATCH(".date", divedate, &dive->when))
+ if (MATCH(".date", divedate, &dive_trip->when))
return;
- if (MATCH(".time", divetime, &dive->when))
+ if (MATCH(".time", divetime, &dive_trip->when)) {
+ dive_trip->when_from_file = dive_trip->when;
return;
- if (MATCH(".location", utf8_string, &dive->location))
+ }
+ if (MATCH(".location", utf8_string, &dive_trip->location))
return;
- if (MATCH(".notes", utf8_string, &dive->notes))
+ if (MATCH(".notes", utf8_string, &dive_trip->notes))
return;
nonmatch("trip", name, buf);
@@ -1212,7 +1187,7 @@ static void trip_start(void)
{
if (cur_trip)
return;
- cur_trip = alloc_dive();
+ cur_trip = calloc(sizeof(dive_trip_t),1);
memset(&cur_tm, 0, sizeof(cur_tm));
}
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, "<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);
diff --git a/statistics.c b/statistics.c
index 5e1c8804a..d7757b914 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 + 1900) {
+ 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 <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;
+}
+
+