summaryrefslogtreecommitdiff
path: root/ext/date/lib/timelib.h
blob: 9dde21e7fbf2163a71002752285f6e6397e61445 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
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
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015-2019 Derick Rethans
 * Copyright (c) 2018 MongoDB, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef __TIMELIB_H__
#define __TIMELIB_H__

#ifdef HAVE_TIMELIB_CONFIG_H
# include "timelib_config.h"
#endif

#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <inttypes.h>

# ifndef HAVE_INT32_T
#  if SIZEOF_INT == 4
typedef int int32_t;
#  elif SIZEOF_LONG == 4
typedef long int int32_t;
#  endif
# endif

# ifndef HAVE_UINT32_T
#  if SIZEOF_INT == 4
typedef unsigned int uint32_t;
#  elif SIZEOF_LONG == 4
typedef unsigned long int uint32_t;
#  endif
# endif

#ifdef _WIN32
# if _MSC_VER >= 1600
# include <stdint.h>
# endif
# ifndef SIZEOF_INT
#  define SIZEOF_INT 4
# endif
# ifndef SIZEOF_LONG
#  define SIZEOF_LONG 4
# endif
# ifndef int32_t
typedef __int32           int32_t;
# endif
# ifndef uint32_t
typedef unsigned __int32  uint32_t;
# endif
# ifndef int64_t
typedef __int64           int64_t;
# endif
# ifndef uint64_t
typedef unsigned __int64  uint64_t;
# endif
# ifndef PRId32
#  define PRId32       "I32d"
# endif
# ifndef PRIu32
#  define PRIu32       "I32u"
# endif
# ifndef PRId64
#  define PRId64       "I64d"
# endif
# ifndef PRIu64
#  define PRIu64       "I64u"
# endif
# ifndef INT32_MAX
#define INT32_MAX    _I32_MAX
# endif
# ifndef INT32_MIN
#define INT32_MIN    ((int32_t)_I32_MIN)
# endif
# ifndef UINT32_MAX
#define UINT32_MAX   _UI32_MAX
# endif
# ifndef INT64_MIN
#define INT64_MIN    ((int64_t)_I64_MIN)
# endif
# ifndef INT64_MAX
#define INT64_MAX    _I64_MAX
# endif
# ifndef UINT64_MAX
#define UINT64_MAX   _UI64_MAX
# endif
#endif

#if (defined(__x86_64__) || defined(__LP64__) || defined(_LP64) || defined(_WIN64)) && !defined(TIMELIB_FORCE_LONG32)
typedef int64_t timelib_long;
typedef uint64_t timelib_ulong;
# define TIMELIB_LONG_MAX INT64_MAX
# define TIMELIB_LONG_MIN INT64_MIN
# define TIMELIB_ULONG_MAX UINT64_MAX
# define TIMELIB_LONG_FMT "%" PRId64
# define TIMELIB_ULONG_FMT "%" PRIu64
#else
typedef int32_t timelib_long;
typedef uint32_t timelib_ulong;
# define TIMELIB_LONG_MAX INT32_MAX
# define TIMELIB_LONG_MIN INT32_MIN
# define TIMELIB_ULONG_MAX UINT32_MAX
# define TIMELIB_LONG_FMT "%" PRId32
# define TIMELIB_ULONG_FMT "%" PRIu32
#endif

#if defined(_MSC_VER)
typedef uint64_t timelib_ull;
typedef int64_t timelib_sll;
# define TIMELIB_LL_CONST(n) n ## i64
#else
typedef unsigned long long timelib_ull;
typedef signed long long timelib_sll;
# define TIMELIB_LL_CONST(n) n ## ll
#endif

typedef struct _ttinfo ttinfo;
typedef struct _tlinfo tlinfo;

typedef struct _tlocinfo
{
	char country_code[3];
	double latitude;
	double longitude;
	char *comments;
} tlocinfo;

typedef struct _timelib_tzinfo
{
	char    *name;
	struct {
		uint32_t ttisgmtcnt;
		uint32_t ttisstdcnt;
		uint32_t leapcnt;
		uint32_t timecnt;
		uint32_t typecnt;
		uint32_t charcnt;
	} _bit32;
	struct {
		uint64_t ttisgmtcnt;
		uint64_t ttisstdcnt;
		uint64_t leapcnt;
		uint64_t timecnt;
		uint64_t typecnt;
		uint64_t charcnt;
	} bit64;

	int64_t *trans;
	unsigned char *trans_idx;

	ttinfo  *type;
	char    *timezone_abbr;

	tlinfo  *leap_times;
	unsigned char bc;
	tlocinfo location;
} timelib_tzinfo;

