From 8ce76f29ab6bc6ea0809c085b4f395373b3cb8d7 Mon Sep 17 00:00:00 2001
From: Ben Longbons <b.r.longbons@gmail.com>
Date: Tue, 9 Apr 2013 17:59:01 -0700
Subject: Also implement x32 support

---
 Makefile                     |   2 +-
 src/char/char.cpp            |  43 +++--
 src/common/extract.hpp       |   7 +
 src/common/operators.hpp     |  47 ++++++
 src/common/socket.cpp        |   8 +-
 src/common/socket.hpp        |   3 +-
 src/common/utils.cpp         |   9 +-
 src/common/utils.hpp         |  70 +++++++-
 src/ladmin/ladmin.cpp        | 143 ++++++++--------
 src/login/login.cpp          | 389 ++++++++++++++++++++-----------------------
 src/map/atcommand.cpp        |   3 +-
 src/map/chrif.cpp            |  14 +-
 src/map/clif.cpp             |  24 +--
 src/map/magic-stmt.cpp       |   6 +-
 src/map/map.cpp              |  11 +-
 src/map/map.hpp              |   6 +-
 src/map/npc.cpp              |  19 +--
 src/map/pc.cpp               |  23 +--
 src/map/pc.hpp               |   2 +-
 src/map/script.cpp           |  46 ++---
 src/map/skill.cpp            |  24 +--
 src/map/tmw.cpp              |   6 +-
 src/poison.hpp               |  11 +-
 src/tool/eathena-monitor.cpp |   7 +-
 24 files changed, 502 insertions(+), 421 deletions(-)
 create mode 100644 src/common/operators.hpp

diff --git a/Makefile b/Makefile
index a975c9b..56ab981 100644
--- a/Makefile
+++ b/Makefile
@@ -63,7 +63,7 @@ ${BUILD_DIR}/char/char: ${BUILD_DIR}/char/char.o ${BUILD_DIR}/char/inter.o ${BUI
 ${BUILD_DIR}/ladmin/ladmin: ${BUILD_DIR}/ladmin/ladmin.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o
 ${BUILD_DIR}/login/login: ${BUILD_DIR}/login/login.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o ${BUILD_DIR}/common/extract.o
 ${BUILD_DIR}/map/map: ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/tmw.o ${BUILD_DIR}/map/magic-interpreter-lexer.o ${BUILD_DIR}/map/magic-interpreter-parser.o ${BUILD_DIR}/map/magic-interpreter-base.o ${BUILD_DIR}/map/magic-expr.o ${BUILD_DIR}/map/magic-stmt.o ${BUILD_DIR}/map/magic.o ${BUILD_DIR}/map/map.o ${BUILD_DIR}/map/chrif.o ${BUILD_DIR}/map/clif.o ${BUILD_DIR}/map/pc.o ${BUILD_DIR}/map/npc.o ${BUILD_DIR}/map/chat.o ${BUILD_DIR}/map/path.o ${BUILD_DIR}/map/itemdb.o ${BUILD_DIR}/map/mob.o ${BUILD_DIR}/map/script.o ${BUILD_DIR}/map/storage.o ${BUILD_DIR}/map/skill.o ${BUILD_DIR}/map/skill-pools.o ${BUILD_DIR}/map/atcommand.o ${BUILD_DIR}/map/battle.o ${BUILD_DIR}/map/intif.o ${BUILD_DIR}/map/trade.o ${BUILD_DIR}/map/party.o ${BUILD_DIR}/common/core.o ${BUILD_DIR}/common/socket.o ${BUILD_DIR}/common/timer.o ${BUILD_DIR}/map/grfio.o ${BUILD_DIR}/common/db.o ${BUILD_DIR}/common/lock.o ${BUILD_DIR}/common/nullpo.o ${BUILD_DIR}/common/random.o ${BUILD_DIR}/common/md5calc.o ${BUILD_DIR}/common/utils.o ${BUILD_DIR}/common/cxxstdio.o ${BUILD_DIR}/common/extract.o
-${BUILD_DIR}/tool/eathena-monitor: ${BUILD_DIR}/tool/eathena-monitor.o
+${BUILD_DIR}/tool/eathena-monitor: ${BUILD_DIR}/tool/eathena-monitor.o ${BUILD_DIR}/common/utils.o
 
 # silence build warnings for code beyond my control
 ${BUILD_DIR}/map/magic-interpreter-lexer.o ${BUILD_DIR}/map/magic-interpreter-parser.o : override WARNINGS=
diff --git a/src/char/char.cpp b/src/char/char.cpp
index 5c501b4..e377ba2 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -98,7 +98,7 @@ struct char_session_data
     unsigned short packet_tmw_version;
     int found_char[9];
     char email[40];             // e-mail (default: a@a.com) by [Yor]
-    time_t connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+    TimeT connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
 };
 
 #define AUTH_FIFO_SIZE 256
@@ -108,7 +108,7 @@ struct
     int account_id, char_id, login_id1, login_id2, ip, char_pos, delflag,
         sex;
     unsigned short packet_tmw_version;
-    time_t connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+    TimeT connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
 } auth_fifo[AUTH_FIFO_SIZE];
 static
 int auth_fifo_pos = 0;
@@ -157,7 +157,7 @@ int online_gm_display_min_level = 20;  // minimum GM level to display 'GM' when
 static
 int *online_chars;              // same size of char_dat, and id value of current server (or -1)
 static
-time_t update_online;           // to update online files when we receiving information from a server (not less than 8 seconds)
+TimeT update_online;           // to update online files when we receiving information from a server (not less than 8 seconds)
 
 static
 pid_t pid = 0;                  // For forked DB writes
@@ -840,8 +840,6 @@ void create_online_files(void)
     FILE *fp;                   // for the txt file
     FILE *fp2;                  // for the html file
     char temp[256];             // to prepare what we must display
-    time_t time_server;         // for number of seconds
-    struct tm *datetime;        // variable for time in structure ->tm_mday, ->tm_sec, ...
     int id[char_num];
 
     // Get number of online players, id of each online players
@@ -933,9 +931,9 @@ void create_online_files(void)
         if (fp2 != NULL)
         {
             // get time
-            time(&time_server);    // get time in seconds since 1/1/1970
-            datetime = localtime(&time_server);    // convert seconds in structure
-            strftime(temp, sizeof(temp), "%d %b %Y %X", datetime);    // like SPRINTF, but only for date/time (05 dec 2003 15:12:52)
+#warning "Need to convert/check the PHP code"
+            timestamp_seconds_buffer timetemp;
+            stamp_time(timetemp);
             // write heading
             FPRINTF(fp2, "<HTML>\n");
             FPRINTF(fp2, "  <META http-equiv=\"Refresh\" content=\"%d\">\n", online_refresh_html); // update on client explorer every x seconds
@@ -945,8 +943,8 @@ void create_online_files(void)
             FPRINTF(fp2, "  </HEAD>\n");
             FPRINTF(fp2, "  <BODY>\n");
             FPRINTF(fp2, "    <H3>Online Players on %s (%s):</H3>\n",
-                     server_name, temp);
-            FPRINTF(fp, "Online Players on %s (%s):\n", server_name, temp);
+                     server_name, timetemp);
+            FPRINTF(fp, "Online Players on %s (%s):\n", server_name, timetemp);
             FPRINTF(fp, "\n");
 
             // If we display at least 1 player
@@ -1350,7 +1348,7 @@ void parse_tologin(int fd)
                             memcpy(sd->email, RFIFOP(fd, 7), 40);
                             if (e_mail_check(sd->email) == 0)
                                 strzcpy(sd->email, "a@a.com", 40); // default e-mail
-                            sd->connect_until_time = (time_t) RFIFOL(fd, 47);
+                            sd->connect_until_time = static_cast<time_t>(RFIFOL(fd, 47));
                             // send characters to player
                             mmo_char_send006b(i, sd);
                         }
@@ -1381,7 +1379,7 @@ void parse_tologin(int fd)
                             memcpy(sd->email, RFIFOP(fd, 6), 40);
                             if (e_mail_check(sd->email) == 0)
                                 strzcpy(sd->email, "a@a.com", 40); // default e-mail
-                            sd->connect_until_time = (time_t) RFIFOL(fd, 46);
+                            sd->connect_until_time = static_cast<time_t>(RFIFOL(fd, 46));
                             break;
                         }
                     }
@@ -1877,8 +1875,7 @@ void parse_frommap(int fd)
                         WFIFOW(fd, 2) = 18 + sizeof(struct mmo_charstatus);
                         WFIFOL(fd, 4) = RFIFOL(fd, 2);
                         WFIFOL(fd, 8) = auth_fifo[i].login_id2;
-                        WFIFOL(fd, 12) =
-                            (unsigned long) auth_fifo[i].connect_until_time;
+                        WFIFOL(fd, 12) = static_cast<time_t>(auth_fifo[i].connect_until_time);
                         char_dat[auth_fifo[i].char_pos].sex =
                             auth_fifo[i].sex;
                         WFIFOW(fd, 16) = auth_fifo[i].packet_tmw_version;
@@ -1927,10 +1924,12 @@ void parse_frommap(int fd)
                             break;
                         }
                 }
-                if (update_online < time(NULL))
-                {               // Time is done
-                    update_online = time(NULL) + 8;
-                    create_online_files(); // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection.
+                if (update_online < TimeT::now())
+                {
+                    // Time is done
+                    update_online = static_cast<time_t>(TimeT::now()) + 8;
+                    create_online_files();
+                    // only every 8 sec. (normally, 1 server send users every 5 sec.) Don't update every time, because that takes time, but only every 2 connection.
                     // it set to 8 sec because is more than 5 (sec) and if we have more than 1 map-server, informations can be received in shifted.
                 }
                 RFIFOSKIP(fd, 6 + i * 4);
@@ -1965,7 +1964,7 @@ void parse_frommap(int fd)
                 auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd, 10);
                 auth_fifo[auth_fifo_pos].delflag = 2;
                 auth_fifo[auth_fifo_pos].char_pos = 0;
-                auth_fifo[auth_fifo_pos].connect_until_time = 0;    // unlimited/unknown time by default (not display in map-server)
+                auth_fifo[auth_fifo_pos].connect_until_time = TimeT();    // unlimited/unknown time by default (not display in map-server)
                 auth_fifo[auth_fifo_pos].ip = RFIFOL(fd, 14);
                 auth_fifo_pos++;
                 WFIFOW(fd, 0) = 0x2b03;
@@ -1990,7 +1989,7 @@ void parse_frommap(int fd)
                 auth_fifo[auth_fifo_pos].login_id2 = RFIFOL(fd, 10);
                 auth_fifo[auth_fifo_pos].delflag = 0;
                 auth_fifo[auth_fifo_pos].sex = RFIFOB(fd, 44);
