2
* @file date.c date formatting routines
4
* Copyright (C) 2008-2009 Lars Lindner <lars.lindner@gmail.com>
5
* Copyright (C) 2004-2006 Nathan J. Conrad <t98502@users.sourceforge.net>
7
* The date formatting was reused from the Evolution code base
9
* Author: Chris Lahey <clahey@ximian.com
11
* Copyright 2001, Ximian, Inc.
13
* parts of the RFC822 timezone decoding were reused from the gmime source
15
* Authors: Michael Zucchi <notzed@helixcode.com>
16
* Jeffrey Stedfast <fejj@helixcode.com>
18
* Copyright 2000 Helix Code, Inc. (www.helixcode.com)
20
* This program is free software; you can redistribute it and/or modify
21
* it under the terms of the GNU General Public License as published by
22
* the Free Software Foundation; either version 2 of the License, or
23
* (at your option) any later version.
25
* This program is distributed in the hope that it will be useful,
26
* but WITHOUT ANY WARRANTY; without even the implied warranty of
27
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28
* GNU General Public License for more details.
30
* You should have received a copy of the GNU General Public License
31
* along with this program; if not, write to the Free Software
32
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35
#define _XOPEN_SOURCE 600 /* glibc2 needs this (man strptime) */
47
#define TIMESTRLEN 256
49
/* date formatting methods */
51
/* This function is originally from the Evolution 2.6.2 code (e-cell-date.c) */
53
date_format_nice (time_t date)
55
time_t nowdate = time(NULL);
57
struct tm then, now, yesterday;
59
gboolean done = FALSE;
65
buf = g_new0(gchar, TIMESTRLEN + 1);
67
localtime_r (&date, &then);
68
localtime_r (&nowdate, &now);
70
/* if (nowdate - date < 60 * 60 * 8 && nowdate > date) {
71
e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%l:%M %p"), &then);
76
if (then.tm_mday == now.tm_mday &&
77
then.tm_mon == now.tm_mon &&
78
then.tm_year == now.tm_year) {
79
/* translation hint: date format for today, reorder format codes as necessary */
80
e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("Today %l:%M %p"), &then);
85
yesdate = nowdate - 60 * 60 * 24;
86
localtime_r (&yesdate, &yesterday);
87
if (then.tm_mday == yesterday.tm_mday &&
88
then.tm_mon == yesterday.tm_mon &&
89
then.tm_year == yesterday.tm_year) {
90
/* translation hint: date format for yesterday, reorder format codes as necessary */
91
e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("Yesterday %l:%M %p"), &then);
97
for (i = 2; i < 7; i++) {
98
yesdate = nowdate - 60 * 60 * 24 * i;
99
localtime_r (&yesdate, &yesterday);
100
if (then.tm_mday == yesterday.tm_mday &&
101
then.tm_mon == yesterday.tm_mon &&
102
then.tm_year == yesterday.tm_year) {
103
/* translation hint: date format for dates older than 2 days but not older than a week, reorder format codes as necessary */
104
e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%a %l:%M %p"), &then);
111
if (then.tm_year == now.tm_year) {
112
/* translation hint: date format for dates older than a week but from this year, reorder format codes as necessary */
113
e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%b %d %l:%M %p"), &then);
115
/* translation hint: date format for dates from the last years, reorder format codes as necessary */
116
e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%b %d %Y"), &then);
121
while ((temp = strstr (temp, " "))) {
122
memmove (temp, temp + 1, strlen (temp));
124
temp = g_strstrip (buf);
129
date_format (time_t date, const gchar *date_format)
135
return g_strdup ("");
139
localtime_r (&date, &date_tm);
141
result = g_new0 (gchar, TIMESTRLEN);
142
e_utf8_strftime_fix_am_pm (result, TIMESTRLEN, date_format, &date_tm);
144
result = date_format_nice (date);
150
/* date parsing methods */
153
date_parse_ISO8601 (const gchar *date)
156
time_t t, t2, offset = 0;
157
gboolean success = FALSE;
160
g_assert (date != NULL);
162
memset (&tm, 0, sizeof (struct tm));
164
/* we expect at least something like "2003-08-07T15:28:19" and
165
don't require the second fractions and the timezone info
167
the most specific format: YYYY-MM-DDThh:mm:ss.sTZD
170
/* full specified variant */
171
pos = strptime (date, "%t%Y-%m-%dT%H:%M%t", &tm);
176
if (isdigit (pos[0]) && !isdigit (pos[1])) {
177
tm.tm_sec = pos[0] - '0';
179
} else if (isdigit (pos[0]) && isdigit (pos[1])) {
180
tm.tm_sec = 10*(pos[0]-'0') + pos[1] - '0';
183
/* Parse second fractions */
185
while (*pos == '.' || isdigit (pos[0]))
191
else if ((*pos == '+' || *pos == '-') && isdigit (pos[1]) && isdigit (pos[2]) && strlen (pos) >= 3) {
192
offset = (10*(pos[1] - '0') + (pos[2] - '0')) * 60 * 60;
194
if (pos[3] == ':' && isdigit (pos[4]) && isdigit (pos[5]))
195
offset += (10*(pos[4] - '0') + (pos[5] - '0')) * 60;
196
else if (isdigit (pos[3]) && isdigit (pos[4]))
197
offset += (10*(pos[3] - '0') + (pos[4] - '0')) * 60;
199
offset *= (pos[0] == '+') ? 1 : -1;
204
} else if (NULL != strptime (date, "%t%Y-%m-%d", &tm)) {
207
/* there were others combinations too... */
210
if ((time_t)(-1) != (t = mktime (&tm))) {
211
/* Correct for the local timezone*/
215
gmtime_r (&t, &tmp_tm);
216
t2 = mktime (&tmp_tm);
221
debug0 (DEBUG_PARSING, "Internal error! time conversion error! mktime failed!");
224
debug0 (DEBUG_PARSING, "Invalid ISO8601 date format! Ignoring <dc:date> information!");
230
/* in theory, we'd need only the RFC822 timezones here
231
in practice, feeds also use other timezones... */
298
/** @returns timezone offset in seconds */
300
date_parse_rfc822_tz (char *token)
303
const char *inptr = token;
304
int num_timezones = sizeof (tz_offsets) / sizeof ((tz_offsets)[0]);
306
if (*inptr == '+' || *inptr == '-') {
307
offset = atoi (inptr);
314
for (t = 0; t < num_timezones; t++)
315
if (!strncmp (inptr, tz_offsets[t].name, strlen (tz_offsets[t].name))) {
316
offset = tz_offsets[t].offset;
321
return 60 * ((offset / 100) * 60 + (offset % 100));
325
date_parse_RFC822 (const gchar *date)
331
gboolean success = FALSE;
333
memset (&tm, 0, sizeof (struct tm));
335
/* we expect at least something like "03 Dec 12 01:38:34"
336
and don't require a day of week or the timezone
338
the most specific format we expect: "Fri, 03 Dec 12 01:38:34 CET"
341
/* skip day of week */
342
pos = g_utf8_strchr(date, -1, ',');
346
/* we expect English month names, so we set the locale */
347
oldlocale = g_strdup (setlocale (LC_TIME, NULL));
348
setlocale (LC_TIME, "C");
350
/* standard format with seconds and 4 digit year */
351
if (NULL != (pos = strptime ((const char *)date, " %d %b %Y %T", &tm)))
353
/* non-standard format without seconds and 4 digit year */
354
else if (NULL != (pos = strptime ((const char *)date, " %d %b %Y %H:%M", &tm)))
356
/* non-standard format with seconds and 2 digit year */
357
else if (NULL != (pos = strptime ((const char *)date, " %d %b %y %T", &tm)))
359
/* non-standard format without seconds 2 digit year */
360
else if (NULL != (pos = strptime ((const char *)date, " %d %b %y %H:%M", &tm)))
363
while (pos && *pos != '\0' && isspace ((int)*pos)) /* skip whitespaces before timezone */
367
setlocale (LC_TIME, oldlocale); /* and reset it again */
372
if ((time_t)(-1) != (t = mktime (&tm))) {
373
/* GMT time, with no daylight savings time
374
correction. (Usually, there is no daylight savings
375
time since the input is GMT.) */
376
t = t - date_parse_rfc822_tz (pos);
377
t2 = mktime (gmtime(&t));
381
debug0 (DEBUG_PARSING, "internal error! time conversion error! mktime failed!");