typedef struct _timelib_rel_time {
	timelib_sll y, m, d; /* Years, Months and Days */
	timelib_sll h, i, s; /* Hours, mInutes and Seconds */
	timelib_sll us;      /* Microseconds */

	int weekday; /* Stores the day in 'next monday' */
	int weekday_behavior; /* 0: the current day should *not* be counted when advancing forwards; 1: the current day *should* be counted */

	int first_last_day_of;
	int invert; /* Whether the difference should be inverted */
	timelib_sll days; /* Contains the number of *days*, instead of Y-M-D differences */

	struct {
		unsigned int type;
		timelib_sll amount;
	} special;

	unsigned int   have_weekday_relative, have_special_relative;
} timelib_rel_time;

typedef struct _timelib_time_offset {
	int32_t      offset;
	unsigned int leap_secs;
	unsigned int is_dst;
	char        *abbr;
	timelib_sll  transition_time;
} timelib_time_offset;

typedef struct _timelib_time {
	timelib_sll      y, m, d;     /* Year, Month, Day */
	timelib_sll      h, i, s;     /* Hour, mInute, Second */
	timelib_sll      us;          /* Microseconds */
	int              z;           /* UTC offset in seconds */
	char            *tz_abbr;     /* Timezone abbreviation (display only) */
	timelib_tzinfo  *tz_info;     /* Timezone structure */
	signed int       dst;         /* Flag if we were parsing a DST zone */
	timelib_rel_time relative;

	timelib_sll      sse;         /* Seconds since epoch */

	unsigned int   have_time, have_date, have_zone, have_relative, have_weeknr_day;

	unsigned int   sse_uptodate; /* !0 if the sse member is up to date with the date/time members */
	unsigned int   tim_uptodate; /* !0 if the date/time members are up to date with the sse member */
	unsigned int   is_localtime; /*  1 if the current struct represents localtime, 0 if it is in GMT */
	unsigned int   zone_type;    /*  1 time offset,
	                              *  3 TimeZone identifier,
	                              *  2 TimeZone abbreviation */
} timelib_time;

typedef struct _timelib_abbr_info {
	timelib_sll  utc_offset;
	char        *abbr;
	int          dst;
} timelib_abbr_info;

#define TIMELIB_WARN_MASK                      0x1ff
#define TIMELIB_ERR_MASK                       0x2ff

#define TIMELIB_WARN_DOUBLE_TZ                 0x101
#define TIMELIB_WARN_INVALID_TIME              0x102
#define TIMELIB_WARN_INVALID_DATE              0x103
#define TIMELIB_WARN_TRAILING_DATA             0x11a

#define TIMELIB_ERR_DOUBLE_TZ                  0x201
#define TIMELIB_ERR_TZID_NOT_FOUND             0x202
#define TIMELIB_ERR_DOUBLE_TIME                0x203
#define TIMELIB_ERR_DOUBLE_DATE                0x204
#define TIMELIB_ERR_UNEXPECTED_CHARACTER       0x205
#define TIMELIB_ERR_EMPTY_STRING               0x206
#define TIMELIB_ERR_UNEXPECTED_DATA            0x207
#define TIMELIB_ERR_NO_TEXTUAL_DAY             0x208
#define TIMELIB_ERR_NO_TWO_DIGIT_DAY           0x209
#define TIMELIB_ERR_NO_THREE_DIGIT_DAY_OF_YEAR 0x20a
#define TIMELIB_ERR_NO_TWO_DIGIT_MONTH         0x20b
#define TIMELIB_ERR_NO_TEXTUAL_MONTH           0x20c
#define TIMELIB_ERR_NO_TWO_DIGIT_YEAR          0x20d
#define TIMELIB_ERR_NO_FOUR_DIGIT_YEAR         0x20e
#define TIMELIB_ERR_NO_TWO_DIGIT_HOUR          0x20f
#define TIMELIB_ERR_HOUR_LARGER_THAN_12        0x210
#define TIMELIB_ERR_MERIDIAN_BEFORE_HOUR       0x211
#define TIMELIB_ERR_NO_MERIDIAN                0x212
#define TIMELIB_ERR_NO_TWO_DIGIT_MINUTE        0x213
#define TIMELIB_ERR_NO_TWO_DIGIT_SECOND        0x214
#define TIMELIB_ERR_NO_SIX_DIGIT_MICROSECOND   0x215
#define TIMELIB_ERR_NO_SEP_SYMBOL              0x216
#define TIMELIB_ERR_EXPECTED_ESCAPE_CHAR       0x217
#define TIMELIB_ERR_NO_ESCAPED_CHAR            0x218
#define TIMELIB_ERR_WRONG_FORMAT_SEP           0x219
#define TIMELIB_ERR_TRAILING_DATA              0x21a
#define TIMELIB_ERR_DATA_MISSING               0x21b
#define TIMELIB_ERR_NO_THREE_DIGIT_MILLISECOND 0x21c
#define TIMELIB_ERR_NO_FOUR_DIGIT_YEAR_ISO     0x21d
#define TIMELIB_ERR_NO_TWO_DIGIT_WEEK          0x21e
#define TIMELIB_ERR_INVALID_WEEK               0x21f
#define TIMELIB_ERR_NO_DAY_OF_WEEK             0x220
#define TIMELIB_ERR_INVALID_DAY_OF_WEEK        0x221
#define TIMELIB_ERR_INVALID_SPECIFIER          0x222
#define TIMELIB_ERR_INVALID_TZ_OFFSET          0x223
#define TIMELIB_ERR_FORMAT_LITERAL_MISMATCH    0x224
#define TIMELIB_ERR_MIX_ISO_WITH_NATURAL       0x225

