gdate.c 69.6 KB
Newer Older
1 2 3 4
/* GLIB - Library of useful routines for C programming
 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8 9 10 11
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16
 */
17

18
/*
19
 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
20 21 22 23 24
 * file for a list of people on the GLib Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GLib at ftp://ftp.gtk.org/pub/gtk/. 
 */

25 26 27 28
/* 
 * MT safe
 */

29
#include "config.h"
30
#include "glibconfig.h"
31

32 33 34 35 36
#define DEBUG_MSG(x)	/* */
#ifdef G_ENABLE_DEBUG
/* #define DEBUG_MSG(args)	g_message args ; */
#endif

37
#include <time.h>
38 39 40 41
#include <string.h>
#include <stdlib.h>
#include <locale.h>

42 43 44 45
#ifdef G_OS_WIN32
#include <windows.h>
#endif

Matthias Clasen's avatar
Matthias Clasen committed
46 47 48 49 50 51 52 53
#include "gdate.h"

#include "gconvert.h"
#include "gmem.h"
#include "gstrfuncs.h"
#include "gtestutils.h"
#include "gthread.h"
#include "gunicode.h"
54

55 56 57 58
#ifdef G_OS_WIN32
#include "garray.h"
#endif

Matthias Clasen's avatar
Matthias Clasen committed
59 60 61 62 63 64 65 66 67 68 69 70 71
/**
 * SECTION:date
 * @title: Date and Time Functions
 * @short_description: calendrical calculations and miscellaneous time stuff
 *
 * The #GDate data structure represents a day between January 1, Year 1,
 * and sometime a few thousand years in the future (right now it will go
 * to the year 65535 or so, but g_date_set_parse() only parses up to the
 * year 8000 or so - just count on "a few thousand"). #GDate is meant to
 * represent everyday dates, not astronomical dates or historical dates
 * or ISO timestamps or the like. It extrapolates the current Gregorian
 * calendar forward and backward in time; there is no attempt to change
 * the calendar to match time periods or locations. #GDate does not store
72
 * time information; it represents a day.
Matthias Clasen's avatar
Matthias Clasen committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
 *
 * The #GDate implementation has several nice features; it is only a
 * 64-bit struct, so storing large numbers of dates is very efficient. It
 * can keep both a Julian and day-month-year representation of the date,
 * since some calculations are much easier with one representation or the
 * other. A Julian representation is simply a count of days since some
 * fixed day in the past; for #GDate the fixed day is January 1, 1 AD.
 * ("Julian" dates in the #GDate API aren't really Julian dates in the
 * technical sense; technically, Julian dates count from the start of the
 * Julian period, Jan 1, 4713 BC).
 *
 * #GDate is simple to use. First you need a "blank" date; you can get a
 * dynamically allocated date from g_date_new(), or you can declare an
 * automatic variable or array and initialize it to a sane state by
 * calling g_date_clear(). A cleared date is sane; it's safe to call
 * g_date_set_dmy() and the other mutator functions to initialize the
 * value of a cleared date. However, a cleared date is initially
90 91 92
 * invalid, meaning that it doesn't represent a day that exists.
 * It is undefined to call any of the date calculation routines on an
 * invalid date. If you obtain a date from a user or other
Matthias Clasen's avatar
Matthias Clasen committed
93 94 95 96 97
 * unpredictable source, you should check its validity with the
 * g_date_valid() predicate. g_date_valid() is also used to check for
 * errors with g_date_set_parse() and other functions that can
 * fail. Dates can be invalidated by calling g_date_clear() again.
 *
98 99
 * It is very important to use the API to access the #GDate
 * struct. Often only the day-month-year or only the Julian
Matthias Clasen's avatar
Matthias Clasen committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
 * representation is valid. Sometimes neither is valid. Use the API.
 *
 * GLib also features #GDateTime which represents a precise time.
 */

/**
 * G_USEC_PER_SEC:
 *
 * Number of microseconds in one second (1 million).
 * This macro is provided for code readability.
 */

/**
 * GTimeVal:
 * @tv_sec: seconds
 * @tv_usec: microseconds
 *
 * Represents a precise time, with seconds and microseconds.
118 119
 * Similar to the struct timeval returned by the gettimeofday()
 * UNIX system call.
Matthias Clasen's avatar
Matthias Clasen committed
120 121 122
 *
 * GLib is attempting to unify around the use of 64bit integers to
 * represent microsecond-precision time. As such, this type will be
123 124 125
 * removed from a future version of GLib. A consequence of using `glong` for
 * `tv_sec` is that on 32-bit systems `GTimeVal` is subject to the year 2038
 * problem.
Matthias Clasen's avatar
Matthias Clasen committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139
 */

/**
 * GDate:
 * @julian_days: the Julian representation of the date
 * @julian: this bit is set if @julian_days is valid
 * @dmy: this is set if @day, @month and @year are valid
 * @day: the day of the day-month-year representation of the date,
 *     as a number between 1 and 31
 * @month: the day of the day-month-year representation of the date,
 *     as a number between 1 and 12
 * @year: the day of the day-month-year representation of the date
 *
 * Represents a day between January 1, Year 1 and a few thousand years in
140 141 142 143 144 145 146 147 148 149
 * the future. None of its members should be accessed directly.
 *
 * If the #GDate-struct is obtained from g_date_new(), it will be safe
 * to mutate but invalid and thus not safe for calendrical computations.
 *
 * If it's declared on the stack, it will contain garbage so must be
 * initialized with g_date_clear(). g_date_clear() makes the date invalid
 * but sane. An invalid date doesn't represent a day, it's "empty." A date
 * becomes valid after you set it to a Julian day or you set a day, month,
 * and year.
Matthias Clasen's avatar
Matthias Clasen committed
150 151 152 153
 */

