1 | /* GLIB - Library of useful routines for C programming |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | /* |
19 | * Modified by the GLib Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GLib Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GLib at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | /* |
26 | * MT safe |
27 | */ |
28 | |
29 | #include "config.h" |
30 | #include "glibconfig.h" |
31 | |
32 | #define DEBUG_MSG(x) /* */ |
33 | #ifdef G_ENABLE_DEBUG |
34 | /* #define DEBUG_MSG(args) g_message args ; */ |
35 | #endif |
36 | |
37 | #include <time.h> |
38 | #include <string.h> |
39 | #include <stdlib.h> |
40 | #include <locale.h> |
41 | |
42 | #ifdef G_OS_WIN32 |
43 | #include <windows.h> |
44 | #endif |
45 | |
46 | #include "gdate.h" |
47 | |
48 | #include "gconvert.h" |
49 | #include "gmem.h" |
50 | #include "gstrfuncs.h" |
51 | #include "gtestutils.h" |
52 | #include "gthread.h" |
53 | #include "gunicode.h" |
54 | |
55 | #ifdef G_OS_WIN32 |
56 | #include "garray.h" |
57 | #endif |
58 | |
59 | /** |
60 | * SECTION:date |
61 | * @title: Date and Time Functions |
62 | * @short_description: calendrical calculations and miscellaneous time stuff |
63 | * |
64 | * The #GDate data structure represents a day between January 1, Year 1, |
65 | * and sometime a few thousand years in the future (right now it will go |
66 | * to the year 65535 or so, but g_date_set_parse() only parses up to the |
67 | * year 8000 or so - just count on "a few thousand"). #GDate is meant to |
68 | * represent everyday dates, not astronomical dates or historical dates |
69 | * or ISO timestamps or the like. It extrapolates the current Gregorian |
70 | * calendar forward and backward in time; there is no attempt to change |
71 | * the calendar to match time periods or locations. #GDate does not store |
72 | * time information; it represents a day. |
73 | * |
74 | * The #GDate implementation has several nice features; it is only a |
75 | * 64-bit struct, so storing large numbers of dates is very efficient. It |
76 | * can keep both a Julian and day-month-year representation of the date, |
77 | * since some calculations are much easier with one representation or the |
78 | * other. A Julian representation is simply a count of days since some |
79 | * fixed day in the past; for #GDate the fixed day is January 1, 1 AD. |
80 | * ("Julian" dates in the #GDate API aren't really Julian dates in the |
81 | * technical sense; technically, Julian dates count from the start of the |
82 | * Julian period, Jan 1, 4713 BC). |
83 | * |
84 | * #GDate is simple to use. First you need a "blank" date; you can get a |
85 | * dynamically allocated date from g_date_new(), or you can declare an |
86 | * automatic variable or array and initialize it by |
87 | * calling g_date_clear(). A cleared date is safe; it's safe to call |
88 | * g_date_set_dmy() and the other mutator functions to initialize the |
89 | * value of a cleared date. However, a cleared date is initially |
90 | * invalid, meaning that it doesn't represent a day that exists. |
91 | * It is undefined to call any of the date calculation routines on an |
92 | * invalid date. If you obtain a date from a user or other |
93 | * unpredictable source, you should check its validity with the |
94 | * g_date_valid() predicate. g_date_valid() is also used to check for |
95 | * errors with g_date_set_parse() and other functions that can |
96 | * fail. Dates can be invalidated by calling g_date_clear() again. |
97 | * |
98 | * It is very important to use the API to access the #GDate |
99 | * struct. Often only the day-month-year or only the Julian |
100 | * representation is valid. Sometimes neither is valid. Use the API. |
101 | * |
102 | * GLib also features #GDateTime which represents a precise time. |
103 | */ |
104 | |
105 | /** |
106 | * G_USEC_PER_SEC: |
107 | * |
108 | * Number of microseconds in one second (1 million). |
109 | * This macro is provided for code readability. |
110 | */ |
111 | |
112 | /** |
113 | * GTimeVal: |
114 | * @tv_sec: seconds |
115 | * @tv_usec: microseconds |
116 | * |
117 | * Represents a precise time, with seconds and microseconds. |
118 | * Similar to the struct timeval returned by the gettimeofday() |
119 | * UNIX system call. |
120 | * |
121 | * GLib is attempting to unify around the use of 64-bit integers to |
122 | * represent microsecond-precision time. As such, this type will be |
123 | * removed from a future version of GLib. A consequence of using `glong` for |
124 | * `tv_sec` is that on 32-bit systems `GTimeVal` is subject to the year 2038 |
125 | * problem. |
126 | * |
127 | * Deprecated: 2.62: Use #GDateTime or #guint64 instead. |
128 | */ |
129 | |
130 | /** |
131 | * GDate: |
132 | * @julian_days: the Julian representation of the date |
133 | * @julian: this bit is set if @julian_days is valid |
134 | * @dmy: this is set if @day, @month and @year are valid |
135 | * @day: the day of the day-month-year representation of the date, |
136 | * as a number between 1 and 31 |
137 | * @month: the day of the day-month-year representation of the date, |
138 | * as a number between 1 and 12 |
139 | * @year: the day of the day-month-year representation of the date |
140 | * |
141 | * Represents a day between January 1, Year 1 and a few thousand years in |
142 | * the future. None of its members should be accessed directly. |
143 | * |
144 | * If the #GDate-struct is obtained from g_date_new(), it will be safe |
145 | * to mutate but invalid and thus not safe for calendrical computations. |
146 | * |
147 | * If it's declared on the stack, it will contain garbage so must be |
148 | * initialized with g_date_clear(). g_date_clear() makes the date invalid |
149 | * but safe. An invalid date doesn't represent a day, it's "empty." A date |
150 | * becomes valid after you set it to a Julian day or you set a day, month, |
151 | * and year. |
152 | */ |
153 | |
154 | /** |
155 | * GTime: |
156 | * |
157 | * Simply a replacement for `time_t`. It has been deprecated |
158 | * since it is not equivalent to `time_t` on 64-bit platforms |
159 | * with a 64-bit `time_t`. Unrelated to #GTimer. |
160 | * |
161 | * Note that #GTime is defined to always be a 32-bit integer, |
162 | * unlike `time_t` which may be 64-bit on some systems. Therefore, |
163 | * #GTime will overflow in the year 2038, and you cannot use the |
164 | * address of a #GTime variable as argument to the UNIX time() |
165 | * function. |
166 | * |
167 | * Instead, do the following: |
168 | * |[<!-- language="C" --> |
169 | * time_t ttime; |
170 | * GTime gtime; |
171 | * |
172 | * time (&ttime); |
173 | * gtime = (GTime)ttime; |
174 | * ]| |
175 | * |
176 | * Deprecated: 2.62: This is not [Y2038-safe](https://en.wikipedia.org/wiki/Year_2038_problem). |
177 | * Use #GDateTime or #time_t instead. |
178 | */ |
179 | |
180 | /** |
181 | * GDateDMY: |
182 | * @G_DATE_DAY: a day |
183 | * @G_DATE_MONTH: a month |
184 | * @G_DATE_YEAR: a year |
185 | * |
186 | * This enumeration isn't used in the API, but may be useful if you need |
187 | * to mark a number as a day, month, or year. |
188 | */ |
189 | |
190 | /** |
191 | * GDateDay: |
192 | * |
193 | * Integer representing a day of the month; between 1 and 31. |
194 | * #G_DATE_BAD_DAY represents an invalid day of the month. |
195 | */ |
196 | |
197 | /** |
198 | * GDateMonth: |
199 | * @G_DATE_BAD_MONTH: invalid value |
200 | * @G_DATE_JANUARY: January |
201 | * @G_DATE_FEBRUARY: February |
202 | * @G_DATE_MARCH: March |
203 | * @G_DATE_APRIL: April |
204 | * @G_DATE_MAY: May |
205 | * @G_DATE_JUNE: June |
206 | * @G_DATE_JULY: July |
207 | * @G_DATE_AUGUST: August |
208 | * @G_DATE_SEPTEMBER: September |
209 | * @G_DATE_OCTOBER: October |
210 | * @G_DATE_NOVEMBER: November |
211 | * @G_DATE_DECEMBER: December |
212 | * |
213 | * Enumeration representing a month; values are #G_DATE_JANUARY, |
214 | * #G_DATE_FEBRUARY, etc. #G_DATE_BAD_MONTH is the invalid value. |
215 | */ |
216 | |
217 | /** |
218 | * GDateYear: |
219 | * |
220 | * Integer representing a year; #G_DATE_BAD_YEAR is the invalid |
221 | * value. The year must be 1 or higher; negative (BC) years are not |
222 | * allowed. The year is represented with four digits. |
223 | */ |
224 | |
225 | /** |
226 | * GDateWeekday: |
227 | * @G_DATE_BAD_WEEKDAY: invalid value |
228 | * @G_DATE_MONDAY: Monday |
229 | * @G_DATE_TUESDAY: Tuesday |
230 | * @G_DATE_WEDNESDAY: Wednesday |
231 | * @G_DATE_THURSDAY: Thursday |
232 | * @G_DATE_FRIDAY: Friday |
233 | * @G_DATE_SATURDAY: Saturday |
234 | * @G_DATE_SUNDAY: Sunday |
235 | * |
236 | * Enumeration representing a day of the week; #G_DATE_MONDAY, |
237 | * #G_DATE_TUESDAY, etc. #G_DATE_BAD_WEEKDAY is an invalid weekday. |
238 | */ |
239 | |
240 | /** |
241 | * G_DATE_BAD_DAY: |
242 | * |
243 | * Represents an invalid #GDateDay. |
244 | */ |
245 | |
246 | /** |
247 | * G_DATE_BAD_JULIAN: |
248 | * |
249 | * Represents an invalid Julian day number. |
250 | */ |
251 | |
252 | /** |
253 | * G_DATE_BAD_YEAR: |
254 | * |
255 | * Represents an invalid year. |
256 | */ |
257 | |
258 | /** |
259 | * g_date_new: |
260 | * |
261 | * Allocates a #GDate and initializes |
262 | * it to a safe state. The new date will |
263 | * be cleared (as if you'd called g_date_clear()) but invalid (it won't |
264 | * represent an existing day). Free the return value with g_date_free(). |
265 | * |
266 | * Returns: a newly-allocated #GDate |
267 | */ |
268 | GDate* |
269 | g_date_new (void) |
270 | { |
271 | GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */ |
272 | |
273 | return d; |
274 | } |
275 | |
276 | /** |
277 | * g_date_new_dmy: |
278 | * @day: day of the month |
279 | * @month: month of the year |
280 | * @year: year |
281 | * |
282 | * Like g_date_new(), but also sets the value of the date. Assuming the |
283 | * day-month-year triplet you pass in represents an existing day, the |
284 | * returned date will be valid. |
285 | * |
286 | * Returns: a newly-allocated #GDate initialized with @day, @month, and @year |
287 | */ |
288 | GDate* |
289 | g_date_new_dmy (GDateDay day, |
290 | GDateMonth m, |
291 | GDateYear y) |
292 | { |
293 | GDate *d; |
294 | g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL); |
295 | |
296 | d = g_new (GDate, 1); |
297 | |
298 | d->julian = FALSE; |
299 | d->dmy = TRUE; |
300 | |
301 | d->month = m; |
302 | d->day = day; |
303 | d->year = y; |
304 | |
305 | g_assert (g_date_valid (d)); |
306 | |
307 | return d; |
308 | } |
309 | |
310 | /** |
311 | * g_date_new_julian: |
312 | * @julian_day: days since January 1, Year 1 |
313 | * |
314 | * Like g_date_new(), but also sets the value of the date. Assuming the |
315 | * Julian day number you pass in is valid (greater than 0, less than an |
316 | * unreasonably large number), the returned date will be valid. |
317 | * |
318 | * Returns: a newly-allocated #GDate initialized with @julian_day |
319 | */ |
320 | GDate* |
321 | g_date_new_julian (guint32 julian_day) |
322 | { |
323 | GDate *d; |
324 | g_return_val_if_fail (g_date_valid_julian (julian_day), NULL); |
325 | |
326 | d = g_new (GDate, 1); |
327 | |
328 | d->julian = TRUE; |
329 | d->dmy = FALSE; |
330 | |
331 | d->julian_days = julian_day; |
332 | |
333 | g_assert (g_date_valid (d)); |
334 | |
335 | return d; |
336 | } |
337 | |
338 | /** |
339 | * g_date_free: |
340 | * @date: a #GDate to free |
341 | * |
342 | * Frees a #GDate returned from g_date_new(). |
343 | */ |
344 | void |
345 | g_date_free (GDate *date) |
346 | { |
347 | g_return_if_fail (date != NULL); |
348 | |
349 | g_free (mem: date); |
350 | } |
351 | |
352 | /** |
353 | * g_date_copy: |
354 | * @date: a #GDate to copy |
355 | * |
356 | * Copies a GDate to a newly-allocated GDate. If the input was invalid |
357 | * (as determined by g_date_valid()), the invalid state will be copied |
358 | * as is into the new object. |
359 | * |
360 | * Returns: (transfer full): a newly-allocated #GDate initialized from @date |
361 | * |
362 | * Since: 2.56 |
363 | */ |
364 | GDate * |
365 | g_date_copy (const GDate *date) |
366 | { |
367 | GDate *res; |
368 | g_return_val_if_fail (date != NULL, NULL); |
369 | |
370 | if (g_date_valid (date)) |
371 | res = g_date_new_julian (julian_day: g_date_get_julian (date)); |
372 | else |
373 | { |
374 | res = g_date_new (); |
375 | *res = *date; |
376 | } |
377 | |
378 | return res; |
379 | } |
380 | |
381 | /** |
382 | * g_date_valid: |
383 | * @date: a #GDate to check |
384 | * |
385 | * Returns %TRUE if the #GDate represents an existing day. The date must not |
386 | * contain garbage; it should have been initialized with g_date_clear() |
387 | * if it wasn't allocated by one of the g_date_new() variants. |
388 | * |
389 | * Returns: Whether the date is valid |
390 | */ |
391 | gboolean |
392 | g_date_valid (const GDate *d) |
393 | { |
394 | g_return_val_if_fail (d != NULL, FALSE); |
395 | |
396 | return (d->julian || d->dmy); |
397 | } |
398 | |
399 | static const guint8 days_in_months[2][13] = |
400 | { /* error, jan feb mar apr may jun jul aug sep oct nov dec */ |
401 | { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, |
402 | { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */ |
403 | }; |
404 | |
405 | static const guint16 days_in_year[2][14] = |
406 | { /* 0, jan feb mar apr may jun jul aug sep oct nov dec */ |
407 | { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, |
408 | { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } |
409 | }; |
410 | |
411 | /** |
412 | * g_date_valid_month: |
413 | * @month: month |
414 | * |
415 | * Returns %TRUE if the month value is valid. The 12 #GDateMonth |
416 | * enumeration values are the only valid months. |
417 | * |
418 | * Returns: %TRUE if the month is valid |
419 | */ |
420 | gboolean |
421 | g_date_valid_month (GDateMonth m) |
422 | { |
423 | return (((gint) m > G_DATE_BAD_MONTH) && ((gint) m < 13)); |
424 | } |
425 | |
426 | /** |
427 | * g_date_valid_year: |
428 | * @year: year |
429 | * |
430 | * Returns %TRUE if the year is valid. Any year greater than 0 is valid, |
431 | * though there is a 16-bit limit to what #GDate will understand. |
432 | * |
433 | * Returns: %TRUE if the year is valid |
434 | */ |
435 | gboolean |
436 | g_date_valid_year (GDateYear y) |
437 | { |
438 | return ( y > G_DATE_BAD_YEAR ); |
439 | } |
440 | |
441 | /** |
442 | * g_date_valid_day: |
443 | * @day: day to check |
444 | * |
445 | * Returns %TRUE if the day of the month is valid (a day is valid if it's |
446 | * between 1 and 31 inclusive). |
447 | * |
448 | * Returns: %TRUE if the day is valid |
449 | */ |
450 | |
451 | gboolean |
452 | g_date_valid_day (GDateDay d) |
453 | { |
454 | return ( (d > G_DATE_BAD_DAY) && (d < 32) ); |
455 | } |
456 | |
457 | /** |
458 | * g_date_valid_weekday: |
459 | * @weekday: weekday |
460 | * |
461 | * Returns %TRUE if the weekday is valid. The seven #GDateWeekday enumeration |
462 | * values are the only valid weekdays. |
463 | * |
464 | * Returns: %TRUE if the weekday is valid |
465 | */ |
466 | gboolean |
467 | g_date_valid_weekday (GDateWeekday w) |
468 | { |
469 | return (((gint) w > G_DATE_BAD_WEEKDAY) && ((gint) w < 8)); |
470 | } |
471 | |
472 | /** |
473 | * g_date_valid_julian: |
474 | * @julian_date: Julian day to check |
475 | * |
476 | * Returns %TRUE if the Julian day is valid. Anything greater than zero |
477 | * is basically a valid Julian, though there is a 32-bit limit. |
478 | * |
479 | * Returns: %TRUE if the Julian day is valid |
480 | */ |
481 | gboolean |
482 | g_date_valid_julian (guint32 j) |
483 | { |
484 | return (j > G_DATE_BAD_JULIAN); |
485 | } |
486 | |
487 | /** |
488 | * g_date_valid_dmy: |
489 | * @day: day |
490 | * @month: month |
491 | * @year: year |
492 | * |
493 | * Returns %TRUE if the day-month-year triplet forms a valid, existing day |
494 | * in the range of days #GDate understands (Year 1 or later, no more than |
495 | * a few thousand years in the future). |
496 | * |
497 | * Returns: %TRUE if the date is a valid one |
498 | */ |
499 | gboolean |
500 | g_date_valid_dmy (GDateDay d, |
501 | GDateMonth m, |
502 | GDateYear y) |
503 | { |
504 | /* No need to check the upper bound of @y, because #GDateYear is 16 bits wide, |
505 | * just like #GDate.year. */ |
506 | return ( (m > G_DATE_BAD_MONTH) && |
507 | (m < 13) && |
508 | (d > G_DATE_BAD_DAY) && |
509 | (y > G_DATE_BAD_YEAR) && /* must check before using g_date_is_leap_year */ |
510 | (d <= (g_date_is_leap_year (year: y) ? |
511 | days_in_months[1][m] : days_in_months[0][m])) ); |
512 | } |
513 | |
514 | |
515 | /* "Julian days" just means an absolute number of days, where Day 1 == |
516 | * Jan 1, Year 1 |
517 | */ |
518 | static void |
519 | g_date_update_julian (const GDate *const_d) |
520 | { |
521 | GDate *d = (GDate *) const_d; |
522 | GDateYear year; |
523 | gint idx; |
524 | |
525 | g_return_if_fail (d != NULL); |
526 | g_return_if_fail (d->dmy != 0); |
527 | g_return_if_fail (!d->julian); |
528 | g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year)); |
529 | |
530 | /* What we actually do is: multiply years * 365 days in the year, |
531 | * add the number of years divided by 4, subtract the number of |
532 | * years divided by 100 and add the number of years divided by 400, |
533 | * which accounts for leap year stuff. Code from Steffen Beyer's |
534 | * DateCalc. |
535 | */ |
536 | |
537 | year = d->year - 1; /* we know d->year > 0 since it's valid */ |
538 | |
539 | d->julian_days = year * 365U; |
540 | d->julian_days += (year >>= 2); /* divide by 4 and add */ |
541 | d->julian_days -= (year /= 25); /* divides original # years by 100 */ |
542 | d->julian_days += year >> 2; /* divides by 4, which divides original by 400 */ |
543 | |
544 | idx = g_date_is_leap_year (year: d->year) ? 1 : 0; |
545 | |
546 | d->julian_days += days_in_year[idx][d->month] + d->day; |
547 | |
548 | g_return_if_fail (g_date_valid_julian (d->julian_days)); |
549 | |
550 | d->julian = TRUE; |
551 | } |
552 | |
553 | static void |
554 | g_date_update_dmy (const GDate *const_d) |
555 | { |
556 | GDate *d = (GDate *) const_d; |
557 | GDateYear y; |
558 | GDateMonth m; |
559 | GDateDay day; |
560 | |
561 | guint32 A, B, C, D, E, M; |
562 | |
563 | g_return_if_fail (d != NULL); |
564 | g_return_if_fail (d->julian); |
565 | g_return_if_fail (!d->dmy); |
566 | g_return_if_fail (g_date_valid_julian (d->julian_days)); |
567 | |
568 | /* Formula taken from the Calendar FAQ; the formula was for the |
569 | * Julian Period which starts on 1 January 4713 BC, so we add |
570 | * 1,721,425 to the number of days before doing the formula. |
571 | * |
572 | * I'm sure this can be simplified for our 1 January 1 AD period |
573 | * start, but I can't figure out how to unpack the formula. |
574 | */ |
575 | |
576 | A = d->julian_days + 1721425 + 32045; |
577 | B = ( 4 *(A + 36524) )/ 146097 - 1; |
578 | C = A - (146097 * B)/4; |
579 | D = ( 4 * (C + 365) ) / 1461 - 1; |
580 | E = C - ((1461*D) / 4); |
581 | M = (5 * (E - 1) + 2)/153; |
582 | |
583 | m = M + 3 - (12*(M/10)); |
584 | day = E - (153*M + 2)/5; |
585 | y = 100 * B + D - 4800 + (M/10); |
586 | |
587 | #ifdef G_ENABLE_DEBUG |
588 | if (!g_date_valid_dmy (d: day, m, y)) |
589 | g_warning ("OOPS julian: %u computed dmy: %u %u %u" , |
590 | d->julian_days, day, m, y); |
591 | #endif |
592 | |
593 | d->month = m; |
594 | d->day = day; |
595 | d->year = y; |
596 | |
597 | d->dmy = TRUE; |
598 | } |
599 | |
600 | /** |
601 | * g_date_get_weekday: |
602 | * @date: a #GDate |
603 | * |
604 | * Returns the day of the week for a #GDate. The date must be valid. |
605 | * |
606 | * Returns: day of the week as a #GDateWeekday. |
607 | */ |
608 | GDateWeekday |
609 | g_date_get_weekday (const GDate *d) |
610 | { |
611 | g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY); |
612 | |
613 | if (!d->julian) |
614 | g_date_update_julian (const_d: d); |
615 | |
616 | g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY); |
617 | |
618 | return ((d->julian_days - 1) % 7) + 1; |
619 | } |
620 | |
621 | /** |
622 | * g_date_get_month: |
623 | * @date: a #GDate to get the month from |
624 | * |
625 | * Returns the month of the year. The date must be valid. |
626 | * |
627 | * Returns: month of the year as a #GDateMonth |
628 | */ |
629 | GDateMonth |
630 | g_date_get_month (const GDate *d) |
631 | { |
632 | g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH); |
633 | |
634 | if (!d->dmy) |
635 | g_date_update_dmy (const_d: d); |
636 | |
637 | g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH); |
638 | |
639 | return d->month; |
640 | } |
641 | |
642 | /** |
643 | * g_date_get_year: |
644 | * @date: a #GDate |
645 | * |
646 | * Returns the year of a #GDate. The date must be valid. |
647 | * |
648 | * Returns: year in which the date falls |
649 | */ |
650 | GDateYear |
651 | g_date_get_year (const GDate *d) |
652 | { |
653 | g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR); |
654 | |
655 | if (!d->dmy) |
656 | g_date_update_dmy (const_d: d); |
657 | |
658 | g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR); |
659 | |
660 | return d->year; |
661 | } |
662 | |
663 | /** |
664 | * g_date_get_day: |
665 | * @date: a #GDate to extract the day of the month from |
666 | * |
667 | * Returns the day of the month. The date must be valid. |
668 | * |
669 | * Returns: day of the month |
670 | */ |
671 | GDateDay |
672 | g_date_get_day (const GDate *d) |
673 | { |
674 | g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY); |
675 | |
676 | if (!d->dmy) |
677 | g_date_update_dmy (const_d: d); |
678 | |
679 | g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY); |
680 | |
681 | return d->day; |
682 | } |
683 | |
684 | /** |
685 | * g_date_get_julian: |
686 | * @date: a #GDate to extract the Julian day from |
687 | * |
688 | * Returns the Julian day or "serial number" of the #GDate. The |
689 | * Julian day is simply the number of days since January 1, Year 1; i.e., |
690 | * January 1, Year 1 is Julian day 1; January 2, Year 1 is Julian day 2, |
691 | * etc. The date must be valid. |
692 | * |
693 | * Returns: Julian day |
694 | */ |
695 | guint32 |
696 | g_date_get_julian (const GDate *d) |
697 | { |
698 | g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN); |
699 | |
700 | if (!d->julian) |
701 | g_date_update_julian (const_d: d); |
702 | |
703 | g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN); |
704 | |
705 | return d->julian_days; |
706 | } |
707 | |
708 | /** |
709 | * g_date_get_day_of_year: |
710 | * @date: a #GDate to extract day of year from |
711 | * |
712 | * Returns the day of the year, where Jan 1 is the first day of the |
713 | * year. The date must be valid. |
714 | * |
715 | * Returns: day of the year |
716 | */ |
717 | guint |
718 | g_date_get_day_of_year (const GDate *d) |
719 | { |
720 | gint idx; |
721 | |
722 | g_return_val_if_fail (g_date_valid (d), 0); |
723 | |
724 | if (!d->dmy) |
725 | g_date_update_dmy (const_d: d); |
726 | |
727 | g_return_val_if_fail (d->dmy, 0); |
728 | |
729 | idx = g_date_is_leap_year (year: d->year) ? 1 : 0; |
730 | |
731 | return (days_in_year[idx][d->month] + d->day); |
732 | } |
733 | |
734 | /** |
735 | * g_date_get_monday_week_of_year: |
736 | * @date: a #GDate |
737 | * |
738 | * Returns the week of the year, where weeks are understood to start on |
739 | * Monday. If the date is before the first Monday of the year, return 0. |
740 | * The date must be valid. |
741 | * |
742 | * Returns: week of the year |
743 | */ |
744 | guint |
745 | g_date_get_monday_week_of_year (const GDate *d) |
746 | { |
747 | GDateWeekday wd; |
748 | guint day; |
749 | GDate first; |
750 | |
751 | g_return_val_if_fail (g_date_valid (d), 0); |
752 | |
753 | if (!d->dmy) |
754 | g_date_update_dmy (const_d: d); |
755 | |
756 | g_return_val_if_fail (d->dmy, 0); |
757 | |
758 | g_date_clear (date: &first, n_dates: 1); |
759 | |
760 | g_date_set_dmy (date: &first, day: 1, month: 1, y: d->year); |
761 | |
762 | wd = g_date_get_weekday (d: &first) - 1; /* make Monday day 0 */ |
763 | day = g_date_get_day_of_year (d) - 1; |
764 | |
765 | return ((day + wd)/7U + (wd == 0 ? 1 : 0)); |
766 | } |
767 | |
768 | /** |
769 | * g_date_get_sunday_week_of_year: |
770 | * @date: a #GDate |
771 | * |
772 | * Returns the week of the year during which this date falls, if |
773 | * weeks are understood to begin on Sunday. The date must be valid. |
774 | * Can return 0 if the day is before the first Sunday of the year. |
775 | * |
776 | * Returns: week number |
777 | */ |
778 | guint |
779 | g_date_get_sunday_week_of_year (const GDate *d) |
780 | { |
781 | GDateWeekday wd; |
782 | guint day; |
783 | GDate first; |
784 | |
785 | g_return_val_if_fail (g_date_valid (d), 0); |
786 | |
787 | if (!d->dmy) |
788 | g_date_update_dmy (const_d: d); |
789 | |
790 | g_return_val_if_fail (d->dmy, 0); |
791 | |
792 | g_date_clear (date: &first, n_dates: 1); |
793 | |
794 | g_date_set_dmy (date: &first, day: 1, month: 1, y: d->year); |
795 | |
796 | wd = g_date_get_weekday (d: &first); |
797 | if (wd == 7) wd = 0; /* make Sunday day 0 */ |
798 | day = g_date_get_day_of_year (d) - 1; |
799 | |
800 | return ((day + wd)/7U + (wd == 0 ? 1 : 0)); |
801 | } |
802 | |
803 | /** |
804 | * g_date_get_iso8601_week_of_year: |
805 | * @date: a valid #GDate |
806 | * |
807 | * Returns the week of the year, where weeks are interpreted according |
808 | * to ISO 8601. |
809 | * |
810 | * Returns: ISO 8601 week number of the year. |
811 | * |
812 | * Since: 2.6 |
813 | **/ |
814 | guint |
815 | g_date_get_iso8601_week_of_year (const GDate *d) |
816 | { |
817 | guint j, d4, L, d1, w; |
818 | |
819 | g_return_val_if_fail (g_date_valid (d), 0); |
820 | |
821 | if (!d->julian) |
822 | g_date_update_julian (const_d: d); |
823 | |
824 | g_return_val_if_fail (d->julian, 0); |
825 | |
826 | /* Formula taken from the Calendar FAQ; the formula was for the |
827 | * Julian Period which starts on 1 January 4713 BC, so we add |
828 | * 1,721,425 to the number of days before doing the formula. |
829 | */ |
830 | j = d->julian_days + 1721425; |
831 | d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461; |
832 | L = d4 / 1460; |
833 | d1 = ((d4 - L) % 365) + L; |
834 | w = d1 / 7 + 1; |
835 | |
836 | return w; |
837 | } |
838 | |
839 | /** |
840 | * g_date_days_between: |
841 | * @date1: the first date |
842 | * @date2: the second date |
843 | * |
844 | * Computes the number of days between two dates. |
845 | * If @date2 is prior to @date1, the returned value is negative. |
846 | * Both dates must be valid. |
847 | * |
848 | * Returns: the number of days between @date1 and @date2 |
849 | */ |
850 | gint |
851 | g_date_days_between (const GDate *d1, |
852 | const GDate *d2) |
853 | { |
854 | g_return_val_if_fail (g_date_valid (d1), 0); |
855 | g_return_val_if_fail (g_date_valid (d2), 0); |
856 | |
857 | return (gint)g_date_get_julian (d: d2) - (gint)g_date_get_julian (d: d1); |
858 | } |
859 | |
860 | /** |
861 | * g_date_clear: |
862 | * @date: pointer to one or more dates to clear |
863 | * @n_dates: number of dates to clear |
864 | * |
865 | * Initializes one or more #GDate structs to a safe but invalid |
866 | * state. The cleared dates will not represent an existing date, but will |
867 | * not contain garbage. Useful to init a date declared on the stack. |
868 | * Validity can be tested with g_date_valid(). |
869 | */ |
870 | void |
871 | g_date_clear (GDate *d, guint ndates) |
872 | { |
873 | g_return_if_fail (d != NULL); |
874 | g_return_if_fail (ndates != 0); |
875 | |
876 | memset (s: d, c: 0x0, n: ndates*sizeof (GDate)); |
877 | } |
878 | |
879 | G_LOCK_DEFINE_STATIC (g_date_global); |
880 | |
881 | /* These are for the parser, output to the user should use * |
882 | * g_date_strftime () - this creates more never-freed memory to annoy |
883 | * all those memory debugger users. :-) |
884 | */ |
885 | |
886 | static gchar *long_month_names[13] = |
887 | { |
888 | NULL, |
889 | }; |
890 | |
891 | static gchar *long_month_names_alternative[13] = |
892 | { |
893 | NULL, |
894 | }; |
895 | |
896 | static gchar *short_month_names[13] = |
897 | { |
898 | NULL, |
899 | }; |
900 | |
901 | static gchar *short_month_names_alternative[13] = |
902 | { |
903 | NULL, |
904 | }; |
905 | |
906 | /* This tells us if we need to update the parse info */ |
907 | static gchar *current_locale = NULL; |
908 | |
909 | /* order of these in the current locale */ |
910 | static GDateDMY dmy_order[3] = |
911 | { |
912 | G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR |
913 | }; |
914 | |
915 | /* Where to chop two-digit years: i.e., for the 1930 default, numbers |
916 | * 29 and below are counted as in the year 2000, numbers 30 and above |
917 | * are counted as in the year 1900. |
918 | */ |
919 | |
920 | static const GDateYear twodigit_start_year = 1930; |
921 | |
922 | /* It is impossible to enter a year between 1 AD and 99 AD with this |
923 | * in effect. |
924 | */ |
925 | static gboolean using_twodigit_years = FALSE; |
926 | |
927 | /* Adjustment of locale era to AD, non-zero means using locale era |
928 | */ |
929 | static gint locale_era_adjust = 0; |
930 | |
931 | struct _GDateParseTokens { |
932 | gint num_ints; |
933 | gint n[3]; |
934 | guint month; |
935 | }; |
936 | |
937 | typedef struct _GDateParseTokens GDateParseTokens; |
938 | |
939 | static inline gboolean |
940 | update_month_match (gsize *longest, |
941 | const gchar *haystack, |
942 | const gchar *needle) |
943 | { |
944 | gsize length; |
945 | |
946 | if (needle == NULL) |
947 | return FALSE; |
948 | |
949 | length = strlen (s: needle); |
950 | if (*longest >= length) |
951 | return FALSE; |
952 | |
953 | if (strstr (haystack: haystack, needle: needle) == NULL) |
954 | return FALSE; |
955 | |
956 | *longest = length; |
957 | return TRUE; |
958 | } |
959 | |
960 | #define NUM_LEN 10 |
961 | |
962 | /* HOLDS: g_date_global_lock */ |
963 | static void |
964 | g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt) |
965 | { |
966 | gchar num[4][NUM_LEN+1]; |
967 | gint i; |
968 | const guchar *s; |
969 | |
970 | /* We count 4, but store 3; so we can give an error |
971 | * if there are 4. |
972 | */ |
973 | num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0'; |
974 | |
975 | s = (const guchar *) str; |
976 | pt->num_ints = 0; |
977 | while (*s && pt->num_ints < 4) |
978 | { |
979 | |
980 | i = 0; |
981 | while (*s && g_ascii_isdigit (*s) && i < NUM_LEN) |
982 | { |
983 | num[pt->num_ints][i] = *s; |
984 | ++s; |
985 | ++i; |
986 | } |
987 | |
988 | if (i > 0) |
989 | { |
990 | num[pt->num_ints][i] = '\0'; |
991 | ++(pt->num_ints); |
992 | } |
993 | |
994 | if (*s == '\0') break; |
995 | |
996 | ++s; |
997 | } |
998 | |
999 | pt->n[0] = pt->num_ints > 0 ? atoi (nptr: num[0]) : 0; |
1000 | pt->n[1] = pt->num_ints > 1 ? atoi (nptr: num[1]) : 0; |
1001 | pt->n[2] = pt->num_ints > 2 ? atoi (nptr: num[2]) : 0; |
1002 | |
1003 | pt->month = G_DATE_BAD_MONTH; |
1004 | |
1005 | if (pt->num_ints < 3) |
1006 | { |
1007 | gsize longest = 0; |
1008 | gchar *casefold; |
1009 | gchar *normalized; |
1010 | |
1011 | casefold = g_utf8_casefold (str, len: -1); |
1012 | normalized = g_utf8_normalize (str: casefold, len: -1, mode: G_NORMALIZE_ALL); |
1013 | g_free (mem: casefold); |
1014 | |
1015 | for (i = 1; i < 13; ++i) |
1016 | { |
1017 | /* Here month names may be in a genitive case if the language |
1018 | * grammatical rules require it. |
1019 | * Examples of how January may look in some languages: |
1020 | * Catalan: "de gener", Croatian: "siječnja", Polish: "stycznia", |
1021 | * Upper Sorbian: "januara". |
1022 | * Note that most of the languages can't or don't use the the |
1023 | * genitive case here so they use nominative everywhere. |
1024 | * For example, English always uses "January". |
1025 | */ |
1026 | if (update_month_match (longest: &longest, haystack: normalized, needle: long_month_names[i])) |
1027 | pt->month = i; |
1028 | |
1029 | /* Here month names will be in a nominative case. |
1030 | * Examples of how January may look in some languages: |
1031 | * Catalan: "gener", Croatian: "Siječanj", Polish: "styczeń", |
1032 | * Upper Sorbian: "Januar". |
1033 | */ |
1034 | if (update_month_match (longest: &longest, haystack: normalized, needle: long_month_names_alternative[i])) |
1035 | pt->month = i; |
1036 | |
1037 | /* Differences between abbreviated nominative and abbreviated |
1038 | * genitive month names are visible in very few languages but |
1039 | * let's handle them. |
1040 | */ |
1041 | if (update_month_match (longest: &longest, haystack: normalized, needle: short_month_names[i])) |
1042 | pt->month = i; |
1043 | |
1044 | if (update_month_match (longest: &longest, haystack: normalized, needle: short_month_names_alternative[i])) |
1045 | pt->month = i; |
1046 | } |
1047 | |
1048 | g_free (mem: normalized); |
1049 | } |
1050 | } |
1051 | |
1052 | /* HOLDS: g_date_global_lock */ |
1053 | static void |
1054 | g_date_prepare_to_parse (const gchar *str, |
1055 | GDateParseTokens *pt) |
1056 | { |
1057 | const gchar *locale = setlocale (LC_TIME, NULL); |
1058 | gboolean recompute_localeinfo = FALSE; |
1059 | GDate d; |
1060 | |
1061 | g_return_if_fail (locale != NULL); /* should not happen */ |
1062 | |
1063 | g_date_clear (d: &d, ndates: 1); /* clear for scratch use */ |
1064 | |
1065 | if ( (current_locale == NULL) || (strcmp (s1: locale, s2: current_locale) != 0) ) |
1066 | recompute_localeinfo = TRUE; /* Uh, there used to be a reason for the temporary */ |
1067 | |
1068 | if (recompute_localeinfo) |
1069 | { |
1070 | int i = 1; |
1071 | GDateParseTokens testpt; |
1072 | gchar buf[128]; |
1073 | |
1074 | g_free (mem: current_locale); /* still works if current_locale == NULL */ |
1075 | |
1076 | current_locale = g_strdup (str: locale); |
1077 | |
1078 | short_month_names[0] = "Error" ; |
1079 | long_month_names[0] = "Error" ; |
1080 | |
1081 | while (i < 13) |
1082 | { |
1083 | gchar *casefold; |
1084 | |
1085 | g_date_set_dmy (date: &d, day: 1, month: i, y: 1976); |
1086 | |
1087 | g_return_if_fail (g_date_valid (&d)); |
1088 | |
1089 | g_date_strftime (s: buf, slen: 127, format: "%b" , date: &d); |
1090 | |
1091 | casefold = g_utf8_casefold (str: buf, len: -1); |
1092 | g_free (mem: short_month_names[i]); |
1093 | short_month_names[i] = g_utf8_normalize (str: casefold, len: -1, mode: G_NORMALIZE_ALL); |
1094 | g_free (mem: casefold); |
1095 | |
1096 | g_date_strftime (s: buf, slen: 127, format: "%B" , date: &d); |
1097 | casefold = g_utf8_casefold (str: buf, len: -1); |
1098 | g_free (mem: long_month_names[i]); |
1099 | long_month_names[i] = g_utf8_normalize (str: casefold, len: -1, mode: G_NORMALIZE_ALL); |
1100 | g_free (mem: casefold); |
1101 | |
1102 | g_date_strftime (s: buf, slen: 127, format: "%Ob" , date: &d); |
1103 | casefold = g_utf8_casefold (str: buf, len: -1); |
1104 | g_free (mem: short_month_names_alternative[i]); |
1105 | short_month_names_alternative[i] = g_utf8_normalize (str: casefold, len: -1, mode: G_NORMALIZE_ALL); |
1106 | g_free (mem: casefold); |
1107 | |
1108 | g_date_strftime (s: buf, slen: 127, format: "%OB" , date: &d); |
1109 | casefold = g_utf8_casefold (str: buf, len: -1); |
1110 | g_free (mem: long_month_names_alternative[i]); |
1111 | long_month_names_alternative[i] = g_utf8_normalize (str: casefold, len: -1, mode: G_NORMALIZE_ALL); |
1112 | g_free (mem: casefold); |
1113 | |
1114 | ++i; |
1115 | } |
1116 | |
1117 | /* Determine DMY order */ |
1118 | |
1119 | /* had to pick a random day - don't change this, some strftimes |
1120 | * are broken on some days, and this one is good so far. */ |
1121 | g_date_set_dmy (date: &d, day: 4, month: 7, y: 1976); |
1122 | |
1123 | g_date_strftime (s: buf, slen: 127, format: "%x" , date: &d); |
1124 | |
1125 | g_date_fill_parse_tokens (str: buf, pt: &testpt); |
1126 | |
1127 | using_twodigit_years = FALSE; |
1128 | locale_era_adjust = 0; |
1129 | dmy_order[0] = G_DATE_DAY; |
1130 | dmy_order[1] = G_DATE_MONTH; |
1131 | dmy_order[2] = G_DATE_YEAR; |
1132 | |
1133 | i = 0; |
1134 | while (i < testpt.num_ints) |
1135 | { |
1136 | switch (testpt.n[i]) |
1137 | { |
1138 | case 7: |
1139 | dmy_order[i] = G_DATE_MONTH; |
1140 | break; |
1141 | case 4: |
1142 | dmy_order[i] = G_DATE_DAY; |
1143 | break; |
1144 | case 76: |
1145 | using_twodigit_years = TRUE; |
1146 | G_GNUC_FALLTHROUGH; |
1147 | case 1976: |
1148 | dmy_order[i] = G_DATE_YEAR; |
1149 | break; |
1150 | default: |
1151 | /* assume locale era */ |
1152 | locale_era_adjust = 1976 - testpt.n[i]; |
1153 | dmy_order[i] = G_DATE_YEAR; |
1154 | break; |
1155 | } |
1156 | ++i; |
1157 | } |
1158 | |
1159 | #if defined(G_ENABLE_DEBUG) && 0 |
1160 | DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules." )); |
1161 | i = 1; |
1162 | while (i < 13) |
1163 | { |
1164 | DEBUG_MSG ((" %s %s" , long_month_names[i], short_month_names[i])); |
1165 | ++i; |
1166 | } |
1167 | DEBUG_MSG (("Alternative month names:" )); |
1168 | i = 1; |
1169 | while (i < 13) |
1170 | { |
1171 | DEBUG_MSG ((" %s %s" , long_month_names_alternative[i], short_month_names_alternative[i])); |
1172 | ++i; |
1173 | } |
1174 | if (using_twodigit_years) |
1175 | { |
1176 | DEBUG_MSG (("**Using twodigit years with cutoff year: %u" , twodigit_start_year)); |
1177 | } |
1178 | { |
1179 | gchar *strings[3]; |
1180 | i = 0; |
1181 | while (i < 3) |
1182 | { |
1183 | switch (dmy_order[i]) |
1184 | { |
1185 | case G_DATE_MONTH: |
1186 | strings[i] = "Month" ; |
1187 | break; |
1188 | case G_DATE_YEAR: |
1189 | strings[i] = "Year" ; |
1190 | break; |
1191 | case G_DATE_DAY: |
1192 | strings[i] = "Day" ; |
1193 | break; |
1194 | default: |
1195 | strings[i] = NULL; |
1196 | break; |
1197 | } |
1198 | ++i; |
1199 | } |
1200 | DEBUG_MSG (("**Order: %s, %s, %s" , strings[0], strings[1], strings[2])); |
1201 | DEBUG_MSG (("**Sample date in this locale: '%s'" , buf)); |
1202 | } |
1203 | #endif |
1204 | } |
1205 | |
1206 | g_date_fill_parse_tokens (str, pt); |
1207 | } |
1208 | |
1209 | /** |
1210 | * g_date_set_parse: |
1211 | * @date: a #GDate to fill in |
1212 | * @str: string to parse |
1213 | * |
1214 | * Parses a user-inputted string @str, and try to figure out what date it |
1215 | * represents, taking the [current locale][setlocale] into account. If the |
1216 | * string is successfully parsed, the date will be valid after the call. |
1217 | * Otherwise, it will be invalid. You should check using g_date_valid() |
1218 | * to see whether the parsing succeeded. |
1219 | * |
1220 | * This function is not appropriate for file formats and the like; it |
1221 | * isn't very precise, and its exact behavior varies with the locale. |
1222 | * It's intended to be a heuristic routine that guesses what the user |
1223 | * means by a given string (and it does work pretty well in that |
1224 | * capacity). |
1225 | */ |
1226 | void |
1227 | g_date_set_parse (GDate *d, |
1228 | const gchar *str) |
1229 | { |
1230 | GDateParseTokens pt; |
1231 | guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR; |
1232 | gsize str_len; |
1233 | |
1234 | g_return_if_fail (d != NULL); |
1235 | |
1236 | /* set invalid */ |
1237 | g_date_clear (d, ndates: 1); |
1238 | |
1239 | /* Anything longer than this is ridiculous and could take a while to normalize. |
1240 | * This limit is chosen arbitrarily. */ |
1241 | str_len = strlen (s: str); |
1242 | if (str_len > 200) |
1243 | return; |
1244 | |
1245 | /* The input has to be valid UTF-8. */ |
1246 | if (!g_utf8_validate_len (str, max_len: str_len, NULL)) |
1247 | return; |
1248 | |
1249 | G_LOCK (g_date_global); |
1250 | |
1251 | g_date_prepare_to_parse (str, pt: &pt); |
1252 | |
1253 | DEBUG_MSG (("Found %d ints, '%d' '%d' '%d' and written out month %d" , |
1254 | pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month)); |
1255 | |
1256 | |
1257 | if (pt.num_ints == 4) |
1258 | { |
1259 | G_UNLOCK (g_date_global); |
1260 | return; /* presumably a typo; bail out. */ |
1261 | } |
1262 | |
1263 | if (pt.num_ints > 1) |
1264 | { |
1265 | int i = 0; |
1266 | int j = 0; |
1267 | |
1268 | g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */ |
1269 | |
1270 | while (i < pt.num_ints && j < 3) |
1271 | { |
1272 | switch (dmy_order[j]) |
1273 | { |
1274 | case G_DATE_MONTH: |
1275 | { |
1276 | if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH) |
1277 | { |
1278 | m = pt.month; |
1279 | ++j; /* skip months, but don't skip this number */ |
1280 | continue; |
1281 | } |
1282 | else |
1283 | m = pt.n[i]; |
1284 | } |
1285 | break; |
1286 | case G_DATE_DAY: |
1287 | { |
1288 | if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH) |
1289 | { |
1290 | day = 1; |
1291 | ++j; /* skip days, since we may have month/year */ |
1292 | continue; |
1293 | } |
1294 | day = pt.n[i]; |
1295 | } |
1296 | break; |
1297 | case G_DATE_YEAR: |
1298 | { |
1299 | y = pt.n[i]; |
1300 | |
1301 | if (locale_era_adjust != 0) |
1302 | { |
1303 | y += locale_era_adjust; |
1304 | } |
1305 | else if (using_twodigit_years && y < 100) |
1306 | { |
1307 | guint two = twodigit_start_year % 100; |
1308 | guint century = (twodigit_start_year / 100) * 100; |
1309 | |
1310 | if (y < two) |
1311 | century += 100; |
1312 | |
1313 | y += century; |
1314 | } |
1315 | } |
1316 | break; |
1317 | default: |
1318 | break; |
1319 | } |
1320 | |
1321 | ++i; |
1322 | ++j; |
1323 | } |
1324 | |
1325 | |
1326 | if (pt.num_ints == 3 && !g_date_valid_dmy (d: day, m, y)) |
1327 | { |
1328 | /* Try YYYY MM DD */ |
1329 | y = pt.n[0]; |
1330 | m = pt.n[1]; |
1331 | day = pt.n[2]; |
1332 | |
1333 | if (using_twodigit_years && y < 100) |
1334 | y = G_DATE_BAD_YEAR; /* avoids ambiguity */ |
1335 | } |
1336 | else if (pt.num_ints == 2) |
1337 | { |
1338 | if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH) |
1339 | m = pt.month; |
1340 | } |
1341 | } |
1342 | else if (pt.num_ints == 1) |
1343 | { |
1344 | if (pt.month != G_DATE_BAD_MONTH) |
1345 | { |
1346 | /* Month name and year? */ |
1347 | m = pt.month; |
1348 | day = 1; |
1349 | y = pt.n[0]; |
1350 | } |
1351 | else |
1352 | { |
1353 | /* Try yyyymmdd and yymmdd */ |
1354 | |
1355 | m = (pt.n[0]/100) % 100; |
1356 | day = pt.n[0] % 100; |
1357 | y = pt.n[0]/10000; |
1358 | |
1359 | /* FIXME move this into a separate function */ |
1360 | if (using_twodigit_years && y < 100) |
1361 | { |
1362 | guint two = twodigit_start_year % 100; |
1363 | guint century = (twodigit_start_year / 100) * 100; |
1364 | |
1365 | if (y < two) |
1366 | century += 100; |
1367 | |
1368 | y += century; |
1369 | } |
1370 | } |
1371 | } |
1372 | |
1373 | /* See if we got anything valid out of all this. */ |
1374 | /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */ |
1375 | if (y < 8000 && g_date_valid_dmy (d: day, m, y)) |
1376 | { |
1377 | d->month = m; |
1378 | d->day = day; |
1379 | d->year = y; |
1380 | d->dmy = TRUE; |
1381 | } |
1382 | #ifdef G_ENABLE_DEBUG |
1383 | else |
1384 | { |
1385 | DEBUG_MSG (("Rejected DMY %u %u %u" , day, m, y)); |
1386 | } |
1387 | #endif |
1388 | G_UNLOCK (g_date_global); |
1389 | } |
1390 | |
1391 | /** |
1392 | * g_date_set_time_t: |
1393 | * @date: a #GDate |
1394 | * @timet: time_t value to set |
1395 | * |
1396 | * Sets the value of a date to the date corresponding to a time |
1397 | * specified as a time_t. The time to date conversion is done using |
1398 | * the user's current timezone. |
1399 | * |
1400 | * To set the value of a date to the current day, you could write: |
1401 | * |[<!-- language="C" --> |
1402 | * time_t now = time (NULL); |
1403 | * if (now == (time_t) -1) |
1404 | * // handle the error |
1405 | * g_date_set_time_t (date, now); |
1406 | * ]| |
1407 | * |
1408 | * Since: 2.10 |
1409 | */ |
1410 | void |
1411 | g_date_set_time_t (GDate *date, |
1412 | time_t timet) |
1413 | { |
1414 | struct tm tm; |
1415 | |
1416 | g_return_if_fail (date != NULL); |
1417 | |
1418 | #ifdef HAVE_LOCALTIME_R |
1419 | localtime_r (timer: &timet, tp: &tm); |
1420 | #else |
1421 | { |
1422 | struct tm *ptm = localtime (&timet); |
1423 | |
1424 | if (ptm == NULL) |
1425 | { |
1426 | /* Happens at least in Microsoft's C library if you pass a |
1427 | * negative time_t. Use 2000-01-01 as default date. |
1428 | */ |
1429 | #ifndef G_DISABLE_CHECKS |
1430 | g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time" , "ptm != NULL" ); |
1431 | #endif |
1432 | |
1433 | tm.tm_mon = 0; |
1434 | tm.tm_mday = 1; |
1435 | tm.tm_year = 100; |
1436 | } |
1437 | else |
1438 | memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm)); |
1439 | } |
1440 | #endif |
1441 | |
1442 | date->julian = FALSE; |
1443 | |
1444 | date->month = tm.tm_mon + 1; |
1445 | date->day = tm.tm_mday; |
1446 | date->year = tm.tm_year + 1900; |
1447 | |
1448 | g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year)); |
1449 | |
1450 | date->dmy = TRUE; |
1451 | } |
1452 | |
1453 | |
1454 | /** |
1455 | * g_date_set_time: |
1456 | * @date: a #GDate. |
1457 | * @time_: #GTime value to set. |
1458 | * |
1459 | * Sets the value of a date from a #GTime value. |
1460 | * The time to date conversion is done using the user's current timezone. |
1461 | * |
1462 | * Deprecated: 2.10: Use g_date_set_time_t() instead. |
1463 | */ |
1464 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
1465 | void |
1466 | g_date_set_time (GDate *date, |
1467 | GTime time_) |
1468 | { |
1469 | g_date_set_time_t (date, timet: (time_t) time_); |
1470 | } |
1471 | G_GNUC_END_IGNORE_DEPRECATIONS |
1472 | |
1473 | /** |
1474 | * g_date_set_time_val: |
1475 | * @date: a #GDate |
1476 | * @timeval: #GTimeVal value to set |
1477 | * |
1478 | * Sets the value of a date from a #GTimeVal value. Note that the |
1479 | * @tv_usec member is ignored, because #GDate can't make use of the |
1480 | * additional precision. |
1481 | * |
1482 | * The time to date conversion is done using the user's current timezone. |
1483 | * |
1484 | * Since: 2.10 |
1485 | * Deprecated: 2.62: #GTimeVal is not year-2038-safe. Use g_date_set_time_t() |
1486 | * instead. |
1487 | */ |
1488 | G_GNUC_BEGIN_IGNORE_DEPRECATIONS |
1489 | void |
1490 | g_date_set_time_val (GDate *date, |
1491 | GTimeVal *timeval) |
1492 | { |
1493 | g_date_set_time_t (date, timet: (time_t) timeval->tv_sec); |
1494 | } |
1495 | G_GNUC_END_IGNORE_DEPRECATIONS |
1496 | |
1497 | /** |
1498 | * g_date_set_month: |
1499 | * @date: a #GDate |
1500 | * @month: month to set |
1501 | * |
1502 | * Sets the month of the year for a #GDate. If the resulting |
1503 | * day-month-year triplet is invalid, the date will be invalid. |
1504 | */ |
1505 | void |
1506 | g_date_set_month (GDate *d, |
1507 | GDateMonth m) |
1508 | { |
1509 | g_return_if_fail (d != NULL); |
1510 | g_return_if_fail (g_date_valid_month (m)); |
1511 | |
1512 | if (d->julian && !d->dmy) g_date_update_dmy(const_d: d); |
1513 | d->julian = FALSE; |
1514 | |
1515 | d->month = m; |
1516 | |
1517 | if (g_date_valid_dmy (d: d->day, m: d->month, y: d->year)) |
1518 | d->dmy = TRUE; |
1519 | else |
1520 | d->dmy = FALSE; |
1521 | } |
1522 | |
1523 | /** |
1524 | * g_date_set_day: |
1525 | * @date: a #GDate |
1526 | * @day: day to set |
1527 | * |
1528 | * Sets the day of the month for a #GDate. If the resulting |
1529 | * day-month-year triplet is invalid, the date will be invalid. |
1530 | */ |
1531 | void |
1532 | g_date_set_day (GDate *d, |
1533 | GDateDay day) |
1534 | { |
1535 | g_return_if_fail (d != NULL); |
1536 | g_return_if_fail (g_date_valid_day (day)); |
1537 | |
1538 | if (d->julian && !d->dmy) g_date_update_dmy(const_d: d); |
1539 | d->julian = FALSE; |
1540 | |
1541 | d->day = day; |
1542 | |
1543 | if (g_date_valid_dmy (d: d->day, m: d->month, y: d->year)) |
1544 | d->dmy = TRUE; |
1545 | else |
1546 | d->dmy = FALSE; |
1547 | } |
1548 | |
1549 | /** |
1550 | * g_date_set_year: |
1551 | * @date: a #GDate |
1552 | * @year: year to set |
1553 | * |
1554 | * Sets the year for a #GDate. If the resulting day-month-year |
1555 | * triplet is invalid, the date will be invalid. |
1556 | */ |
1557 | void |
1558 | g_date_set_year (GDate *d, |
1559 | GDateYear y) |
1560 | { |
1561 | g_return_if_fail (d != NULL); |
1562 | g_return_if_fail (g_date_valid_year (y)); |
1563 | |
1564 | if (d->julian && !d->dmy) g_date_update_dmy(const_d: d); |
1565 | d->julian = FALSE; |
1566 | |
1567 | d->year = y; |
1568 | |
1569 | if (g_date_valid_dmy (d: d->day, m: d->month, y: d->year)) |
1570 | d->dmy = TRUE; |
1571 | else |
1572 | d->dmy = FALSE; |
1573 | } |
1574 | |
1575 | /** |
1576 | * g_date_set_dmy: |
1577 | * @date: a #GDate |
1578 | * @day: day |
1579 | * @month: month |
1580 | * @y: year |
1581 | * |
1582 | * Sets the value of a #GDate from a day, month, and year. |
1583 | * The day-month-year triplet must be valid; if you aren't |
1584 | * sure it is, call g_date_valid_dmy() to check before you |
1585 | * set it. |
1586 | */ |
1587 | void |
1588 | g_date_set_dmy (GDate *d, |
1589 | GDateDay day, |
1590 | GDateMonth m, |
1591 | GDateYear y) |
1592 | { |
1593 | g_return_if_fail (d != NULL); |
1594 | g_return_if_fail (g_date_valid_dmy (day, m, y)); |
1595 | |
1596 | d->julian = FALSE; |
1597 | |
1598 | d->month = m; |
1599 | d->day = day; |
1600 | d->year = y; |
1601 | |
1602 | d->dmy = TRUE; |
1603 | } |
1604 | |
1605 | /** |
1606 | * g_date_set_julian: |
1607 | * @date: a #GDate |
1608 | * @julian_date: Julian day number (days since January 1, Year 1) |
1609 | * |
1610 | * Sets the value of a #GDate from a Julian day number. |
1611 | */ |
1612 | void |
1613 | g_date_set_julian (GDate *d, |
1614 | guint32 j) |
1615 | { |
1616 | g_return_if_fail (d != NULL); |
1617 | g_return_if_fail (g_date_valid_julian (j)); |
1618 | |
1619 | d->julian_days = j; |
1620 | d->julian = TRUE; |
1621 | d->dmy = FALSE; |
1622 | } |
1623 | |
1624 | /** |
1625 | * g_date_is_first_of_month: |
1626 | * @date: a #GDate to check |
1627 | * |
1628 | * Returns %TRUE if the date is on the first of a month. |
1629 | * The date must be valid. |
1630 | * |
1631 | * Returns: %TRUE if the date is the first of the month |
1632 | */ |
1633 | gboolean |
1634 | g_date_is_first_of_month (const GDate *d) |
1635 | { |
1636 | g_return_val_if_fail (g_date_valid (d), FALSE); |
1637 | |
1638 | if (!d->dmy) |
1639 | g_date_update_dmy (const_d: d); |
1640 | |
1641 | g_return_val_if_fail (d->dmy, FALSE); |
1642 | |
1643 | if (d->day == 1) return TRUE; |
1644 | else return FALSE; |
1645 | } |
1646 | |
1647 | /** |
1648 | * g_date_is_last_of_month: |
1649 | * @date: a #GDate to check |
1650 | * |
1651 | * Returns %TRUE if the date is the last day of the month. |
1652 | * The date must be valid. |
1653 | * |
1654 | * Returns: %TRUE if the date is the last day of the month |
1655 | */ |
1656 | gboolean |
1657 | g_date_is_last_of_month (const GDate *d) |
1658 | { |
1659 | gint idx; |
1660 | |
1661 | g_return_val_if_fail (g_date_valid (d), FALSE); |
1662 | |
1663 | if (!d->dmy) |
1664 | g_date_update_dmy (const_d: d); |
1665 | |
1666 | g_return_val_if_fail (d->dmy, FALSE); |
1667 | |
1668 | idx = g_date_is_leap_year (year: d->year) ? 1 : 0; |
1669 | |
1670 | if (d->day == days_in_months[idx][d->month]) return TRUE; |
1671 | else return FALSE; |
1672 | } |
1673 | |
1674 | /** |
1675 | * g_date_add_days: |
1676 | * @date: a #GDate to increment |
1677 | * @n_days: number of days to move the date forward |
1678 | * |
1679 | * Increments a date some number of days. |
1680 | * To move forward by weeks, add weeks*7 days. |
1681 | * The date must be valid. |
1682 | */ |
1683 | void |
1684 | g_date_add_days (GDate *d, |
1685 | guint ndays) |
1686 | { |
1687 | g_return_if_fail (g_date_valid (d)); |
1688 | |
1689 | if (!d->julian) |
1690 | g_date_update_julian (const_d: d); |
1691 | |
1692 | g_return_if_fail (d->julian); |
1693 | g_return_if_fail (ndays <= G_MAXUINT32 - d->julian_days); |
1694 | |
1695 | d->julian_days += ndays; |
1696 | d->dmy = FALSE; |
1697 | } |
1698 | |
1699 | /** |
1700 | * g_date_subtract_days: |
1701 | * @date: a #GDate to decrement |
1702 | * @n_days: number of days to move |
1703 | * |
1704 | * Moves a date some number of days into the past. |
1705 | * To move by weeks, just move by weeks*7 days. |
1706 | * The date must be valid. |
1707 | */ |
1708 | void |
1709 | g_date_subtract_days (GDate *d, |
1710 | guint ndays) |
1711 | { |
1712 | g_return_if_fail (g_date_valid (d)); |
1713 | |
1714 | if (!d->julian) |
1715 | g_date_update_julian (const_d: d); |
1716 | |
1717 | g_return_if_fail (d->julian); |
1718 | g_return_if_fail (d->julian_days > ndays); |
1719 | |
1720 | d->julian_days -= ndays; |
1721 | d->dmy = FALSE; |
1722 | } |
1723 | |
1724 | /** |
1725 | * g_date_add_months: |
1726 | * @date: a #GDate to increment |
1727 | * @n_months: number of months to move forward |
1728 | * |
1729 | * Increments a date by some number of months. |
1730 | * If the day of the month is greater than 28, |
1731 | * this routine may change the day of the month |
1732 | * (because the destination month may not have |
1733 | * the current day in it). The date must be valid. |
1734 | */ |
1735 | void |
1736 | g_date_add_months (GDate *d, |
1737 | guint nmonths) |
1738 | { |
1739 | guint years, months; |
1740 | gint idx; |
1741 | |
1742 | g_return_if_fail (g_date_valid (d)); |
1743 | |
1744 | if (!d->dmy) |
1745 | g_date_update_dmy (const_d: d); |
1746 | |
1747 | g_return_if_fail (d->dmy != 0); |
1748 | g_return_if_fail (nmonths <= G_MAXUINT - (d->month - 1)); |
1749 | |
1750 | nmonths += d->month - 1; |
1751 | |
1752 | years = nmonths/12; |
1753 | months = nmonths%12; |
1754 | |
1755 | g_return_if_fail (years <= (guint) (G_MAXUINT16 - d->year)); |
1756 | |
1757 | d->month = months + 1; |
1758 | d->year += years; |
1759 | |
1760 | idx = g_date_is_leap_year (year: d->year) ? 1 : 0; |
1761 | |
1762 | if (d->day > days_in_months[idx][d->month]) |
1763 | d->day = days_in_months[idx][d->month]; |
1764 | |
1765 | d->julian = FALSE; |
1766 | |
1767 | g_return_if_fail (g_date_valid (d)); |
1768 | } |
1769 | |
1770 | /** |
1771 | * g_date_subtract_months: |
1772 | * @date: a #GDate to decrement |
1773 | * @n_months: number of months to move |
1774 | * |
1775 | * Moves a date some number of months into the past. |
1776 | * If the current day of the month doesn't exist in |
1777 | * the destination month, the day of the month |
1778 | * may change. The date must be valid. |
1779 | */ |
1780 | void |
1781 | g_date_subtract_months (GDate *d, |
1782 | guint nmonths) |
1783 | { |
1784 | guint years, months; |
1785 | gint idx; |
1786 | |
1787 | g_return_if_fail (g_date_valid (d)); |
1788 | |
1789 | if (!d->dmy) |
1790 | g_date_update_dmy (const_d: d); |
1791 | |
1792 | g_return_if_fail (d->dmy != 0); |
1793 | |
1794 | years = nmonths/12; |
1795 | months = nmonths%12; |
1796 | |
1797 | g_return_if_fail (d->year > years); |
1798 | |
1799 | d->year -= years; |
1800 | |
1801 | if (d->month > months) d->month -= months; |
1802 | else |
1803 | { |
1804 | months -= d->month; |
1805 | d->month = 12 - months; |
1806 | d->year -= 1; |
1807 | } |
1808 | |
1809 | idx = g_date_is_leap_year (year: d->year) ? 1 : 0; |
1810 | |
1811 | if (d->day > days_in_months[idx][d->month]) |
1812 | d->day = days_in_months[idx][d->month]; |
1813 | |
1814 | d->julian = FALSE; |
1815 | |
1816 | g_return_if_fail (g_date_valid (d)); |
1817 | } |
1818 | |
1819 | /** |
1820 | * g_date_add_years: |
1821 | * @date: a #GDate to increment |
1822 | * @n_years: number of years to move forward |
1823 | * |
1824 | * Increments a date by some number of years. |
1825 | * If the date is February 29, and the destination |
1826 | * year is not a leap year, the date will be changed |
1827 | * to February 28. The date must be valid. |
1828 | */ |
1829 | void |
1830 | g_date_add_years (GDate *d, |
1831 | guint nyears) |
1832 | { |
1833 | g_return_if_fail (g_date_valid (d)); |
1834 | |
1835 | if (!d->dmy) |
1836 | g_date_update_dmy (const_d: d); |
1837 | |
1838 | g_return_if_fail (d->dmy != 0); |
1839 | g_return_if_fail (nyears <= (guint) (G_MAXUINT16 - d->year)); |
1840 | |
1841 | d->year += nyears; |
1842 | |
1843 | if (d->month == 2 && d->day == 29) |
1844 | { |
1845 | if (!g_date_is_leap_year (year: d->year)) |
1846 | d->day = 28; |
1847 | } |
1848 | |
1849 | d->julian = FALSE; |
1850 | } |
1851 | |
1852 | /** |
1853 | * g_date_subtract_years: |
1854 | * @date: a #GDate to decrement |
1855 | * @n_years: number of years to move |
1856 | * |
1857 | * Moves a date some number of years into the past. |
1858 | * If the current day doesn't exist in the destination |
1859 | * year (i.e. it's February 29 and you move to a non-leap-year) |
1860 | * then the day is changed to February 29. The date |
1861 | * must be valid. |
1862 | */ |
1863 | void |
1864 | g_date_subtract_years (GDate *d, |
1865 | guint nyears) |
1866 | { |
1867 | g_return_if_fail (g_date_valid (d)); |
1868 | |
1869 | if (!d->dmy) |
1870 | g_date_update_dmy (const_d: d); |
1871 | |
1872 | g_return_if_fail (d->dmy != 0); |
1873 | g_return_if_fail (d->year > nyears); |
1874 | |
1875 | d->year -= nyears; |
1876 | |
1877 | if (d->month == 2 && d->day == 29) |
1878 | { |
1879 | if (!g_date_is_leap_year (year: d->year)) |
1880 | d->day = 28; |
1881 | } |
1882 | |
1883 | d->julian = FALSE; |
1884 | } |
1885 | |
1886 | /** |
1887 | * g_date_is_leap_year: |
1888 | * @year: year to check |
1889 | * |
1890 | * Returns %TRUE if the year is a leap year. |
1891 | * |
1892 | * For the purposes of this function, leap year is every year |
1893 | * divisible by 4 unless that year is divisible by 100. If it |
1894 | * is divisible by 100 it would be a leap year only if that year |
1895 | * is also divisible by 400. |
1896 | * |
1897 | * Returns: %TRUE if the year is a leap year |
1898 | */ |
1899 | gboolean |
1900 | g_date_is_leap_year (GDateYear year) |
1901 | { |
1902 | g_return_val_if_fail (g_date_valid_year (year), FALSE); |
1903 | |
1904 | return ( (((year % 4) == 0) && ((year % 100) != 0)) || |
1905 | (year % 400) == 0 ); |
1906 | } |
1907 | |
1908 | /** |
1909 | * g_date_get_days_in_month: |
1910 | * @month: month |
1911 | * @year: year |
1912 | * |
1913 | * Returns the number of days in a month, taking leap |
1914 | * years into account. |
1915 | * |
1916 | * Returns: number of days in @month during the @year |
1917 | */ |
1918 | guint8 |
1919 | g_date_get_days_in_month (GDateMonth month, |
1920 | GDateYear year) |
1921 | { |
1922 | gint idx; |
1923 | |
1924 | g_return_val_if_fail (g_date_valid_year (year), 0); |
1925 | g_return_val_if_fail (g_date_valid_month (month), 0); |
1926 | |
1927 | idx = g_date_is_leap_year (year) ? 1 : 0; |
1928 | |
1929 | return days_in_months[idx][month]; |
1930 | } |
1931 | |
1932 | /** |
1933 | * g_date_get_monday_weeks_in_year: |
1934 | * @year: a year |
1935 | * |
1936 | * Returns the number of weeks in the year, where weeks |
1937 | * are taken to start on Monday. Will be 52 or 53. The |
1938 | * date must be valid. (Years always have 52 7-day periods, |
1939 | * plus 1 or 2 extra days depending on whether it's a leap |
1940 | * year. This function is basically telling you how many |
1941 | * Mondays are in the year, i.e. there are 53 Mondays if |
1942 | * one of the extra days happens to be a Monday.) |
1943 | * |
1944 | * Returns: number of Mondays in the year |
1945 | */ |
1946 | guint8 |
1947 | g_date_get_monday_weeks_in_year (GDateYear year) |
1948 | { |
1949 | GDate d; |
1950 | |
1951 | g_return_val_if_fail (g_date_valid_year (year), 0); |
1952 | |
1953 | g_date_clear (d: &d, ndates: 1); |
1954 | g_date_set_dmy (d: &d, day: 1, m: 1, y: year); |
1955 | if (g_date_get_weekday (d: &d) == G_DATE_MONDAY) return 53; |
1956 | g_date_set_dmy (d: &d, day: 31, m: 12, y: year); |
1957 | if (g_date_get_weekday (d: &d) == G_DATE_MONDAY) return 53; |
1958 | if (g_date_is_leap_year (year)) |
1959 | { |
1960 | g_date_set_dmy (d: &d, day: 2, m: 1, y: year); |
1961 | if (g_date_get_weekday (d: &d) == G_DATE_MONDAY) return 53; |
1962 | g_date_set_dmy (d: &d, day: 30, m: 12, y: year); |
1963 | if (g_date_get_weekday (d: &d) == G_DATE_MONDAY) return 53; |
1964 | } |
1965 | return 52; |
1966 | } |
1967 | |
1968 | /** |
1969 | * g_date_get_sunday_weeks_in_year: |
1970 | * @year: year to count weeks in |
1971 | * |
1972 | * Returns the number of weeks in the year, where weeks |
1973 | * are taken to start on Sunday. Will be 52 or 53. The |
1974 | * date must be valid. (Years always have 52 7-day periods, |
1975 | * plus 1 or 2 extra days depending on whether it's a leap |
1976 | * year. This function is basically telling you how many |
1977 | * Sundays are in the year, i.e. there are 53 Sundays if |
1978 | * one of the extra days happens to be a Sunday.) |
1979 | * |
1980 | * Returns: the number of weeks in @year |
1981 | */ |
1982 | guint8 |
1983 | g_date_get_sunday_weeks_in_year (GDateYear year) |
1984 | { |
1985 | GDate d; |
1986 | |
1987 | g_return_val_if_fail (g_date_valid_year (year), 0); |
1988 | |
1989 | g_date_clear (d: &d, ndates: 1); |
1990 | g_date_set_dmy (d: &d, day: 1, m: 1, y: year); |
1991 | if (g_date_get_weekday (d: &d) == G_DATE_SUNDAY) return 53; |
1992 | g_date_set_dmy (d: &d, day: 31, m: 12, y: year); |
1993 | if (g_date_get_weekday (d: &d) == G_DATE_SUNDAY) return 53; |
1994 | if (g_date_is_leap_year (year)) |
1995 | { |
1996 | g_date_set_dmy (d: &d, day: 2, m: 1, y: year); |
1997 | if (g_date_get_weekday (d: &d) == G_DATE_SUNDAY) return 53; |
1998 | g_date_set_dmy (d: &d, day: 30, m: 12, y: year); |
1999 | if (g_date_get_weekday (d: &d) == G_DATE_SUNDAY) return 53; |
2000 | } |
2001 | return 52; |
2002 | } |
2003 | |
2004 | /** |
2005 | * g_date_compare: |
2006 | * @lhs: first date to compare |
2007 | * @rhs: second date to compare |
2008 | * |
2009 | * qsort()-style comparison function for dates. |
2010 | * Both dates must be valid. |
2011 | * |
2012 | * Returns: 0 for equal, less than zero if @lhs is less than @rhs, |
2013 | * greater than zero if @lhs is greater than @rhs |
2014 | */ |
2015 | gint |
2016 | g_date_compare (const GDate *lhs, |
2017 | const GDate *rhs) |
2018 | { |
2019 | g_return_val_if_fail (lhs != NULL, 0); |
2020 | g_return_val_if_fail (rhs != NULL, 0); |
2021 | g_return_val_if_fail (g_date_valid (lhs), 0); |
2022 | g_return_val_if_fail (g_date_valid (rhs), 0); |
2023 | |
2024 | /* Remember the self-comparison case! I think it works right now. */ |
2025 | |
2026 | while (TRUE) |
2027 | { |
2028 | if (lhs->julian && rhs->julian) |
2029 | { |
2030 | if (lhs->julian_days < rhs->julian_days) return -1; |
2031 | else if (lhs->julian_days > rhs->julian_days) return 1; |
2032 | else return 0; |
2033 | } |
2034 | else if (lhs->dmy && rhs->dmy) |
2035 | { |
2036 | if (lhs->year < rhs->year) return -1; |
2037 | else if (lhs->year > rhs->year) return 1; |
2038 | else |
2039 | { |
2040 | if (lhs->month < rhs->month) return -1; |
2041 | else if (lhs->month > rhs->month) return 1; |
2042 | else |
2043 | { |
2044 | if (lhs->day < rhs->day) return -1; |
2045 | else if (lhs->day > rhs->day) return 1; |
2046 | else return 0; |
2047 | } |
2048 | |
2049 | } |
2050 | |
2051 | } |
2052 | else |
2053 | { |
2054 | if (!lhs->julian) g_date_update_julian (const_d: lhs); |
2055 | if (!rhs->julian) g_date_update_julian (const_d: rhs); |
2056 | g_return_val_if_fail (lhs->julian, 0); |
2057 | g_return_val_if_fail (rhs->julian, 0); |
2058 | } |
2059 | |
2060 | } |
2061 | return 0; /* warnings */ |
2062 | } |
2063 | |
2064 | /** |
2065 | * g_date_to_struct_tm: |
2066 | * @date: a #GDate to set the struct tm from |
2067 | * @tm: (not nullable): struct tm to fill |
2068 | * |
2069 | * Fills in the date-related bits of a struct tm using the @date value. |
2070 | * Initializes the non-date parts with something safe but meaningless. |
2071 | */ |
2072 | void |
2073 | g_date_to_struct_tm (const GDate *d, |
2074 | struct tm *tm) |
2075 | { |
2076 | GDateWeekday day; |
2077 | |
2078 | g_return_if_fail (g_date_valid (d)); |
2079 | g_return_if_fail (tm != NULL); |
2080 | |
2081 | if (!d->dmy) |
2082 | g_date_update_dmy (const_d: d); |
2083 | |
2084 | g_return_if_fail (d->dmy != 0); |
2085 | |
2086 | /* zero all the irrelevant fields to be sure they're valid */ |
2087 | |
2088 | /* On Linux and maybe other systems, there are weird non-POSIX |
2089 | * fields on the end of struct tm that choke strftime if they |
2090 | * contain garbage. So we need to 0 the entire struct, not just the |
2091 | * fields we know to exist. |
2092 | */ |
2093 | |
2094 | memset (s: tm, c: 0x0, n: sizeof (struct tm)); |
2095 | |
2096 | tm->tm_mday = d->day; |
2097 | tm->tm_mon = d->month - 1; /* 0-11 goes in tm */ |
2098 | tm->tm_year = ((int)d->year) - 1900; /* X/Open says tm_year can be negative */ |
2099 | |
2100 | day = g_date_get_weekday (d); |
2101 | if (day == 7) day = 0; /* struct tm wants days since Sunday, so Sunday is 0 */ |
2102 | |
2103 | tm->tm_wday = (int)day; |
2104 | |
2105 | tm->tm_yday = g_date_get_day_of_year (d) - 1; /* 0 to 365 */ |
2106 | tm->tm_isdst = -1; /* -1 means "information not available" */ |
2107 | } |
2108 | |
2109 | /** |
2110 | * g_date_clamp: |
2111 | * @date: a #GDate to clamp |
2112 | * @min_date: minimum accepted value for @date |
2113 | * @max_date: maximum accepted value for @date |
2114 | * |
2115 | * If @date is prior to @min_date, sets @date equal to @min_date. |
2116 | * If @date falls after @max_date, sets @date equal to @max_date. |
2117 | * Otherwise, @date is unchanged. |
2118 | * Either of @min_date and @max_date may be %NULL. |
2119 | * All non-%NULL dates must be valid. |
2120 | */ |
2121 | void |
2122 | g_date_clamp (GDate *date, |
2123 | const GDate *min_date, |
2124 | const GDate *max_date) |
2125 | { |
2126 | g_return_if_fail (g_date_valid (date)); |
2127 | |
2128 | if (min_date != NULL) |
2129 | g_return_if_fail (g_date_valid (min_date)); |
2130 | |
2131 | if (max_date != NULL) |
2132 | g_return_if_fail (g_date_valid (max_date)); |
2133 | |
2134 | if (min_date != NULL && max_date != NULL) |
2135 | g_return_if_fail (g_date_compare (min_date, max_date) <= 0); |
2136 | |
2137 | if (min_date && g_date_compare (lhs: date, rhs: min_date) < 0) |
2138 | *date = *min_date; |
2139 | |
2140 | if (max_date && g_date_compare (lhs: max_date, rhs: date) < 0) |
2141 | *date = *max_date; |
2142 | } |
2143 | |
2144 | /** |
2145 | * g_date_order: |
2146 | * @date1: the first date |
2147 | * @date2: the second date |
2148 | * |
2149 | * Checks if @date1 is less than or equal to @date2, |
2150 | * and swap the values if this is not the case. |
2151 | */ |
2152 | void |
2153 | g_date_order (GDate *date1, |
2154 | GDate *date2) |
2155 | { |
2156 | g_return_if_fail (g_date_valid (date1)); |
2157 | g_return_if_fail (g_date_valid (date2)); |
2158 | |
2159 | if (g_date_compare (lhs: date1, rhs: date2) > 0) |
2160 | { |
2161 | GDate tmp = *date1; |
2162 | *date1 = *date2; |
2163 | *date2 = tmp; |
2164 | } |
2165 | } |
2166 | |
2167 | #ifdef G_OS_WIN32 |
2168 | static gboolean |
2169 | append_month_name (GArray *result, |
2170 | LCID lcid, |
2171 | SYSTEMTIME *systemtime, |
2172 | gboolean abbreviated, |
2173 | gboolean alternative) |
2174 | { |
2175 | int n; |
2176 | WORD base; |
2177 | LPCWSTR lpFormat; |
2178 | |
2179 | if (alternative) |
2180 | { |
2181 | base = abbreviated ? LOCALE_SABBREVMONTHNAME1 : LOCALE_SMONTHNAME1; |
2182 | n = GetLocaleInfoW (lcid, base + systemtime->wMonth - 1, NULL, 0); |
2183 | if (n == 0) |
2184 | return FALSE; |
2185 | |
2186 | g_array_set_size (result, result->len + n); |
2187 | if (GetLocaleInfoW (lcid, base + systemtime->wMonth - 1, |
2188 | ((wchar_t *) result->data) + result->len - n, n) != n) |
2189 | return FALSE; |
2190 | |
2191 | g_array_set_size (result, result->len - 1); |
2192 | } |
2193 | else |
2194 | { |
2195 | /* According to MSDN, this is the correct method to obtain |
2196 | * the form of the month name used when formatting a full |
2197 | * date; it must be a genitive case in some languages. |
2198 | * |
2199 | * (n == 0) indicates an error, whereas (n < 2) is something we’d never |
2200 | * expect from the given format string, and would break the subsequent code. |
2201 | */ |
2202 | lpFormat = abbreviated ? L"ddMMM" : L"ddMMMM" ; |
2203 | n = GetDateFormatW (lcid, 0, systemtime, lpFormat, NULL, 0); |
2204 | if (n < 2) |
2205 | return FALSE; |
2206 | |
2207 | g_array_set_size (result, result->len + n); |
2208 | if (GetDateFormatW (lcid, 0, systemtime, lpFormat, |
2209 | ((wchar_t *) result->data) + result->len - n, n) != n) |
2210 | return FALSE; |
2211 | |
2212 | /* We have obtained a day number as two digits and the month name. |
2213 | * Now let's get rid of those two digits: overwrite them with the |
2214 | * month name. |
2215 | */ |
2216 | memmove (((wchar_t *) result->data) + result->len - n, |
2217 | ((wchar_t *) result->data) + result->len - n + 2, |
2218 | (n - 2) * sizeof (wchar_t)); |
2219 | g_array_set_size (result, result->len - 3); |
2220 | } |
2221 | |
2222 | return TRUE; |
2223 | } |
2224 | |
2225 | static gsize |
2226 | win32_strftime_helper (const GDate *d, |
2227 | const gchar *format, |
2228 | const struct tm *tm, |
2229 | gchar *s, |
2230 | gsize slen) |
2231 | { |
2232 | SYSTEMTIME systemtime; |
2233 | TIME_ZONE_INFORMATION tzinfo; |
2234 | LCID lcid; |
2235 | int n, k; |
2236 | GArray *result; |
2237 | const gchar *p; |
2238 | gunichar c, modifier; |
2239 | const wchar_t digits[] = L"0123456789" ; |
2240 | gchar *convbuf; |
2241 | glong convlen = 0; |
2242 | gsize retval; |
2243 | |
2244 | systemtime.wYear = tm->tm_year + 1900; |
2245 | systemtime.wMonth = tm->tm_mon + 1; |
2246 | systemtime.wDayOfWeek = tm->tm_wday; |
2247 | systemtime.wDay = tm->tm_mday; |
2248 | systemtime.wHour = tm->tm_hour; |
2249 | systemtime.wMinute = tm->tm_min; |
2250 | systemtime.wSecond = tm->tm_sec; |
2251 | systemtime.wMilliseconds = 0; |
2252 | |
2253 | lcid = GetThreadLocale (); |
2254 | result = g_array_sized_new (FALSE, FALSE, sizeof (wchar_t), MAX (128, strlen (format) * 2)); |
2255 | |
2256 | p = format; |
2257 | while (*p) |
2258 | { |
2259 | c = g_utf8_get_char (p); |
2260 | if (c == '%') |
2261 | { |
2262 | p = g_utf8_next_char (p); |
2263 | if (!*p) |
2264 | { |
2265 | s[0] = '\0'; |
2266 | g_array_free (result, TRUE); |
2267 | |
2268 | return 0; |
2269 | } |
2270 | |
2271 | modifier = '\0'; |
2272 | c = g_utf8_get_char (p); |
2273 | if (c == 'E' || c == 'O') |
2274 | { |
2275 | /* "%OB", "%Ob", and "%Oh" are supported, ignore other modified |
2276 | * conversion specifiers for now. |
2277 | */ |
2278 | modifier = c; |
2279 | p = g_utf8_next_char (p); |
2280 | if (!*p) |
2281 | { |
2282 | s[0] = '\0'; |
2283 | g_array_free (result, TRUE); |
2284 | |
2285 | return 0; |
2286 | } |
2287 | |
2288 | c = g_utf8_get_char (p); |
2289 | } |
2290 | |
2291 | switch (c) |
2292 | { |
2293 | case 'a': |
2294 | if (systemtime.wDayOfWeek == 0) |
2295 | k = 6; |
2296 | else |
2297 | k = systemtime.wDayOfWeek - 1; |
2298 | n = GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, NULL, 0); |
2299 | g_array_set_size (result, result->len + n); |
2300 | GetLocaleInfoW (lcid, LOCALE_SABBREVDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n); |
2301 | g_array_set_size (result, result->len - 1); |
2302 | break; |
2303 | case 'A': |
2304 | if (systemtime.wDayOfWeek == 0) |
2305 | k = 6; |
2306 | else |
2307 | k = systemtime.wDayOfWeek - 1; |
2308 | n = GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, NULL, 0); |
2309 | g_array_set_size (result, result->len + n); |
2310 | GetLocaleInfoW (lcid, LOCALE_SDAYNAME1+k, ((wchar_t *) result->data) + result->len - n, n); |
2311 | g_array_set_size (result, result->len - 1); |
2312 | break; |
2313 | case 'b': |
2314 | case 'h': |
2315 | if (!append_month_name (result, lcid, &systemtime, TRUE, modifier == 'O')) |
2316 | { |
2317 | /* Ignore the error; this placeholder will be replaced with nothing */ |
2318 | } |
2319 | break; |
2320 | case 'B': |
2321 | if (!append_month_name (result, lcid, &systemtime, FALSE, modifier == 'O')) |
2322 | { |
2323 | /* Ignore the error; this placeholder will be replaced with nothing */ |
2324 | } |
2325 | break; |
2326 | case 'c': |
2327 | n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0); |
2328 | if (n > 0) |
2329 | { |
2330 | g_array_set_size (result, result->len + n); |
2331 | GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); |
2332 | g_array_set_size (result, result->len - 1); |
2333 | } |
2334 | g_array_append_vals (result, L" " , 1); |
2335 | n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0); |
2336 | if (n > 0) |
2337 | { |
2338 | g_array_set_size (result, result->len + n); |
2339 | GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); |
2340 | g_array_set_size (result, result->len - 1); |
2341 | } |
2342 | break; |
2343 | case 'C': |
2344 | g_array_append_vals (result, digits + systemtime.wYear/1000, 1); |
2345 | g_array_append_vals (result, digits + (systemtime.wYear/1000)%10, 1); |
2346 | break; |
2347 | case 'd': |
2348 | g_array_append_vals (result, digits + systemtime.wDay/10, 1); |
2349 | g_array_append_vals (result, digits + systemtime.wDay%10, 1); |
2350 | break; |
2351 | case 'D': |
2352 | g_array_append_vals (result, digits + systemtime.wMonth/10, 1); |
2353 | g_array_append_vals (result, digits + systemtime.wMonth%10, 1); |
2354 | g_array_append_vals (result, L"/" , 1); |
2355 | g_array_append_vals (result, digits + systemtime.wDay/10, 1); |
2356 | g_array_append_vals (result, digits + systemtime.wDay%10, 1); |
2357 | g_array_append_vals (result, L"/" , 1); |
2358 | g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1); |
2359 | g_array_append_vals (result, digits + systemtime.wYear%10, 1); |
2360 | break; |
2361 | case 'e': |
2362 | if (systemtime.wDay >= 10) |
2363 | g_array_append_vals (result, digits + systemtime.wDay/10, 1); |
2364 | else |
2365 | g_array_append_vals (result, L" " , 1); |
2366 | g_array_append_vals (result, digits + systemtime.wDay%10, 1); |
2367 | break; |
2368 | |
2369 | /* A GDate has no time fields, so for now we can |
2370 | * hardcode all time conversions into zeros (or 12 for |
2371 | * %I). The alternative code snippets in the #else |
2372 | * branches are here ready to be taken into use when |
2373 | * needed by a g_strftime() or g_date_and_time_format() |
2374 | * or whatever. |
2375 | */ |
2376 | case 'H': |
2377 | #if 1 |
2378 | g_array_append_vals (result, L"00" , 2); |
2379 | #else |
2380 | g_array_append_vals (result, digits + systemtime.wHour/10, 1); |
2381 | g_array_append_vals (result, digits + systemtime.wHour%10, 1); |
2382 | #endif |
2383 | break; |
2384 | case 'I': |
2385 | #if 1 |
2386 | g_array_append_vals (result, L"12" , 2); |
2387 | #else |
2388 | if (systemtime.wHour == 0) |
2389 | g_array_append_vals (result, L"12" , 2); |
2390 | else |
2391 | { |
2392 | g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1); |
2393 | g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1); |
2394 | } |
2395 | #endif |
2396 | break; |
2397 | case 'j': |
2398 | g_array_append_vals (result, digits + (tm->tm_yday+1)/100, 1); |
2399 | g_array_append_vals (result, digits + ((tm->tm_yday+1)/10)%10, 1); |
2400 | g_array_append_vals (result, digits + (tm->tm_yday+1)%10, 1); |
2401 | break; |
2402 | case 'm': |
2403 | g_array_append_vals (result, digits + systemtime.wMonth/10, 1); |
2404 | g_array_append_vals (result, digits + systemtime.wMonth%10, 1); |
2405 | break; |
2406 | case 'M': |
2407 | #if 1 |
2408 | g_array_append_vals (result, L"00" , 2); |
2409 | #else |
2410 | g_array_append_vals (result, digits + systemtime.wMinute/10, 1); |
2411 | g_array_append_vals (result, digits + systemtime.wMinute%10, 1); |
2412 | #endif |
2413 | break; |
2414 | case 'n': |
2415 | g_array_append_vals (result, L"\n" , 1); |
2416 | break; |
2417 | case 'p': |
2418 | n = GetTimeFormatW (lcid, 0, &systemtime, L"tt" , NULL, 0); |
2419 | if (n > 0) |
2420 | { |
2421 | g_array_set_size (result, result->len + n); |
2422 | GetTimeFormatW (lcid, 0, &systemtime, L"tt" , ((wchar_t *) result->data) + result->len - n, n); |
2423 | g_array_set_size (result, result->len - 1); |
2424 | } |
2425 | break; |
2426 | case 'r': |
2427 | /* This is a rather odd format. Hard to say what to do. |
2428 | * Let's always use the POSIX %I:%M:%S %p |
2429 | */ |
2430 | #if 1 |
2431 | g_array_append_vals (result, L"12:00:00" , 8); |
2432 | #else |
2433 | if (systemtime.wHour == 0) |
2434 | g_array_append_vals (result, L"12" , 2); |
2435 | else |
2436 | { |
2437 | g_array_append_vals (result, digits + (systemtime.wHour%12)/10, 1); |
2438 | g_array_append_vals (result, digits + (systemtime.wHour%12)%10, 1); |
2439 | } |
2440 | g_array_append_vals (result, L":" , 1); |
2441 | g_array_append_vals (result, digits + systemtime.wMinute/10, 1); |
2442 | g_array_append_vals (result, digits + systemtime.wMinute%10, 1); |
2443 | g_array_append_vals (result, L":" , 1); |
2444 | g_array_append_vals (result, digits + systemtime.wSecond/10, 1); |
2445 | g_array_append_vals (result, digits + systemtime.wSecond%10, 1); |
2446 | g_array_append_vals (result, L" " , 1); |
2447 | #endif |
2448 | n = GetTimeFormatW (lcid, 0, &systemtime, L"tt" , NULL, 0); |
2449 | if (n > 0) |
2450 | { |
2451 | g_array_set_size (result, result->len + n); |
2452 | GetTimeFormatW (lcid, 0, &systemtime, L"tt" , ((wchar_t *) result->data) + result->len - n, n); |
2453 | g_array_set_size (result, result->len - 1); |
2454 | } |
2455 | break; |
2456 | case 'R': |
2457 | #if 1 |
2458 | g_array_append_vals (result, L"00:00" , 5); |
2459 | #else |
2460 | g_array_append_vals (result, digits + systemtime.wHour/10, 1); |
2461 | g_array_append_vals (result, digits + systemtime.wHour%10, 1); |
2462 | g_array_append_vals (result, L":" , 1); |
2463 | g_array_append_vals (result, digits + systemtime.wMinute/10, 1); |
2464 | g_array_append_vals (result, digits + systemtime.wMinute%10, 1); |
2465 | #endif |
2466 | break; |
2467 | case 'S': |
2468 | #if 1 |
2469 | g_array_append_vals (result, L"00" , 2); |
2470 | #else |
2471 | g_array_append_vals (result, digits + systemtime.wSecond/10, 1); |
2472 | g_array_append_vals (result, digits + systemtime.wSecond%10, 1); |
2473 | #endif |
2474 | break; |
2475 | case 't': |
2476 | g_array_append_vals (result, L"\t" , 1); |
2477 | break; |
2478 | case 'T': |
2479 | #if 1 |
2480 | g_array_append_vals (result, L"00:00:00" , 8); |
2481 | #else |
2482 | g_array_append_vals (result, digits + systemtime.wHour/10, 1); |
2483 | g_array_append_vals (result, digits + systemtime.wHour%10, 1); |
2484 | g_array_append_vals (result, L":" , 1); |
2485 | g_array_append_vals (result, digits + systemtime.wMinute/10, 1); |
2486 | g_array_append_vals (result, digits + systemtime.wMinute%10, 1); |
2487 | g_array_append_vals (result, L":" , 1); |
2488 | g_array_append_vals (result, digits + systemtime.wSecond/10, 1); |
2489 | g_array_append_vals (result, digits + systemtime.wSecond%10, 1); |
2490 | #endif |
2491 | break; |
2492 | case 'u': |
2493 | if (systemtime.wDayOfWeek == 0) |
2494 | g_array_append_vals (result, L"7" , 1); |
2495 | else |
2496 | g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1); |
2497 | break; |
2498 | case 'U': |
2499 | n = g_date_get_sunday_week_of_year (d); |
2500 | g_array_append_vals (result, digits + n/10, 1); |
2501 | g_array_append_vals (result, digits + n%10, 1); |
2502 | break; |
2503 | case 'V': |
2504 | n = g_date_get_iso8601_week_of_year (d); |
2505 | g_array_append_vals (result, digits + n/10, 1); |
2506 | g_array_append_vals (result, digits + n%10, 1); |
2507 | break; |
2508 | case 'w': |
2509 | g_array_append_vals (result, digits + systemtime.wDayOfWeek, 1); |
2510 | break; |
2511 | case 'W': |
2512 | n = g_date_get_monday_week_of_year (d); |
2513 | g_array_append_vals (result, digits + n/10, 1); |
2514 | g_array_append_vals (result, digits + n%10, 1); |
2515 | break; |
2516 | case 'x': |
2517 | n = GetDateFormatW (lcid, 0, &systemtime, NULL, NULL, 0); |
2518 | if (n > 0) |
2519 | { |
2520 | g_array_set_size (result, result->len + n); |
2521 | GetDateFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); |
2522 | g_array_set_size (result, result->len - 1); |
2523 | } |
2524 | break; |
2525 | case 'X': |
2526 | n = GetTimeFormatW (lcid, 0, &systemtime, NULL, NULL, 0); |
2527 | if (n > 0) |
2528 | { |
2529 | g_array_set_size (result, result->len + n); |
2530 | GetTimeFormatW (lcid, 0, &systemtime, NULL, ((wchar_t *) result->data) + result->len - n, n); |
2531 | g_array_set_size (result, result->len - 1); |
2532 | } |
2533 | break; |
2534 | case 'y': |
2535 | g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1); |
2536 | g_array_append_vals (result, digits + systemtime.wYear%10, 1); |
2537 | break; |
2538 | case 'Y': |
2539 | g_array_append_vals (result, digits + systemtime.wYear/1000, 1); |
2540 | g_array_append_vals (result, digits + (systemtime.wYear/100)%10, 1); |
2541 | g_array_append_vals (result, digits + (systemtime.wYear/10)%10, 1); |
2542 | g_array_append_vals (result, digits + systemtime.wYear%10, 1); |
2543 | break; |
2544 | case 'Z': |
2545 | n = GetTimeZoneInformation (&tzinfo); |
2546 | if (n == TIME_ZONE_ID_UNKNOWN) |
2547 | ; |
2548 | else if (n == TIME_ZONE_ID_STANDARD) |
2549 | g_array_append_vals (result, tzinfo.StandardName, wcslen (tzinfo.StandardName)); |
2550 | else if (n == TIME_ZONE_ID_DAYLIGHT) |
2551 | g_array_append_vals (result, tzinfo.DaylightName, wcslen (tzinfo.DaylightName)); |
2552 | break; |
2553 | case '%': |
2554 | g_array_append_vals (result, L"%" , 1); |
2555 | break; |
2556 | } |
2557 | } |
2558 | else if (c <= 0xFFFF) |
2559 | { |
2560 | wchar_t wc = c; |
2561 | g_array_append_vals (result, &wc, 1); |
2562 | } |
2563 | else |
2564 | { |
2565 | glong nwc; |
2566 | wchar_t *ws; |
2567 | |
2568 | ws = g_ucs4_to_utf16 (&c, 1, NULL, &nwc, NULL); |
2569 | g_array_append_vals (result, ws, nwc); |
2570 | g_free (ws); |
2571 | } |
2572 | p = g_utf8_next_char (p); |
2573 | } |
2574 | |
2575 | convbuf = g_utf16_to_utf8 ((wchar_t *) result->data, result->len, NULL, &convlen, NULL); |
2576 | g_array_free (result, TRUE); |
2577 | |
2578 | if (!convbuf) |
2579 | { |
2580 | s[0] = '\0'; |
2581 | return 0; |
2582 | } |
2583 | |
2584 | if (slen <= convlen) |
2585 | { |
2586 | /* Ensure only whole characters are copied into the buffer. */ |
2587 | gchar *end = g_utf8_find_prev_char (convbuf, convbuf + slen); |
2588 | g_assert (end != NULL); |
2589 | convlen = end - convbuf; |
2590 | |
2591 | /* Return 0 because the buffer isn't large enough. */ |
2592 | retval = 0; |
2593 | } |
2594 | else |
2595 | retval = convlen; |
2596 | |
2597 | memcpy (s, convbuf, convlen); |
2598 | s[convlen] = '\0'; |
2599 | g_free (convbuf); |
2600 | |
2601 | return retval; |
2602 | } |
2603 | |
2604 | #endif |
2605 | |
2606 | /** |
2607 | * g_date_strftime: |
2608 | * @s: destination buffer |
2609 | * @slen: buffer size |
2610 | * @format: format string |
2611 | * @date: valid #GDate |
2612 | * |
2613 | * Generates a printed representation of the date, in a |
2614 | * [locale][setlocale]-specific way. |
2615 | * Works just like the platform's C library strftime() function, |
2616 | * but only accepts date-related formats; time-related formats |
2617 | * give undefined results. Date must be valid. Unlike strftime() |
2618 | * (which uses the locale encoding), works on a UTF-8 format |
2619 | * string and stores a UTF-8 result. |
2620 | * |
2621 | * This function does not provide any conversion specifiers in |
2622 | * addition to those implemented by the platform's C library. |
2623 | * For example, don't expect that using g_date_strftime() would |
2624 | * make the \%F provided by the C99 strftime() work on Windows |
2625 | * where the C library only complies to C89. |
2626 | * |
2627 | * Returns: number of characters written to the buffer, or 0 the buffer was too small |
2628 | */ |
2629 | #pragma GCC diagnostic push |
2630 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" |
2631 | |
2632 | gsize |
2633 | g_date_strftime (gchar *s, |
2634 | gsize slen, |
2635 | const gchar *format, |
2636 | const GDate *d) |
2637 | { |
2638 | struct tm tm; |
2639 | #ifndef G_OS_WIN32 |
2640 | gsize locale_format_len = 0; |
2641 | gchar *locale_format; |
2642 | gsize tmplen; |
2643 | gchar *tmpbuf; |
2644 | gsize tmpbufsize; |
2645 | gsize convlen = 0; |
2646 | gchar *convbuf; |
2647 | GError *error = NULL; |
2648 | gsize retval; |
2649 | #endif |
2650 | |
2651 | g_return_val_if_fail (g_date_valid (d), 0); |
2652 | g_return_val_if_fail (slen > 0, 0); |
2653 | g_return_val_if_fail (format != NULL, 0); |
2654 | g_return_val_if_fail (s != NULL, 0); |
2655 | |
2656 | g_date_to_struct_tm (d, tm: &tm); |
2657 | |
2658 | #ifdef G_OS_WIN32 |
2659 | if (!g_utf8_validate (format, -1, NULL)) |
2660 | { |
2661 | s[0] = '\0'; |
2662 | return 0; |
2663 | } |
2664 | return win32_strftime_helper (d, format, &tm, s, slen); |
2665 | #else |
2666 | |
2667 | locale_format = g_locale_from_utf8 (utf8string: format, len: -1, NULL, bytes_written: &locale_format_len, error: &error); |
2668 | |
2669 | if (error) |
2670 | { |
2671 | g_warning (G_STRLOC "Error converting format to locale encoding: %s" , error->message); |
2672 | g_error_free (error); |
2673 | |
2674 | s[0] = '\0'; |
2675 | return 0; |
2676 | } |
2677 | |
2678 | tmpbufsize = MAX (128, locale_format_len * 2); |
2679 | while (TRUE) |
2680 | { |
2681 | tmpbuf = g_malloc (n_bytes: tmpbufsize); |
2682 | |
2683 | /* Set the first byte to something other than '\0', to be able to |
2684 | * recognize whether strftime actually failed or just returned "". |
2685 | */ |
2686 | tmpbuf[0] = '\1'; |
2687 | tmplen = strftime (s: tmpbuf, maxsize: tmpbufsize, format: locale_format, tp: &tm); |
2688 | |
2689 | if (tmplen == 0 && tmpbuf[0] != '\0') |
2690 | { |
2691 | g_free (mem: tmpbuf); |
2692 | tmpbufsize *= 2; |
2693 | |
2694 | if (tmpbufsize > 65536) |
2695 | { |
2696 | g_warning (G_STRLOC "Maximum buffer size for g_date_strftime exceeded: giving up" ); |
2697 | g_free (mem: locale_format); |
2698 | |
2699 | s[0] = '\0'; |
2700 | return 0; |
2701 | } |
2702 | } |
2703 | else |
2704 | break; |
2705 | } |
2706 | g_free (mem: locale_format); |
2707 | |
2708 | convbuf = g_locale_to_utf8 (opsysstring: tmpbuf, len: tmplen, NULL, bytes_written: &convlen, error: &error); |
2709 | g_free (mem: tmpbuf); |
2710 | |
2711 | if (error) |
2712 | { |
2713 | g_warning (G_STRLOC "Error converting results of strftime to UTF-8: %s" , error->message); |
2714 | g_error_free (error); |
2715 | |
2716 | s[0] = '\0'; |
2717 | return 0; |
2718 | } |
2719 | |
2720 | if (slen <= convlen) |
2721 | { |
2722 | /* Ensure only whole characters are copied into the buffer. |
2723 | */ |
2724 | gchar *end = g_utf8_find_prev_char (str: convbuf, p: convbuf + slen); |
2725 | g_assert (end != NULL); |
2726 | convlen = end - convbuf; |
2727 | |
2728 | /* Return 0 because the buffer isn't large enough. |
2729 | */ |
2730 | retval = 0; |
2731 | } |
2732 | else |
2733 | retval = convlen; |
2734 | |
2735 | memcpy (dest: s, src: convbuf, n: convlen); |
2736 | s[convlen] = '\0'; |
2737 | g_free (mem: convbuf); |
2738 | |
2739 | return retval; |
2740 | #endif |
2741 | } |
2742 | |
2743 | #pragma GCC diagnostic pop |
2744 | |