#define TIMELIB_ZONETYPE_OFFSET 1
#define TIMELIB_ZONETYPE_ABBR   2
#define TIMELIB_ZONETYPE_ID     3

typedef struct _timelib_error_message {
	int         error_code;
	int         position;
	char        character;
	char       *message;
} timelib_error_message;

typedef struct _timelib_error_container {
	timelib_error_message *error_messages;
	timelib_error_message *warning_messages;
	int                    error_count;
	int                    warning_count;
} timelib_error_container;

typedef struct _timelib_tz_lookup_table {
	char       *name;
	int         type;
	float       gmtoffset;
	char       *full_tz_name;
} timelib_tz_lookup_table;

typedef struct _timelib_tzdb_index_entry {
	char *id;
	unsigned int pos;
} timelib_tzdb_index_entry;

typedef struct _timelib_tzdb {
	char                           *version;
	int                             index_size;
	const timelib_tzdb_index_entry *index;
	const unsigned char            *data;
} timelib_tzdb;

#ifndef timelib_malloc
# define timelib_malloc  malloc
# define timelib_realloc realloc
# define timelib_calloc  calloc
# define timelib_strdup  strdup
# define timelib_free    free
#endif

#define TIMELIB_VERSION 201803
#define TIMELIB_EXTENDED_VERSION 20180301
#define TIMELIB_ASCII_VERSION "2018.03"

#define TIMELIB_NONE             0x00
#define TIMELIB_OVERRIDE_TIME    0x01
#define TIMELIB_NO_CLONE         0x02

#define TIMELIB_UNSET   -99999

/* An entry for each of these error codes is also in the
 * timelib_error_messages array in timelib.c */
#define TIMELIB_ERROR_NO_ERROR                            0x00
#define TIMELIB_ERROR_CANNOT_ALLOCATE                     0x01
#define TIMELIB_ERROR_CORRUPT_TRANSITIONS_DONT_INCREASE   0x02
#define TIMELIB_ERROR_CORRUPT_NO_64BIT_PREAMBLE           0x03
#define TIMELIB_ERROR_CORRUPT_NO_ABBREVIATION             0x04
#define TIMELIB_ERROR_UNSUPPORTED_VERSION                 0x05
#define TIMELIB_ERROR_NO_SUCH_TIMEZONE                    0x06