/**
 * GTime:
154
 *
155
 * Simply a replacement for time_t. It has been deprecated
156 157
 * since it is not equivalent to time_t on 64-bit platforms
 * with a 64-bit time_t. Unrelated to #GTimer.
158
 *
159 160
 * Note that #GTime is defined to always be a 32-bit integer,
 * unlike time_t which may be 64-bit on some systems. Therefore,
161 162 163
 * #GTime will overflow in the year 2038, and you cannot use the
 * address of a #GTime variable as argument to the UNIX time()
 * function.
Matthias Clasen's avatar
Matthias Clasen committed
164
 *
165
 * Instead, do the following:
166
 * |[<!-- language="C" -->
Matthias Clasen's avatar
Matthias Clasen committed
167 168 169
 * time_t ttime;
 * GTime gtime;
 *
170
 * time (&ttime);
Matthias Clasen's avatar
Matthias Clasen committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
 * gtime = (GTime)ttime;
 * ]|
 */

/**
 * GDateDMY:
 * @G_DATE_DAY: a day
 * @G_DATE_MONTH: a month
 * @G_DATE_YEAR: a year
 *
 * This enumeration isn't used in the API, but may be useful if you need
 * to mark a number as a day, month, or year.
 */

/**
 * GDateDay:
 *
188 189
 * Integer representing a day of the month; between 1 and 31.
 * #G_DATE_BAD_DAY represents an invalid day of the month.
Matthias Clasen's avatar
Matthias Clasen committed
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
 */

/**
 * GDateMonth:
 * @G_DATE_BAD_MONTH: invalid value
 * @G_DATE_JANUARY: January
 * @G_DATE_FEBRUARY: February
 * @G_DATE_MARCH: March
 * @G_DATE_APRIL: April
 * @G_DATE_MAY: May
 * @G_DATE_JUNE: June
 * @G_DATE_JULY: July
 * @G_DATE_AUGUST: August
 * @G_DATE_SEPTEMBER: September
 * @G_DATE_OCTOBER: October
 * @G_DATE_NOVEMBER: November
 * @G_DATE_DECEMBER: December
 *
 * Enumeration representing a month; values are #G_DATE_JANUARY,
 * #G_DATE_FEBRUARY, etc. #G_DATE_BAD_MONTH is the invalid value.
 */

/**
 * GDateYear:
 *
 * Integer representing a year; #G_DATE_BAD_YEAR is the invalid
 * value. The year must be 1 or higher; negative (BC) years are not
 * allowed. The year is represented with four digits.
 */

/**
 * GDateWeekday:
 * @G_DATE_BAD_WEEKDAY: invalid value
 * @G_DATE_MONDAY: Monday
 * @G_DATE_TUESDAY: Tuesday
 * @G_DATE_WEDNESDAY: Wednesday
 * @G_DATE_THURSDAY: Thursday
 * @G_DATE_FRIDAY: Friday
 * @G_DATE_SATURDAY: Saturday
 * @G_DATE_SUNDAY: Sunday
 *
 * Enumeration representing a day of the week; #G_DATE_MONDAY,
 * #G_DATE_TUESDAY, etc. #G_DATE_BAD_WEEKDAY is an invalid weekday.
 */

/**
 * G_DATE_BAD_DAY:
 *
 * Represents an invalid #GDateDay.
 */

/**
 * G_DATE_BAD_JULIAN:
 *
 * Represents an invalid Julian day number.
 */

/**
 * G_DATE_BAD_YEAR:
 *
 * Represents an invalid year.
 */

/**
 * g_date_new:
 *
 * Allocates a #GDate and initializes
 * it to a sane state. The new date will
 * be cleared (as if you'd called g_date_clear()) but invalid (it won't
 * represent an existing day). Free the return value with g_date_free().
 *
 * Returns: a newly-allocated #GDate
 */
263
GDate*
264
g_date_new (void)
265 266 267 268 269 270
{
  GDate *d = g_new0 (GDate, 1); /* happily, 0 is the invalid flag for everything. */
  
  return d;
}

Matthias Clasen's avatar
Matthias Clasen committed
271 272 273 274 275 276 277 278 279 280 281 282
/**
 * g_date_new_dmy:
 * @day: day of the month
 * @month: month of the year
 * @year: year
 *
 * Like g_date_new(), but also sets the value of the date. Assuming the
 * day-month-year triplet you pass in represents an existing day, the
 * returned date will be valid.
 *
 * Returns: a newly-allocated #GDate initialized with @day, @month, and @year
 */
283
GDate*
284 285 286
g_date_new_dmy (GDateDay   day, 
                GDateMonth m, 
                GDateYear  y)
287 288
{
  GDate *d;
289
  g_return_val_if_fail (g_date_valid_dmy (day, m, y), NULL);
290 291
  
  d = g_new (GDate, 1);
292
  
293
  d->julian = FALSE;
294
  d->dmy    = TRUE;
295
  
296 297 298 299 300
  d->month = m;
  d->day   = day;
  d->year  = y;
  
  g_assert (g_date_valid (d));
301
  
302 303 304
  return d;
}

Matthias Clasen's avatar
Matthias Clasen committed
305 306
/**
 * g_date_new_julian:
307
 * @julian_day: days since January 1, Year 1
Matthias Clasen's avatar
Matthias Clasen committed
308 309 310 311 312 313 314
 *
 * Like g_date_new(), but also sets the value of the date. Assuming the
 * Julian day number you pass in is valid (greater than 0, less than an
 * unreasonably large number), the returned date will be valid.
 *
 * Returns: a newly-allocated #GDate initialized with @julian_day
 */
315
GDate*
316
g_date_new_julian (guint32 julian_day)
317 318
{
  GDate *d;
319
  g_return_val_if_fail (g_date_valid_julian (julian_day), NULL);
320 321
  
  d = g_new (GDate, 1);
322
  
323
  d->julian = TRUE;
324
  d->dmy    = FALSE;
325
  
326
  d->julian_days = julian_day;
327 328
  
  g_assert (g_date_valid (d));
329
  
330 331 332
  return d;
}

Matthias Clasen's avatar
Matthias Clasen committed
333 334
/**
 * g_date_free:
335
 * @date: a #GDate to free
Matthias Clasen's avatar
Matthias Clasen committed
336 337 338
 *
 * Frees a #GDate returned from g_date_new().
 */
339
void
340
g_date_free (GDate *date)
341
{
342
  g_return_if_fail (date != NULL);
343
  
344
  g_free (date);
345 346
}

