bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen
4510b57951cdedca279ae838cd42c21069393a72Timo Sirainen#include "lib.h"
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen#include "utc-mktime.h"
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainenstatic int tm_cmp(const struct tm *tm1, const struct tm *tm2)
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen{
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen int diff;
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if ((diff = tm1->tm_year - tm2->tm_year) != 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return diff;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if ((diff = tm1->tm_mon - tm2->tm_mon) != 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return diff;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if ((diff = tm1->tm_mday - tm2->tm_mday) != 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return diff;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if ((diff = tm1->tm_hour - tm2->tm_hour) != 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return diff;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if ((diff = tm1->tm_min - tm2->tm_min) != 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return diff;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return tm1->tm_sec - tm2->tm_sec;
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen}
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvistatic inline void adjust_leap_second(struct tm *tm)
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi{
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi if (tm->tm_sec == 60)
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi tm->tm_sec = 59;
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi}
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen#ifdef HAVE_TIMEGM
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi/* Normalization done by timegm is considered a failure here, since it means
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi * the timestamp is not valid as-is. Leap second 60 is adjusted to 59 before
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi * this though. */
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainentime_t utc_mktime(const struct tm *tm)
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen{
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi struct tm leap_adj_tm = *tm;
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi adjust_leap_second(&leap_adj_tm);
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi struct tm tmp = leap_adj_tm;
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen time_t t;
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi t = timegm(&tmp);
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi if (tm_cmp(&leap_adj_tm, &tmp) != 0)
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen return (time_t)-1;
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen return t;
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen}
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen#else
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainentime_t utc_mktime(const struct tm *tm)
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen{
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi struct tm leap_adj_tm = *tm;
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi adjust_leap_second(&leap_adj_tm);
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen const struct tm *try_tm;
529c74561ad458d89305e6adfc0e54eafaac5c8dTimo Sirainen time_t t;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen int bits, dir;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen /* we'll do a binary search across the entire valid time_t range.
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen when gmtime()'s output matches the tm parameter, we've found the
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen correct time_t value. this also means that if tm contains invalid
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen values, -1 is returned. */
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen#ifdef TIME_T_SIGNED
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen t = 0;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen#else
3070d992bd069d4e209e4191e3009156274720d8Timo Sirainen t = (time_t)1 << (TIME_T_MAX_BITS - 1);
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen#endif
7d67cdacb79a7ba44be2d77627643dda66714cd1Timo Sirainen for (bits = TIME_T_MAX_BITS - 2;; bits--) {
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen try_tm = gmtime(&t);
ac3de45a2b7f7411f18bf3c0f0bc8dd54519944aMartti Rannanjärvi dir = tm_cmp(&leap_adj_tm, try_tm);
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if (dir == 0)
7d67cdacb79a7ba44be2d77627643dda66714cd1Timo Sirainen return t;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if (bits < 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen break;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen if (dir < 0)
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen t -= (time_t)1 << bits;
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen else
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen t += (time_t)1 << bits;
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen }
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen
11a8445dc9d6c54288e7fa930378bb4d5669a3f9Timo Sirainen return (time_t)-1;
201108608e6686d3e9dce9b95678a814122b6471Timo Sirainen}
e5a3e4396b53591bf1f22a73f9ec1a022f2c1e46Timo Sirainen#endif