~ubuntu-branches/ubuntu/vivid/liferea/vivid-proposed

« back to all changes in this revision

Viewing changes to src/date.c

  • Committer: Package Import Robot
  • Author(s): bojo42
  • Date: 2012-03-29 14:17:21 UTC
  • mfrom: (1.3.9) (3.2.5 sid)
  • Revision ID: package-import@ubuntu.com-20120329141721-tbfopcrc5797wxt7
Tags: 1.8.3-0.1ubuntu1
* New upstream release (LP: #290666, #371754, #741543, #716688)
* Merge from Debian unstable (LP: #935147), remaining changes:
* debian/patches:
  - drop gtk-status-icon.patch & notification-append as in upstream
  - drop fix_systray_behavior as mostly upstreamed and rest seems unused
  - 01_ubuntu_feedlists: update & rename, move planets to "Open Source"  
  - add_X-Ubuntu-Gettext-Domain: rebase
  - libunity.patch: rebase, apply before indicator patch (liferea_shell.c)
  - libindicate_increase_version.patch: exclude from libindicate.patch
  - deactivate libindicate.patch, seems partly upstreamed and needs rework
* debian/control: libindicate-dev, libindicate-gtk-dev & libunity-dev
* debian/liferea.indicate & liferea.install: ship indicator desktop file
* debian/rules: enable libindicate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * @file date.c date formatting routines
 
3
 * 
 
4
 * Copyright (C) 2008-2009  Lars Lindner <lars.lindner@gmail.com>
 
5
 * Copyright (C) 2004-2006  Nathan J. Conrad <t98502@users.sourceforge.net>
 
6
 * 
 
7
 * The date formatting was reused from the Evolution code base
 
8
 *
 
9
 *    Author: Chris Lahey <clahey@ximian.com
 
10
 *
 
11
 *    Copyright 2001, Ximian, Inc.
 
12
 *
 
13
 * parts of the RFC822 timezone decoding were reused from the gmime source
 
14
 *
 
15
 *    Authors: Michael Zucchi <notzed@helixcode.com>
 
16
 *             Jeffrey Stedfast <fejj@helixcode.com>
 
17
 *
 
18
 *    Copyright 2000 Helix Code, Inc. (www.helixcode.com)
 
19
 *
 
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.
 
24
 *
 
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.
 
29
 *
 
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
 
33
 */
 
34
 
 
35
#define _XOPEN_SOURCE   600 /* glibc2 needs this (man strptime) */
 
36
 
 
37
#include "date.h"
 
38
 
 
39
#include <locale.h>
 
40
#include <ctype.h>
 
41
#include <string.h>
 
42
 
 
43
#include "common.h"
 
44
#include "debug.h"
 
45
#include "e-date.h"
 
46
 
 
47
#define TIMESTRLEN      256
 
48
 
 
49
/* date formatting methods */
 
50
 
 
51
/* This function is originally from the Evolution 2.6.2 code (e-cell-date.c) */
 
52
static gchar *
 
53
date_format_nice (time_t date)
 
54
{
 
55
        time_t nowdate = time(NULL);
 
56
        time_t yesdate;
 
57
        struct tm then, now, yesterday;
 
58
        gchar *temp, *buf;
 
59
        gboolean done = FALSE;
 
60
        
 
61
        if (date == 0) {
 
62
                return g_strdup ("");
 
63
        }
 
64
        
 
65
        buf = g_new0(gchar, TIMESTRLEN + 1);
 
66
 
 
67
        localtime_r (&date, &then);
 
68
        localtime_r (&nowdate, &now);
 
69
 
 
70
/*      if (nowdate - date < 60 * 60 * 8 && nowdate > date) {
 
71
                e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%l:%M %p"), &then);
 
72
                done = TRUE;
 
73
        }*/
 
74
 
 
75
        if (!done) {
 
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);
 
81
                        done = TRUE;
 
82
                }
 
83
        }
 
84
        if (!done) {
 
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);
 
92
                        done = TRUE;
 
93
                }
 
94
        }
 
95
        if (!done) {
 
96
                int i;
 
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);
 
105
                                done = TRUE;
 
106
                                break;
 
107
                        }
 
108
                }
 
109
        }
 
110
        if (!done) {
 
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);
 
114
                } else {
 
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);
 
117
                }
 
118
        }
 
119
 
 
120
        temp = buf;
 
121
        while ((temp = strstr (temp, "  "))) {
 
122
                memmove (temp, temp + 1, strlen (temp));
 
123
        }
 
124
        temp = g_strstrip (buf);
 
125
        return temp;
 
126
}
 
127
 
 
128
gchar *
 
129
date_format (time_t date, const gchar *date_format)
 