Andrew Potter's avatar
Andrew Potter committed
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
/**
 * g_date_copy:
 * @date: a #GDate to copy
 *
 * Copies a GDate to a newly-allocated GDate. If the input was invalid
 * (as determined by g_date_valid()), the invalid state will be copied
 * as is into the new object.
 *
 * Returns: (transfer full): a newly-allocated #GDate initialized from @date
 *
 * Since: 2.56
 */
GDate *
g_date_copy (const GDate *date)
{
  GDate *res;
  g_return_val_if_fail (date != NULL, NULL);

  if (g_date_valid (date))
    res = g_date_new_julian (g_date_get_julian (date));
  else
    {
      res = g_date_new ();
      *res = *date;
    }

  return res;
}

Matthias Clasen's avatar
Matthias Clasen committed
376 377 378 379 380 381 382 383 384 385
/**
 * g_date_valid:
 * @date: a #GDate to check
 *
 * Returns %TRUE if the #GDate represents an existing day. The date must not
 * contain garbage; it should have been initialized with g_date_clear()
 * if it wasn't allocated by one of the g_date_new() variants.
 *
 * Returns: Whether the date is valid
 */
386
gboolean     
387
g_date_valid (const GDate *d)
388 389
{
  g_return_val_if_fail (d != NULL, FALSE);
390
  
391
  return (d->julian || d->dmy);
392 393 394 395 396 397 398 399 400 401 402 403 404 405
}

static const guint8 days_in_months[2][13] = 
{  /* error, jan feb mar apr may jun jul aug sep oct nov dec */
  {  0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
  {  0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } /* leap year */
};

static const guint16 days_in_year[2][14] = 
{  /* 0, jan feb mar apr may  jun  jul  aug  sep  oct  nov  dec */
  {  0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 
  {  0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};

Matthias Clasen's avatar
Matthias Clasen committed
406 407 408 409 410 411 412 413 414
/**
 * g_date_valid_month:
 * @month: month
 *
 * Returns %TRUE if the month value is valid. The 12 #GDateMonth
 * enumeration values are the only valid months.
 *
 * Returns: %TRUE if the month is valid
 */
415
gboolean     
416
g_date_valid_month (GDateMonth m)
417 418 419 420
{ 
  return ( (m > G_DATE_BAD_MONTH) && (m < 13) );
}

Matthias Clasen's avatar
Matthias Clasen committed
421 422 423 424 425 426 427 428 429
/**
 * g_date_valid_year:
 * @year: year
 *
 * Returns %TRUE if the year is valid. Any year greater than 0 is valid,
 * though there is a 16-bit limit to what #GDate will understand.
 *
 * Returns: %TRUE if the year is valid
 */
430
gboolean     
431
g_date_valid_year (GDateYear y)
432 433 434 435
{
  return ( y > G_DATE_BAD_YEAR );
}

Matthias Clasen's avatar
Matthias Clasen committed
436 437 438 439 440 441 442 443 444 445
/**
 * g_date_valid_day:
 * @day: day to check
 *
 * Returns %TRUE if the day of the month is valid (a day is valid if it's
 * between 1 and 31 inclusive).
 *
 * Returns: %TRUE if the day is valid
 */

446
gboolean     
447
g_date_valid_day (GDateDay d)
448 449 450 451
{
  return ( (d > G_DATE_BAD_DAY) && (d < 32) );
}

Matthias Clasen's avatar
Matthias Clasen committed
452 453 454 455 456 457 458 459 460
/**
 * g_date_valid_weekday:
 * @weekday: weekday
 *
 * Returns %TRUE if the weekday is valid. The seven #GDateWeekday enumeration
 * values are the only valid weekdays.
 *
 * Returns: %TRUE if the weekday is valid
 */
461 462 463 464 465 466
gboolean     
g_date_valid_weekday (GDateWeekday w)
{
  return ( (w > G_DATE_BAD_WEEKDAY) && (w < 8) );
}

Matthias Clasen's avatar
Matthias Clasen committed
467 468 469 470 471 472 473 474 475
/**
 * g_date_valid_julian:
 * @julian_date: Julian day to check
 *
 * Returns %TRUE if the Julian day is valid. Anything greater than zero
 * is basically a valid Julian, though there is a 32-bit limit.
 *
 * Returns: %TRUE if the Julian day is valid
 */
476
gboolean     
477
g_date_valid_julian (guint32 j)
478 479 480 481
{
  return (j > G_DATE_BAD_JULIAN);
}

Matthias Clasen's avatar
Matthias Clasen committed
482 483 484 485 486 487 488 489 490 491 492 493
/**
 * g_date_valid_dmy:
 * @day: day
 * @month: month
 * @year: year
 *
 * Returns %TRUE if the day-month-year triplet forms a valid, existing day
 * in the range of days #GDate understands (Year 1 or later, no more than
 * a few thousand years in the future).
 *
 * Returns: %TRUE if the date is a valid one
 */
494
gboolean     
495 496 497
g_date_valid_dmy (GDateDay   d, 
                  GDateMonth m, 
		  GDateYear  y)
498
{
499 500
  /* No need to check the upper bound of @y, because #GDateYear is 16 bits wide,
   * just like #GDate.year. */
501 502 503 504 505
  return ( (m > G_DATE_BAD_MONTH) &&
           (m < 13)               && 
           (d > G_DATE_BAD_DAY)   && 
           (y > G_DATE_BAD_YEAR)  &&   /* must check before using g_date_is_leap_year */
           (d <=  (g_date_is_leap_year (y) ? 
506
		   days_in_months[1][m] : days_in_months[0][m])) );
507 508 509 510 511 512 513
}


/* "Julian days" just means an absolute number of days, where Day 1 ==
 *   Jan 1, Year 1
 */
static void
514
g_date_update_julian (const GDate *const_d)
515
{
516
  GDate *d = (GDate *) const_d;
517
  GDateYear year;
518
  gint idx;
519
  
520
  g_return_if_fail (d != NULL);
521
  g_return_if_fail (d->dmy != 0);
522
  g_return_if_fail (!d->julian);
523
  g_return_if_fail (g_date_valid_dmy (d->day, d->month, d->year));
524
  
525
  /* What we actually do is: multiply years * 365 days in the year,
526 527 528 529
   * add the number of years divided by 4, subtract the number of
   * years divided by 100 and add the number of years divided by 400,
   * which accounts for leap year stuff. Code from Steffen Beyer's
   * DateCalc. 
530
   */
531
  
532
  year = d->year - 1; /* we know d->year > 0 since it's valid */
533
  
534 535 536 537
  d->julian_days = year * 365U;
  d->julian_days += (year >>= 2); /* divide by 4 and add */
  d->julian_days -= (year /= 25); /* divides original # years by 100 */
  d->julian_days += year >> 2;    /* divides by 4, which divides original by 400 */
538
  
539
  idx = g_date_is_leap_year (d->year) ? 1 : 0;
540
  
541
  d->julian_days += days_in_year[idx][d->month] + d->day;
542
  
543
  g_return_if_fail (g_date_valid_julian (d->julian_days));
544
  
545 546 547 548
  d->julian = TRUE;
}

static void 
549
g_date_update_dmy (const GDate *const_d)
550
{
551
  GDate *d = (GDate *) const_d;
552 553 554
  GDateYear y;
  GDateMonth m;
  GDateDay day;
555
  
556
  guint32 A, B, C, D, E, M;
557
  
558 559
  g_return_if_fail (d != NULL);
  g_return_if_fail (d->julian);
560
  g_return_if_fail (!d->dmy);
561
  g_return_if_fail (g_date_valid_julian (d->julian_days));
562
  
563 564 565 566 567 568 569
  /* Formula taken from the Calendar FAQ; the formula was for the
   *  Julian Period which starts on 1 January 4713 BC, so we add
   *  1,721,425 to the number of days before doing the formula.
   *
   * I'm sure this can be simplified for our 1 January 1 AD period
   * start, but I can't figure out how to unpack the formula.  
   */
570
  
571 572 573 574 575 576 577 578 579 580
  A = d->julian_days + 1721425 + 32045;
  B = ( 4 *(A + 36524) )/ 146097 - 1;
  C = A - (146097 * B)/4;
  D = ( 4 * (C + 365) ) / 1461 - 1;
  E = C - ((1461*D) / 4);
  M = (5 * (E - 1) + 2)/153;
  
  m = M + 3 - (12*(M/10));
  day = E - (153*M + 2)/5;
  y = 100 * B + D - 4800 + (M/10);
581
  
582
#ifdef G_ENABLE_DEBUG
583
  if (!g_date_valid_dmy (day, m, y)) 
584
    g_warning ("OOPS julian: %u  computed dmy: %u %u %u",
585
	       d->julian_days, day, m, y);
586
#endif
587
  
588 589 590
  d->month = m;
  d->day   = day;
  d->year  = y;
591
  
592
  d->dmy = TRUE;
593 594
}

Matthias Clasen's avatar
Matthias Clasen committed
595 596 597 598 599 600 601 602
/**
 * g_date_get_weekday:
 * @date: a #GDate
 *
 * Returns the day of the week for a #GDate. The date must be valid.
 *
 * Returns: day of the week as a #GDateWeekday.
 */
603
GDateWeekday 
604
g_date_get_weekday (const GDate *d)
605 606
{
  g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_WEEKDAY);
607
  
608
  if (!d->julian) 
609 610
    g_date_update_julian (d);

611
  g_return_val_if_fail (d->julian, G_DATE_BAD_WEEKDAY);
612
  
613 614 615
  return ((d->julian_days - 1) % 7) + 1;
}

Matthias Clasen's avatar
Matthias Clasen committed
616 617 618 619 620 621 622 623
/**
 * g_date_get_month:
 * @date: a #GDate to get the month from
 *
 * Returns the month of the year. The date must be valid.
 *
 * Returns: month of the year as a #GDateMonth
 */
624
GDateMonth   
625
g_date_get_month (const GDate *d)
626 627
{
  g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_MONTH);
628
  
629
  if (!d->dmy) 
630 631
    g_date_update_dmy (d);

632
  g_return_val_if_fail (d->dmy, G_DATE_BAD_MONTH);
633 634 635 636
  
  return d->month;
}

Matthias Clasen's avatar
Matthias Clasen committed
637 638 639 640 641 642 643 644
/**
 * g_date_get_year:
 * @date: a #GDate
 *
 * Returns the year of a #GDate. The date must be valid.
 *
 * Returns: year in which the date falls
 */
645
GDateYear    
646
g_date_get_year (const GDate *d)
647 648
{
  g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_YEAR);
649
  
650
  if (!d->dmy) 
651 652
    g_date_update_dmy (d);

653
  g_return_val_if_fail (d->dmy, G_DATE_BAD_YEAR);  
654
  
655 656 657
  return d->year;
}

