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