130
{
 
131
        gchar           *result;
 
132
        struct tm       date_tm;
 
133
        
 
134
        if (date == 0) {
 
135
                return g_strdup ("");
 
136
        }
 
137
 
 
138
        if (date_format) {
 
139
                localtime_r (&date, &date_tm);
 
140
        
 
141
                result = g_new0 (gchar, TIMESTRLEN);
 
142
                e_utf8_strftime_fix_am_pm (result, TIMESTRLEN, date_format, &date_tm);
 
143
        } else {
 
144
                result = date_format_nice (date);
 
145
        }
 
146
 
 
147
        return result;
 
148
}
 
149
 
 
150
/* date parsing methods */
 
151
 
 
152
time_t
 
153
date_parse_ISO8601 (const gchar *date)
 
154
{
 
155
        struct tm       tm;
 
156
        time_t          t, t2, offset = 0;
 
157
        gboolean        success = FALSE;
 
158
        gchar *pos;
 
159
        
 
160
        g_assert (date != NULL);
 
161
        
 
162
        memset (&tm, 0, sizeof (struct tm));
 
163
        
 
164
        /* we expect at least something like "2003-08-07T15:28:19" and
 
165
           don't require the second fractions and the timezone info
 
166
 
 
167
           the most specific format:   YYYY-MM-DDThh:mm:ss.sTZD
 
168
         */
 
169
         
 
170
        /* full specified variant */
 
171
        pos = strptime (date, "%t%Y-%m-%dT%H:%M%t", &tm);
 
172
        if (pos) {
 
173
                /* Parse seconds */
 
174
                if (*pos == ':')
 
175
                        pos++;
 
176
                if (isdigit (pos[0]) && !isdigit (pos[1])) {
 
177
                        tm.tm_sec = pos[0] - '0';
 
178
                        pos++;
 
179
                } else if (isdigit (pos[0]) && isdigit (pos[1])) {
 
180
                        tm.tm_sec = 10*(pos[0]-'0') + pos[1] - '0';
 
181
                        pos +=2;
 
182
                }
 
183
                /* Parse second fractions */
 
184
                if (*pos == '.') {
 
185
                        while (*pos == '.' || isdigit (pos[0]))
 
186
                                pos++;
 
187
                }
 
188
                /* Parse timezone */
 
189
                if (*pos == 'Z')
 
190
                        offset = 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;
 
193
                        
 
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;
 
198
                        
 
199
                        offset *= (pos[0] == '+') ? 1 : -1;
 
200
 
 
201
                }
 
202
                success = TRUE;
 
203
        /* only date */
 
204
        } else if (NULL != strptime (date, "%t%Y-%m-%d", &tm)) {
 
205
                success = TRUE;
 
206
        }
 
207
        /* there were others combinations too... */
 
208
 
 
209
        if (success) {
 
210
                if ((time_t)(-1) != (t = mktime (&tm))) {
 
211
                        /* Correct for the local timezone*/
 
212
                        struct tm tmp_tm;
 
213
                        
 
214
                        t = t - offset;
 
215
                        gmtime_r (&t, &tmp_tm);
 
216
                        t2 = mktime (&tmp_tm);
 
217
                        t = t - (t2 - t);
 
218
                        
 
219
                        return t;
 
220
                } else {
 
221
                        debug0 (DEBUG_PARSING, "Internal error! time conversion error! mktime failed!");
 
222
                }
 
223
        } else {
 
224
                debug0 (DEBUG_PARSING, "Invalid ISO8601 date format! Ignoring <dc:date> information!");
 
225
        }
 
226
        
 
227
        return 0;
 
228
}
 
229
 
 
230
/* in theory, we'd need only the RFC822 timezones here
 
231
   in practice, feeds also use other timezones...        */
 