-                auth_fifo[auth_fifo_pos].connect_until_time = 0;    // unlimited/unknown time by default (not display in map-server)
+                auth_fifo[auth_fifo_pos].connect_until_time = TimeT();    // unlimited/unknown time by default (not display in map-server)
                 auth_fifo[auth_fifo_pos].ip = RFIFOL(fd, 45);
                 for (i = 0; i < char_num; i++)
                     if (char_dat[i].account_id == RFIFOL(fd, 2) &&
@@ -2475,7 +2474,7 @@ void parse_char(int fd)
                         CREATE(sd, struct char_session_data, 1);
                         session[fd]->session_data = sd;
                         memcpy(sd->email, "no mail", 40);  // put here a mail without '@' to refuse deletion if we don't receive the e-mail
-                        sd->connect_until_time = 0; // unknow or illimited (not displaying on map-server)
+                        sd->connect_until_time = TimeT(); // unknow or illimited (not displaying on map-server)
                     }
                     sd->account_id = RFIFOL(fd, 2);
                     sd->login_id1 = RFIFOL(fd, 6);
@@ -3299,7 +3298,7 @@ int do_init(int argc, char **argv)
 
     mmo_char_init();
 
-    update_online = time(NULL);
+    update_online = TimeT::now();
     create_online_files();     // update online players files at start of the server
 
     inter_init((argc > 2) ? argv[2] : inter_cfgName);  // inter server 初期化
diff --git a/src/common/extract.hpp b/src/common/extract.hpp
index 3198d5d..ae1a74b 100644
--- a/src/common/extract.hpp
+++ b/src/common/extract.hpp
@@ -25,6 +25,7 @@
 
 #include "const_array.hpp"
 #include "mmo.hpp"
+#include "utils.hpp"
 
 template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value>::type>
 bool extract(const_string str, T *iv)
@@ -58,6 +59,12 @@ bool extract(const_string str, T *iv)
     }
 }
 
+inline
+bool extract(const_string str, TimeT *tv)
+{
+    return extract(str, &tv->value);
+}
+
 // extra typename=void to workaround some duplicate overload rule
 template<class T, typename=typename std::enable_if<std::is_enum<T>::value>::type, typename=void>
 bool extract(const_string str, T *iv)
diff --git a/src/common/operators.hpp b/src/common/operators.hpp
new file mode 100644
index 0000000..3d44b81
--- /dev/null
+++ b/src/common/operators.hpp
@@ -0,0 +1,47 @@
+#ifndef OPERATORS_HPP
+#define OPERATORS_HPP
+
+namespace _operators
+{
+    class Comparable {};
+
+    template<class T>
+    bool operator == (T l, T r)
+    {
+        return l.value == r.value;
+    }
+
+    template<class T>
+    bool operator != (T l, T r)
+    {
+        return l.value != r.value;
+    }
+
+    template<class T>
+    bool operator < (T l, T r)
+    {
+        return l.value < r.value;
+    }
+
+    template<class T>
+    bool operator <= (T l, T r)
+    {
+        return l.value <= r.value;
+    }
+
+    template<class T>
+    bool operator > (T l, T r)
+    {
+        return l.value > r.value;
+    }
+
+    template<class T>
+    bool operator >= (T l, T r)
+    {
+        return l.value >= r.value;
+    }
+}
+
+using _operators::Comparable;
+
+#endif // OPERATORS_HPP
diff --git a/src/common/socket.cpp b/src/common/socket.cpp
index 96de47c..a8c1eee 100644
--- a/src/common/socket.cpp
+++ b/src/common/socket.cpp
@@ -161,7 +161,7 @@ void connect_client(int listen_fd)
     session[fd]->func_send = send_from_fifo;
     session[fd]->func_parse = default_func_parse;
     session[fd]->client_addr = client_address;
-    session[fd]->created = time(NULL);
+    session[fd]->created = TimeT::now();
     session[fd]->connected = 0;
 
     currentuse++;
@@ -213,7 +213,7 @@ int make_listen_port(uint16_t port)
     CREATE(session[fd], struct socket_data, 1);
 
     session[fd]->func_recv = connect_client;
-    session[fd]->created = time(NULL);
+    session[fd]->created = TimeT::now();
     session[fd]->connected = 1;
 
     currentuse++;
@@ -265,7 +265,7 @@ int make_connection(uint32_t ip, uint16_t port)
     session[fd]->func_recv = recv_to_fifo;
     session[fd]->func_send = send_from_fifo;
     session[fd]->func_parse = default_func_parse;
-    session[fd]->created = time(NULL);
+    session[fd]->created = TimeT::now();
     session[fd]->connected = 1;
 
     currentuse++;
@@ -377,7 +377,7 @@ void do_parsepacket(void)
         if (!session[i])
             continue;
         if (!session[i]->connected
-            && time(NULL) - session[i]->created > CONNECT_TIMEOUT)
+            && static_cast<time_t>(TimeT::now()) - static_cast<time_t>(session[i]->created) > CONNECT_TIMEOUT)
         {
             PRINTF("Session #%d timed out\n", i);
             session[i]->eof = 1;
diff --git a/src/common/socket.hpp b/src/common/socket.hpp
index c5db89c..d7c6b9c 100644
--- a/src/common/socket.hpp
+++ b/src/common/socket.hpp
@@ -7,6 +7,7 @@
 
 # include <cstdio>
 
+# include "utils.hpp"
 # include "timer.t.hpp"
 
 // Struct declaration
@@ -14,7 +15,7 @@
 struct socket_data
 {
     /// Checks whether a newly-connected socket actually does anything
-    time_t created;
+    TimeT created;
     bool connected;
 
     /// Flag needed since structure must be freed in a server-dependent manner
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
index 33a96b4..49dce34 100644
--- a/src/common/utils.cpp
+++ b/src/common/utils.cpp
@@ -125,16 +125,17 @@ bool split_key_value(const std::string& line, std::string *w1, std::string *w2)
 static_assert(sizeof(timestamp_seconds_buffer) == 20, "seconds buffer");
 static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer");
 
-void stamp_time(timestamp_seconds_buffer& out, time_t *t)
+void stamp_time(timestamp_seconds_buffer& out, TimeT *t)
 {
-    time_t when = t ? *t : time(NULL);
-    strftime(out, 20, "%Y-%m-%d %H:%M:%S", gmtime(&when));
+    struct tm when = t ? *t : TimeT::now();
+    strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when);
 }
 void stamp_time(timestamp_milliseconds_buffer& out)
 {
     struct timeval tv;
     gettimeofday(&tv, NULL);
-    strftime(out, 20, "%Y-%m-%d %H:%M:%S", gmtime(&tv.tv_sec));
+    struct tm when = TimeT(tv.tv_sec);
+    strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when);
     sprintf(out + 19, ".%03d", int(tv.tv_usec / 1000));
 }
 