#ifdef __cplusplus
extern "C" {
#endif

typedef enum _timelib_format_specifier_code {
	TIMELIB_FORMAT_ALLOW_EXTRA_CHARACTERS = 0,
	TIMELIB_FORMAT_ANY_SEPARATOR,
	TIMELIB_FORMAT_DAY_TWO_DIGIT,
	TIMELIB_FORMAT_DAY_TWO_DIGIT_PADDED,
	TIMELIB_FORMAT_DAY_OF_WEEK_ISO,
	TIMELIB_FORMAT_DAY_OF_WEEK,
	TIMELIB_FORMAT_DAY_OF_YEAR,
	TIMELIB_FORMAT_DAY_SUFFIX,
	TIMELIB_FORMAT_END,
	TIMELIB_FORMAT_EPOCH_SECONDS,
	TIMELIB_FORMAT_ESCAPE,
	TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX,
	TIMELIB_FORMAT_HOUR_TWO_DIGIT_12_MAX_PADDED,
	TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX,
	TIMELIB_FORMAT_HOUR_TWO_DIGIT_24_MAX_PADDED,
	TIMELIB_FORMAT_LITERAL,
	TIMELIB_FORMAT_MERIDIAN,
	TIMELIB_FORMAT_MICROSECOND_SIX_DIGIT,
	TIMELIB_FORMAT_MILLISECOND_THREE_DIGIT,
	TIMELIB_FORMAT_MINUTE_TWO_DIGIT,
	TIMELIB_FORMAT_MONTH_TWO_DIGIT,
	TIMELIB_FORMAT_MONTH_TWO_DIGIT_PADDED,
	TIMELIB_FORMAT_RANDOM_CHAR,
	TIMELIB_FORMAT_RESET_ALL,
	TIMELIB_FORMAT_RESET_ALL_WHEN_NOT_SET,
	TIMELIB_FORMAT_SECOND_TWO_DIGIT,
	TIMELIB_FORMAT_SEPARATOR,
	TIMELIB_FORMAT_SKIP_TO_SEPARATOR,
	TIMELIB_FORMAT_TEXTUAL_DAY_3_LETTER,
	TIMELIB_FORMAT_TEXTUAL_DAY_FULL,
	TIMELIB_FORMAT_TEXTUAL_MONTH_3_LETTER,
	TIMELIB_FORMAT_TEXTUAL_MONTH_FULL,
	TIMELIB_FORMAT_TIMEZONE_OFFSET,
	TIMELIB_FORMAT_TIMEZONE_OFFSET_MINUTES,
	TIMELIB_FORMAT_WEEK_OF_YEAR_ISO,
	TIMELIB_FORMAT_WEEK_OF_YEAR,
	TIMELIB_FORMAT_WHITESPACE,
	TIMELIB_FORMAT_YEAR_TWO_DIGIT,
	TIMELIB_FORMAT_YEAR_FOUR_DIGIT,
	TIMELIB_FORMAT_YEAR_ISO
} timelib_format_specifier_code;

typedef struct _timelib_format_specifier {
	char                          specifier;
	timelib_format_specifier_code code;
} timelib_format_specifier;

typedef struct _timelib_format_config {
	const timelib_format_specifier *format_map;
	/* Format speciifiers must be preceded by 'prefix_char' if not '\0'. */
	char                            prefix_char;
} timelib_format_config;

/* Function pointers */
typedef timelib_tzinfo* (*timelib_tz_get_wrapper)(char *tzname, const timelib_tzdb *tzdb, int *error_code);

/* From dow.c */
/* Calculates the day of the week from y, m, and d. 0=Sunday..6=Saturday */
timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d);

/* Calculates the day of the ISO week from y, m, and d. 1=Monday, 7=Sunday */
timelib_sll timelib_iso_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d);

/* Calculates the day of the year according to y-m-d. 0=Jan 1st..364/365=Dec
 * 31st */
timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d);

/* Calculates the day of the year according to y-w-dow. 0..364/365 */
timelib_sll timelib_daynr_from_weeknr(timelib_sll iy, timelib_sll iw, timelib_sll id);

/* Calculates the number of days in month m for year y. 28..31 */
timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m);

/* Calculates the ISO year and week from y, m, and d, into iw and iy */
void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy);

/* Calculates the ISO year, week, and day of week from y, m, and d, into iy,
 * iw, and id */
void timelib_isodate_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iy, timelib_sll *iw, timelib_sll *id);

/* Calculates the year, month, and day from iy, iw, and iw, into y, m, and d */
void timelib_date_from_isodate(timelib_sll iy, timelib_sll iw, timelib_sll id, timelib_sll *y, timelib_sll *m, timelib_sll *d);

/* Returns true if h, i and s fit in the range 00:00:00..23:59:59, false
 * otherwise */
int timelib_valid_time(timelib_sll h, timelib_sll i, timelib_sll s);

/* Returns true if m fits in the range 1..12, and d fits in the range
 * 1..<days-in-month> for year y */
int timelib_valid_date(timelib_sll y, timelib_sll m, timelib_sll d);

/* From parse_date.re */

/* Parses the date/time string in 's' with length 'len' into the constituent
 * parts of timelib_time*.
 *
 * Depending on the contents of the string 's', not all elements might be
 * filled. You can check whether a specific element has been parsed by
 * comparing with the TIMELIB_UNSET define.
 *
 * If errors occur, this function keeps already parsed elements in the
 * returned timelib_time* value.
 *
 * If the **errors points to a timelib_error_container variable, warnings
 * and errors will be recorded. You are responsible for freeing the stored
 * information with timelib_error_container_dtor(). To see whether errors have
 * occurred, inspect errors->errors_count. To see whether warnings have occurred,
 * inspect errors->warnings_count.
 *
 * The returned timelib_time* value is dynamically allocated and should be
 * freed with timelib_time_dtor().
 */