Matthias Clasen's avatar
Matthias Clasen committed
658 659 660 661 662 663 664 665
/**
 * g_date_get_day:
 * @date: a #GDate to extract the day of the month from
 *
 * Returns the day of the month. The date must be valid.
 *
 * Returns: day of the month
 */
666
GDateDay     
667
g_date_get_day (const GDate *d)
668 669
{
  g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_DAY);
670
  
671
  if (!d->dmy) 
672 673
    g_date_update_dmy (d);

674
  g_return_val_if_fail (d->dmy, G_DATE_BAD_DAY);  
675 676 677 678
  
  return d->day;
}

Matthias Clasen's avatar
Matthias Clasen committed
679 680 681 682 683 684 685 686 687 688 689
/**
 * g_date_get_julian:
 * @date: a #GDate to extract the Julian day from
 *
 * Returns the Julian day or "serial number" of the #GDate. The
 * Julian day is simply the number of days since January 1, Year 1; i.e.,
 * January 1, Year 1 is Julian day 1; January 2, Year 1 is Julian day 2,
 * etc. The date must be valid.
 *
 * Returns: Julian day
 */
690
guint32      
691
g_date_get_julian (const GDate *d)
692 693
{
  g_return_val_if_fail (g_date_valid (d), G_DATE_BAD_JULIAN);
694
  
695
  if (!d->julian) 
696 697
    g_date_update_julian (d);

698 699 700 701 702
  g_return_val_if_fail (d->julian, G_DATE_BAD_JULIAN);  
  
  return d->julian_days;
}

Matthias Clasen's avatar
Matthias Clasen committed
703 704 705 706 707 708 709 710 711
/**
 * g_date_get_day_of_year:
 * @date: a #GDate to extract day of year from
 *
 * Returns the day of the year, where Jan 1 is the first day of the
 * year. The date must be valid.
 *
 * Returns: day of the year
 */
