From dce08deb34939eaed349d315777214c3181c1a8d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 19 Sep 2012 17:35:52 -0700 Subject: [PATCH] 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; +} + +