timelib_time *timelib_strtotime(char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper);

/* Parses the date/time string in 's' with length 'len' into the constituent
 * parts of timelib_time* according to the format in 'format'.
 *
 * Depending on the contents of the string 's', not all elements might be
 * filled. You can check whether a specific element has been parsed by
 * comparing with the TIMELIB_UNSET define.
 *
 * If errors occur, this function keeps already parsed elements in the
 * returned timelib_time* value.
 *
 * If the **errors points to a timelib_error_container variable, warnings
 * and errors will be recorded. You are responsible for freeing the stored
 * information with timelib_error_container_dtor(). To see whether errors have
 * occurred, inspect errors->errors_count. To see whether warnings have occurred,
 * inspect errors->warnings_count.
 *
 * The returned timelib_time* value is dynamically allocated and should be
 * freed with timelib_time_dtor().
 */
timelib_time *timelib_parse_from_format(char *format, char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper);

/* Parses the date/time string in 's' with length 'len' into the constituent
 * parts of timelib_time* according to the format in 'format' with format
 * specifier configuration 'format_config'.
 *
 * 'format_map' is an array of pairs, with the first element being the format
 * specifier as a character and the second element corresponds to the
 * representation of the specifier from the enum list
 * 'timelib_format_specifier_code'.
 *
 * Note: 'format_map' must be terminated with specifier '\0' to indicate to the
 * parser that there are no more format specifiers in the list.
 */
timelib_time *timelib_parse_from_format_with_map(char *format, char *s, size_t len, timelib_error_container **errors, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_get_wrapper, const timelib_format_config* format_config);

/* Fills the gaps in the parsed timelib_time with information from the reference date/time in 'now'
 *
 * If any of the 'parsed' y, m, d, h, i or s parameters is unset (TIMELIB_UNSET):
 * - if microtime (us) is unset, then the us of the parsed time is set to 0.
 * - else if microtime (us) is unset and 'now'->'us' is set, use it, otherwise use 0.
 *
 * For either of the 'parsed' y, m, d, h, i, s, z (utc offset in seconds) or
 * dst is unset, set it to the corresponding value in 'now' if set, otherwise
 * set it to 0.
 *
 * It duplicates tz_abbr if unset in 'parsed' but set in 'now'.
 *
 * It duplicates tz_info if unset in 'parsed', but set in 'now' unless
 * TIMELIB_NO_CLONE is passed, in which case only the pointer in 'parsed' is
 * set to 'now'.
 *
 * If the option TIMELIB_OVERRIDE_TIME is passed and the parsed date/time has
 * no time portion, the function will ignore the time aspect in 'now' and
 * instead fill it with zeros.
 */
void timelib_fill_holes(timelib_time *parsed, timelib_time *now, int options);

/* Tries to convert a time zone abbreviation, gmtoffset and/or isdst flag
 * combination to a time zone identifier.
 *
 * If 'abbr' is either 'utc' or 'gmt' (case insensitve) then "UTC" is
 * returned.
 *
 * It first uses the data in the timezonemap.h file to find a matching
 * abbreviation/GMT offset combination. If not found, it uses the data in
 * fallbackmap.h to match only the GMT offset/isdst flag to try to find a
 * match. If nothing is found, NULL is returned.
 *
 * The returned char* is not duplicated, and should not be freed.
 */
char *timelib_timezone_id_from_abbr(const char *abbr, timelib_long gmtoffset, int isdst);

/* Returns an array of known time zone abbreviations
 *
 * This file is generated from the time zone database through the
 * gettzmapping.php scripts, which requires that an up-to-date time zone
 * database is used with the PHP binary that runs the script.
 *
 * Each item in the returned list contains the abbreviation, a flag whether
 * it's an abbreviation used with DST, the UTC offset in seconds, and the name
 * of the time zone identifier that this abbreviation belongs to.
 *
 * The order for each specific abbreviation is controlled through the
 * preference list in the gettzmapping.php script. Time zones that match the
 * pattern ±\d{2,4} are excluded
 */
const timelib_tz_lookup_table *timelib_timezone_abbreviations_list(void);

/**
 * DEPRECATED, but still used by PHP.
 */
timelib_long timelib_parse_zone(char **ptr, int *dst, timelib_time *t, int *tz_not_found, const timelib_tzdb *tzdb, timelib_tz_get_wrapper tz_wrapper);

/* From parse_iso_intervals.re */