diff --git a/src/common/utils.hpp b/src/common/utils.hpp
index a9d0244..0de8d81 100644
--- a/src/common/utils.hpp
+++ b/src/common/utils.hpp
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "const_array.hpp"
+#include "operators.hpp"
 #include "utils2.hpp"
 
 /*
@@ -62,11 +63,78 @@ void strzcpy(char *dest, const char *src, size_t n)
     }
 }
 
+// Exists in place of time_t, to give it a predictable printf-format.
+// (on x86 and amd64, time_t == long, but not on x32)
+static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t");
+struct TimeT : Comparable
+{
+    long long value;
+
+    // conversion
+    TimeT(time_t t=0) : value(t) {}
+    TimeT(struct tm t) : value(timegm(&t)) {}
+    operator time_t() { return value; }
+    operator struct tm() { return *gmtime(&value); }
+
+    explicit operator bool() { return value; }
+    bool operator !() { return !value; }
+
+    // prevent surprises
+    template<class T>
+    TimeT(T) = delete;
+    template<class T>
+    operator T() = delete;
+
+    static
+    TimeT now()
+    {
+        // poisoned, but this is still in header-land
+        return time(NULL);
+    }
+
+    bool error()
+    {
+        return value == -1;
+    }
+    bool okay()
+    {
+        return !error();
+    }
+};
+
+inline
+long long convert_for_printf(TimeT t)
+{
+    return t.value;
+}
+
+inline
+long long& convert_for_scanf(TimeT& t)
+{
+    return t.value;
+}
+
 typedef char timestamp_seconds_buffer[20];
 typedef char timestamp_milliseconds_buffer[24];
-void stamp_time(timestamp_seconds_buffer&, time_t *t=nullptr);
+void stamp_time(timestamp_seconds_buffer&, TimeT *t=nullptr);
 void stamp_time(timestamp_milliseconds_buffer&);
 
 void log_with_timestamp(FILE *out, const_string line);
 
+#define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS"
+static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
+        "timestamp size");
+#define WITH_TIMESTAMP(str) str TIMESTAMP_DUMMY
+//  str:            prefix: YYYY-MM-DD HH:MM:SS
+//  sizeof:        01234567890123456789012345678
+//  str + sizeof:                               ^
+//  -1:                     ^
+#define REPLACE_TIMESTAMP(str, t)                           \
+    stamp_time(                                             \
+            reinterpret_cast<timestamp_seconds_buffer *>(   \
+                str + sizeof(str)                           \
+            )[-1],                                          \
+            &t                                              \
+    )
+
 #endif //UTILS_HPP
diff --git a/src/ladmin/ladmin.cpp b/src/ladmin/ladmin.cpp
index e28286e..d6a30dd 100644
--- a/src/ladmin/ladmin.cpp
+++ b/src/ladmin/ladmin.cpp
@@ -1122,15 +1122,14 @@ int banaddaccount(const char *param)
 // Set the final date of a banishment of an account
 //-----------------------------------------------------------------------
 static
-int bansetaccountsub(const char *name, const char *date, const char *time)
+int bansetaccountsub(const char *name, const char *date, const char *time_)
 {
     int year, month, day, hour, minute, second;
-    time_t ban_until_time;      // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
-    struct tm *tmtime;
-
     year = month = day = hour = minute = second = 0;
-    ban_until_time = 0;
-    tmtime = localtime(&ban_until_time);   // initialize
+
+    // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
+    TimeT ban_until_time = TimeT();
+    struct tm tmtime = ban_until_time;   // initialize
 
     if (verify_accountname(name) == 0)
     {
@@ -1141,7 +1140,7 @@ int bansetaccountsub(const char *name, const char *date, const char *time)
         ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 &&
           sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 &&
           sscanf(date, "%d.%d.%d", &year, &month, &day) < 3) ||
-         sscanf(time, "%d:%d:%d", &hour, &minute, &second) < 3))
+         sscanf(time_, "%d:%d:%d", &hour, &minute, &second) < 3))
     {
         PRINTF("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n");
         PRINTF("You can imput 0 instead of if you use 'banset' command.\n");
@@ -1151,7 +1150,7 @@ int bansetaccountsub(const char *name, const char *date, const char *time)
 
     if (atoi(date) == 0)
     {
-        ban_until_time = 0;
+        ban_until_time = TimeT();
     }
     else
     {
@@ -1202,15 +1201,15 @@ int bansetaccountsub(const char *name, const char *date, const char *time)
             LADMIN_LOG("Invalid second for the time ('banset' or 'ban' command).\n");
             return 102;
         }
-        tmtime->tm_year = year;
-        tmtime->tm_mon = month;
-        tmtime->tm_mday = day;
-        tmtime->tm_hour = hour;
-        tmtime->tm_min = minute;
-        tmtime->tm_sec = second;
-        tmtime->tm_isdst = -1;  // -1: no winter/summer time modification
-        ban_until_time = timegm(tmtime);
-        if (ban_until_time == -1)
+        tmtime.tm_year = year;
+        tmtime.tm_mon = month;
+        tmtime.tm_mday = day;
+        tmtime.tm_hour = hour;
+        tmtime.tm_min = minute;
+        tmtime.tm_sec = second;
+        tmtime.tm_isdst = -1;  // -1: no winter/summer time modification
+        ban_until_time = tmtime;
+        if (ban_until_time.error())
         {
             PRINTF("Invalid date.\n");
             PRINTF("Please input a date and a time (format: yyyy/mm/dd hh:mm:ss).\n");
@@ -1224,7 +1223,7 @@ int bansetaccountsub(const char *name, const char *date, const char *time)
 
     WFIFOW(login_fd, 0) = 0x794a;
     memcpy(WFIFOP(login_fd, 2), name, 24);
-    WFIFOL(login_fd, 26) = (int) ban_until_time;
+    WFIFOL(login_fd, 26) = static_cast<time_t>(ban_until_time);
     WFIFOSET(login_fd, 30);
     bytes_to_read = 1;
 
@@ -1237,15 +1236,15 @@ int bansetaccountsub(const char *name, const char *date, const char *time)
 static
 int banaccount(const char *param)
 {
-    char name[1023], date[1023], time[1023];
+    char name[1023], date[1023], time_[1023];
 
     memset(name, '\0', sizeof(name));
     memset(date, '\0', sizeof(date));
-    memset(time, '\0', sizeof(time));
+    memset(time_, '\0', sizeof(time_));
 
-    if (sscanf(param, "%s %s \"%[^\"]\"", date, time, name) < 3 &&
-        sscanf(param, "%s %s '%[^']'", date, time, name) < 3 &&
-        sscanf(param, "%s %s %[^\r\n]", date, time, name) < 3)
+    if (sscanf(param, "%s %s \"%[^\"]\"", date, time_, name) < 3 &&
+        sscanf(param, "%s %s '%[^']'", date, time_, name) < 3 &&
+        sscanf(param, "%s %s %[^\r\n]", date, time_, name) < 3)
     {
         PRINTF("Please input an account name, a date and a hour.\n");
         PRINTF("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n");
@@ -1257,7 +1256,7 @@ int banaccount(const char *param)
         return 136;
     }
 
-    return bansetaccountsub(name, date, time);
+    return bansetaccountsub(name, date, time_);
 }
 
 //------------------------------------------------------------------------
@@ -1266,16 +1265,16 @@ int banaccount(const char *param)
 static
 int bansetaccount(const char *param)
 {
-    char name[1023], date[1023], time[1023];
+    char name[1023], date[1023], time_[1023];
 
     memset(name, '\0', sizeof(name));
     memset(date, '\0', sizeof(date));
-    memset(time, '\0', sizeof(time));
+    memset(time_, '\0', sizeof(time_));
 
-    if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 &&   // if date = 0, time can be void
-        sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 &&  // if date = 0, time can be void
-        sscanf(param, "%s %s %[^\r\n]", name, date, time) < 2)
-    {                           // if date = 0, time can be void
+    if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time_) < 2 &&   // if date = 0, time_ can be void
+        sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time_) < 2 &&  // if date = 0, time_ can be void
+        sscanf(param, "%s %s %[^\r\n]", name, date, time_) < 2)
+    {                           // if date = 0, time_ can be void
         PRINTF("Please input an account name, a date and a hour.\n");
         PRINTF("<example>: banset <account_name> yyyy/mm/dd [hh:mm:ss]\n");
         PRINTF("           banset <account_name> 0   (0 = un-banished)\n");
@@ -1286,10 +1285,10 @@ int bansetaccount(const char *param)
         return 136;
     }
 
-    if (time[0] == '\0')
-        strcpy(time, "23:59:59");
+    if (time_[0] == '\0')
+        strcpy(time_, "23:59:59");
 
-    return bansetaccountsub(name, date, time);
+    return bansetaccountsub(name, date, time_);
 }
 
 //-------------------------------------------------
@@ -2236,22 +2235,22 @@ int timeaddaccount(const char *param)
 static
 int timesetaccount(const char *param)
 {
-    char name[1023], date[1023], time[1023];
+    char name[1023], date[1023], time_[1023];
     int year, month, day, hour, minute, second;
-    time_t connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
-    struct tm *tmtime;
 
     memset(name, '\0', sizeof(name));
     memset(date, '\0', sizeof(date));
-    memset(time, '\0', sizeof(time));
+    memset(time_, '\0', sizeof(time_));
     year = month = day = hour = minute = second = 0;
-    connect_until_time = 0;
-    tmtime = localtime(&connect_until_time);   // initialize
 
-    if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time) < 2 &&   // if date = 0, time can be void
-        sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time) < 2 &&  // if date = 0, time can be void
-        sscanf(param, "%s %s %[^\r\n]", name, date, time) < 2)
-    {                           // if date = 0, time can be void
+    // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+    TimeT connect_until_time = TimeT();
+    struct tm tmtime = connect_until_time;   // initialize
+
+    if (sscanf(param, "\"%[^\"]\" %s %[^\r\n]", name, date, time_) < 2 &&   // if date = 0, time_ can be void
+        sscanf(param, "'%[^']' %s %[^\r\n]", name, date, time_) < 2 &&  // if date = 0, time_ can be void
+        sscanf(param, "%s %s %[^\r\n]", name, date, time_) < 2)
+    {                           // if date = 0, time_ can be void
         PRINTF("Please input an account name, a date and a hour.\n");
         PRINTF("<example>: timeset <account_name> yyyy/mm/dd [hh:mm:ss]\n");
         PRINTF("           timeset <account_name> 0   (0 = unlimited)\n");
@@ -2264,15 +2263,15 @@ int timesetaccount(const char *param)
         return 102;
     }
 
-    if (time[0] == '\0')
-        strcpy(time, "23:59:59");
+    if (time_[0] == '\0')
+        strcpy(time_, "23:59:59");
 
     if (atoi(date) != 0 &&
         ((sscanf(date, "%d/%d/%d", &year, &month, &day) < 3 &&
           sscanf(date, "%d-%d-%d", &year, &month, &day) < 3 &&
           sscanf(date, "%d.%d.%d", &year, &month, &day) < 3 &&
           sscanf(date, "%d'%d'%d", &year, &month, &day) < 3) ||
-         sscanf(time, "%d:%d:%d", &hour, &minute, &second) < 3))
+         sscanf(time_, "%d:%d:%d", &hour, &minute, &second) < 3))
     {
         PRINTF("Please input 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n");
         LADMIN_LOG("Invalid format for the date/time ('timeset' command).\n");
@@ -2281,7 +2280,7 @@ int timesetaccount(const char *param)
 
     if (atoi(date) == 0)
     {
-        connect_until_time = 0;
+        connect_until_time = TimeT();
     }
     else
     {
@@ -2332,15 +2331,15 @@ int timesetaccount(const char *param)
             LADMIN_LOG("Invalid second for the time ('timeset' command).\n");
             return 102;
         }
-        tmtime->tm_year = year;
-        tmtime->tm_mon = month;
-        tmtime->tm_mday = day;
-        tmtime->tm_hour = hour;
-        tmtime->tm_min = minute;
-        tmtime->tm_sec = second;
-        tmtime->tm_isdst = -1;  // -1: no winter/summer time modification
-        connect_until_time = timegm(tmtime);
-        if (connect_until_time == -1)
+        tmtime.tm_year = year;
+        tmtime.tm_mon = month;
+        tmtime.tm_mday = day;
+        tmtime.tm_hour = hour;
+        tmtime.tm_min = minute;
+        tmtime.tm_sec = second;
+        tmtime.tm_isdst = -1;  // -1: no winter/summer time modification
+        connect_until_time = tmtime;
+        if (connect_until_time.error())
         {
             PRINTF("Invalid date.\n");
             PRINTF("Please add 0 or a date and a time (format: 0 or yyyy/mm/dd hh:mm:ss).\n");
@@ -2353,7 +2352,7 @@ int timesetaccount(const char *param)
 
     WFIFOW(login_fd, 0) = 0x7948;
     memcpy(WFIFOP(login_fd, 2), name, 24);
-    WFIFOL(login_fd, 26) = (int) connect_until_time;
+    WFIFOL(login_fd, 26) = static_cast<time_t>(connect_until_time);
     WFIFOSET(login_fd, 30);
     bytes_to_read = 1;
 
@@ -3234,8 +3233,8 @@ void parse_fromlogin(int fd)
                 }
                 else
                 {
-                    time_t timestamp = RFIFOL(fd, 30);
-                    if (timestamp == 0)
+                    TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 30));
+                    if (!timestamp)
                     {
                         PRINTF("Validity Limit of the account [%s][id: %d] successfully changed to [unlimited].\n",
                                 static_cast<const char *>(RFIFOP(fd, 6)), RFIFOL(fd, 2));
@@ -3269,8 +3268,8 @@ void parse_fromlogin(int fd)
                 }
                 else
                 {
-                    time_t timestamp = RFIFOL(fd, 30);
-                    if (timestamp == 0)
+                    TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 30));
+                    if (!timestamp)
                     {
                         PRINTF("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n",
                                 static_cast<const char *>(RFIFOP(fd, 6)), RFIFOL(fd, 2));
@@ -3304,8 +3303,8 @@ void parse_fromlogin(int fd)
                 }
                 else
                 {
-                    time_t timestamp = RFIFOL(fd, 30);
-                    if (timestamp == 0)
+                    TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 30));
+                    if (!timestamp)
                     {
                         PRINTF("Final date of banishment of the account [%s][id: %d] successfully changed to [unbanished].\n",
                                 static_cast<const char *>(RFIFOP(fd, 6)), RFIFOL(fd, 2));
@@ -3357,8 +3356,8 @@ void parse_fromlogin(int fd)
                 }
                 else
                 {
-                    time_t timestamp = RFIFOL(fd, 30);
-                    if (timestamp == 0)
+                    TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 30));
+                    if (!timestamp)
                     {
                         PRINTF("Validity limit of the account [%s][id: %d] unchanged.\n",
                                 static_cast<const char *>(RFIFOP(fd, 6)), RFIFOL(fd, 2));
@@ -3390,8 +3389,8 @@ void parse_fromlogin(int fd)
                 {
                     char userid[24], error_message[20], lastlogin[24],
                         last_ip[16], email[40], memo[255];
-                    time_t ban_until_time;  // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
-                    time_t connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+                    TimeT ban_until_time;  // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
+                    TimeT connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
                     memcpy(userid, RFIFOP(fd, 7), sizeof(userid));
                     userid[sizeof(userid) - 1] = '\0';
                     memcpy(error_message, RFIFOP(fd, 40),
@@ -3403,8 +3402,8 @@ void parse_fromlogin(int fd)
                     last_ip[sizeof(last_ip) - 1] = '\0';
                     memcpy(email, RFIFOP(fd, 100), sizeof(email));
                     email[sizeof(email) - 1] = '\0';
-                    connect_until_time = (time_t) RFIFOL(fd, 140);
-                    ban_until_time = (time_t) RFIFOL(fd, 144);
+                    connect_until_time = static_cast<time_t>(RFIFOL(fd, 140));
+                    ban_until_time = static_cast<time_t>(RFIFOL(fd, 144));
                     memset(memo, '\0', sizeof(memo));
                     strncpy(memo, (const char *)RFIFOP(fd, 150), RFIFOW(fd, 148));
                     if (RFIFOL(fd, 2) == -1)
@@ -3480,7 +3479,7 @@ void parse_fromlogin(int fd)
                                      RFIFOL(fd, 36));
                                 break;
                         }
-                        if (ban_until_time == 0)
+                        if (!ban_until_time)
                         {
                             PRINTF(" Banishment: not banished.\n");
                         }
@@ -3498,7 +3497,7 @@ void parse_fromlogin(int fd)
                                     RFIFOL(fd, 32));
                         PRINTF(" Last connection at: %s (ip: %s)\n",
                                 lastlogin, last_ip);
-                        if (connect_until_time == 0)
+                        if (!connect_until_time)
                         {
                             PRINTF(" Validity limit: unlimited.\n");
                         }
@@ -3668,8 +3667,6 @@ int do_init(int argc, char **argv)
     LADMIN_LOG("");
     LADMIN_LOG("Configuration file readed.\n");
 
-    srand(time(NULL));
-
     set_defaultparse(parse_fromlogin);
 
     Iprintf("EAthena login-server administration tool.\n");
diff --git a/src/login/login.cpp b/src/login/login.cpp
index ccdaa97..583cff9 100644
--- a/src/login/login.cpp
+++ b/src/login/login.cpp
@@ -28,8 +28,6 @@
 
 #include "../poison.hpp"
 
-static_assert(std::is_same<time_t, long>::value, "much code assumes time_t is a long (sorry)");
-
 constexpr int MAX_SERVERS = 30;
 
 #define LOGIN_CONF_NAME "conf/login_athena.conf"
@@ -175,8 +173,8 @@ struct auth_dat
     int state;                 // packet 0x006a value + 1 (0: compte OK)
     char email[40];             // e-mail (by default: a@a.com)
     char error_message[20];     // Message of error code #6 = Your are Prohibited to log in until %s (packet 0x006a)
-    time_t ban_until_time;      // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
-    time_t connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+    TimeT ban_until_time;      // # of seconds 1/1/1970 (timestamp): ban time limit of the account (0 = no ban)
+    TimeT connect_until_time;  // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
     char last_ip[16];           // save of last IP of connection
     char memo[255];             // a memo field
     int account_reg2_num;
@@ -504,10 +502,10 @@ std::string mmo_auth_tostr(struct auth_dat *p)
             "%d\t"
             "%s\t"
             "%s\t"
-            "%ld\t"
+            "%lld\t"
             "%s\t"
             "%s\t"
-            "%ld\t",
+            "%lld\t",
             p->account_id,
             p->userid,
             p->pass,
@@ -842,8 +840,6 @@ void check_GM_file(TimerData *, tick_t)
 static
 int mmo_auth_new(struct mmo_account *account, char sex, const char *email)
 {
-    time_t timestamp, timestamp_temp;
-    struct tm *tmtime;
     int i = auth_num;
 
     if (auth_num >= auth_max)
@@ -880,20 +876,17 @@ int mmo_auth_new(struct mmo_account *account, char sex, const char *email)
 
     strncpy(auth_dat[i].error_message, "-", 20);
 
-    auth_dat[i].ban_until_time = 0;
+    auth_dat[i].ban_until_time = TimeT();
 
     if (start_limited_time < 0)
-        auth_dat[i].connect_until_time = 0; // unlimited
+        auth_dat[i].connect_until_time = TimeT(); // unlimited
     else
-    {                           // limited time
-        timestamp = time(NULL) + start_limited_time;
-        // double conversion to be sure that it is possible
-        tmtime = gmtime(&timestamp);
-        timestamp_temp = mktime(tmtime);
-        if (timestamp_temp != -1 && (timestamp_temp + 3600) >= timestamp)   // check possible value and overflow (and avoid summer/winter hour)
-            auth_dat[i].connect_until_time = timestamp_temp;
-        else
-            auth_dat[i].connect_until_time = 0; // unlimited
+    {
+        // limited time
+        TimeT timestamp = static_cast<time_t>(TimeT::now()) + start_limited_time;
+        // there used to be a silly overflow check here, but it wasn't
+        // correct, and we don't support time-limited accounts.
+        auth_dat[i].connect_until_time = timestamp;
     }
 
     strncpy(auth_dat[i].last_ip, "-", 16);
@@ -990,12 +983,12 @@ int mmo_auth(struct mmo_account *account, int fd)
             }
         }
 
-        if (auth_dat[i].ban_until_time != 0)
+        if (auth_dat[i].ban_until_time)
         {
             // if account is banned
             timestamp_seconds_buffer tmpstr;
             stamp_time(tmpstr, &auth_dat[i].ban_until_time);
-            if (auth_dat[i].ban_until_time > time(NULL))
+            if (auth_dat[i].ban_until_time > TimeT::now())
             {
                 // always banned
                 LOGIN_LOG("Connection refused (account: %s, banned until %s, ip: %s)\n",
@@ -1007,12 +1000,12 @@ int mmo_auth(struct mmo_account *account, int fd)
                 // ban is finished
                 LOGIN_LOG("End of ban (account: %s, previously banned until %s -> not more banned, ip: %s)\n",
                      account->userid, tmpstr, ip);
-                auth_dat[i].ban_until_time = 0; // reset the ban time
+                auth_dat[i].ban_until_time = TimeT(); // reset the ban time
             }
         }
 
-        if (auth_dat[i].connect_until_time != 0
-            && auth_dat[i].connect_until_time < time(NULL))
+        if (auth_dat[i].connect_until_time
+            && auth_dat[i].connect_until_time < TimeT::now())
         {
             LOGIN_LOG("Connection refused (account: %s, expired ID, ip: %s)\n",
                  account->userid, ip);
@@ -1171,9 +1164,7 @@ void parse_fromchar(int fd)
                                     WFIFOB(fd, 6) = 0;
                                     memcpy(WFIFOP(fd, 7), auth_dat[k].email,
                                             40);
-                                    WFIFOL(fd, 47) =
-                                        (unsigned long)
-                                        auth_dat[k].connect_until_time;
+                                    WFIFOL(fd, 47) = static_cast<time_t>(auth_dat[k].connect_until_time);
                                     WFIFOSET(fd, 51);
                                     break;
                                 }
@@ -1258,8 +1249,7 @@ void parse_fromchar(int fd)
                         WFIFOW(fd, 0) = 0x2717;
                         WFIFOL(fd, 2) = RFIFOL(fd, 2);
                         memcpy(WFIFOP(fd, 6), auth_dat[i].email, 40);
-                        WFIFOL(fd, 46) =
-                            (unsigned long) auth_dat[i].connect_until_time;
+                        WFIFOL(fd, 46) = static_cast<time_t>(auth_dat[i].connect_until_time);
                         WFIFOSET(fd, 50);
                         break;
                     }
@@ -1459,50 +1449,43 @@ void parse_fromchar(int fd)
                     {
                         if (auth_dat[i].account_id == acc)
                         {
-                            time_t timestamp;
-                            struct tm *tmtime;
-                            if (auth_dat[i].ban_until_time == 0
-                                || auth_dat[i].ban_until_time < time(NULL))
-                                timestamp = time(NULL);
+                            TimeT now = TimeT::now();
+                            TimeT timestamp;
+                            if (!auth_dat[i].ban_until_time
+                                || auth_dat[i].ban_until_time < now)
+                                timestamp = now;
                             else
                                 timestamp = auth_dat[i].ban_until_time;
-                            tmtime = gmtime(&timestamp);
-                            tmtime->tm_year =
-                                tmtime->tm_year + (short) RFIFOW(fd, 6);
-                            tmtime->tm_mon =
-                                tmtime->tm_mon + (short) RFIFOW(fd, 8);
-                            tmtime->tm_mday =
-                                tmtime->tm_mday + (short) RFIFOW(fd, 10);
-                            tmtime->tm_hour =
-                                tmtime->tm_hour + (short) RFIFOW(fd, 12);
-                            tmtime->tm_min =
-                                tmtime->tm_min + (short) RFIFOW(fd, 14);
-                            tmtime->tm_sec =
-                                tmtime->tm_sec + (short) RFIFOW(fd, 16);
-                            timestamp = timegm(tmtime);
-                            if (timestamp != -1)
+                            struct tm tmtime = timestamp;
+                            tmtime.tm_year += (short) RFIFOW(fd, 6);
+                            tmtime.tm_mon += (short) RFIFOW(fd, 8);
+                            tmtime.tm_mday += (short) RFIFOW(fd, 10);
+                            tmtime.tm_hour += (short) RFIFOW(fd, 12);
+                            tmtime.tm_min += (short) RFIFOW(fd, 14);
+                            tmtime.tm_sec += (short) RFIFOW(fd, 16);
+                            timestamp = tmtime;
+                            if (timestamp.okay())
                             {
-                                if (timestamp <= time(NULL))
-                                    timestamp = 0;
+                                if (timestamp <= now)
+                                    timestamp = TimeT();
                                 if (auth_dat[i].ban_until_time != timestamp)
                                 {
-                                    if (timestamp != 0)
+                                    if (timestamp)
                                     {
                                         unsigned char buf[16];
                                         timestamp_seconds_buffer tmpstr;
-                                        stamp_time(tmpstr, &timestamp);
-                                        LOGIN_LOG("Char-server '%s': Ban request (account: %d, new final date of banishment: %ld (%s), ip: %s).\n",
-                                             server[id].name, acc,
-                                             timestamp,
-                                             timestamp
-                                             ? tmpstr
-                                             : "no banishment",
-                                             ip);
+                                        if (timestamp)
+                                            stamp_time(tmpstr, &timestamp);
+                                        LOGIN_LOG("Char-server '%s': Ban request (account: %d, new final date of banishment: %lld (%s), ip: %s).\n",
+                                                server[id].name, acc,
+                                                timestamp,
+                                                tmpstr,
+                                                ip);
                                         WBUFW(buf, 0) = 0x2731;
                                         WBUFL(buf, 2) =
                                             auth_dat[i].account_id;
                                         WBUFB(buf, 6) = 1; // 0: change of statut, 1: ban
-                                        WBUFL(buf, 7) = timestamp; // status or final date of a banishment
+                                        WBUFL(buf, 7) = static_cast<time_t>(timestamp); // status or final date of a banishment
                                         charif_sendallwos(-1, buf, 11);
                                         for (j = 0; j < AUTH_FIFO_SIZE; j++)
                                             if (auth_fifo[j].account_id ==
@@ -1642,9 +1625,9 @@ void parse_fromchar(int fd)
                     {
                         if (auth_dat[i].account_id == acc)
                         {
-                            if (auth_dat[i].ban_until_time != 0)
+                            if (auth_dat[i].ban_until_time)
                             {
-                                auth_dat[i].ban_until_time = 0;
+                                auth_dat[i].ban_until_time = TimeT();
                                 LOGIN_LOG("Char-server '%s': UnBan request (account: %d, ip: %s).\n",
                                      server[id].name, acc, ip);
                             }
@@ -1876,7 +1859,7 @@ void parse_admin(int fd)
                                     24);
                             WFIFOB(fd, len + 29) = auth_dat[j].sex;
                             WFIFOL(fd, len + 30) = auth_dat[j].logincount;
-                            if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time != 0)  // if no state and banished
+                            if (auth_dat[j].state == 0 && auth_dat[j].ban_until_time)  // if no state and banished
                                 WFIFOL(fd, len + 34) = 7;  // 6 = Your are Prohibited to log in until %s
                             else
                                 WFIFOL(fd, len + 34) = auth_dat[j].state;
@@ -2466,14 +2449,14 @@ void parse_admin(int fd)
                     memcpy(WFIFOP(fd, 6), auth_dat[i].userid, 24);
                     WFIFOL(fd, 2) = auth_dat[i].account_id;
                     LOGIN_LOG("'ladmin': Request (by the name) of an account id (account: %s, id: %d, ip: %s)\n",
-                         auth_dat[i].userid, auth_dat[i].account_id,
-                         ip);
+                            auth_dat[i].userid, auth_dat[i].account_id,
+                            ip);
                 }
                 else
                 {
                     memcpy(WFIFOP(fd, 6), account_name, 24);
                     LOGIN_LOG("'ladmin': ID request (by the name) of an unknown account (account: %s, ip: %s)\n",
-                         account_name, ip);
+                            account_name, ip);
                 }
                 WFIFOSET(fd, 30);
                 RFIFOSKIP(fd, 26);
@@ -2513,28 +2496,32 @@ void parse_admin(int fd)
                     WFIFOL(fd, 2) = -1;
                     strzcpy(account_name, static_cast<const char *>(RFIFOP(fd, 2)), 24);
                     remove_control_chars(account_name);
-                    time_t timestamp = static_cast<time_t>(RFIFOL(fd, 26));
-                    timestamp_seconds_buffer tmpstr;
-                    stamp_time(tmpstr, &timestamp);
+                    TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 26));
+                    timestamp_seconds_buffer tmpstr = "unlimited";
+                    if (timestamp)
+                        stamp_time(tmpstr, &timestamp);
                     i = search_account_index(account_name);
                     if (i != -1)
                     {
                         memcpy(WFIFOP(fd, 6), auth_dat[i].userid, 24);
-                        LOGIN_LOG("'ladmin': Change of a validity limit (account: %s, new validity: %ld (%s), ip: %s)\n",
-                             auth_dat[i].userid, timestamp,
-                             timestamp ? tmpstr : "unlimited",
-                             ip);
+                        LOGIN_LOG("'ladmin': Change of a validity limit (account: %s, new validity: %lld (%s), ip: %s)\n",
+                                auth_dat[i].userid,
+                                timestamp,
+                                tmpstr,
+                                ip);
                         auth_dat[i].connect_until_time = timestamp;
                         WFIFOL(fd, 2) = auth_dat[i].account_id;
                     }
                     else
                     {
                         memcpy(WFIFOP(fd, 6), account_name, 24);
-                        LOGIN_LOG("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %ld (%s), ip: %s)\n", account_name, timestamp,
-                             timestamp ? tmpstr : "unlimited",
-                             ip);
+                        LOGIN_LOG("'ladmin': Attempt to change the validity limit of an unknown account (account: %s, received validity: %lld (%s), ip: %s)\n",
+                                account_name,
+                                timestamp,
+                                tmpstr,
+                                ip);
                     }
-                    WFIFOL(fd, 30) = timestamp;
+                    WFIFOL(fd, 30) = static_cast<time_t>(timestamp);
                 }
                 WFIFOSET(fd, 34);
                 RFIFOSKIP(fd, 30);
@@ -2548,29 +2535,30 @@ void parse_admin(int fd)
                     WFIFOL(fd, 2) = -1;
                     strzcpy(account_name, static_cast<const char *>(RFIFOP(fd, 2)), 24);
                     remove_control_chars(account_name);
-                    time_t timestamp = static_cast<time_t>(RFIFOL(fd, 26));
-                    if (timestamp <= time(NULL))
-                        timestamp = 0;
-                    timestamp_seconds_buffer tmpstr;
-                    stamp_time(tmpstr, &timestamp);
+                    TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 26));
+                    if (timestamp <= TimeT::now())
+                        timestamp = TimeT();
+                    timestamp_seconds_buffer tmpstr = "no banishment";
+                    if (timestamp)
+                        stamp_time(tmpstr, &timestamp);
                     i = search_account_index(account_name);
                     if (i != -1)
                     {
                         memcpy(WFIFOP(fd, 6), auth_dat[i].userid, 24);
                         WFIFOL(fd, 2) = auth_dat[i].account_id;
-                        LOGIN_LOG("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %ld (%s), ip: %s)\n",
-                             auth_dat[i].userid, timestamp,
-                             timestamp ? tmpstr : "no banishment",
-                             ip);
+                        LOGIN_LOG("'ladmin': Change of the final date of a banishment (account: %s, new final date of banishment: %lld (%s), ip: %s)\n",
+                                auth_dat[i].userid, timestamp,
+                                tmpstr,
+                                ip);
                         if (auth_dat[i].ban_until_time != timestamp)
                         {
-                            if (timestamp != 0)
+                            if (timestamp)
                             {
                                 unsigned char buf[16];
                                 WBUFW(buf, 0) = 0x2731;
                                 WBUFL(buf, 2) = auth_dat[i].account_id;
                                 WBUFB(buf, 6) = 1; // 0: change of statut, 1: ban
-                                WBUFL(buf, 7) = timestamp; // status or final date of a banishment
+                                WBUFL(buf, 7) = static_cast<time_t>(timestamp); // status or final date of a banishment
                                 charif_sendallwos(-1, buf, 11);
                                 for (j = 0; j < AUTH_FIFO_SIZE; j++)
                                     if (auth_fifo[j].account_id ==
@@ -2583,12 +2571,12 @@ void parse_admin(int fd)
                     else
                     {
                         memcpy(WFIFOP(fd, 6), account_name, 24);
-                        LOGIN_LOG("'ladmin': Attempt to change the final date of a banishment of an unknown account (account: %s, received final date of banishment: %ld (%s), ip: %s)\n",
-                             account_name, timestamp,
-                             timestamp ? tmpstr : "no banishment",
-                             ip);
+                        LOGIN_LOG("'ladmin': Attempt to change the final date of a banishment of an unknown account (account: %s, received final date of banishment: %lld (%s), ip: %s)\n",
+                                account_name, timestamp,
+                                tmpstr,
+                                ip);
                     }
-                    WFIFOL(fd, 30) = timestamp;
+                    WFIFOL(fd, 30) = static_cast<time_t>(timestamp);
                 }
                 WFIFOSET(fd, 34);
                 RFIFOSKIP(fd, 30);
@@ -2598,8 +2586,6 @@ void parse_admin(int fd)
                 if (RFIFOREST(fd) < 38)
                     return;
                 {
-                    time_t timestamp;
-                    struct tm *tmtime;
                     WFIFOW(fd, 0) = 0x794d;
                     WFIFOL(fd, 2) = -1;
                     strzcpy(account_name, static_cast<const char *>(RFIFOP(fd, 2)), 24);
@@ -2609,51 +2595,45 @@ void parse_admin(int fd)
                     {
                         WFIFOL(fd, 2) = auth_dat[i].account_id;
                         memcpy(WFIFOP(fd, 6), auth_dat[i].userid, 24);
-                        if (auth_dat[i].ban_until_time == 0
-                            || auth_dat[i].ban_until_time < time(NULL))
-                            timestamp = time(NULL);
+                        TimeT timestamp;
+                        TimeT now = TimeT::now();
+                        if (!auth_dat[i].ban_until_time
+                            || auth_dat[i].ban_until_time < now)
+                            timestamp = now;
                         else
                             timestamp = auth_dat[i].ban_until_time;
-                        tmtime = gmtime(&timestamp);
-                        tmtime->tm_year =
-                            tmtime->tm_year + (short) RFIFOW(fd, 26);
-                        tmtime->tm_mon =
-                            tmtime->tm_mon + (short) RFIFOW(fd, 28);
-                        tmtime->tm_mday =
-                            tmtime->tm_mday + (short) RFIFOW(fd, 30);
-                        tmtime->tm_hour =
-                            tmtime->tm_hour + (short) RFIFOW(fd, 32);
-                        tmtime->tm_min =
-                            tmtime->tm_min + (short) RFIFOW(fd, 34);
-                        tmtime->tm_sec =
-                            tmtime->tm_sec + (short) RFIFOW(fd, 36);
-                        timestamp = mktime(tmtime);
-                        if (timestamp != -1)
+                        struct tm tmtime = timestamp;
+                        tmtime.tm_year += (short) RFIFOW(fd, 26);
+                        tmtime.tm_mon += (short) RFIFOW(fd, 28);
+                        tmtime.tm_mday += (short) RFIFOW(fd, 30);
+                        tmtime.tm_hour += (short) RFIFOW(fd, 32);
+                        tmtime.tm_min += (short) RFIFOW(fd, 34);
+                        tmtime.tm_sec += (short) RFIFOW(fd, 36);
+                        timestamp = tmtime;
+                        if (timestamp.okay())
                         {
-                            if (timestamp <= time(NULL))
-                                timestamp = 0;
-                            timestamp_seconds_buffer tmpstr;
-                            stamp_time(tmpstr, &timestamp);
-                            LOGIN_LOG("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %ld (%s), ip: %s)\n",
-                                 auth_dat[i].userid,
-                                 (short) RFIFOW(fd, 26), (short) RFIFOW(fd,
-                                                                          28),
-                                 (short) RFIFOW(fd, 30), (short) RFIFOW(fd,
-                                                                          32),
-                                 (short) RFIFOW(fd, 34), (short) RFIFOW(fd,
-                                                                          36),
-                                 timestamp,
-                                 timestamp ? tmpstr : "no banishment",
-                                 ip);
+                            if (timestamp <= now)
+                                timestamp = TimeT();
+                            timestamp_seconds_buffer tmpstr = "no banishment";
+                            if (timestamp)
+                                stamp_time(tmpstr, &timestamp);
+                            LOGIN_LOG("'ladmin': Adjustment of a final date of a banishment (account: %s, (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %lld (%s), ip: %s)\n",
+                                    auth_dat[i].userid,
+                                    (short) RFIFOW(fd, 26), (short) RFIFOW(fd, 28),
+                                    (short) RFIFOW(fd, 30), (short) RFIFOW(fd, 32),
+                                    (short) RFIFOW(fd, 34), (short) RFIFOW(fd, 36),
+                                    timestamp,
+                                    tmpstr,
+                                    ip);
                             if (auth_dat[i].ban_until_time != timestamp)
                             {
-                                if (timestamp != 0)
+                                if (timestamp)
                                 {
                                     unsigned char buf[16];
                                     WBUFW(buf, 0) = 0x2731;
                                     WBUFL(buf, 2) = auth_dat[i].account_id;
                                     WBUFB(buf, 6) = 1; // 0: change of statut, 1: ban
-                                    WBUFL(buf, 7) = timestamp; // status or final date of a banishment
+                                    WBUFL(buf, 7) = static_cast<time_t>(timestamp); // status or final date of a banishment
                                     charif_sendallwos(-1, buf, 11);
                                     for (j = 0; j < AUTH_FIFO_SIZE; j++)
                                         if (auth_fifo[j].account_id ==
@@ -2665,30 +2645,25 @@ void parse_admin(int fd)
                         }
                         else
                         {
-                            timestamp_seconds_buffer tmpstr;
-                            stamp_time(tmpstr, &auth_dat[i].ban_until_time);
-                            LOGIN_LOG("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %ld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n",
-                                 auth_dat[i].userid,
-                                 auth_dat[i].ban_until_time,
-                                 auth_dat[i].ban_until_time
-                                 ? tmpstr
-                                 : "no banishment",
-                                 (short) RFIFOW(fd, 26), (short) RFIFOW(fd,
-                                                                          28),
-                                 (short) RFIFOW(fd, 30), (short) RFIFOW(fd,
-                                                                          32),
-                                 (short) RFIFOW(fd, 34), (short) RFIFOW(fd,
-                                                                          36),
-                                 ip);
+                            timestamp_seconds_buffer tmpstr = "no banishment";
+                            if (auth_dat[i].ban_until_time)
+                                stamp_time(tmpstr, &auth_dat[i].ban_until_time);
+                            LOGIN_LOG("'ladmin': Impossible to adjust the final date of a banishment (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n",
+                                    auth_dat[i].userid,
+                                    auth_dat[i].ban_until_time,
+                                    tmpstr,
+                                    (short) RFIFOW(fd, 26), (short) RFIFOW(fd, 28),
+                                    (short) RFIFOW(fd, 30), (short) RFIFOW(fd, 32),
+                                    (short) RFIFOW(fd, 34), (short) RFIFOW(fd, 36),
+                                    ip);
                         }
-                        WFIFOL(fd, 30) =
-                            (unsigned long) auth_dat[i].ban_until_time;
+                        WFIFOL(fd, 30) = static_cast<time_t>(auth_dat[i].ban_until_time);
                     }
                     else
                     {
                         memcpy(WFIFOP(fd, 6), account_name, 24);
                         LOGIN_LOG("'ladmin': Attempt to adjust the final date of a banishment of an unknown account (account: %s, ip: %s)\n",
-                             account_name, ip);
+                                account_name, ip);
                         WFIFOL(fd, 30) = 0;
                     }
                 }
@@ -2748,8 +2723,6 @@ void parse_admin(int fd)
                 if (RFIFOREST(fd) < 38)
                     return;
                 {
-                    time_t timestamp;
-                    struct tm *tmtime;
                     WFIFOW(fd, 0) = 0x7951;
                     WFIFOL(fd, 2) = -1;
                     strzcpy(account_name, static_cast<const char *>(RFIFOP(fd, 2)), 24);
@@ -2759,8 +2732,7 @@ void parse_admin(int fd)
                     {
                         WFIFOL(fd, 2) = auth_dat[i].account_id;
                         memcpy(WFIFOP(fd, 6), auth_dat[i].userid, 24);
-                        timestamp = auth_dat[i].connect_until_time;
-                        if (add_to_unlimited_account == 0 && timestamp == 0)
+                        if (add_to_unlimited_account == 0 && !auth_dat[i].connect_until_time)
                         {
                             LOGIN_LOG("'ladmin': Attempt to adjust the validity limit of an unlimited account (account: %s, ip: %s)\n",
                                  auth_dat[i].userid, ip);
@@ -2768,61 +2740,58 @@ void parse_admin(int fd)
                         }
                         else
                         {
-                            if (timestamp == 0 || timestamp < time(NULL))
-                                timestamp = time(NULL);
-                            tmtime = gmtime(&timestamp);
-                            tmtime->tm_year =
-                                tmtime->tm_year + (short) RFIFOW(fd, 26);
-                            tmtime->tm_mon =
-                                tmtime->tm_mon + (short) RFIFOW(fd, 28);
-                            tmtime->tm_mday =
-                                tmtime->tm_mday + (short) RFIFOW(fd, 30);
-                            tmtime->tm_hour =
-                                tmtime->tm_hour + (short) RFIFOW(fd, 32);
-                            tmtime->tm_min =
-                                tmtime->tm_min + (short) RFIFOW(fd, 34);
-                            tmtime->tm_sec =
-                                tmtime->tm_sec + (short) RFIFOW(fd, 36);
-                            timestamp = mktime(tmtime);
-                            if (timestamp != -1)
+                            TimeT now = TimeT::now();
+                            TimeT timestamp;
+                            if (!timestamp || timestamp < now)
+                                timestamp = now;
+                            struct tm tmtime = timestamp;
+                            tmtime.tm_year += (short) RFIFOW(fd, 26);
+                            tmtime.tm_mon += (short) RFIFOW(fd, 28);
+                            tmtime.tm_mday += (short) RFIFOW(fd, 30);
+                            tmtime.tm_hour += (short) RFIFOW(fd, 32);
+                            tmtime.tm_min += (short) RFIFOW(fd, 34);
+                            tmtime.tm_sec += (short) RFIFOW(fd, 36);
+                            timestamp = tmtime;
+                            if (timestamp.okay())
                             {
-                                timestamp_seconds_buffer tmpstr;
-                                timestamp_seconds_buffer tmpstr2;
-                                stamp_time(tmpstr, &auth_dat[i].connect_until_time);
-                                stamp_time(tmpstr2, &timestamp);
-                                LOGIN_LOG("'ladmin': Adjustment of a validity limit (account: %s, %ld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %ld (%s), ip: %s)\n",
-                                     auth_dat[i].userid,
-                                     auth_dat[i].connect_until_time,
-                                     (auth_dat[i].connect_until_time ==
-                                      0 ? "unlimited" : tmpstr),
-                                     (short) RFIFOW(fd, 26),
-                                     (short) RFIFOW(fd, 28),
-                                     (short) RFIFOW(fd, 30),
-                                     (short) RFIFOW(fd, 32),
-                                     (short) RFIFOW(fd, 34),
-                                     (short) RFIFOW(fd, 36), timestamp,
-                                     timestamp ? tmpstr2 : "unlimited",
-                                     ip);
+                                timestamp_seconds_buffer tmpstr = "unlimited";
+                                timestamp_seconds_buffer tmpstr2 = "unlimited";
+                                if (auth_dat[i].connect_until_time)
+                                    stamp_time(tmpstr, &auth_dat[i].connect_until_time);
+                                if (timestamp)
+                                    stamp_time(tmpstr2, &timestamp);
+                                LOGIN_LOG("'ladmin': Adjustment of a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> new validity: %lld (%s), ip: %s)\n",
+                                        auth_dat[i].userid,
+                                        auth_dat[i].connect_until_time,
+                                        tmpstr,
+                                        (short) RFIFOW(fd, 26),
+                                        (short) RFIFOW(fd, 28),
+                                        (short) RFIFOW(fd, 30),
+                                        (short) RFIFOW(fd, 32),
+                                        (short) RFIFOW(fd, 34),
+                                        (short) RFIFOW(fd, 36),
+                                        timestamp,
+                                        tmpstr2,
+                                        ip);
                                 auth_dat[i].connect_until_time = timestamp;
-                                WFIFOL(fd, 30) =
-                                    (unsigned long)
-                                    auth_dat[i].connect_until_time;
+                                WFIFOL(fd, 30) = static_cast<time_t>(timestamp);
                             }
                             else
                             {
-                                timestamp_seconds_buffer tmpstr;
-                                stamp_time(tmpstr, &auth_dat[i].connect_until_time);
-                                LOGIN_LOG("'ladmin': Impossible to adjust a validity limit (account: %s, %ld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n",
-                                     auth_dat[i].userid,
-                                     auth_dat[i].connect_until_time,
-                                     (auth_dat[i].connect_until_time ==
-                                      0 ? "unlimited" : tmpstr),
-                                     (short) RFIFOW(fd, 26),
-                                     (short) RFIFOW(fd, 28),
-                                     (short) RFIFOW(fd, 30),
-                                     (short) RFIFOW(fd, 32),
-                                     (short) RFIFOW(fd, 34),
-                                     (short) RFIFOW(fd, 36), ip);
+                                timestamp_seconds_buffer tmpstr = "unlimited";
+                                if (auth_dat[i].connect_until_time)
+                                    stamp_time(tmpstr, &auth_dat[i].connect_until_time);
+                                LOGIN_LOG("'ladmin': Impossible to adjust a validity limit (account: %s, %lld (%s) + (%+d y %+d m %+d d %+d h %+d mn %+d s) -> ???, ip: %s)\n",
+                                        auth_dat[i].userid,
+                                        auth_dat[i].connect_until_time,
+                                        tmpstr,
+                                        (short) RFIFOW(fd, 26),
+                                        (short) RFIFOW(fd, 28),
+                                        (short) RFIFOW(fd, 30),
+                                        (short) RFIFOW(fd, 32),
+                                        (short) RFIFOW(fd, 34),
+                                        (short) RFIFOW(fd, 36),
+                                        ip);
                                 WFIFOL(fd, 30) = 0;
                             }
                         }
@@ -2860,10 +2829,8 @@ void parse_admin(int fd)
                     memcpy(WFIFOP(fd, 60), auth_dat[i].lastlogin, 24);
                     memcpy(WFIFOP(fd, 84), auth_dat[i].last_ip, 16);
                     memcpy(WFIFOP(fd, 100), auth_dat[i].email, 40);
-                    WFIFOL(fd, 140) =
-                        (unsigned long) auth_dat[i].connect_until_time;
-                    WFIFOL(fd, 144) =
-                        (unsigned long) auth_dat[i].ban_until_time;
+                    WFIFOL(fd, 140) = static_cast<time_t>(auth_dat[i].connect_until_time);
+                    WFIFOL(fd, 144) = static_cast<time_t>(auth_dat[i].ban_until_time);
                     WFIFOW(fd, 148) = strlen(auth_dat[i].memo);
                     if (auth_dat[i].memo[0])
                     {
@@ -2909,10 +2876,8 @@ void parse_admin(int fd)
                         memcpy(WFIFOP(fd, 60), auth_dat[i].lastlogin, 24);
                         memcpy(WFIFOP(fd, 84), auth_dat[i].last_ip, 16);
                         memcpy(WFIFOP(fd, 100), auth_dat[i].email, 40);
-                        WFIFOL(fd, 140) =
-                            (unsigned long) auth_dat[i].connect_until_time;
-                        WFIFOL(fd, 144) =
-                            (unsigned long) auth_dat[i].ban_until_time;
+                        WFIFOL(fd, 140) = static_cast<time_t>(auth_dat[i].connect_until_time);
+                        WFIFOL(fd, 144) = static_cast<time_t>(auth_dat[i].ban_until_time);
                         WFIFOW(fd, 148) = strlen(auth_dat[i].memo);
                         if (auth_dat[i].memo[0])
                         {
@@ -3239,7 +3204,7 @@ void parse_login(int fd)
                         int i = search_account_index(account.userid);
                         if (i != -1)
                         {
-                            if (auth_dat[i].ban_until_time != 0)
+                            if (auth_dat[i].ban_until_time)
                             {
                                 // if account is banned, we send ban timestamp
                                 timestamp_seconds_buffer tmpstr;
diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp
index 116139d..ea13a04 100644
--- a/src/map/atcommand.cpp
+++ b/src/map/atcommand.cpp
@@ -386,8 +386,7 @@ FILE *get_gm_log()
     if (!gm_logfile_name)
         return NULL;
 
-    time_t ts = time(NULL);
-    struct tm ctime = *gmtime(&ts);
+    struct tm ctime = TimeT::now();
 
     int year = ctime.tm_year + 1900;
     int month = ctime.tm_mon + 1;
diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp
index 9743072..47eef1a 100644
--- a/src/map/chrif.cpp
+++ b/src/map/chrif.cpp
@@ -880,13 +880,11 @@ int chrif_accountban(int fd)
                 }
             }
             else if (RFIFOB(fd, 6) == 1)
-            {                   // 0: change of statut, 1: ban
-                time_t timestamp;
-                char tmpstr[2048];
-                timestamp = (time_t) RFIFOL(fd, 7);    // status or final date of a banishment
-                strcpy(tmpstr, "Your account has been banished until ");
-                strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S",
-                          gmtime(&timestamp));
+            {
+                // 0: change of statut, 1: ban
+                TimeT timestamp = static_cast<time_t>(RFIFOL(fd, 7));    // status or final date of a banishment
+                char tmpstr[] = WITH_TIMESTAMP("Your account has been banished until ");
+                REPLACE_TIMESTAMP(tmpstr, timestamp);
                 clif_displaymessage(sd->fd, tmpstr);
             }
             clif_setwaitclose(sd->fd); // forced to disconnect for the change
@@ -1098,7 +1096,7 @@ void chrif_parse(int fd)
                 break;
             case 0x2afd:
                 pc_authok(RFIFOL(fd, 4), RFIFOL(fd, 8),
-                           (time_t) RFIFOL(fd, 12), RFIFOW(fd, 16),
+                           static_cast<time_t>(RFIFOL(fd, 12)), RFIFOW(fd, 16),
                            (const struct mmo_charstatus *) RFIFOP(fd, 18));
                 break;
             case 0x2afe:
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index 1691959..ca8e487 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -5629,12 +5629,12 @@ int clif_check_packet_flood(int fd, int cmd)
     // They are flooding
     if (tick < sd->flood_rates[cmd] + rate)
     {
-        time_t now = time(NULL);
+        TimeT now = TimeT::now();
 
         // If it's a nasty flood we log and possibly kick
         if (now > sd->packet_flood_reset_due)
         {
-            sd->packet_flood_reset_due = now + battle_config.packet_spam_threshold;
+            sd->packet_flood_reset_due = static_cast<time_t>(now) + battle_config.packet_spam_threshold;
             sd->packet_flood_in = 0;
         }
 
@@ -5914,7 +5914,6 @@ void clif_parse(int fd)
                 int i;
                 FILE *fp;
                 char packet_txt[256] = "save/packet.txt";
-                time_t now;
                 PRINTF("---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
                 for (i = 0; i < packet_len; i++)
                 {
@@ -5942,24 +5941,25 @@ void clif_parse(int fd)
                 }
                 else
                 {
-                    time(&now);
+                    timestamp_seconds_buffer now;
+                    stamp_time(now);
                     if (sd && sd->state.auth)
                     {
                         if (sd->status.name != NULL)
                             FPRINTF(fp,
-                                     "%sPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n",
-                                     asctime(gmtime(&now)),
-                                     sd->status.account_id,
-                                     sd->status.char_id, sd->status.name);
+                                    "%s\nPlayer with account ID %d (character ID %d, player name %s) sent wrong packet:\n",
+                                    now,
+                                    sd->status.account_id,
+                                    sd->status.char_id, sd->status.name);
                         else
                             FPRINTF(fp,
-                                     "%sPlayer with account ID %d sent wrong packet:\n",
-                                     asctime(gmtime(&now)), sd->bl.id);
+                                    "%s\nPlayer with account ID %d sent wrong packet:\n",
+                                    now, sd->bl.id);
                     }
                     else if (sd)    // not authentified! (refused by char-server or disconnect before to be authentified)
                         FPRINTF(fp,
-                                 "%sPlayer with account ID %d sent wrong packet:\n",
-                                 asctime(gmtime(&now)), sd->bl.id);
+                                "%s\nPlayer with account ID %d sent wrong packet:\n",
+                                now, sd->bl.id);
 
                     FPRINTF(fp,
                              "\t---- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F");
diff --git a/src/map/magic-stmt.cpp b/src/map/magic-stmt.cpp
index 8058310..c36e334 100644
--- a/src/map/magic-stmt.cpp
+++ b/src/map/magic-stmt.cpp
@@ -870,7 +870,7 @@ int op_drop_item_for (env_t *, int args_nr, val_t *args)
     int stackable;
     location_t *loc = &ARGLOCATION(0);
     int count = ARGINT(2);
-    interval_t time = static_cast<interval_t>(ARGINT(3));
+    interval_t interval = static_cast<interval_t>(ARGINT(3));
     character_t *c = ((args_nr > 4) && (ENTITY_TYPE(4) == BL::PC)) ? ARGPC(4) : NULL;
     interval_t delay = (args_nr > 5) ? static_cast<interval_t>(ARGINT(5)) : interval_t::zero();
     interval_t delaytime[3] = { delay, delay, delay };
@@ -880,11 +880,11 @@ int op_drop_item_for (env_t *, int args_nr, val_t *args)
 
     if (stackable)
         map_addflooritem_any(&item, count, loc->m, loc->x, loc->y,
-                owners, delaytime, time, 0);
+                owners, delaytime, interval, 0);
     else
         while (count-- > 0)
             map_addflooritem_any(&item, 1, loc->m, loc->x, loc->y,
-                    owners, delaytime, time, 0);
+                    owners, delaytime, interval, 0);
 
     return 0;
 }
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 4fa56c2..63fc8c9 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -1540,9 +1540,9 @@ void map_close_logfile(void)
 }
 
 static
-void map_start_logfile(long suffix)
+void map_start_logfile(long index)
 {
-    map_logfile_index = suffix >> LOGFILE_SECONDS_PER_CHUNK_SHIFT;
+    map_logfile_index = index;
 
     std::string filename_buf = STRPRINTF(
             "%s.%ld",
@@ -1571,12 +1571,13 @@ void map_log(const_string line)
     if (!map_logfile)
         return;
 
-    time_t t = time(NULL);
+    time_t t = TimeT::now();
+    long i = t >> LOGFILE_SECONDS_PER_CHUNK_SHIFT;
 
-    if ((t >> LOGFILE_SECONDS_PER_CHUNK_SHIFT) != map_logfile_index)
+    if (i != map_logfile_index)
     {
         map_close_logfile();
-        map_start_logfile(t);
+        map_start_logfile(i);
     }
 
     log_with_timestamp(map_logfile, line);
diff --git a/src/map/map.hpp b/src/map/map.hpp
index 7fb6026..ab9b4c8 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -251,14 +251,14 @@ struct map_session_data
         unsigned in_progress:1;
     } auto_ban_info;
 
-    time_t chat_reset_due;
-    time_t chat_repeat_reset_due;
+    TimeT chat_reset_due;
+    TimeT chat_repeat_reset_due;
     int chat_lines_in;
     int chat_total_repeats;
     char chat_lastmsg[513];
 
     tick_t flood_rates[0x220];
-    time_t packet_flood_reset_due;
+    TimeT packet_flood_reset_due;
     int packet_flood_in;
 
     struct in_addr ip;
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index f0cacb9..7b88705 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -241,30 +241,29 @@ int npc_event_do_l(const char *name, int rid, int argc, argrec_t *args)
 static
 void npc_event_do_clock(TimerData *, tick_t)
 {
-    time_t timer = time(NULL);
-    struct tm *t = gmtime(&timer);
+    struct tm t = TimeT::now();
 
-    if (t->tm_min != ev_tm_b.tm_min)
+    if (t.tm_min != ev_tm_b.tm_min)
     {
         std::string buf;
-        buf = STRPRINTF("OnMinute%02d", t->tm_min);
+        buf = STRPRINTF("OnMinute%02d", t.tm_min);
         npc_event_doall(buf.c_str());
-        buf = STRPRINTF("OnClock%02d%02d", t->tm_hour, t->tm_min);
+        buf = STRPRINTF("OnClock%02d%02d", t.tm_hour, t.tm_min);
         npc_event_doall(buf.c_str());
     }
-    if (t->tm_hour != ev_tm_b.tm_hour)
+    if (t.tm_hour != ev_tm_b.tm_hour)
     {
         std::string buf;
-        buf = STRPRINTF("OnHour%02d", t->tm_hour);
+        buf = STRPRINTF("OnHour%02d", t.tm_hour);
         npc_event_doall(buf.c_str());
     }
-    if (t->tm_mday != ev_tm_b.tm_mday)
+    if (t.tm_mday != ev_tm_b.tm_mday)
     {
         std::string buf;
-        buf = STRPRINTF("OnDay%02d%02d", t->tm_mon + 1, t->tm_mday);
+        buf = STRPRINTF("OnDay%02d%02d", t.tm_mon + 1, t.tm_mday);
         npc_event_doall(buf.c_str());
     }
-    memcpy(&ev_tm_b, t, sizeof(ev_tm_b));
+    ev_tm_b = t;
 }
 
 /*==========================================
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index a344839..232a767 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -615,7 +615,7 @@ int pc_isequip(struct map_session_data *sd, int n)
  * char鯖から送られてきたステータスを設定
  *------------------------------------------
  */
