/*
* GIT - The information manager from hell
*
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
*/
static time_t tm_to_time_t(const struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
};
int year = tm->tm_year - 70;
int month = tm->tm_mon;
int day = tm->tm_mday;
if (year < 0 || year > 129) /* algo only works for 1970-2099 */
return -1;
if (month < 0 || month > 11) /* array bounds */
return -1;
if (month < 2 || (year + 2) % 4)
day--;
if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
return -1;
return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
}
static const char *month_names[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
static const char *weekday_names[] = {
"Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
};
static time_t gm_time_t(unsigned long time, int tz)
{
int minutes;
minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
return time + minutes * 60;
}
/*
* The "tz" thing is passed in as this strange "decimal parse of tz"
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
static struct tm *time_to_tm(unsigned long time, int tz)
{
time_t t = gm_time_t(time, tz);
return gmtime(&t);
}
/*
* What value of "tz" was in effect back then at "time" in the
* local timezone?
*/
static int local_tzoffset(unsigned long time)
{
time_t t, t_local;
struct tm tm;
int offset, eastwest;
t = time;
localtime_r(&t, &tm);
t_local = tm_to_time_t(&tm);
if (t_local < t) {
eastwest = -1;
offset = t - t_local;
} else {
eastwest = 1;
offset = t_local - t;
}
offset /= 60; /* in minutes */
offset = (offset % 60) + ((offset / 60) * 100);
return offset * eastwest;
}
const char *show_date_relative(unsigned long time, int tz,
const struct timeval *now,
char *timebuf,
size_t timebuf_size)
{
unsigned long diff;
if (now->tv_sec < time)
return "in the future";
diff = now->tv_sec - time;
if (diff < 90) {
snprintf(timebuf, timebuf_size, "%lu seconds ago", diff);
return timebuf;
}
/* Turn it into minutes */
diff = (diff + 30) / 60;
if (diff < 90) {
snprintf(timebuf, timebuf_size, "%lu minutes ago", diff);
return timebuf;
}
/* Turn it into hours */
diff = (diff + 30) / 60;
if (diff < 36) {
snprintf(timebuf, timebuf_size, "%lu hours ago", diff);
return timebuf;
}
/* We deal with number of days from here on */
diff = (diff + 12) / 24;
if (diff < 14) {
snprintf(timebuf, timebuf_size, "%lu days ago", diff);
return timebuf;
}
/* Say weeks for the past 10 weeks or so */
if (diff < 70) {
snprintf(timebuf, timebuf_size, "%lu weeks ago", (diff + 3) / 7);
return timebuf;
}
/* Say months for the past 12 months or so */
if (diff < 365) {
snprintf(timebuf, timebuf_size, "%lu months ago", (diff + 15) / 30);
return timebuf;
}
/* Give years and months for 5 years or so */
if (diff < 1825) {
unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
unsigned long years = totalmonths / 12;
unsigned long months = totalmonths % 12;
int n;
n = snprintf(timebuf, timebuf_size, "%lu year%s",
years, (years > 1 ? "s" : ""));
if (months)
snprintf(timebuf + n, timebuf_size - n,
", %lu month%s ago",
months, (months > 1 ? "s" : ""));
else
snprintf(timebuf + n, timebuf_size - n, " ago");
return timebuf;
}
/* Otherwise, just years. Centuries is probably overkill. */
snprintf(timebuf, timebuf_size, "%lu years ago", (diff + 183) / 365);
return timebuf;
}
const char *show_date(unsigned long time, int tz, enum date_mode mode)
{
struct tm *tm;
static char timebuf[200];
if (mode == DATE_RAW) {
snprintf(timebuf, sizeof(timebuf), "%lu %+05d", time, tz);
return timebuf;
}
if (mode == DATE_RELATIVE) {
struct timeval now;
gettimeofday(&now, NULL);
return show_date_relative(time, tz, &now,
timebuf, sizeof(timebuf));
}
if (mode == DATE_LOCAL)
tz = local_tzoffset(time);
tm = time_to_tm(time, tz);
|