712
guint        
713
g_date_get_day_of_year (const GDate *d)
714
{
715
  gint idx;
716
  
717
  g_return_val_if_fail (g_date_valid (d), 0);
718
  
719
  if (!d->dmy) 
720 721
    g_date_update_dmy (d);

722
  g_return_val_if_fail (d->dmy, 0);  
723
  
724
  idx = g_date_is_leap_year (d->year) ? 1 : 0;
725
  
726
  return (days_in_year[idx][d->month] + d->day);
727 728
}

Matthias Clasen's avatar
Matthias Clasen committed
729 730 731 732 733
/**
 * g_date_get_monday_week_of_year:
 * @date: a #GDate
 *
 * Returns the week of the year, where weeks are understood to start on
Matthias Clasen's avatar
Matthias Clasen committed
734 735
 * Monday. If the date is before the first Monday of the year, return 0.
 * The date must be valid.
Matthias Clasen's avatar
Matthias Clasen committed
736 737 738
 *
 * Returns: week of the year
 */
739
guint        
740
g_date_get_monday_week_of_year (const GDate *d)
741 742 743 744
{
  GDateWeekday wd;
  guint day;
  GDate first;
745
  
746
  g_return_val_if_fail (g_date_valid (d), 0);
747
  
748
  if (!d->dmy) 
749 750
    g_date_update_dmy (d);

751
  g_return_val_if_fail (d->dmy, 0);  
752
  
753
  g_date_clear (&first, 1);
754
  
755
  g_date_set_dmy (&first, 1, 1, d->year);
756
  
757 758
  wd = g_date_get_weekday (&first) - 1; /* make Monday day 0 */
  day = g_date_get_day_of_year (d) - 1;
759
  
760 761 762
  return ((day + wd)/7U + (wd == 0 ? 1 : 0));
}

Matthias Clasen's avatar
Matthias Clasen committed
763 764 765 766
/**
 * g_date_get_sunday_week_of_year:
 * @date: a #GDate
 *
Matthias Clasen's avatar
Matthias Clasen committed
767
 * Returns the week of the year during which this date falls, if
Michael Catanzaro's avatar
Michael Catanzaro committed
768
 * weeks are understood to begin on Sunday. The date must be valid.
Matthias Clasen's avatar
Matthias Clasen committed
769
 * Can return 0 if the day is before the first Sunday of the year.
Matthias Clasen's avatar
Matthias Clasen committed
770 771 772
 *
 * Returns: week number
 */
773
guint        
774
g_date_get_sunday_week_of_year (const GDate *d)
775 776 777 778
{
  GDateWeekday wd;
  guint day;
  GDate first;
779
  
780
  g_return_val_if_fail (g_date_valid (d), 0);
781
  
782
  if (!d->dmy) 
783 784
    g_date_update_dmy (d);

785
  g_return_val_if_fail (d->dmy, 0);  
786
  
787
  g_date_clear (&first, 1);
788
  
789
  g_date_set_dmy (&first, 1, 1, d->year);
790
  
791
  wd = g_date_get_weekday (&first);
792
  if (wd == 7) wd = 0; /* make Sunday day 0 */
793
  day = g_date_get_day_of_year (d) - 1;
794
  
795 796 797
  return ((day + wd)/7U + (wd == 0 ? 1 : 0));
}

798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
/**
 * g_date_get_iso8601_week_of_year:
 * @date: a valid #GDate
 *
 * Returns the week of the year, where weeks are interpreted according
 * to ISO 8601. 
 * 
 * Returns: ISO 8601 week number of the year.
 *
 * Since: 2.6
 **/
guint
g_date_get_iso8601_week_of_year (const GDate *d)
{
  guint j, d4, L, d1, w;

  g_return_val_if_fail (g_date_valid (d), 0);
  
  if (!d->julian)
    g_date_update_julian (d);
818

819 820 821 822 823 824
  g_return_val_if_fail (d->julian, 0);

  /* Formula taken from the Calendar FAQ; the formula was for the
   * Julian Period which starts on 1 January 4713 BC, so we add
   * 1,721,425 to the number of days before doing the formula. 
   */
825
  j  = d->julian_days + 1721425;
826 827 828 829 830 831 832 833
  d4 = (j + 31741 - (j % 7)) % 146097 % 36524 % 1461;
  L  = d4 / 1460;
  d1 = ((d4 - L) % 365) + L;
  w  = d1 / 7 + 1;

  return w;
}

Matthias Clasen's avatar
Matthias Clasen committed
834 835 836 837 838 839 840 841 842 843 844
/**
 * g_date_days_between:
 * @date1: the first date
 * @date2: the second date
 *
 * Computes the number of days between two dates.
 * If @date2 is prior to @date1, the returned value is negative.
 * Both dates must be valid.
 *
 * Returns: the number of days between @date1 and @date2
 */
845 846 847 848 849 850 851 852 853 854
gint
g_date_days_between (const GDate *d1,
		     const GDate *d2)
{
  g_return_val_if_fail (g_date_valid (d1), 0);
  g_return_val_if_fail (g_date_valid (d2), 0);

  return (gint)g_date_get_julian (d2) - (gint)g_date_get_julian (d1);
}

Matthias Clasen's avatar
Matthias Clasen committed
855 856 857 858 859 860 861 862 863 864
/**
 * g_date_clear:
 * @date: pointer to one or more dates to clear
 * @n_dates: number of dates to clear
 *
 * Initializes one or more #GDate structs to a sane but invalid
 * state. The cleared dates will not represent an existing date, but will
 * not contain garbage. Useful to init a date declared on the stack.
 * Validity can be tested with g_date_valid().
 */
865
void         
866
g_date_clear (GDate *d, guint ndates)
867 868 869
{
  g_return_if_fail (d != NULL);
  g_return_if_fail (ndates != 0);
870
  
871 872 873
  memset (d, 0x0, ndates*sizeof (GDate)); 
}

874
G_LOCK_DEFINE_STATIC (g_date_global);
875

876 877 878 879 880 881 882
/* These are for the parser, output to the user should use *
 * g_date_strftime () - this creates more never-freed memory to annoy
 * all those memory debugger users. :-) 
 */