-int pc_authok(int id, int login_id2, time_t connect_until_time,
+int pc_authok(int id, int login_id2, TimeT connect_until_time,
                short tmw_version, const struct mmo_charstatus *st)
 {
     struct map_session_data *sd = NULL;
@@ -783,24 +783,27 @@ int pc_authok(int id, int login_id2, time_t connect_until_time,
     sd->auto_ban_info.in_progress = 0;
 
     // Initialize antispam vars
-    sd->chat_reset_due = sd->chat_lines_in = sd->chat_total_repeats =
-        sd->chat_repeat_reset_due = 0;
+    sd->chat_reset_due = TimeT();
+    sd->chat_lines_in = sd->chat_total_repeats = 0;
+    sd->chat_repeat_reset_due = TimeT();
     sd->chat_lastmsg[0] = '\0';
 
     memset(sd->flood_rates, 0, sizeof(sd->flood_rates));
-    sd->packet_flood_reset_due = sd->packet_flood_in = 0;
+    sd->packet_flood_reset_due = TimeT();
+    sd->packet_flood_in = 0;
 
     // Obtain IP address (if they are still connected)
     if (!getpeername(sd->fd, (struct sockaddr *)&sai, &sa_len))
         sd->ip = sai.sin_addr;
 
     // message of the limited time of the account
-    if (connect_until_time != 0)
-    {                           // don't display if it's unlimited or unknow value
-        char tmpstr[1024];
-        strftime(tmpstr, sizeof(tmpstr) - 1, "Your account time limit is: %d-%m-%Y %H:%M:%S.", gmtime(&connect_until_time));
-        clif_wis_message(sd->fd, wisp_server_name, tmpstr,
-                          strlen(tmpstr) + 1);
+    if (connect_until_time)
+    {
+        // don't display if it's unlimited or unknow value
+        char tmpstr[] = WITH_TIMESTAMP("Your account time limit is: ");
+        REPLACE_TIMESTAMP(tmpstr, connect_until_time);
+
+        clif_wis_message(sd->fd, wisp_server_name, tmpstr, sizeof(tmpstr));
     }
     pc_calcstatus(sd, 1);
 
diff --git a/src/map/pc.hpp b/src/map/pc.hpp
index 12935b5..201a58e 100644
--- a/src/map/pc.hpp
+++ b/src/map/pc.hpp
@@ -56,7 +56,7 @@ int pc_counttargeted(struct map_session_data *sd, struct block_list *src,
 int pc_setrestartvalue(struct map_session_data *sd, int type);
 int pc_makesavestatus(struct map_session_data *);
 int pc_setnewpc(struct map_session_data *, int, int, int, tick_t, int);
-int pc_authok(int, int, time_t, short tmw_version, const struct mmo_charstatus *);
+int pc_authok(int, int, TimeT, short tmw_version, const struct mmo_charstatus *);
 int pc_authfail(int);
 
 EPOS pc_equippoint(struct map_session_data *sd, int n);
diff --git a/src/map/script.cpp b/src/map/script.cpp
index ffa72e7..1a954da 100644
--- a/src/map/script.cpp
+++ b/src/map/script.cpp
@@ -2640,23 +2640,19 @@ void builtin_gettimetick(ScriptState *st)   /* Asgard Version */
         /* Number of seconds elapsed today(0-86399, 00:00:00-23:59:59). */
         case 1:
         {
-            time_t timer;
-            struct tm *t;
-
-            time(&timer);
-            t = gmtime(&timer);
+            struct tm t = TimeT::now();
             push_val(st->stack, ScriptCode::INT,
-                      ((t->tm_hour) * 3600 + (t->tm_min) * 60 + t->tm_sec));
+                    t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec);
             break;
         }
         /* Seconds since Unix epoch. */
         case 2:
-            push_val(st->stack, ScriptCode::INT, (int) time(NULL));
+            push_val(st->stack, ScriptCode::INT, static_cast<time_t>(TimeT::now()));
             break;
         /* System tick(unsigned int, and yes, it will wrap). */
         case 0:
         default:
-            push_val(st->stack, ScriptCode::INT, (int) gettick().time_since_epoch().count());
+            push_val(st->stack, ScriptCode::INT, gettick().time_since_epoch().count());
             break;
     }
 }
@@ -2671,37 +2667,32 @@ void builtin_gettimetick(ScriptState *st)   /* Asgard Version */
 static
 void builtin_gettime(ScriptState *st)   /* Asgard Version */
 {
-    int type;
-    time_t timer;
-    struct tm *t;
-
-    type = conv_num(st, &(st->stack->stack_data[st->start + 2]));
+    int type = conv_num(st, &(st->stack->stack_data[st->start + 2]));
 
-    time(&timer);
-    t = gmtime(&timer);
+    struct tm t = TimeT::now();
 
     switch (type)
     {
         case 1:                //Sec(0~59)
-            push_val(st->stack, ScriptCode::INT, t->tm_sec);
+            push_val(st->stack, ScriptCode::INT, t.tm_sec);
             break;
         case 2:                //Min(0~59)
-            push_val(st->stack, ScriptCode::INT, t->tm_min);
+            push_val(st->stack, ScriptCode::INT, t.tm_min);
             break;
         case 3:                //Hour(0~23)
-            push_val(st->stack, ScriptCode::INT, t->tm_hour);
+            push_val(st->stack, ScriptCode::INT, t.tm_hour);
             break;
         case 4:                //WeekDay(0~6)
-            push_val(st->stack, ScriptCode::INT, t->tm_wday);
+            push_val(st->stack, ScriptCode::INT, t.tm_wday);
             break;
         case 5:                //MonthDay(01~31)
-            push_val(st->stack, ScriptCode::INT, t->tm_mday);
+            push_val(st->stack, ScriptCode::INT, t.tm_mday);
             break;
         case 6:                //Month(01~12)
-            push_val(st->stack, ScriptCode::INT, t->tm_mon + 1);
+            push_val(st->stack, ScriptCode::INT, t.tm_mon + 1);
             break;
         case 7:                //Year(20xx)
-            push_val(st->stack, ScriptCode::INT, t->tm_year + 1900);
+            push_val(st->stack, ScriptCode::INT, t.tm_year + 1900);
             break;
         default:               //(format error)
             push_val(st->stack, ScriptCode::INT, -1);
@@ -2716,16 +2707,13 @@ void builtin_gettime(ScriptState *st)   /* Asgard Version */
 static
 void builtin_gettimestr(ScriptState *st)
 {
-    char *tmpstr;
-    int maxlen;
-    time_t now = time(NULL);
+    struct tm now = TimeT::now();
 
     const char *fmtstr = conv_str(st, &(st->stack->stack_data[st->start + 2]));
-    maxlen = conv_num(st, &(st->stack->stack_data[st->start + 3]));
+    int maxlen = conv_num(st, &(st->stack->stack_data[st->start + 3]));
 
-    tmpstr = (char *) calloc(maxlen + 1, 1);
-    strftime(tmpstr, maxlen, fmtstr, gmtime(&now));
-    tmpstr[maxlen] = '\0';
+    char *tmpstr = (char *) calloc(maxlen + 1, 1);
+    strftime(tmpstr, maxlen, fmtstr, &now);
 
     push_str(st->stack, ScriptCode::STR, tmpstr);
 }
diff --git a/src/map/skill.cpp b/src/map/skill.cpp
index 4375541..df6ee13 100644
--- a/src/map/skill.cpp
+++ b/src/map/skill.cpp
@@ -561,7 +561,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl,
  * 詠唱時間計算
  *------------------------------------------
  */
-interval_t skill_castfix(struct block_list *bl, interval_t time)
+interval_t skill_castfix(struct block_list *bl, interval_t interval)
 {
     struct mob_data *md;        // [Valaris]
     eptr<struct status_change, StatusChange> sc_data;
@@ -592,48 +592,48 @@ interval_t skill_castfix(struct block_list *bl, interval_t time)
 
     castnodex = skill_get_castnodex(skill, lv);
 
-    if (time == interval_t::zero())
+    if (interval == interval_t::zero())
         return interval_t::zero();
     if (castnodex > 0 && bl->type == BL::PC)
         castrate = 100;
     else if (castnodex <= 0 && bl->type == BL::PC)
     {
         castrate = 100;
-        time =
-            time * castrate * (battle_config.castrate_dex_scale -
+        interval =
+            interval * castrate * (battle_config.castrate_dex_scale -
                                dex) / (battle_config.castrate_dex_scale *
                                        100);
-        time = time * battle_config.cast_rate / 100;
+        interval = interval * battle_config.cast_rate / 100;
     }
 
-    return std::max(time, interval_t::zero());
+    return std::max(interval, interval_t::zero());
 }
 
 /*==========================================
  * ディレイ計算
  *------------------------------------------
  */
-interval_t skill_delayfix(struct block_list *bl, interval_t time)
+interval_t skill_delayfix(struct block_list *bl, interval_t interval)
 {
     eptr<struct status_change, StatusChange> sc_data;
 
     nullpo_retr(interval_t::zero(), bl);
 
     sc_data = battle_get_sc_data(bl);
-    if (time <= interval_t::zero())
+    if (interval <= interval_t::zero())
         return interval_t::zero();
 
     if (bl->type == BL::PC)
     {
         if (battle_config.delay_dependon_dex)   /* dexの影響を計算する */
-            time =
-                time * (battle_config.castrate_dex_scale -
+            interval =
+                interval * (battle_config.castrate_dex_scale -
                         battle_get_dex(bl)) /
                 battle_config.castrate_dex_scale;
-        time = time * battle_config.delay_rate / 100;
+        interval = interval * battle_config.delay_rate / 100;
     }
 
-    return std::max(time, interval_t::zero());
+    return std::max(interval, interval_t::zero());
 }
 
 /*==========================================
diff --git a/src/map/tmw.cpp b/src/map/tmw.cpp
index 0229f17..66b8bb9 100644
--- a/src/map/tmw.cpp
+++ b/src/map/tmw.cpp
@@ -27,21 +27,21 @@ int tmw_ShorterStrlen(const char *s1, const char *s2);
 int tmw_CheckChatSpam(struct map_session_data *sd, const char *message)
 {
     nullpo_retr(1, sd);
-    time_t now = time(NULL);
+    TimeT now = TimeT::now();
 
     if (pc_isGM(sd))
         return 0;
 
     if (now > sd->chat_reset_due)
     {
-        sd->chat_reset_due = now + battle_config.chat_spam_threshold;
+        sd->chat_reset_due = static_cast<time_t>(now) + battle_config.chat_spam_threshold;
         sd->chat_lines_in = 0;
     }
 
     if (now > sd->chat_repeat_reset_due)
     {
         sd->chat_repeat_reset_due =
-            now + (battle_config.chat_spam_threshold * 60);
+            static_cast<time_t>(now) + (battle_config.chat_spam_threshold * 60);
         sd->chat_total_repeats = 0;
     }
 
diff --git a/src/poison.hpp b/src/poison.hpp
index 1b3191a..11ea41d 100644
--- a/src/poison.hpp
+++ b/src/poison.hpp
@@ -3,5 +3,14 @@
 #pragma GCC poison gets
 
 // TODO fill in as they are removed from source code:
-// float/double (use a fixed class)
+// double (use a fixed class)
+#pragma GCC poison float
 // mem* and str* from <string.h>, in favor of <algorithm>
+
+// Local time is forbidden.
+#pragma GCC poison timelocal // timegm
+#pragma GCC poison mktime // timegm
+#pragma GCC poison localtime // gmtime
+#pragma GCC poison localtime_r // gmtime_r
+
+#pragma GCC poison time // TimeT::now() or gettick()
diff --git a/src/tool/eathena-monitor.cpp b/src/tool/eathena-monitor.cpp
index 0a07d3b..30e7184 100644
--- a/src/tool/eathena-monitor.cpp
+++ b/src/tool/eathena-monitor.cpp
@@ -18,6 +18,7 @@
 #include <ctime>
 
 #include "../common/cxxstdio.hpp"
+#include "../common/utils.hpp"
 
 #include "../poison.hpp"
 
@@ -212,10 +213,8 @@ int main(int argc, char *argv[]) {
     }
     while (1) {
         // write stuff to stderr
-        time_t t = time(NULL);
-        struct tm *tmp = localtime(&t);
-        char timestamp[256];
-        strftime(timestamp, sizeof(timestamp), "%F %T", tmp);
+        timestamp_seconds_buffer timestamp;
+        stamp_time(timestamp);
 
         if (!pid_login) {
             pid_login = start_process(login_server);
-- 
cgit v1.2.3-70-g09d2