/**
 * Parses a subset of an ISO 8601 intervals specification string into its
 * constituent parts.
 *
 * If the **errors points to a timelib_error_container variable, warnings
 * and errors will be recorded. You are responsible for freeing the stored
 * information with timelib_error_container_dtor(). To see whether errors have
 * occurred, inspect errors->errors_count. To see whether warnings have occurred,
 * inspect errors->warnings_count.
 */
void timelib_strtointerval(char *s, size_t len,
                           timelib_time **begin, timelib_time **end,
                           timelib_rel_time **period, int *recurrences,
                           timelib_error_container **errors);


/* From tm2unixtime.c */

/**
 * Uses the y/m/d/h/i/s fields to calculate and store the equivalent timestamp
 * in the sse field.
 *
 * It uses the time zone information associated with 'time' to account for the
 * right UTC offset and/or DST rules. You can associate time zone information
 * with the timelib_set_timezone_* functions (see below).
 *
 * If the type is 'TIMELIB_ZONETYPE_ID' and there is no associated tzinfo, it
 * will use the second argument 'tzi' to provide the rules necessary to
 * calculate the right timestamp.
 */
void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi);

/**
 * Takes the information from the y/m/d/h/i/s fields and makes sure their
 * values are in the right range.
 *
 * If a value under- or overflows it will adjust the larger measure up (or
 * down). It also takes into account leap days.
 */
void timelib_do_normalize(timelib_time *base);

/**
 * Takes the information from the y/m/d/h/i/s fields of 'rt' and makes sure
 * their values are in the right range.
 *
 * If a value under- or overflows it will adjust the larger measure up (or
 * down). As this function operates on a *relative date/time*, it also takes
 * into account leap days and correctly accounts for the difference depending
 * on the base date/time in 'base'.
 */
void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt);

/* From unixtime2tm.c */

/**
 * Takes the unix timestamp in seconds from 'ts' and populates the y/m/d/h/i/s
 * fields of 'tm' without taking time zones into account
 */
void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts);

/**
 * Takes the Unix timestamp from 'ts', and calculates the y/m/d/h/i/s fields
 * according to the time zone information attached to 'tm'.
 */
void timelib_unixtime2local(timelib_time *tm, timelib_sll ts);

/**
 * Takes the Unix timestamp stored in 'tm', and calculates the y/m/d/h/i/s
 * fields according to the time zone information attached to 'tm'.
 */
void timelib_update_from_sse(timelib_time *tm);

/**
 * Attaches the UTC offset as time zone information to 't'.
 *
 * 'utc_offset' is in seconds East of UTC.
 */
void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset);

/**
 * Attaches the information from 'abbr_info' as time zone information to 't'.
 *
 * The timelib_abbr_info struct contains an abbreviation ('abbr') which string
 * value is duplicated, as well as a 'utc_offset' and 'dst' flag. It only
 * supports a 'dst' change over of 1 hour.
 */
void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info);

/**
 * Attaches the time zone information in 'tz' to to 't'.
 *
 * It fetches the right UTC offset that is currently stored in the time
 * stamp field in 't' ('sse'), and assigns that to the 'z' field and 'dst'
 * field (whether DST is in effect at the time). It also sets the current
 * abbreviation to the 'tz_addr' field, making sure that if a value was already
 * set it was freed.
 *
 * The time zone information in 'tz' is *not* duplicated into the 't' field so
 * it should not be freed until all timelib_time* variables have been freed as
 * well.
 */
void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz);

/* From parse_tz.c */

/**
 * Returns whether the time zone ID 'timezone' is available in the time zone
 * database as pointed to be 'tzdb'.
 */
int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb);

/**
 * Converts the binary stored time zone information from 'tzdb' for the time
 * zone 'timeozne' into a structure the library can use for calculations.
 *
 * The function can be used on both timelib_builtin_db as well as a time zone
 * db as opened by timelib_zoneinfo.
 * The function will return null upon failure, and also set an error code
 * through 'error_code'. 'error_code' must not be a null pointer. The error
 * code is one of the TIMELIB_ERROR_* constants as listed above. These error
 * constants can be converted into a string by timelib_get_error_message.
 *
 * This function allocates memory for the new time zone structure, which must
 * be freed after use. Although it is recommended that a cache of each used
 * time zone is kept.
 */
timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb, int *error_code);

/**
 * Frees up the resources allocated by 'timelib_parse_tzfile'.
 */
void timelib_tzinfo_dtor(timelib_tzinfo *tz);

/**
 * Deep-clones a timelib_tzinfo structure.
 *
 * This allocates resources that need to be freed with 'timelib_tzinfo_dtor'
 */