static gchar *long_month_names[13] = 
{ 
883
  NULL,
884 885
};

886 887 888 889 890
static gchar *long_month_names_alternative[13] =
{
  NULL,
};

891 892
static gchar *short_month_names[13] = 
{
893
  NULL, 
894 895
};

896 897 898 899 900
static gchar *short_month_names_alternative[13] =
{
  NULL,
};

901 902 903 904
/* This tells us if we need to update the parse info */
static gchar *current_locale = NULL;

/* order of these in the current locale */
905
static GDateDMY dmy_order[3] = 
906
{
907
   G_DATE_DAY, G_DATE_MONTH, G_DATE_YEAR
908 909 910 911 912 913 914
};

/* Where to chop two-digit years: i.e., for the 1930 default, numbers
 * 29 and below are counted as in the year 2000, numbers 30 and above
 * are counted as in the year 1900.  
 */

915
static const GDateYear twodigit_start_year = 1930;
916 917 918 919 920 921

/* It is impossible to enter a year between 1 AD and 99 AD with this
 * in effect.  
 */
static gboolean using_twodigit_years = FALSE;

922 923 924 925
/* Adjustment of locale era to AD, non-zero means using locale era
 */
static gint locale_era_adjust = 0;

926 927 928 929 930 931 932 933
struct _GDateParseTokens {
  gint num_ints;
  gint n[3];
  guint month;
};

typedef struct _GDateParseTokens GDateParseTokens;

934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954
static inline gboolean
update_month_match (gsize *longest,
                    const gchar *haystack,
                    const gchar *needle)
{
  gsize length;

  if (needle == NULL)
    return FALSE;

  length = strlen (needle);
  if (*longest >= length)
    return FALSE;

  if (strstr (haystack, needle) == NULL)
    return FALSE;

  *longest = length;
  return TRUE;
}

955 956
#define NUM_LEN 10

957
/* HOLDS: g_date_global_lock */
958 959 960 961 962
static void
g_date_fill_parse_tokens (const gchar *str, GDateParseTokens *pt)
{
  gchar num[4][NUM_LEN+1];
  gint i;
963
  const guchar *s;
964
  
965 966 967 968 969
  /* We count 4, but store 3; so we can give an error
   * if there are 4.
   */
  num[0][0] = num[1][0] = num[2][0] = num[3][0] = '\0';
  
970
  s = (const guchar *) str;
971 972 973
  pt->num_ints = 0;
  while (*s && pt->num_ints < 4) 
    {
974
      
975
      i = 0;
976
      while (*s && g_ascii_isdigit (*s) && i < NUM_LEN)
977 978 979 980 981
        {
          num[pt->num_ints][i] = *s;
          ++s; 
          ++i;
        }
982
      
983 984 985 986 987
      if (i > 0) 
        {
          num[pt->num_ints][i] = '\0';
          ++(pt->num_ints);
        }
988
      
989
      if (*s == '\0') break;
990
      
991 992 993 994 995 996
      ++s;
    }
  
  pt->n[0] = pt->num_ints > 0 ? atoi (num[0]) : 0;
  pt->n[1] = pt->num_ints > 1 ? atoi (num[1]) : 0;
  pt->n[2] = pt->num_ints > 2 ? atoi (num[2]) : 0;
997
  
998
  pt->month = G_DATE_BAD_MONTH;
999
  
1000 1001
  if (pt->num_ints < 3)
    {
1002
      gsize longest = 0;
1003 1004
      gchar *casefold;
      gchar *normalized;
1005
      
1006 1007
      casefold = g_utf8_casefold (str, -1);
      normalized = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1008 1009
      g_free (casefold);

1010
      for (i = 1; i < 13; ++i)
1011
        {
1012 1013
          /* Here month names may be in a genitive case if the language
           * grammatical rules require it.
1014 1015 1016
           * Examples of how January may look in some languages:
           * Catalan: "de gener", Croatian: "siječnja", Polish: "stycznia",
           * Upper Sorbian: "januara".
1017 1018 1019
           * Note that most of the languages can't or don't use the the
           * genitive case here so they use nominative everywhere.
           * For example, English always uses "January".
1020
           */
1021 1022
          if (update_month_match (&longest, normalized, long_month_names[i]))
            pt->month = i;
1023 1024 1025 1026 1027 1028

          /* Here month names will be in a nominative case.
           * Examples of how January may look in some languages:
           * Catalan: "gener", Croatian: "Siječanj", Polish: "styczeń",
           * Upper Sorbian: "Januar".
           */
1029 1030
          if (update_month_match (&longest, normalized, long_month_names_alternative[i]))
            pt->month = i;
1031 1032 1033 1034 1035

          /* Differences between abbreviated nominative and abbreviated
           * genitive month names are visible in very few languages but
           * let's handle them.
           */
1036 1037
          if (update_month_match (&longest, normalized, short_month_names[i]))
            pt->month = i;
1038

1039 1040
          if (update_month_match (&longest, normalized, short_month_names_alternative[i]))
            pt->month = i;
1041 1042 1043
        }

      g_free (normalized);
1044 1045 1046
    }
}

1047
/* HOLDS: g_date_global_lock */
1048
static void
1049 1050
g_date_prepare_to_parse (const gchar      *str, 
                         GDateParseTokens *pt)
