1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-timestamp.c:
4  *
5  * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 
22 #include <gsf-config.h>
23 #include <gsf/gsf-timestamp.h>
24 #include <gsf/gsf.h>
25 
26 #include <string.h>
27 #include <time.h>
28 #ifdef G_OS_WIN32
29 #include <windows.h>
30 #endif
31 
32 static void
timestamp_to_string(GValue const * src_value,GValue * dest_value)33 timestamp_to_string (GValue const *src_value, GValue *dest_value)
34 {
35 	char *str = gsf_timestamp_as_string (g_value_get_boxed (src_value));
36 	g_value_set_string (dest_value, str);
37 	g_free (str);
38 }
39 
40 GType
gsf_timestamp_get_type(void)41 gsf_timestamp_get_type (void)
42 {
43 	static GType our_type = 0;
44 
45 	if (our_type == 0) {
46 		our_type = g_boxed_type_register_static ("GsfTimestamp",
47 					(GBoxedCopyFunc)gsf_timestamp_copy,
48 					(GBoxedFreeFunc)gsf_timestamp_free);
49 		g_value_register_transform_func	(our_type, G_TYPE_STRING,
50 			&timestamp_to_string);
51 	}
52 	return our_type;
53 }
54 
55 GsfTimestamp *
gsf_timestamp_new(void)56 gsf_timestamp_new (void)
57 {
58 	GsfTimestamp *res = g_new0 (GsfTimestamp, 1);
59 	res->timet = -1;
60 	return res;
61 }
62 
63 /**
64  * gsf_timestamp_copy:
65  * @stamp: timestamp to be copied
66  *
67  * Copies a timestamp.
68  *
69  * Returns: a separate copy of @stamp.
70  */
71 GsfTimestamp *
gsf_timestamp_copy(GsfTimestamp const * stamp)72 gsf_timestamp_copy (GsfTimestamp const *stamp)
73 {
74 	GsfTimestamp *res = gsf_timestamp_new ();
75 	res->timet = stamp->timet;
76 	return res;
77 }
78 
79 /**
80  * gsf_timestamp_free:
81  * @stamp: timestamp to be freed
82  *
83  * Releases the memory in use for @stamp (if any).
84  **/
85 void
gsf_timestamp_free(GsfTimestamp * stamp)86 gsf_timestamp_free (GsfTimestamp *stamp)
87 {
88 	g_free (stamp);
89 }
90 
91 /**
92  * gsf_timestamp_load_from_string:
93  * @stamp: #GsfTimestamp
94  * @spec: The string to parse
95  *
96  * Very simple parser for time stamps.  Currently requires a format of
97  * 	'YYYY-MM-DDThh:mm:ss'
98  * and does only rudimentary range checking
99  *
100  * Since: 1.14.24
101  *
102  * Returns: %TRUE on success
103  **/
104 int
gsf_timestamp_load_from_string(GsfTimestamp * stamp,char const * spec)105 gsf_timestamp_load_from_string (GsfTimestamp *stamp, char const *spec)
106 {
107 	guint year, month, day, hour, minute;
108 	float second;
109 	GDateTime *dt;
110 
111 	/* 'YYYY-MM-DDThh:mm:ss' */
112 	if (6 != sscanf (spec, "%u-%u-%uT%u:%u:%f",
113 			 &year, &month, &day, &hour, &minute, &second))
114 		return FALSE;
115 
116 	/* g_date_time_new_utc documentation says: */
117 	/* It not considered a programmer error for the values to this function to be out of range,*/
118 	/* but in the case that they are, the function will return NULL. */
119 	/* Nevertheless it seems to fail on values that are extremely out of range, see bug #702671 */
120 	if (second < 0.0 || second >= 60.0)
121 		return FALSE;
122 	if (minute > 59 || hour > 23)
123 		return FALSE;
124 	if (day > 32 || month > 12 || year > 9999)
125 		return FALSE;
126 
127 	dt = g_date_time_new_utc ((int)year, (int)month, (int)day, (int)hour, (int)minute, second);
128 	if (!dt)
129 		return FALSE;
130 
131 	stamp->timet = g_date_time_to_unix (dt);
132 
133 	g_date_time_unref (dt);
134 	return TRUE;
135 }
136 
137 /**
138  * gsf_timestamp_from_string: (skip)
139  * @spec: The string to parse
140  * @stamp: #GsfTimestamp
141  *
142  * Very simple parser for time stamps.  Currently requires a format of
143  * 	'YYYY-MM-DDThh:mm:ss'
144  * and does no bounds checking.
145  *
146  * Deprecated: 1.14.24, use gsf_timestamp_load_from_string
147  *
148  * Returns: %TRUE on success
149  **/
150 int
gsf_timestamp_from_string(char const * spec,GsfTimestamp * stamp)151 gsf_timestamp_from_string (char const *spec, GsfTimestamp *stamp)
152 {
153 	return gsf_timestamp_load_from_string (stamp, spec);
154 }
155 
156 /**
157  * gsf_timestamp_parse: (skip)
158  * @spec: The string to parse
159  * @stamp: #GsfTimestamp
160  *
161  * Very simple parser for time stamps.  Currently requires a format of
162  * 	'YYYY-MM-DDThh:mm:ss'
163  * and does no bounds checking.
164  *
165  * Deprecated: Use gsf_timestamp_load_from_string
166  *
167  * Returns: %TRUE on success
168  **/
169 int
gsf_timestamp_parse(char const * spec,GsfTimestamp * stamp)170 gsf_timestamp_parse (char const *spec, GsfTimestamp *stamp)
171 {
172 	return gsf_timestamp_load_from_string (stamp, spec);
173 }
174 
175 /**
176  * gsf_timestamp_as_string:
177  * @stamp: timestamp to be converted.
178  *
179  * Produce a string representation (ISO 8601 format) of @stamp.
180  *
181  * Returns: a string representation of @stamp. When @stamp is %NULL, the
182  * representation is "&lt;invalid&gt;".
183  */
184 char *
gsf_timestamp_as_string(GsfTimestamp const * stamp)185 gsf_timestamp_as_string	(GsfTimestamp const *stamp)
186 {
187 	time_t    t;
188 	struct tm tm;
189 
190 	g_return_val_if_fail (stamp != NULL, g_strdup ("<invalid>"));
191 
192 	t = stamp->timet;	/* Use an honest time_t for gmtime_r.  */
193 #ifdef HAVE_GMTIME_R
194 	gmtime_r (&t, &tm);
195 #else
196 	/* -NOT- thread-safe */
197 	tm = *gmtime (&t);
198 #endif
199 
200 
201 	/* using 'YYYY-MM-DDThh:mm:ss' */
202 	return g_strdup_printf ("%4d-%02d-%02dT%02d:%02d:%02dZ",
203 		tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
204 		tm.tm_hour, tm.tm_min, tm.tm_sec);
205 }
206 
207 guint
gsf_timestamp_hash(GsfTimestamp const * stamp)208 gsf_timestamp_hash (GsfTimestamp const *stamp)
209 {
210 	return stamp->timet;
211 }
212 
213 /**
214  * gsf_timestamp_equal:
215  * @a: a timestamp
216  * @b: another timestamp
217  *
218  * Compare timestamps @a and @b.
219  *
220  * Returns: true if @a and @b represent the same point in time; false otherwise.
221  *
222  **/
223 gboolean
gsf_timestamp_equal(GsfTimestamp const * a,GsfTimestamp const * b)224 gsf_timestamp_equal (GsfTimestamp const *a, GsfTimestamp const *b)
225 {
226 	return a->timet == b->timet;
227 }
228 
229 /**
230  * gsf_timestamp_to_value:
231  * @stamp: #GsfTimestamp
232  * @value: #GValue
233  *
234  * Calls g_value_set_box (value, stamp);
235  *
236  * Since: 1.14.24
237  **/
238 void
gsf_timestamp_to_value(GsfTimestamp const * stamp,GValue * value)239 gsf_timestamp_to_value (GsfTimestamp const *stamp, GValue *value)
240 {
241 	g_value_set_boxed (value, stamp);
242 }
243 
244 /**
245  * gsf_value_set_timestamp: (skip)
246  * @value: #GValue
247  * @stamp: #GsfTimestamp
248  *
249  * Deprecated: 1.14.24, use gsf_timestamp_to_value.
250  **/
251 void
gsf_value_set_timestamp(GValue * value,GsfTimestamp const * stamp)252 gsf_value_set_timestamp (GValue *value, GsfTimestamp const *stamp)
253 {
254 	g_value_set_boxed (value, stamp);
255 }
256 
257 void
gsf_timestamp_set_time(GsfTimestamp * stamp,guint64 t)258 gsf_timestamp_set_time (GsfTimestamp *stamp, guint64 t)
259 {
260 	stamp->timet = t;
261 }
262