timelib_tzinfo* timelib_tzinfo_clone(timelib_tzinfo *tz);

/**
 * Returns whether DST is active with time zone 'tz' for the time stamp 'ts'.
 *
 * Returns 0 if DST is not active, 1 if DST is active, or -1 if no transitions
 * were available through 'tz'.
 */
int timelib_timestamp_is_in_dst(timelib_sll ts, timelib_tzinfo *tz);

/**
 * Returns offset information with time zone 'tz' for the time stamp 'ts'.
 *
 * The returned information contains: the offset in seconds East of UTC (in
 * 'offset'), whether DST is active ('is_dst'), what the current time zone
 * abbreviation is ('abbr') and the transition time that got to this state (in
 * 'transition_time');
 */
timelib_time_offset *timelib_get_time_zone_info(timelib_sll ts, timelib_tzinfo *tz);

/**
 * Returns the UTC offset currently applicable for the information stored in 't'.
 *
 * The value returned is the UTC offset in seconds East.
 */
timelib_sll timelib_get_current_offset(timelib_time *t);

/**
 * Displays debugging information about the time zone information in 'tz'.
 */
void timelib_dump_tzinfo(timelib_tzinfo *tz);

/**
 * Returns a pointer to the built-in time zone database.
 *
 * You must *not* free the returned pointer as it is part of the text segment.
 */
const timelib_tzdb *timelib_builtin_db(void);

/**
 * Returns a pointer to the start of an array containing a list of timezone identifiers.
 *
 * The amount of entries in the array is returned through the 'count' OUT parameter.
 *
 * Each entry contains the time zone ID ('id' field), and the position within the time zone
 * information ('pos' field). The pos field should not be used.
 */
const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_tzdb *tzdb, int *count);

/* From parse_zoneinfo.c */

/**
 * Scans the directory and subdirectories of 'directory' for valid time zone files and builds
 * a time zone database out of these files.
 *
 * Typically, the directory should point to '/usr/share/zoneinfo'.
 *
 * Unlike 'timelib_builtin_db', the return value of this function must be freed
 * with the 'timelib_zoneinfo_dtor' function.
 */
timelib_tzdb *timelib_zoneinfo(char *directory);

/**
 * Frees up the resources as created through 'timelib_zoneinfo'.
 *
 * This function must be used to free up all the resources that have been
 * allocated while calling 'timelib_zoneinfo'.
 */
void timelib_zoneinfo_dtor(timelib_tzdb *tzdb);

/* From timelib.c */

/**
 * Returns a static string containing an error message belonging to a specific
 * error code.
 */
const char *timelib_get_error_message(int error_code);

/**
 * Allocates resources for the relative time structure.
 *
 * Must be freed with 'timelib_rel_time_dtor'.
 */
timelib_rel_time* timelib_rel_time_ctor(void);

/**
 * Frees up the resources as allocated through 'timelib_rel_time_ctor'.
 */
void timelib_rel_time_dtor(timelib_rel_time* t);

/**
 * Creates a new timelib_rel_time resource and copies over the information
 * from 'tz'.
 *
 * Must be freed with 'timelib_rel_time_dtor'.
 */
timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *tz);

/**
 * Allocates resources for the time structure.
 *
 * Must be freed with 'timelib_time_dtor'.
 */
timelib_time* timelib_time_ctor(void);

/**
 * Frees up the resources as allocated through 'timelib_time_ctor'.
 */
void timelib_time_dtor(timelib_time* t);

/**
 * Creates a new timelib_time resource and copies over the information
 * from 'orig'.
 *
 * Must be freed with 'timelib_time_dtor'.
 */
timelib_time* timelib_time_clone(timelib_time* orig);

/**
 * Compares two timelib_time structures and returns which one is earlier in
 * time.
 *
 * To decide which comes earlier it uses the 'sse' (Seconds Since Epoch) and
 * 'us' (microseconds) fields.
 *
 * Returns -1 if t1 < t2, 0 if t1 == t2, and -1 if t1 > t2.
 */
int timelib_time_compare(timelib_time *t1, timelib_time *t2);

/**
 * Allocates resources for the time offset structure.
 *
 * Must be freed with 'timelib_time_offset_dtor'.
 */
timelib_time_offset* timelib_time_offset_ctor(void);

/**
 * Frees up the resources as allocated through 'timelib_time_offset_ctor'.
 */
void timelib_time_offset_dtor(timelib_time_offset* t);

/**
 * Frees up the resources allocated while converting strings to timelib_time
 * structures with the timelib_strtotime and timelib_strtointerval functions.
 */
void timelib_error_container_dtor(timelib_error_container *errors);