232
static struct {
 
233
        const char *name;
 
234
        int offset;
 
235
} tz_offsets [] = {
 
236
        { "IDLW", -1200 },
 
237
        { "HAST", -1000 },
 
238
        { "AKST", -900 },
 
239
        { "AKDT", -800 },
 
240
        { "WESZ", 100 },
 
241
        { "WEST", 100 },
 
242
        { "WEDT", 100 },
 
243
        { "MEST", 200 },
 
244
        { "MESZ", 200 },
 
245
        { "CEST", 200 },
 
246
        { "CEDT", 200 },
 
247
        { "EEST", 300 },
 
248
        { "EEDT", 300 },
 
249
        { "IRST", 430 },
 
250
        { "CNST", 800 },
 
251
        { "ACST", 930 },
 
252
        { "ACDT", 1030 },
 
253
        { "AEST", 1000 },
 
254
        { "AEDT", 1100 },
 
255
        { "IDLE", 1200 },
 
256
        { "NZST", 1200 },
 
257
        { "NZDT", 1300 },
 
258
        { "GMT", 0 },
 
259
        { "EST", -500 },
 
260
        { "EDT", -400 },
 
261
        { "CST", -600 },
 
262
        { "CDT", -500 },
 
263
        { "MST", -700 },
 
264
        { "MDT", -600 },
 
265
        { "PST", -800 },
 
266
        { "PDT", -700 },
 
267
        { "HDT", -900 },
 
268
        { "YST", -900 },
 
269
        { "YDT", -800 },
 
270
        { "AST", -400 },
 
271
        { "ADT", -300 },
 
272
        { "VST", -430 },
 
273
        { "NST", -330 },
 
274
        { "NDT", -230 },
 
275
        { "WET", 0 },
 
276
        { "WEZ", 0 },
 
277
        { "IST", 100 },
 
278
        { "CET", 100 },
 
279
        { "MEZ", 100 },
 
280
        { "EET", 200 },
 
281
        { "MSK", 300 },
 
282
        { "MSD", 400 },
 
283
        { "IRT", 330 },
 
284
        { "IST", 530 },
 
285
        { "ICT", 700 },
 
286
        { "JST", 900 },
 
287
        { "NFT", 1130 },
 
288
        { "UT", 0 },
 
289
        { "PT", -800 },
 
290
        { "BT", 300 },
 
291
        { "Z", 0 },
 
292
        { "A", -100 },
 
293
        { "M", -1200 },
 
294
        { "N", 100 },
 
295
        { "Y", 1200 }
 
296
};
 
297
 
 
298
/** @returns timezone offset in seconds */
 
299
static time_t
 
300
date_parse_rfc822_tz (char *token)
 
301
{
 
302
        int offset = 0;
 
303
        const char *inptr = token;
 
304
        int num_timezones = sizeof (tz_offsets) / sizeof ((tz_offsets)[0]);
 
305
 
 
306
        if (*inptr == '+' || *inptr == '-') {
 
307
                offset = atoi (inptr);
 
308
        } else {
 
309
                int t;
 
310
 
 
311
                if (*inptr == '(')
 
312
                        inptr++;
 
313
 
 
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;
 
317
                                break;
 
318
                        }
 
319
        }
 
320
        
 
321
        return 60 * ((offset / 100) * 60 + (offset % 100));
 
322
}
 
323
 
 
324
time_t
 
325
date_parse_RFC822 (const gchar *date)
 
326
{
 
327
        struct tm       tm;
 
328
        time_t          t, t2;
 
329
        char            *oldlocale;
 
330
        char            *pos;
 
331
        gboolean        success = FALSE;
 
332
 
 
333
        memset (&tm, 0, sizeof (struct tm));
 
334
 
 
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
 
337
 
 
338
           the most specific format we expect:  "Fri, 03 Dec 12 01:38:34 CET"
 
339
         */
 
340
        
 
341
        /* skip day of week */
 
342
        pos = g_utf8_strchr(date, -1, ',');
 
343
        if (pos)
 
344
                date = ++pos;
 
345
 
 
346
        /* we expect English month names, so we set the locale */
 
347
        oldlocale = g_strdup (setlocale (LC_TIME, NULL));
 
348
        setlocale (LC_TIME, "C");
 
349
        
 
350
        /* standard format with seconds and 4 digit year */
 
351
        if (NULL != (pos = strptime ((const char *)date, " %d %b %Y %T", &tm)))
 
352
                success = TRUE;
 
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)))
 
355
                success = TRUE;
 
356
        /* non-standard format with seconds and 2 digit year */
 
357
        else if (NULL != (pos = strptime ((const char *)date, " %d %b %y %T", &tm)))
 
358
                success = TRUE;
 
359
        /* non-standard format without seconds 2 digit year */
 
360
        else if (NULL != (pos = strptime ((const char *)date, " %d %b %y %H:%M", &tm)))
 
361
                success = TRUE;
 
362
        
 
363
        while (pos && *pos != '\0' && isspace ((int)*pos))       /* skip whitespaces before timezone */
 
364
                pos++;
 
365
        
 
366
        if (oldlocale) {
 
367
                setlocale (LC_TIME, oldlocale); /* and reset it again */
 
368
                g_free (oldlocale);
 
369
        }
 
370
        
 
371
        if (success) {
 
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));
 
378
                        t = t - (t2 - t);
 
379
                        return t;
 
380
                } else {
 
381
                        debug0 (DEBUG_PARSING, "internal error! time conversion error! mktime failed!");
 
382
                }
 
383
        }
 
384
        
 
385
        return 0;
 
386
}
 
387