1051 1052 1053 1054
{
  const gchar *locale = setlocale (LC_TIME, NULL);
  gboolean recompute_localeinfo = FALSE;
  GDate d;
1055
  
1056
  g_return_if_fail (locale != NULL); /* should not happen */
1057
  
1058
  g_date_clear (&d, 1);              /* clear for scratch use */
1059
  
1060
  if ( (current_locale == NULL) || (strcmp (locale, current_locale) != 0) ) 
1061
    recompute_localeinfo = TRUE;  /* Uh, there used to be a reason for the temporary */
1062 1063 1064 1065 1066 1067
  
  if (recompute_localeinfo)
    {
      int i = 1;
      GDateParseTokens testpt;
      gchar buf[128];
1068
      
1069
      g_free (current_locale); /* still works if current_locale == NULL */
1070
      
1071
      current_locale = g_strdup (locale);
1072
      
1073 1074 1075
      short_month_names[0] = "Error";
      long_month_names[0] = "Error";

1076 1077
      while (i < 13) 
        {
1078 1079
	  gchar *casefold;
	  
1080
          g_date_set_dmy (&d, 1, i, 1976);
1081
	  
1082
          g_return_if_fail (g_date_valid (&d));
1083
	  
1084
          g_date_strftime (buf, 127, "%b", &d);
1085

1086
	  casefold = g_utf8_casefold (buf, -1);
1087
          g_free (short_month_names[i]);
1088
          short_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1089
	  g_free (casefold);
1090
	  
1091
          g_date_strftime (buf, 127, "%B", &d);
1092
	  casefold = g_utf8_casefold (buf, -1);
1093
          g_free (long_month_names[i]);
1094
          long_month_names[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
1095
	  g_free (casefold);
1096
          
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
          g_date_strftime (buf, 127, "%Ob", &d);
          casefold = g_utf8_casefold (buf, -1);
          g_free (short_month_names_alternative[i]);
          short_month_names_alternative[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
          g_free (casefold);

          g_date_strftime (buf, 127, "%OB", &d);
          casefold = g_utf8_casefold (buf, -1);
          g_free (long_month_names_alternative[i]);
          long_month_names_alternative[i] = g_utf8_normalize (casefold, -1, G_NORMALIZE_ALL);
          g_free (casefold);

1109 1110
          ++i;
        }
1111
      
1112
      /* Determine DMY order */
1113
      
1114 1115 1116
      /* had to pick a random day - don't change this, some strftimes
       * are broken on some days, and this one is good so far. */
      g_date_set_dmy (&d, 4, 7, 1976);
1117 1118 1119 1120
      
      g_date_strftime (buf, 127, "%x", &d);
      
      g_date_fill_parse_tokens (buf, &testpt);
1121 1122 1123 1124 1125 1126

      using_twodigit_years = FALSE;
      locale_era_adjust = 0;
      dmy_order[0] = G_DATE_DAY;
      dmy_order[1] = G_DATE_MONTH;
      dmy_order[2] = G_DATE_YEAR;
1127 1128 1129 1130 1131 1132 1133
      
      i = 0;
      while (i < testpt.num_ints)
        {
          switch (testpt.n[i])
            {
            case 7:
1134
              dmy_order[i] = G_DATE_MONTH;
1135 1136
              break;
            case 4:
1137
              dmy_order[i] = G_DATE_DAY;
1138 1139 1140
              break;
            case 76:
              using_twodigit_years = TRUE; /* FALL THRU */
1141
            case 1976:
1142
              dmy_order[i] = G_DATE_YEAR;
1143 1144
              break;
            default:
1145 1146 1147
              /* assume locale era */
              locale_era_adjust = 1976 - testpt.n[i];
              dmy_order[i] = G_DATE_YEAR;
1148 1149 1150 1151
              break;
            }
          ++i;
        }
1152
      
Dan Winship's avatar
Dan Winship committed
1153
#if defined(G_ENABLE_DEBUG) && 0
1154
      DEBUG_MSG (("**GDate prepared a new set of locale-specific parse rules."));
1155 1156 1157
      i = 1;
      while (i < 13) 
        {
1158
          DEBUG_MSG (("  %s   %s", long_month_names[i], short_month_names[i]));
1159 1160
          ++i;
        }
1161 1162 1163 1164 1165 1166 1167
      DEBUG_MSG (("Alternative month names:"));
      i = 1;
      while (i < 13)
        {
          DEBUG_MSG (("  %s   %s", long_month_names_alternative[i], short_month_names_alternative[i]));
          ++i;
        }
1168
      if (using_twodigit_years)
1169 1170 1171
        {
	  DEBUG_MSG (("**Using twodigit years with cutoff year: %u", twodigit_start_year));
        }
1172 1173 1174 1175 1176
      { 
        gchar *strings[3];
        i = 0;
        while (i < 3)
          {
1177
            switch (dmy_order[i])
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
              {
              case G_DATE_MONTH:
                strings[i] = "Month";
                break;
              case G_DATE_YEAR:
                strings[i] = "Year";
                break;
              case G_DATE_DAY:
                strings[i] = "Day";
                break;
              default:
                strings[i] = NULL;
                break;
              }
            ++i;
          }
1194
        DEBUG_MSG (("**Order: %s, %s, %s", strings[0], strings[1], strings[2]));
1195
        DEBUG_MSG (("**Sample date in this locale: '%s'", buf));
1196 1197 1198
      }
#endif
    }
1199
  
1200 1201 1202
  g_date_fill_parse_tokens (str, pt);
}

Matthias Clasen's avatar
Matthias Clasen committed
1203 1204 1205 1206 1207 1208
/**
 * g_date_set_parse:
 * @date: a #GDate to fill in
 * @str: string to parse
 *
 * Parses a user-inputted string @str, and try to figure out what date it
1209 1210 1211 1212
 * represents, taking the [current locale][setlocale] into account. If the
 * string is successfully parsed, the date will be valid after the call.
 * Otherwise, it will be invalid. You should check using g_date_valid()
 * to see whether the parsing succeeded.
Matthias Clasen's avatar
Matthias Clasen committed
1213 1214 1215 1216 1217 1218 1219
 *
 * This function is not appropriate for file formats and the like; it
 * isn't very precise, and its exact behavior varies with the locale.
 * It's intended to be a heuristic routine that guesses what the user
 * means by a given string (and it does work pretty well in that
 * capacity).
 */
1220 1221 1222 1223 1224 1225
void         
g_date_set_parse (GDate       *d, 
                  const gchar *str)
{
  GDateParseTokens pt;
  guint m = G_DATE_BAD_MONTH, day = G_DATE_BAD_DAY, y = G_DATE_BAD_YEAR;
1226
  
1227
  g_return_if_fail (d != NULL);
1228
  
1229 1230
  /* set invalid */
  g_date_clear (d, 1);
1231
  
1232
  G_LOCK (g_date_global);
1233

1234
  g_date_prepare_to_parse (str, &pt);
1235
  
1236
  DEBUG_MSG (("Found %d ints, '%d' '%d' '%d' and written out month %d",
1237
	      pt.num_ints, pt.n[0], pt.n[1], pt.n[2], pt.month));
1238
  
1239
  
1240 1241
  if (pt.num_ints == 4) 
    {
1242
      G_UNLOCK (g_date_global);
1243 1244
      return; /* presumably a typo; bail out. */
    }
1245
  
1246 1247 1248 1249
  if (pt.num_ints > 1)
    {
      int i = 0;
      int j = 0;
1250
      
1251
      g_assert (pt.num_ints < 4); /* i.e., it is 2 or 3 */
1252
      
1253 1254
      while (i < pt.num_ints && j < 3) 
        {
1255
          switch (dmy_order[j])
1256 1257
            {
            case G_DATE_MONTH:
1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268
	    {
	      if (pt.num_ints == 2 && pt.month != G_DATE_BAD_MONTH)
		{
		  m = pt.month;
		  ++j;      /* skip months, but don't skip this number */
		  continue;
		}
	      else 
		m = pt.n[i];
	    }
	    break;
1269
            case G_DATE_DAY:
1270 1271 1272 1273 1274 1275 1276 1277 1278 1279
	    {
	      if (pt.num_ints == 2 && pt.month == G_DATE_BAD_MONTH)
		{
		  day = 1;
		  ++j;      /* skip days, since we may have month/year */
		  continue;
		}
	      day = pt.n[i];
	    }
	    break;
1280
            case G_DATE_YEAR:
1281 1282 1283
	    {
	      y  = pt.n[i];
	      
1284 1285 1286 1287 1288
	      if (locale_era_adjust != 0)
	        {
		  y += locale_era_adjust;
	        }
	      else if (using_twodigit_years && y < 100)
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299
		{
		  guint two     =  twodigit_start_year % 100;
		  guint century = (twodigit_start_year / 100) * 100;
		  
		  if (y < two)
		    century += 100;
		  
		  y += century;
		}
	    }
	    break;
1300 1301 1302
            default:
              break;
            }
1303
	  
1304 1305 1306 1307
          ++i;
          ++j;
        }
      
1308
      
1309
      if (pt.num_ints == 3 && !g_date_valid_dmy (day, m, y))
1310 1311 1312 1313 1314 1315 1316 1317 1318
        {
          /* Try YYYY MM DD */
          y   = pt.n[0];
          m   = pt.n[1];
          day = pt.n[2];
          
          if (using_twodigit_years && y < 100) 
            y = G_DATE_BAD_YEAR; /* avoids ambiguity */
        }
1319 1320 1321
      else if (pt.num_ints == 2)
	{
	  if (m == G_DATE_BAD_MONTH && pt.month != G_DATE_BAD_MONTH)
1322
	    m = pt.month;
1323
	}
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
    }
  else if (pt.num_ints == 1) 
    {
      if (pt.month != G_DATE_BAD_MONTH)
        {
          /* Month name and year? */
          m    = pt.month;
          day  = 1;
          y = pt.n[0];
        }
      else
        {
          /* Try yyyymmdd and yymmdd */
1337
	  
1338 1339 1340
          m   = (pt.n[0]/100) % 100;
          day = pt.n[0] % 100;
          y   = pt.n[0]/10000;
1341
	  
1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
          /* FIXME move this into a separate function */
          if (using_twodigit_years && y < 100)
            {
              guint two     =  twodigit_start_year % 100;
              guint century = (twodigit_start_year / 100) * 100;
              
              if (y < two)
                century += 100;
              
              y += century;
            }
        }
    }
1355
  
1356 1357
  /* See if we got anything valid out of all this. */
  /* y < 8000 is to catch 19998 style typos; the library is OK up to 65535 or so */
1358
  if (y < 8000 && g_date_valid_dmy (day, m, y)) 
1359 1360 1361 1362
    {
      d->month = m;
      d->day   = day;
      d->year  = y;
1363
      d->dmy   = TRUE;
1364 1365 1366
    }
#ifdef G_ENABLE_DEBUG
  else 
1367 1368 1369
    {
      DEBUG_MSG (("Rejected DMY %u %u %u", day, m, y));
    }
1370
#endif
1371
  G_UNLOCK (g_date_global);
1372 1373
}

1374 1375 1376
/**
 * g_date_set_time_t:
 * @date: a #GDate 
1377
 * @timet: time_t value to set
1378
 *
1379 1380 1381
 * Sets the value of a date to the date corresponding to a time 
 * specified as a time_t. The time to date conversion is done using 
 * the user's current timezone.
1382 1383
 *
 * To set the value of a date to the current day, you could write:
1384
 * |[<!-- language="C" -->
1385 1386 1387 1388
 *  time_t now = time (NULL);
 *  if (now == (time_t) -1)
 *    // handle the error
 *  g_date_set_time_t (date, now);
Matthias Clasen's avatar
Matthias Clasen committed
1389
 * ]|
1390 1391 1392
 *
 * Since: 2.10
 */
1393
void         
1394 1395
g_date_set_time_t (GDate *date,
		   time_t timet)
1396
{
1397
  struct tm tm;
1398
  
1399
  g_return_if_fail (date != NULL);
1400
  
1401
#ifdef HAVE_LOCALTIME_R
1402
  localtime_r (&timet, &tm);
1403
#else
1404
  {
1405
    struct tm *ptm = localtime (&timet);
1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421

    if (ptm == NULL)
      {
	/* Happens at least in Microsoft's C library if you pass a
	 * negative time_t. Use 2000-01-01 as default date.
	 */
#ifndef G_DISABLE_CHECKS
	g_return_if_fail_warning (G_LOG_DOMAIN, "g_date_set_time", "ptm != NULL");
#endif

	tm.tm_mon = 0;
	tm.tm_mday = 1;
	tm.tm_year = 100;
      }
    else
      memcpy ((void *) &tm, (void *) ptm, sizeof(struct tm));
1422
  }
1423
#endif
1424
  
1425
  date->julian = FALSE;
1426
  
1427 1428 1429
  date->month = tm.tm_mon + 1;
  date->day   = tm.tm_mday;
  date->year  = tm.tm_year + 1900;
1430
  
1431
  g_return_if_fail (g_date_valid_dmy (date->day, date->month, date->year));
1432
  
1433 1434 1435 1436 1437