/**
 * Converts the 'sse' value of 'd' to a timelib_long type.
 *
 * If the value fits in the TIMELIB_LONG_MIN and TIMELIB_LONG_MAX range, the
 * value is cast to (timelib_long) and returned. If *error is not a NULL
 * pointer, it will be set to 0.
 *
 * If the value does *not* fit in the range, the function returns 0 and if
 * *error is not a NULL pointer, it will be set to 1.
 *
 * timelib_long is a 32 bit signed long integer on 32 bit platforms, and a 64
 * bit signed long long integer on 64 bit platforms. In other words, it makes
 * sure that the value in 'sse' (which is always a signed long long 64 bit
 * integer) can be used safely outside of the library.
 */
timelib_long timelib_date_to_int(timelib_time *d, int *error);

/**
 * Displays debugging information about the date/time information stored in 'd'.
 *
 * 'options' is a bit field, where:
 * - 1 controls whether the relative time portion is shown.
 * - 2 controls whether the zone type is shown.
 */
void timelib_dump_date(timelib_time *d, int options);

/**
 * Displays debugging information about the relative time information stored
 * in 'd'.
 */
void timelib_dump_rel_time(timelib_rel_time *d);

/**
 * Converts a decimal hour into hour/min/sec components
 */
void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec);

/**
 * Converts hour/min/sec values into a decimal hour
 */
void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h);

/* from astro.c */

/**
 * Converts the Unix Epoch time stamp 'ts' to a Julian Day
 *
 * The value returned is the number of whole days since -4714-11-24T12:00:00 UTC
 * (in the proleptic Gregorian calendar):
 * https://en.wikipedia.org/wiki/Julian_day
 */
double timelib_ts_to_julianday(timelib_sll ts);

/**
 * Converts the Unix Epoch time stamp 'ts' to the J2000 epoch
 *
 * The value returned is the number of whole days since 2000-01-01T12:00:00
 * UTC: https://en.wikipedia.org/wiki/Epoch_(astronomy)#Julian_years_and_J2000
 */
double timelib_ts_to_j2000(timelib_sll ts);

/**
 * Calculates when the Sun is above a certain latitude.
 *
 * Parameters:
 * - time: A timelib_time time describing that needs to specific midnight for a
 *         specific day.
 * - lon: The longitude of the observer (East positive, West negative).
 * - lat: The latitude of the observer (North positive, South negative).
 * - altit: The altitude. Set to -35/60 for rise/set, -6 for civil twilight,
 *          -12 for nautical, and -18 for astronomical twilight.
 * - upper_limb: set to non-zero for rise/set calculations, and 0 for twilight
 *               calculations.
 *
 * Out Parameters:
 * - h_rise: The decimal hour when the Sun rises
 * - h_set: The decimal hour when the Sun sets
 * - ts_rise: The Unix timestamp of the Sun rising
 * - ts_set: The Unix timestamp of the Sun setting
 * - ts_transit: The Unix timestmap of the Sun transitting through South
 *
 * Return Values:
 * - 0: The Sun rises and sets.
 * - +1: The Sun is always above the horizon. (ts_rise is set to ts_transit -
 *       (12 * 3600); ts_set is set to ts_transit + (12 * 3600).
 * - -1: The Sun is awlays below the horizon. (ts_rise and ts_set are set
 *       to ts_transit)
 */
int timelib_astro_rise_set_altitude(timelib_time *time, double lon, double lat, double altit, int upper_limb, double *h_rise, double *h_set, timelib_sll *ts_rise, timelib_sll *ts_set, timelib_sll *ts_transit);

/* from interval.c */

/**
 * Calculates the difference between two times
 *
 * The result is a timelib_rel_time structure that describes how you can
 * convert from 'one' to 'two' with 'timelib_add'. This does *not* necessarily
 * mean that you can go from 'two' to 'one' by using 'timelib_sub' due to the
 * way months and days are calculated.
 */
timelib_rel_time *timelib_diff(timelib_time *one, timelib_time *two);

/**
 * Adds the relative time information 'interval' to the base time 't'.
 *
 * This can be a relative time as created by 'timelib_diff', but also by more
 * complex statements such as "next workday".
 */
timelib_time *timelib_add(timelib_time *t, timelib_rel_time *interval);

/**
 * Subtracts the relative time information 'interval' to the base time 't'.
 *
 * This can be a relative time as created by 'timelib_diff'. Unlike with
 * 'timelib_add', this does not support more complex statements such as "next
 * workday".
 */
timelib_time *timelib_sub(timelib_time *t, timelib_rel_time *interval);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif