├── tests ├── c │ ├── files │ │ ├── New_York_Fat │ │ ├── New_York_Slim │ │ ├── Nicosia_TZif4 │ │ ├── Nuuk_AmazonLinux1 │ │ ├── Casablanca_AmazonLinux1 │ │ ├── New_York_mod_Full_Year_DST │ │ └── NonContinuous │ ├── all_tests.cpp │ ├── timelib_juliandate.cpp │ ├── warn_on_slim.cpp │ ├── parse_intervals.cpp │ ├── astro_rise_set_altitude.cpp │ ├── timelib_get_current_offset_test.cpp │ ├── timelib_hmsf_to_decimal_hour.cpp │ ├── timelib_get_offset_info_test.cpp │ ├── diff_days.cpp │ ├── dow.cpp │ ├── timelib_decimal_hour.cpp │ ├── timezones_same.cpp │ ├── parse_tz.cpp │ ├── render.cpp │ ├── transitions.cpp │ ├── interval.cpp │ ├── php-rfc.cpp │ └── issue0120.cpp ├── tester-iso-week.c ├── test-abbr-to-id.c ├── tester-render-ts.c ├── tester-render-ts-zoneinfo.c ├── tester-diff.c ├── enumerate-timezones.c ├── test-astro.c ├── tester-parse-string.c ├── tester-parse-string-by-format.c ├── tester-parse-interval.c ├── tester-create-ts.c └── date_from_isodate.c ├── zones ├── old-tzcode-32-bit-output.tar.gz ├── update-package-version.php ├── update-package-xml.php ├── README.rst ├── create-timezonedb.php └── Makefile ├── README.rst ├── .gitignore ├── LICENSE.rst ├── fallbackmap.h ├── docs ├── show-tzinfo.c ├── date-from-parts.c ├── date-from-iso-parts.c ├── date-to-parts.c └── date-from-string.c ├── astro.h ├── timelib_private.h ├── Makefile ├── dow.c ├── unixtime2tm.c ├── parse_zoneinfo.c ├── parse_iso_intervals.re └── timelib.c /tests/c/files/New_York_Fat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/tests/c/files/New_York_Fat -------------------------------------------------------------------------------- /tests/c/files/New_York_Slim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/tests/c/files/New_York_Slim -------------------------------------------------------------------------------- /tests/c/files/Nicosia_TZif4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/tests/c/files/Nicosia_TZif4 -------------------------------------------------------------------------------- /tests/c/files/Nuuk_AmazonLinux1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/tests/c/files/Nuuk_AmazonLinux1 -------------------------------------------------------------------------------- /tests/c/files/Casablanca_AmazonLinux1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/tests/c/files/Casablanca_AmazonLinux1 -------------------------------------------------------------------------------- /zones/old-tzcode-32-bit-output.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/zones/old-tzcode-32-bit-output.tar.gz -------------------------------------------------------------------------------- /tests/c/files/New_York_mod_Full_Year_DST: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derickr/timelib/HEAD/tests/c/files/New_York_mod_Full_Year_DST -------------------------------------------------------------------------------- /tests/c/files/NonContinuous: -------------------------------------------------------------------------------- 1 | TZif2TZif2 -------------------------------------------------------------------------------- /tests/c/all_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/CommandLineTestRunner.h" 2 | #include "timelib.h" 3 | 4 | timelib_tzdb *zoneinfo; 5 | 6 | int main(int ac, char **av) 7 | { 8 | int return_value; 9 | 10 | zoneinfo = timelib_zoneinfo("/usr/share/zoneinfo"); 11 | return_value = CommandLineTestRunner::RunAllTests(ac, av); 12 | timelib_zoneinfo_dtor(zoneinfo); 13 | 14 | return return_value; 15 | } 16 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | timelib 2 | ======= 3 | 4 | Timelib is a timezone and date/time library that can calculate local time, 5 | convert between timezones and parse textual descriptions of date/time 6 | information. 7 | 8 | It is the library supporting PHP's Date/Time extension and MongoDB's time zone 9 | support. 10 | 11 | Build Requirements 12 | ------------------ 13 | 14 | On Debian: ``apt install libcpputest-dev re2c`` 15 | -------------------------------------------------------------------------------- /zones/update-package-version.php: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /zones/update-package-xml.php: -------------------------------------------------------------------------------- 1 | 20.*@', "release>{$version}", $e ); 11 | $g = preg_replace( '@api>20.*@', "api>{$version}", $f ); 12 | $h = preg_replace( '@version 20.*@', "version {$version} ({$tzversion})", $g ); 13 | $date = date( 'Y-m-d' ); 14 | $i = preg_replace( '@date>.*{$date} 18 | -------------------------------------------------------------------------------- /zones/README.rst: -------------------------------------------------------------------------------- 1 | Building the timezone files 2 | --------------------------- 3 | 4 | The building of files is done through the ``Makefile`` with: 5 | 6 | - ``make clean`` (important if data files have changed) 7 | - ``make release-pecl`` (PECL timezonedb extension) 8 | - ``make release-docs`` (documentation updates) 9 | - ``make release-php`` (changes to embed database in PHP) 10 | 11 | It has the following prerequisites: 12 | 13 | - The directory contains **one** ``tzcode2014i.tar.gz`` file and **one** 14 | ``tzdata2014i.tar.gz`` file. 15 | - You have a PECL SVN checkout in ``~/dev/php/pecl`` 16 | - You have a PHPDOC GIT checkout in ``~/dev/php/phpdoc`` 17 | - You can commit to PHP's GIT ``php-src`` repository. 18 | - You can commit to PHP's GIT ``doc-base`` and ``doc-en`` repositories. 19 | - You can commit to PECL's SVN repository. 20 | - ``php`` is in your path. 21 | 22 | Do not run this script, unless you're Derick Rethans. 23 | -------------------------------------------------------------------------------- /tests/c/timelib_juliandate.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | 4 | TEST_GROUP(j2000) 5 | { 6 | }; 7 | 8 | TEST(j2000, J2000Epoch) 9 | { 10 | double d = 946728000; 11 | double js = timelib_ts_to_j2000(d); 12 | 13 | DOUBLES_EQUAL(0, js, 0.0000001); 14 | } 15 | 16 | TEST(j2000, August2017) 17 | { 18 | double d = 1502755200; 19 | double js = timelib_ts_to_j2000(d); 20 | 21 | DOUBLES_EQUAL(6435.5, js, 0.0000001); 22 | } 23 | 24 | TEST_GROUP(julian_day) 25 | { 26 | }; 27 | 28 | TEST(julian_day, JulianDayEpoch) 29 | { 30 | double d = -210866760000; 31 | double js = timelib_ts_to_julianday(d); 32 | 33 | DOUBLES_EQUAL(0, js, 0.0000001); 34 | } 35 | 36 | TEST(j2000, JulianDateExampleFromWikipedia) 37 | { 38 | double d = 1357000200; 39 | double js = timelib_ts_to_julianday(d); 40 | 41 | DOUBLES_EQUAL(2456293.520833, js, 0.000001); 42 | } 43 | 44 | TEST(julian_day, August2017) 45 | { 46 | double d = 1502755200; 47 | double js = timelib_ts_to_julianday(d); 48 | 49 | DOUBLES_EQUAL(2457980.5, js, 0.0000001); 50 | } 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | .cvsignore 3 | .idea 4 | .svn/ 5 | astro.o 6 | callgrind.out.* 7 | ctest 8 | docs/date-from-iso-parts 9 | docs/date-from-parts 10 | docs/date-from-string 11 | docs/date-to-parts 12 | dow.o 13 | interval.o 14 | parse_date.c 15 | parse_date.o 16 | parse_iso_intervals.c 17 | parse_iso_intervals.o 18 | parse_posix.o 19 | parse_tz.o 20 | parse_zoneinfo.o 21 | show-tzinfo 22 | svn-commit.tmp 23 | test-abbr-to-id 24 | test-astro 25 | tester-create-ts 26 | tester-diff 27 | tester-iso-week 28 | tester-parse-interval 29 | tester-parse-string 30 | tester-parse-string-by-format 31 | tester-render-ts 32 | tester-render-ts-zoneinfo 33 | tests/c/*.o 34 | tests/date_from_isodate 35 | tests/enumerate-timezones 36 | timelib.a 37 | timelib.o 38 | timelib.so 39 | timelib_config.h 40 | tm2unixtime.o 41 | unixtime2tm.o 42 | zones/.cvsignore 43 | zones/code/ 44 | zones/oldtz/ 45 | zones/timezonedb-*.zip 46 | zones/timezonedb-20*.tgz 47 | zones/timezonedb.dta 48 | zones/timezonedb.h 49 | zones/timezonedb.idx 50 | zones/timezonedb.idx.php 51 | zones/timezonedb.tgz 52 | zones/timezonedb.zip 53 | zones/tzcode*.tar.gz 54 | zones/tzdata*.tar.gz 55 | zones/version-info.txt 56 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2021 Derick Rethans 4 | Copyright (c) 2017-2019,2021 MongoDB, Inc. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /tests/c/warn_on_slim.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | 5 | TEST_GROUP(warn_on_slim) 6 | { 7 | }; 8 | 9 | 10 | TEST(warn_on_slim, warn_on_slim_001) 11 | { 12 | int error_code; 13 | timelib_tzinfo *tzi; 14 | timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); 15 | 16 | tzi = timelib_parse_tzfile((char*) "New_York_Slim", test_directory, &error_code); 17 | 18 | LONGS_EQUAL(TIMELIB_ERROR_NO_ERROR, error_code); 19 | 20 | timelib_tzinfo_dtor(tzi); 21 | timelib_zoneinfo_dtor(test_directory); 22 | } 23 | 24 | TEST(warn_on_slim, dont_warn_on_fat_001) 25 | { 26 | int error_code; 27 | timelib_tzinfo *tzi; 28 | timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); 29 | 30 | tzi = timelib_parse_tzfile((char*) "New_York_Fat", test_directory, &error_code); 31 | 32 | LONGS_EQUAL(TIMELIB_ERROR_NO_ERROR, error_code); 33 | 34 | timelib_tzinfo_dtor(tzi); 35 | timelib_zoneinfo_dtor(test_directory); 36 | } 37 | 38 | TEST(warn_on_slim, tzif4_format) 39 | { 40 | int error_code; 41 | timelib_tzinfo *tzi; 42 | timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); 43 | 44 | tzi = timelib_parse_tzfile((char*) "Nicosia_TZif4", test_directory, &error_code); 45 | 46 | LONGS_EQUAL(TIMELIB_ERROR_NO_ERROR, error_code); 47 | LONGS_EQUAL(20, tzi->bit64.leapcnt); 48 | 49 | timelib_tzinfo_dtor(tzi); 50 | timelib_zoneinfo_dtor(test_directory); 51 | } 52 | -------------------------------------------------------------------------------- /tests/c/parse_intervals.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | #include 5 | 6 | TEST_GROUP(parse_intervals) 7 | { 8 | timelib_time *b = NULL, *e = NULL; 9 | timelib_rel_time *p = NULL; 10 | int r = 0; 11 | timelib_error_container *errors; 12 | 13 | 14 | void test_parse(const char *input) 15 | { 16 | if (p) { 17 | timelib_rel_time_dtor(p); 18 | timelib_error_container_dtor(errors); 19 | } 20 | 21 | char *inputCopy = strdup(input); 22 | timelib_strtointerval(inputCopy, strlen(inputCopy), &b, &e, &p, &r, &errors); 23 | free(inputCopy); 24 | } 25 | 26 | TEST_TEARDOWN() 27 | { 28 | timelib_rel_time_dtor(p); 29 | timelib_error_container_dtor(errors); 30 | } 31 | }; 32 | 33 | TEST(parse_intervals, weeksOnly) 34 | { 35 | test_parse("P2W"); 36 | LONGS_EQUAL(0, errors->warning_count); 37 | LONGS_EQUAL(0, errors->error_count); 38 | LONGS_EQUAL(0, p->y); 39 | LONGS_EQUAL(0, p->m); 40 | LONGS_EQUAL(14, p->d); 41 | } 42 | 43 | TEST(parse_intervals, combiningWeeksAndDays) 44 | { 45 | test_parse("P2W3D"); 46 | LONGS_EQUAL(0, errors->warning_count); 47 | LONGS_EQUAL(0, errors->error_count); 48 | LONGS_EQUAL(0, p->y); 49 | LONGS_EQUAL(0, p->m); 50 | LONGS_EQUAL(17, p->d); 51 | 52 | test_parse("P1Y3M1W5DT2H"); 53 | LONGS_EQUAL(0, errors->warning_count); 54 | LONGS_EQUAL(0, errors->error_count); 55 | LONGS_EQUAL(1, p->y); 56 | LONGS_EQUAL(3, p->m); 57 | LONGS_EQUAL(12, p->d); 58 | LONGS_EQUAL(2, p->h); 59 | } 60 | -------------------------------------------------------------------------------- /tests/c/astro_rise_set_altitude.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | 4 | TEST_GROUP(sun_rise_set) 5 | { 6 | }; 7 | 8 | TEST(sun_rise_set, PHPSunInfo001) 9 | { 10 | double h_rise, h_set; 11 | timelib_sll ts_rise, ts_set, ts_transit; 12 | timelib_time *t = timelib_time_ctor(); 13 | 14 | t->y = 2006; 15 | t->m = 12; 16 | t->d = 12; 17 | t->h = 00; 18 | t->i = 00; 19 | t->s = 00; 20 | timelib_update_ts(t, NULL); 21 | 22 | timelib_astro_rise_set_altitude(t, 31.7667, 35.2333, -35.0/60.0, 1, 23 | &h_rise, &h_set, &ts_rise, &ts_set, &ts_transit); 24 | 25 | DOUBLES_EQUAL( 4.86, h_rise, 0.01); 26 | DOUBLES_EQUAL(14.69, h_set, 0.01); 27 | LONGS_EQUAL(1165899111, ts_rise); 28 | LONGS_EQUAL(1165934475, ts_set); 29 | LONGS_EQUAL((1165899111LL + 1165934475LL) / 2, ts_transit); 30 | 31 | timelib_time_dtor(t); 32 | } 33 | 34 | TEST(sun_rise_set, PHPSunInfo002) 35 | { 36 | double h_rise, h_set; 37 | timelib_sll ts_rise, ts_set, ts_transit; 38 | timelib_time *t = timelib_time_ctor(); 39 | 40 | t->y = 2007; 41 | t->m = 4; 42 | t->d = 13; 43 | t->h = 11; 44 | t->i = 10; 45 | t->s = 54; 46 | timelib_update_ts(t, NULL); 47 | 48 | timelib_astro_rise_set_altitude(t, 9.61, 59.21, -35.0/60.0, 1, 49 | &h_rise, &h_set, &ts_rise, &ts_set, &ts_transit); 50 | 51 | DOUBLES_EQUAL( 4.23, h_rise, 0.01); 52 | DOUBLES_EQUAL(18.51, h_set, 0.01); 53 | LONGS_EQUAL(1176437611, ts_rise); 54 | LONGS_EQUAL(1176489051, ts_set); 55 | LONGS_EQUAL((1176489051LL + 1176437611LL) / 2, ts_transit); 56 | 57 | timelib_time_dtor(t); 58 | } 59 | -------------------------------------------------------------------------------- /tests/tester-iso-week.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | timelib_sll y, m, d; 30 | timelib_sll year, week, day; 31 | 32 | y = atoi(argv[1]); 33 | m = atoi(argv[2]); 34 | d = atoi(argv[3]); 35 | 36 | timelib_isoweek_from_date(y, m, d, &week, &year); 37 | printf("%llu-%llu\n", week, year); 38 | 39 | timelib_isodate_from_date(y, m, d, &year, &week, &day); 40 | printf("%lluW%lluD%llu\n", year, week, day); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /tests/test-abbr-to-id.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | 27 | int main(void) 28 | { 29 | const char *id; 30 | 31 | id = timelib_timezone_id_from_abbr("cest", 10800, 1); printf("%s\n", id); 32 | id = timelib_timezone_id_from_abbr("cest", 7200, 1); printf("%s\n", id); 33 | id = timelib_timezone_id_from_abbr("cest", 7200, 0); printf("%s\n", id); 34 | id = timelib_timezone_id_from_abbr("foobar", 7200, 0); printf("%s\n", id); 35 | id = timelib_timezone_id_from_abbr("foobar", -7 * 3600, 0); printf("%s\n", id); 36 | id = timelib_timezone_id_from_abbr("foobar", -5 * 3600, 1); printf("%s\n", id); 37 | id = timelib_timezone_id_from_abbr("foobar", 7201, 1); printf("%s\n", id); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /tests/tester-render-ts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | signed long long ts; 30 | timelib_time *t; 31 | char *tz; 32 | timelib_tzinfo *tzi; 33 | int dummy_error; 34 | 35 | if (argc < 3) { 36 | printf("Usage:\n\ttester-render [t] [tz specification]\n\tExample: ./tester-render \"1114819200\" \"Europe/Amsterdam\"\n\n"); 37 | exit(-1); 38 | } 39 | ts = atoll(argv[1]); 40 | tz = argv[2]; 41 | tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &dummy_error); 42 | 43 | t = timelib_time_ctor(); 44 | timelib_set_timezone(t, tzi); 45 | timelib_unixtime2local(t, ts); 46 | timelib_dump_date(t, 3); 47 | timelib_tzinfo_dtor(t->tz_info); 48 | timelib_time_dtor(t); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /fallbackmap.h: -------------------------------------------------------------------------------- 1 | { "sst", 0, -660 * 60, "Pacific/Apia" }, 2 | { "hst", 0, -600 * 60, "Pacific/Honolulu" }, 3 | { "akst", 0, -540 * 60, "America/Anchorage" }, 4 | { "akdt", 1, -480 * 60, "America/Anchorage" }, 5 | { "pst", 0, -480 * 60, "America/Los_Angeles" }, 6 | { "pdt", 1, -420 * 60, "America/Los_Angeles" }, 7 | { "mst", 0, -420 * 60, "America/Denver" }, 8 | { "mdt", 1, -360 * 60, "America/Denver" }, 9 | { "cst", 0, -360 * 60, "America/Chicago" }, 10 | { "cdt", 1, -300 * 60, "America/Chicago" }, 11 | { "est", 0, -300 * 60, "America/New_York" }, 12 | { "vet", 0, -270 * 60, "America/Caracas" }, 13 | { "edt", 1, -240 * 60, "America/New_York" }, 14 | { "ast", 0, -240 * 60, "America/Halifax" }, 15 | { "adt", 1, -180 * 60, "America/Halifax" }, 16 | { "brt", 0, -180 * 60, "America/Sao_Paulo" }, 17 | { "brst", 1, -120 * 60, "America/Sao_Paulo" }, 18 | { "azost", 0, -60 * 60, "Atlantic/Azores" }, 19 | { "azodt", 1, 0 * 60, "Atlantic/Azores" }, 20 | { "gmt", 0, 0 * 60, "Europe/London" }, 21 | { "bst", 1, 60 * 60, "Europe/London" }, 22 | { "cet", 0, 60 * 60, "Europe/Paris" }, 23 | { "cest", 1, 120 * 60, "Europe/Paris" }, 24 | { "eet", 0, 120 * 60, "Europe/Helsinki" }, 25 | { "eest", 1, 180 * 60, "Europe/Helsinki" }, 26 | { "msk", 0, 180 * 60, "Europe/Moscow" }, 27 | { "msd", 1, 240 * 60, "Europe/Moscow" }, 28 | { "gst", 0, 240 * 60, "Asia/Dubai" }, 29 | { "pkt", 0, 300 * 60, "Asia/Karachi" }, 30 | { "ist", 0, 330 * 60, "Asia/Kolkata" }, 31 | { "npt", 0, 345 * 60, "Asia/Katmandu" }, 32 | { "yekt", 1, 360 * 60, "Asia/Yekaterinburg" }, 33 | { "novst", 1, 420 * 60, "Asia/Novosibirsk" }, 34 | { "krat", 0, 420 * 60, "Asia/Krasnoyarsk" }, 35 | { "cst", 0, 480 * 60, "Asia/Shanghai" }, 36 | { "krast", 1, 480 * 60, "Asia/Krasnoyarsk" }, 37 | { "jst", 0, 540 * 60, "Asia/Tokyo" }, 38 | { "est", 0, 600 * 60, "Australia/Melbourne" }, 39 | { "cst", 1, 630 * 60, "Australia/Adelaide" }, 40 | { "est", 1, 660 * 60, "Australia/Melbourne" }, 41 | { "nzst", 0, 720 * 60, "Pacific/Auckland" }, 42 | { "nzdt", 1, 780 * 60, "Pacific/Auckland" }, 43 | -------------------------------------------------------------------------------- /tests/c/timelib_get_current_offset_test.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | 4 | TEST_GROUP(get_current_offset) 5 | { 6 | }; 7 | 8 | TEST(get_current_offset, UTCplus0500) 9 | { 10 | timelib_time* t = timelib_time_ctor(); 11 | timelib_set_timezone_from_offset(t, 5 * 3600); 12 | 13 | timelib_unixtime2local(t, 1483280063); 14 | LONGS_EQUAL(18000, timelib_get_current_offset(t)); 15 | 16 | timelib_time_dtor(t); 17 | } 18 | 19 | TEST(get_current_offset, EST) 20 | { 21 | timelib_abbr_info ai = { -5 * 3600, (char*) "EST", 0 }; 22 | timelib_time* t = timelib_time_ctor(); 23 | timelib_set_timezone_from_abbr(t, ai); 24 | 25 | timelib_unixtime2local(t, 1483280063); 26 | LONGS_EQUAL(-18000, timelib_get_current_offset(t)); 27 | 28 | timelib_time_dtor(t); 29 | } 30 | 31 | TEST(get_current_offset, EDT) 32 | { 33 | timelib_abbr_info ai = { -5 * 3600, (char*) "EDT", 1 }; 34 | timelib_time* t = timelib_time_ctor(); 35 | timelib_set_timezone_from_abbr(t, ai); 36 | 37 | timelib_unixtime2local(t, 1483280063); 38 | LONGS_EQUAL(-14400, timelib_get_current_offset(t)); 39 | 40 | timelib_time_dtor(t); 41 | } 42 | 43 | TEST(get_current_offset, London) 44 | { 45 | int code; 46 | timelib_tzinfo* tzi = timelib_parse_tzfile((char*) "Europe/London", timelib_builtin_db(), &code); 47 | timelib_time* t = timelib_time_ctor(); 48 | timelib_set_timezone(t, tzi); 49 | 50 | timelib_unixtime2local(t, 1483280063); 51 | LONGS_EQUAL(0, timelib_get_current_offset(t)); 52 | 53 | timelib_unixtime2local(t, 1501074654); 54 | LONGS_EQUAL(3600, timelib_get_current_offset(t)); 55 | 56 | timelib_tzinfo_dtor(tzi); 57 | timelib_time_dtor(t); 58 | } 59 | 60 | TEST(get_current_offset, Amsterdam) 61 | { 62 | int code; 63 | timelib_tzinfo* tzi = timelib_parse_tzfile((char*) "Europe/Amsterdam", timelib_builtin_db(), &code); 64 | timelib_time* t = timelib_time_ctor(); 65 | timelib_set_timezone(t, tzi); 66 | 67 | timelib_unixtime2local(t, 1483280063); 68 | LONGS_EQUAL(3600, timelib_get_current_offset(t)); 69 | 70 | timelib_unixtime2local(t, 1501074654); 71 | LONGS_EQUAL(7200, timelib_get_current_offset(t)); 72 | 73 | timelib_tzinfo_dtor(tzi); 74 | timelib_time_dtor(t); 75 | } 76 | -------------------------------------------------------------------------------- /docs/show-tzinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | timelib_tzinfo *tz; 30 | int dummy_error; 31 | 32 | if (argc == 3) { 33 | timelib_tzdb *db = timelib_zoneinfo(argv[2]); 34 | 35 | if (!db) { 36 | fprintf(stderr, "Cannot read timezone database in '%s'.\n", argv[2]); 37 | return 2; 38 | } 39 | 40 | tz = timelib_parse_tzfile(argv[1], db, &dummy_error); 41 | if (!tz) { 42 | fprintf(stderr, "Cannot read timezone identifier '%s' from database in '%s'.\n", argv[1], argv[2]); 43 | 44 | timelib_zoneinfo_dtor(db); 45 | return 3; 46 | } 47 | 48 | timelib_zoneinfo_dtor(db); 49 | } else { 50 | tz = timelib_parse_tzfile(argv[1], timelib_builtin_db(), &dummy_error); 51 | if (!tz) { 52 | fprintf(stderr, "Cannot read timezone identifier '%s' from built in database.\n", argv[1]); 53 | return 4; 54 | } 55 | } 56 | timelib_dump_tzinfo(tz); 57 | 58 | timelib_tzinfo_dtor(tz); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /tests/tester-render-ts-zoneinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | signed long long ts; 30 | timelib_time *t; 31 | char *tz; 32 | timelib_tzinfo *tzi; 33 | timelib_tzdb *zoneinfo; 34 | int dummy_error; 35 | 36 | if (argc < 3) { 37 | printf("Usage:\n\ttester-render [t] [tz specification]\n\tExample: ./tester-render \"1114819200\" \"Europe/Amsterdam\"\n\n"); 38 | exit(-1); 39 | } 40 | ts = atoll(argv[1]); 41 | tz = argv[2]; 42 | zoneinfo = timelib_zoneinfo("/usr/share/zoneinfo"); 43 | tzi = timelib_parse_tzfile(tz, zoneinfo, &dummy_error); 44 | 45 | if (!tzi) { 46 | printf("Timezone identifier \"%s\" does not exist\n", tz); 47 | exit(-2); 48 | } 49 | 50 | t = timelib_time_ctor(); 51 | timelib_set_timezone(t, tzi); 52 | timelib_unixtime2local(t, ts); 53 | timelib_dump_date(t, 3); 54 | timelib_tzinfo_dtor(t->tz_info); 55 | timelib_time_dtor(t); 56 | timelib_zoneinfo_dtor(zoneinfo); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /tests/c/timelib_hmsf_to_decimal_hour.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | #include 5 | 6 | 7 | TEST_GROUP(hmsf_to_decimal_hour) 8 | { 9 | double result; 10 | 11 | void test_convert(int hour, int min, int sec, int us) 12 | { 13 | timelib_hmsf_to_decimal_hour(hour, min, sec, us, &result); 14 | } 15 | }; 16 | 17 | TEST(hmsf_to_decimal_hour, zero) 18 | { 19 | test_convert(0, 0, 0, 0); 20 | DOUBLES_EQUAL(0, result, 0.00000000001); 21 | } 22 | 23 | TEST(hmsf_to_decimal_hour, smallest_positive) 24 | { 25 | test_convert(0, 0, 0, 1); 26 | DOUBLES_EQUAL(2.777778e-10, result, 0.00000000001); 27 | } 28 | 29 | TEST(hmsf_to_decimal_hour, one_second_positive) 30 | { 31 | test_convert(0, 0, 1, 0); 32 | DOUBLES_EQUAL(0.00027777778, result, 0.00000000001); 33 | } 34 | 35 | TEST(hmsf_to_decimal_hour, six_minute_positive) 36 | { 37 | test_convert(0, 6, 0, 0); 38 | DOUBLES_EQUAL(0.1, result, 0.00000000001); 39 | } 40 | 41 | TEST(hmsf_to_decimal_hour, three_hours_positive) 42 | { 43 | test_convert(3, 0, 0, 0); 44 | DOUBLES_EQUAL(3, result, 0.00000000001); 45 | } 46 | 47 | TEST(hmsf_to_decimal_hour, three_hours_fifteen_minutes_positive) 48 | { 49 | test_convert(3, 15, 0, 0); 50 | DOUBLES_EQUAL(3.25, result, 0.00000000001); 51 | } 52 | 53 | TEST(hmsf_to_decimal_hour, smallest_negative) 54 | { 55 | test_convert(0, 0, 0, 1); 56 | DOUBLES_EQUAL(2.777778e-10, result, 0.00000000001); 57 | } 58 | 59 | TEST(hmsf_to_decimal_hour, one_second_negative) 60 | { 61 | test_convert(0, 0, -1, 0); 62 | DOUBLES_EQUAL(-0.00027777778, result, 0.00000000001); 63 | } 64 | 65 | TEST(hmsf_to_decimal_hour, six_minute_negative) 66 | { 67 | test_convert(0, -6, 0, 0); 68 | DOUBLES_EQUAL(-0.1, result, 0.00000000001); 69 | } 70 | 71 | TEST(hmsf_to_decimal_hour, three_hours_negative) 72 | { 73 | test_convert(-3, 0, 0, 0); 74 | DOUBLES_EQUAL(-3, result, 0.00000000001); 75 | } 76 | 77 | TEST(hmsf_to_decimal_hour, three_hours_fifteen_minutes_negative) 78 | { 79 | test_convert(-3, 15, 0, 0); 80 | DOUBLES_EQUAL(-3.25, result, 0.00000000001); 81 | } 82 | 83 | TEST(hmsf_to_decimal_hour, three_hours_negative_fifteen_minutes_negative) 84 | { 85 | test_convert(-3, -15, 0, 0); 86 | DOUBLES_EQUAL(-2.75, result, 0.00000000001); 87 | } 88 | -------------------------------------------------------------------------------- /tests/tester-diff.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | 26 | int main(int argc, char *argv[]) 27 | { 28 | timelib_time *t1, *t2, *now; 29 | timelib_rel_time *rt; 30 | 31 | if (argc < 3) { 32 | printf("Usage:\n\ttester-diff [t1] [t2]\n\tExample: ./tester-create-ts \"9/11\" \"2008-03-26\"\n\n"); 33 | exit(-1); 34 | } 35 | 36 | t1 = timelib_strtotime(argv[1], strlen(argv[1]), NULL, timelib_builtin_db(), timelib_parse_tzfile); 37 | t2 = timelib_strtotime(argv[2], strlen(argv[2]), NULL, timelib_builtin_db(), timelib_parse_tzfile); 38 | now = timelib_strtotime("1970-01-01 00:00:00 UTC", strlen("1970-01-01 00:00:00 UTC"), NULL, timelib_builtin_db(), timelib_parse_tzfile); 39 | timelib_fill_holes(t1, now, TIMELIB_OVERRIDE_TIME); 40 | timelib_fill_holes(t2, now, TIMELIB_OVERRIDE_TIME); 41 | 42 | timelib_update_ts(t1, t1->tz_info); 43 | timelib_update_ts(t2, t2->tz_info); 44 | 45 | timelib_dump_date(t1, 1); 46 | timelib_dump_date(t2, 1); 47 | rt = timelib_diff(t1, t2); 48 | 49 | timelib_dump_rel_time(rt); 50 | 51 | timelib_time_dtor(t1); 52 | timelib_time_dtor(t2); 53 | timelib_time_dtor(now); 54 | timelib_rel_time_dtor(rt); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/enumerate-timezones.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | int count, i; 30 | timelib_tzdb *db; 31 | const timelib_tzdb_index_entry *entries; 32 | timelib_tzinfo *tzi; 33 | 34 | if (argc > 2) { 35 | printf("Usage:\n\tenumerate-timezones [zoneinfo path]\n\tExample: ./enumerate-timezone /usr/share/zoneinfo\"\n\n"); 36 | exit(-1); 37 | } 38 | if (argc == 1) { 39 | db = (timelib_tzdb*) timelib_builtin_db(); 40 | } else { 41 | db = timelib_zoneinfo(argv[1]); 42 | } 43 | 44 | if (!db) { 45 | printf("FAIL: Can not open timezone database\n"); 46 | exit(-1); 47 | } 48 | 49 | entries = timelib_timezone_identifiers_list(db, &count); 50 | 51 | for (i = 0; i < count; i++) { 52 | int error_code; 53 | 54 | tzi = timelib_parse_tzfile(entries[i].id, db, &error_code); 55 | if (!tzi) { 56 | printf("FAIL: %s: [%d] %s\n", entries[i].id, error_code, timelib_get_error_message(error_code)); 57 | } else { 58 | printf("OK: %s\n", entries[i].id); 59 | timelib_tzinfo_dtor(tzi); 60 | } 61 | } 62 | 63 | if (db != timelib_builtin_db()) { 64 | timelib_zoneinfo_dtor(db); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/test-astro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include "astro.h" 26 | #include "math.h" 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | timelib_time *t; 31 | timelib_sll rise, set, transit; 32 | int rs, h, m, s; 33 | double h_rise, h_set; 34 | 35 | if (argc != 4) { 36 | printf("Usage: ./test-astro time longitude latitude\n ./test-astro \"2005-10-17 00:00:00\" 9.627 59.186\n"); 37 | return -1; 38 | } 39 | 40 | t = timelib_strtotime(argv[1] /*"2005-10-17 00:00:00"*/, strlen(argv[1]), NULL, timelib_builtin_db(), timelib_parse_tzfile); 41 | timelib_dump_date(t, 1); 42 | rs = timelib_astro_rise_set_altitude(t, atof(argv[2]) /*9.627*/, atof(argv[3]) /*59.186*/, 0, 0, &h_rise, &h_set, &rise, &set, &transit); 43 | 44 | switch (rs) { 45 | case 0: 46 | break; 47 | case +1: 48 | printf( "Sun always above horizon\n"); 49 | break; 50 | case -1: 51 | printf( "Sun always below horizon\n"); 52 | break; 53 | } 54 | timelib_unixtime2local(t, rise); 55 | timelib_dump_date(t, 1); 56 | timelib_decimal_hour_to_hms(h_rise, &h, &m, &s); 57 | 58 | timelib_unixtime2local(t, set); 59 | timelib_dump_date(t, 1); 60 | timelib_decimal_hour_to_hms(h_set, &h, &m, &s); 61 | 62 | if (t->tz_info) { 63 | timelib_tzinfo_dtor(t->tz_info); 64 | } 65 | timelib_time_dtor(t); 66 | 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /tests/c/timelib_get_offset_info_test.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | 4 | TEST_GROUP(get_offset_info) 5 | { 6 | }; 7 | 8 | TEST(get_offset_info, UTCPlusOffset) 9 | { 10 | int32_t offset; 11 | timelib_time* t = timelib_time_ctor(); 12 | timelib_set_timezone_from_offset(t, 5 * 3600); 13 | 14 | timelib_unixtime2local(t, 1483280063); 15 | CHECK_FALSE(timelib_get_time_zone_offset_info(t->sse, t->tz_info, &offset, NULL, NULL)); 16 | 17 | timelib_time_dtor(t); 18 | } 19 | 20 | TEST(get_offset_info, AbbreviatedTimeZone) 21 | { 22 | int32_t offset; 23 | timelib_abbr_info ai = { -5 * 3600, (char*) "EST", 0 }; 24 | timelib_time* t = timelib_time_ctor(); 25 | timelib_set_timezone_from_abbr(t, ai); 26 | 27 | timelib_unixtime2local(t, 1483280063); 28 | CHECK_FALSE(timelib_get_time_zone_offset_info(t->sse, t->tz_info, &offset, NULL, NULL)); 29 | 30 | timelib_time_dtor(t); 31 | } 32 | 33 | TEST(get_offset_info, London) 34 | { 35 | int code; 36 | int32_t offset; 37 | timelib_sll transition; 38 | unsigned int is_dst; 39 | timelib_tzinfo* tzi = timelib_parse_tzfile((char*) "Europe/London", timelib_builtin_db(), &code); 40 | timelib_time* t = timelib_time_ctor(); 41 | timelib_set_timezone(t, tzi); 42 | 43 | timelib_unixtime2local(t, 1483280063); // 1483280063 = 2017-01-01 44 | CHECK_TRUE(timelib_get_time_zone_offset_info(t->sse, t->tz_info, &offset, &transition, &is_dst)); 45 | LONGS_EQUAL(0, offset); 46 | CHECK_FALSE(is_dst); 47 | LONGS_EQUAL(1477789200, transition); // 1477789200 = 2016-10-30 48 | 49 | timelib_unixtime2local(t, 1501074654); // 1501074654 = 2017-07-26 50 | CHECK_TRUE(timelib_get_time_zone_offset_info(t->sse, t->tz_info, &offset, &transition, &is_dst)); 51 | LONGS_EQUAL(3600, offset); 52 | CHECK_TRUE(is_dst); 53 | LONGS_EQUAL(1490490000, transition); // 1490490000 = 2017-03-26 54 | 55 | timelib_tzinfo_dtor(tzi); 56 | timelib_time_dtor(t); 57 | } 58 | 59 | TEST(get_offset_info, Amsterdam) 60 | { 61 | int code; 62 | int32_t offset; 63 | timelib_tzinfo* tzi = timelib_parse_tzfile((char*) "Europe/Amsterdam", timelib_builtin_db(), &code); 64 | timelib_time* t = timelib_time_ctor(); 65 | timelib_set_timezone(t, tzi); 66 | 67 | timelib_unixtime2local(t, 1483280063); 68 | CHECK_TRUE(timelib_get_time_zone_offset_info(t->sse, t->tz_info, &offset, NULL, NULL)); 69 | LONGS_EQUAL(3600, offset); 70 | 71 | timelib_unixtime2local(t, 1501074654); 72 | CHECK_TRUE(timelib_get_time_zone_offset_info(t->sse, t->tz_info, &offset, NULL, NULL)); 73 | LONGS_EQUAL(7200, offset); 74 | 75 | timelib_tzinfo_dtor(tzi); 76 | timelib_time_dtor(t); 77 | } 78 | -------------------------------------------------------------------------------- /tests/tester-parse-string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | #include 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | timelib_time *t; 31 | int i, errors_found; 32 | timelib_error_container *errors; 33 | 34 | t = timelib_strtotime(argv[1], strlen(argv[1]), &errors, timelib_builtin_db(), timelib_parse_tzfile); 35 | if (errors->warning_count) { 36 | printf("W=%d ", errors->warning_count); 37 | } 38 | if (errors->error_count) { 39 | printf("E=%d ", errors->error_count); 40 | } 41 | timelib_dump_date(t, 1); 42 | if (t->tz_info) { 43 | timelib_tzinfo_dtor(t->tz_info); 44 | } 45 | timelib_time_dtor(t); 46 | 47 | if (errors->warning_count) { 48 | printf("Warnings found while parsing '%s'\n", argv[1]); 49 | for (i = 0; i < errors->warning_count; i++) { 50 | printf("W %s @ pos %d (char=[%c])\n", errors->warning_messages[i].message, errors->warning_messages[i].position, errors->warning_messages[i].character); 51 | } 52 | } 53 | if (errors->error_count) { 54 | printf("Errors found while parsing '%s'\n", argv[1]); 55 | for (i = 0; i < errors->error_count; i++) { 56 | printf("E %s @ pos %d (char=[%c])\n", errors->error_messages[i].message, errors->error_messages[i].position, errors->error_messages[i].character); 57 | } 58 | } 59 | 60 | errors_found = errors->error_count ? 1 : 0; 61 | timelib_error_container_dtor(errors); 62 | return errors_found; 63 | } 64 | -------------------------------------------------------------------------------- /tests/tester-parse-string-by-format.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015,2021 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | #include 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | timelib_time *t; 31 | int i, errors_found; 32 | timelib_error_container *errors; 33 | 34 | if (argc < 3) { 35 | printf("Usage:\n\ttester-parse-string-by-format [format] [string]\n\tExample: ./tester-parse-string-by-format \"d M Y h:i A\" \"07 Apr 2021 3:34 PM\"\n\n"); 36 | exit(-1); 37 | } 38 | 39 | t = timelib_parse_from_format(argv[1], argv[2], strlen(argv[2]), &errors, timelib_builtin_db(), timelib_parse_tzfile); 40 | if (errors->warning_count) { 41 | printf("W=%d ", errors->warning_count); 42 | } 43 | if (errors->error_count) { 44 | printf("E=%d ", errors->error_count); 45 | } 46 | timelib_dump_date(t, 1); 47 | if (t->tz_info) { 48 | timelib_tzinfo_dtor(t->tz_info); 49 | } 50 | timelib_time_dtor(t); 51 | 52 | if (errors->warning_count) { 53 | printf("Warnings found while parsing '%s'\n", argv[1]); 54 | for (i = 0; i < errors->warning_count; i++) { 55 | printf("W %s @ pos %d (char=[%c])\n", errors->warning_messages[i].message, errors->warning_messages[i].position, errors->warning_messages[i].character); 56 | } 57 | } 58 | if (errors->error_count) { 59 | printf("Errors found while parsing '%s'\n", argv[1]); 60 | for (i = 0; i < errors->error_count; i++) { 61 | printf("E %s @ pos %d (char=[%c])\n", errors->error_messages[i].message, errors->error_messages[i].position, errors->error_messages[i].character); 62 | } 63 | } 64 | 65 | errors_found = errors->error_count ? 1 : 0; 66 | timelib_error_container_dtor(errors); 67 | return errors_found; 68 | } 69 | -------------------------------------------------------------------------------- /tests/tester-parse-interval.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | #include 27 | 28 | int main(int argc, char *argv[]) 29 | { 30 | timelib_time *b = NULL, *e = NULL; 31 | timelib_rel_time *p = NULL; 32 | int r = 0; 33 | int i, errors_found; 34 | timelib_error_container *errors; 35 | 36 | timelib_strtointerval(argv[1], strlen(argv[1]), &b, &e, &p, &r, &errors); 37 | if (errors->warning_count) { 38 | printf("W=%d ", errors->warning_count); 39 | } 40 | if (errors->error_count) { 41 | printf("E=%d ", errors->error_count); 42 | } 43 | printf("\n"); 44 | if (b) { 45 | printf("B "); 46 | timelib_dump_date(b, 1); 47 | timelib_time_dtor(b); 48 | } 49 | if (e) { 50 | printf("E "); 51 | timelib_dump_date(e, 1); 52 | timelib_time_dtor(e); 53 | } 54 | if (p) { 55 | printf("P "); 56 | timelib_dump_rel_time(p); 57 | timelib_rel_time_dtor(p); 58 | } 59 | if (r) { 60 | printf("Recurrences: %d\n", r); 61 | } 62 | 63 | if (errors->warning_count) { 64 | printf("Warnings found while parsing '%s'\n", argv[1]); 65 | for (i = 0; i < errors->warning_count; i++) { 66 | printf("W %s @ pos %d (char=[%c])\n", errors->warning_messages[i].message, errors->warning_messages[i].position, errors->warning_messages[i].character); 67 | } 68 | } 69 | if (errors->error_count) { 70 | printf("Errors found while parsing '%s'\n", argv[1]); 71 | for (i = 0; i < errors->error_count; i++) { 72 | printf("E %s @ pos %d (char=[%c])\n", errors->error_messages[i].message, errors->error_messages[i].position, errors->error_messages[i].character); 73 | } 74 | } 75 | 76 | errors_found = errors->error_count ? 1 : 0; 77 | timelib_error_container_dtor(errors); 78 | return errors_found; 79 | } 80 | -------------------------------------------------------------------------------- /tests/tester-create-ts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | #include "timelib.h" 25 | #include 26 | #include 27 | 28 | static void handle_errors(timelib_error_container **errors) 29 | { 30 | int i; 31 | 32 | if (!errors || !*errors) { 33 | return; 34 | } 35 | 36 | for (i = 0; i < (*errors)->error_count; i++) { 37 | printf("Error %03X @ position %d: %s\n", 38 | (*errors)->error_messages[i].error_code, 39 | (*errors)->error_messages[i].position, 40 | (*errors)->error_messages[i].message); 41 | } 42 | 43 | timelib_error_container_dtor(*errors); 44 | } 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | timelib_time *t, *now; 49 | char *tz; 50 | timelib_tzinfo *tzi; 51 | int dummy_error; 52 | timelib_error_container *errors; 53 | 54 | if (argc < 4) { 55 | printf("Usage:\n\ttester-create-ts [t] [reference] [tz specification]\n\tExample: ./tester-create-ts \"9/11\" \"00:00:00\" \"Europe/Amsterdam\"\n\n"); 56 | exit(-1); 57 | } 58 | 59 | t = timelib_strtotime(argv[1], strlen(argv[1]), &errors, timelib_builtin_db(), timelib_parse_tzfile); 60 | handle_errors(&errors); 61 | 62 | now = timelib_strtotime(argv[2], strlen(argv[2]), &errors, timelib_builtin_db(), timelib_parse_tzfile); 63 | handle_errors(&errors); 64 | 65 | tz = argv[3]; 66 | tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &dummy_error); 67 | 68 | timelib_fill_holes(t, now, TIMELIB_OVERRIDE_TIME); 69 | if (now->tz_info && (now->tz_info != tzi)) { 70 | timelib_tzinfo_dtor(now->tz_info); 71 | } 72 | timelib_time_dtor(now); 73 | timelib_update_ts(t, tzi); 74 | 75 | timelib_dump_date(t, 1); 76 | if (t->tz_info && (t->tz_info != tzi)) { 77 | timelib_tzinfo_dtor(t->tz_info); 78 | } 79 | timelib_time_dtor(t); 80 | if (tzi) { 81 | timelib_tzinfo_dtor(tzi); 82 | } 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /tests/c/diff_days.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | #include 5 | 6 | 7 | TEST_GROUP(diff_days) 8 | { 9 | int days; 10 | timelib_time *t1, *t2; 11 | timelib_tzinfo *tzi1, *tzi2; 12 | int dummy_error; 13 | 14 | TEST_SETUP() 15 | { 16 | days = 0; 17 | t1 = t2 = NULL; 18 | tzi1 = NULL; 19 | tzi2 = NULL; 20 | } 21 | 22 | void test_parse(const char *str1, const char *str2) 23 | { 24 | t1 = timelib_strtotime(str1, strlen(str1), NULL, timelib_builtin_db(), timelib_parse_tzfile); 25 | t2 = timelib_strtotime(str2, strlen(str2), NULL, timelib_builtin_db(), timelib_parse_tzfile); 26 | 27 | days = timelib_diff_days(t1, t2); 28 | } 29 | 30 | void test_parse_with_tz(const char *str1, const char *str2, const char *tz1, const char *tz2) 31 | { 32 | t1 = timelib_strtotime(str1, strlen(str1), NULL, timelib_builtin_db(), timelib_parse_tzfile); 33 | t2 = timelib_strtotime(str2, strlen(str2), NULL, timelib_builtin_db(), timelib_parse_tzfile); 34 | 35 | if (tz1) { 36 | tzi1 = timelib_parse_tzfile((char*) tz1, timelib_builtin_db(), &dummy_error); 37 | timelib_update_ts(t1, tzi1); 38 | } else { 39 | timelib_update_ts(t1, NULL); 40 | } 41 | if (tz2) { 42 | tzi2 = timelib_parse_tzfile((char*) tz2, timelib_builtin_db(), &dummy_error); 43 | timelib_update_ts(t2, tzi2); 44 | } else { 45 | timelib_update_ts(t2, NULL); 46 | } 47 | 48 | days = timelib_diff_days(t1, t2); 49 | } 50 | 51 | TEST_TEARDOWN() 52 | { 53 | if (t1) { 54 | timelib_time_dtor(t1); 55 | } 56 | if (t2) { 57 | timelib_time_dtor(t2); 58 | } 59 | if (tzi1) { 60 | timelib_tzinfo_dtor(tzi1); 61 | } 62 | if (tzi2) { 63 | timelib_tzinfo_dtor(tzi2); 64 | } 65 | } 66 | }; 67 | 68 | TEST(diff_days, a_year) 69 | { 70 | test_parse("2021-01-01 GMT", "2021-12-31 GMT"); 71 | LONGS_EQUAL(364, days); 72 | } 73 | 74 | /* PHP Bug #81458 75 | $first = (new DateTime('2018-07-01 00:00:00.000000 America/Toronto')) 76 | ->setTimezone(new DateTimeZone('UTC')); 77 | $second = new DateTime('2018-07-02 00:00:00.000000 America/Toronto'); 78 | */ 79 | TEST(diff_days, php81458_1) 80 | { 81 | test_parse_with_tz("2018-07-01 04:00 GMT+0000", "2018-07-02 00:00", NULL, "America/Toronto"); 82 | LONGS_EQUAL(1, days); 83 | } 84 | 85 | TEST(diff_days, php81458_2) 86 | { 87 | test_parse_with_tz("2018-12-01 00:00", "2018-12-02 00:01", "UTC", "UTC"); 88 | LONGS_EQUAL(1, days); 89 | } 90 | 91 | TEST(diff_days, php78452) 92 | { 93 | test_parse_with_tz("2019-09-24 11:47:24", "2019-08-21 12:47:24", "Asia/Tehran", "Asia/Tehran"); 94 | LONGS_EQUAL(33, days); 95 | } 96 | 97 | TEST(diff_days, php74524) 98 | { 99 | test_parse_with_tz("2017-11-17 22:05:26.000000", "2017-04-03 22:29:15.079459", "Europe/Amsterdam", "Europe/Amsterdam"); 100 | LONGS_EQUAL(227, days); 101 | } 102 | 103 | TEST(diff_days, DateTime_data_fall_type2_type2) 104 | { 105 | test_parse("2010-11-07 00:15:35 EDT", "2010-11-07 00:10:20 EDT"); 106 | LONGS_EQUAL(0, days); 107 | } 108 | 109 | TEST(diff_days, DateTime_data_fall_type3_type3) 110 | { 111 | test_parse_with_tz("2010-11-07 00:15:35", "2010-11-07 00:10:20", "America/New_York", "America/New_York"); 112 | LONGS_EQUAL(0, days); 113 | } 114 | -------------------------------------------------------------------------------- /docs/date-from-parts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * Example that shows how to convert a date/time in its parts, to a 27 | * Unix timestamp. 28 | * 29 | * Compile with: 30 | * gcc -ggdb3 -o date-from-parts date-from-parts.c ../timelib.a -lm 31 | */ 32 | 33 | #include 34 | #include 35 | #include "../timelib.h" 36 | 37 | struct { 38 | timelib_tzdb *db; 39 | /* cache *tz_cache; */ 40 | } global; 41 | 42 | void create_cache(timelib_tzdb *db) 43 | { 44 | global.db = db; 45 | 46 | /* Loop over all the entries and store in tz_cache */ 47 | } 48 | 49 | void cleanup_cache() 50 | { 51 | if (global.db != timelib_builtin_db()) { 52 | timelib_zoneinfo_dtor(global.db); 53 | } 54 | 55 | /* Loop over all the entries in tz_cache and free */ 56 | } 57 | 58 | timelib_tzinfo *cached_tzfile_wrapper(const char *tz_id, const timelib_tzdb *db, int *error) 59 | { 60 | /* return tz_cache[tzid]; (pseudo code) */ 61 | return timelib_parse_tzfile(tz_id, global.db, error); 62 | } 63 | 64 | timelib_tzinfo *cached_fetch_tzinfo(const char *tz_id) 65 | { 66 | int dummy_error; 67 | 68 | return cached_tzfile_wrapper(tz_id, global.db, &dummy_error); 69 | } 70 | 71 | int main(void) 72 | { 73 | timelib_sll ty = 2017; 74 | timelib_sll tm = 6; 75 | timelib_sll td = 6; 76 | timelib_sll th = 12; 77 | timelib_sll ti = 50; 78 | timelib_sll ts = 58; 79 | timelib_sll tus = 713 * 1000; 80 | const char *tz_id = "America/New_York"; 81 | timelib_time *t; 82 | timelib_tzinfo *tzi; 83 | 84 | create_cache((timelib_tzdb*) timelib_builtin_db()); 85 | 86 | tzi = cached_fetch_tzinfo(tz_id); 87 | 88 | t = timelib_time_ctor(); 89 | t->y = ty; t->m = tm; t->d = td; 90 | t->h = th; t->i = ti; t->s = ts; 91 | t->us = tus; 92 | 93 | timelib_update_ts(t, tzi); 94 | timelib_set_timezone(t, tzi); 95 | timelib_unixtime2gmt(t, t->sse); /* Note it says gmt in the function name */ 96 | 97 | 98 | #define LLABS(y) (y < 0 ? (y * -1) : y) 99 | 100 | /* Show parts Y/m/d */ 101 | { 102 | printf( 103 | "%s%04lld-%02lld-%02lld %02lld:%02lld:%02lld", 104 | t->y < 0 ? "-" : "", LLABS(t->y), 105 | t->m, t->d, t->h, t->i, t->s 106 | ); 107 | if (t->us > 0) { 108 | printf(".%06lld", t->us); 109 | } 110 | printf("\n"); 111 | } 112 | 113 | 114 | /* Show Unix timestamp */ 115 | { 116 | printf("Timestamp: %lld\n", t->sse); 117 | } 118 | 119 | 120 | timelib_tzinfo_dtor(tzi); 121 | timelib_time_dtor(t); 122 | 123 | cleanup_cache(); 124 | } 125 | -------------------------------------------------------------------------------- /tests/c/dow.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | 5 | TEST_GROUP(dow) 6 | { 7 | timelib_sll iy; 8 | timelib_sll iw; 9 | timelib_sll id; 10 | 11 | void test_ymd(timelib_sll y, timelib_sll m, timelib_sll d) 12 | { 13 | timelib_isodate_from_date(y, m, d, &iy, &iw, &id); 14 | } 15 | 16 | void check_ywd(timelib_sll e_iy, timelib_sll e_iw, timelib_sll e_id) 17 | { 18 | LONGS_EQUAL(iy, e_iy); 19 | LONGS_EQUAL(iw, e_iw); 20 | LONGS_EQUAL(id, e_id); 21 | } 22 | }; 23 | 24 | TEST(dow, t_n0101_12_29) { test_ymd(-101, 12, 29); check_ywd(-101, 52, 5); } 25 | TEST(dow, t_n0101_12_30) { test_ymd(-101, 12, 30); check_ywd(-101, 52, 6); } 26 | TEST(dow, t_n0101_12_31) { test_ymd(-101, 12, 31); check_ywd(-101, 52, 7); } 27 | TEST(dow, t_n0100_01_01) { test_ymd(-100, 1, 1); check_ywd(-100, 1, 1); } 28 | TEST(dow, t_n0100_01_02) { test_ymd(-100, 1, 2); check_ywd(-100, 1, 2); } 29 | TEST(dow, t_n0100_01_03) { test_ymd(-100, 1, 3); check_ywd(-100, 1, 3); } 30 | TEST(dow, t_n0100_01_04) { test_ymd(-100, 1, 4); check_ywd(-100, 1, 4); } 31 | TEST(dow, t_n0100_01_05) { test_ymd(-100, 1, 5); check_ywd(-100, 1, 5); } 32 | TEST(dow, t_n0100_01_06) { test_ymd(-100, 1, 6); check_ywd(-100, 1, 6); } 33 | TEST(dow, t_n0100_01_07) { test_ymd(-100, 1, 7); check_ywd(-100, 1, 7); } 34 | TEST(dow, t_n0100_01_08) { test_ymd(-100, 1, 8); check_ywd(-100, 2, 1); } 35 | TEST(dow, t_n0100_01_09) { test_ymd(-100, 1, 9); check_ywd(-100, 2, 2); } 36 | 37 | TEST(dow, t_n0100_12_29) { test_ymd(-100, 12, 29); check_ywd(-100, 52, 6); } 38 | TEST(dow, t_n0100_12_30) { test_ymd(-100, 12, 30); check_ywd(-100, 52, 7); } 39 | TEST(dow, t_n0100_12_31) { test_ymd(-100, 12, 31); check_ywd( -99, 1, 1); } 40 | TEST(dow, t_n0099_01_01) { test_ymd( -99, 1, 1); check_ywd( -99, 1, 2); } 41 | TEST(dow, t_n0099_01_02) { test_ymd( -99, 1, 2); check_ywd( -99, 1, 3); } 42 | TEST(dow, t_n0099_01_03) { test_ymd( -99, 1, 3); check_ywd( -99, 1, 4); } 43 | TEST(dow, t_n0099_01_04) { test_ymd( -99, 1, 4); check_ywd( -99, 1, 5); } 44 | TEST(dow, t_n0099_01_05) { test_ymd( -99, 1, 5); check_ywd( -99, 1, 6); } 45 | TEST(dow, t_n0099_01_06) { test_ymd( -99, 1, 6); check_ywd( -99, 1, 7); } 46 | TEST(dow, t_n0099_01_07) { test_ymd( -99, 1, 7); check_ywd( -99, 2, 1); } 47 | TEST(dow, t_n0099_01_08) { test_ymd( -99, 1, 8); check_ywd( -99, 2, 2); } 48 | TEST(dow, t_n0099_01_09) { test_ymd( -99, 1, 9); check_ywd( -99, 2, 3); } 49 | 50 | TEST(dow, t_n0001_12_26) { test_ymd(-1, 12, 26); check_ywd(-1, 51, 7); } 51 | TEST(dow, t_n0001_12_27) { test_ymd(-1, 12, 27); check_ywd(-1, 52, 1); } 52 | TEST(dow, t_n0001_12_28) { test_ymd(-1, 12, 28); check_ywd(-1, 52, 2); } 53 | TEST(dow, t_n0001_12_29) { test_ymd(-1, 12, 29); check_ywd(-1, 52, 3); } 54 | TEST(dow, t_n0001_12_30) { test_ymd(-1, 12, 30); check_ywd(-1, 52, 4); } 55 | TEST(dow, t_n0001_12_31) { test_ymd(-1, 12, 31); check_ywd(-1, 52, 5); } 56 | TEST(dow, t_0000_01_01) { test_ymd( 0, 1, 1); check_ywd(-1, 52, 6); } 57 | TEST(dow, t_0000_01_02) { test_ymd( 0, 1, 2); check_ywd(-1, 52, 7); } 58 | TEST(dow, t_0000_01_03) { test_ymd( 0, 1, 3); check_ywd( 0, 1, 1); } 59 | TEST(dow, t_0000_01_04) { test_ymd( 0, 1, 4); check_ywd( 0, 1, 2); } 60 | TEST(dow, t_0000_01_05) { test_ymd( 0, 1, 5); check_ywd( 0, 1, 3); } 61 | TEST(dow, t_0000_01_06) { test_ymd( 0, 1, 6); check_ywd( 0, 1, 4); } 62 | TEST(dow, t_0000_01_07) { test_ymd( 0, 1, 7); check_ywd( 0, 1, 5); } 63 | TEST(dow, t_0000_01_08) { test_ymd( 0, 1, 8); check_ywd( 0, 1, 6); } 64 | TEST(dow, t_0000_01_09) { test_ymd( 0, 1, 9); check_ywd( 0, 1, 7); } 65 | TEST(dow, t_0000_01_10) { test_ymd( 0, 1, 10); check_ywd( 0, 2, 1); } 66 | TEST(dow, t_0000_01_11) { test_ymd( 0, 1, 11); check_ywd( 0, 2, 2); } 67 | TEST(dow, t_0000_01_12) { test_ymd( 0, 1, 12); check_ywd( 0, 2, 3); } 68 | TEST(dow, t_0000_01_13) { test_ymd( 0, 1, 13); check_ywd( 0, 2, 4); } 69 | -------------------------------------------------------------------------------- /tests/date_from_isodate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * Example that shows how to convert a date/time in its parts, to a 27 | * Unix timestamp. 28 | * 29 | * Compile with: 30 | * gcc -ggdb3 -o date-from-parts date-from-parts.c ../timelib.a -lm 31 | */ 32 | 33 | #include 34 | #include 35 | #include "timelib.h" 36 | 37 | #define TEST_COUNT 37 38 | 39 | timelib_sll tests[TEST_COUNT][2][3] = { 40 | { { 2014, 52, 1 }, { 2014, 12, 22 } }, 41 | { { 2014, 52, 7 }, { 2014, 12, 28 } }, 42 | { { 2015, 1, 1 }, { 2014, 12, 29 } }, 43 | { { 2015, 1, 3 }, { 2014, 12, 31 } }, 44 | { { 2015, 1, 4 }, { 2015, 1, 1 } }, 45 | { { 2015, 52, 7 }, { 2015, 12, 27 } }, 46 | { { 2015, 53, 1 }, { 2015, 12, 28 } }, 47 | { { 2015, 53, 4 }, { 2015, 12, 31 } }, 48 | { { 2015, 53, 5 }, { 2016, 1, 1 } }, 49 | { { 2016, 1, 1 }, { 2016, 1, 4 } }, 50 | { { 2016, 1, 3 }, { 2016, 1, 6 } }, 51 | { { 2016, 1, 4 }, { 2016, 1, 7 } }, 52 | { { 2016, 51, 7 }, { 2016, 12, 25 } }, 53 | { { 2016, 52, 1 }, { 2016, 12, 26 } }, 54 | { { 2016, 52, 4 }, { 2016, 12, 29 } }, 55 | { { 2016, 52, 7 }, { 2017, 1, 1 } }, 56 | { { 2017, 8, 6 }, { 2017, 2, 25 } }, 57 | { { 2017, 8, 7 }, { 2017, 2, 26 } }, 58 | { { 2017, 9, 1 }, { 2017, 2, 27 } }, 59 | { { 2017, 9, 2 }, { 2017, 2, 28 } }, 60 | { { 2017, 9, 3 }, { 2017, 3, 1 } }, 61 | { { 2020, 9, 2 }, { 2020, 2, 25 } }, 62 | { { 2020, 9, 3 }, { 2020, 2, 26 } }, 63 | { { 2020, 9, 5 }, { 2020, 2, 28 } }, 64 | { { 2020, 9, 6 }, { 2020, 2, 29 } }, 65 | { { 2020, 9, 7 }, { 2020, 3, 1 } }, 66 | { { 2043, 53, 1 }, { 2043, 12, 28 } }, 67 | { { 2043, 53, 2 }, { 2043, 12, 29 } }, 68 | { { 2043, 53, 3 }, { 2043, 12, 30 } }, 69 | { { 2043, 53, 4 }, { 2043, 12, 31 } }, 70 | { { 2043, 53, 5 }, { 2044, 1, 1 } }, 71 | { { 2043, 53, 6 }, { 2044, 1, 2 } }, 72 | { { 2043, 53, 7 }, { 2044, 1, 3 } }, 73 | { { 2019, 0, 1 }, { 2018, 12, 24 } }, 74 | { { 2019, -1, 1 }, { 2018, 12, 17 } }, 75 | { { 2019, 62, 1 }, { 2020, 3, 2 } }, 76 | { { 2019,110, 1 }, { 2021, 2, 1 } }, 77 | }; 78 | 79 | int main(int argc, char *argv[]) 80 | { 81 | int i; 82 | timelib_sll y, m, d; 83 | char expected[20], actual[20]; 84 | 85 | for (i = 0; i < TEST_COUNT; i++) { 86 | timelib_date_from_isodate(tests[i][0][0], tests[i][0][1], tests[i][0][2], &y, &m, &d); 87 | snprintf(actual, 19, "%04lld-%02lld-%02lld", y, m, d); 88 | snprintf(expected, 19, "%04lld-%02lld-%02lld", tests[i][1][0], tests[i][1][1], tests[i][1][2]); 89 | 90 | if (strcmp(actual, expected) != 0) { 91 | printf("FAIL: ACTUAL(%s) != EXPECTED(%s)\n", actual, expected); 92 | } else { 93 | printf("OK: ACTUAL(%s) == EXPECTED(%s)\n", actual, expected); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /docs/date-from-iso-parts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * Example that shows how to convert a date/time in its parts, to a 27 | * Unix timestamp. 28 | * 29 | * Compile with: 30 | * gcc -ggdb3 -o date-from-parts date-from-parts.c ../timelib.a -lm 31 | */ 32 | 33 | #include 34 | #include 35 | #include "../timelib.h" 36 | 37 | struct { 38 | timelib_tzdb *db; 39 | /* cache *tz_cache; */ 40 | } global; 41 | 42 | void create_cache(timelib_tzdb *db) 43 | { 44 | global.db = db; 45 | 46 | /* Loop over all the entries and store in tz_cache */ 47 | } 48 | 49 | void cleanup_cache() 50 | { 51 | if (global.db != timelib_builtin_db()) { 52 | timelib_zoneinfo_dtor(global.db); 53 | } 54 | 55 | /* Loop over all the entries in tz_cache and free */ 56 | } 57 | 58 | timelib_tzinfo *cached_tzfile_wrapper(const char *tz_id, const timelib_tzdb *db, int *error) 59 | { 60 | /* return tz_cache[tzid]; (pseudo code) */ 61 | return timelib_parse_tzfile(tz_id, global.db, error); 62 | } 63 | 64 | timelib_tzinfo *cached_fetch_tzinfo(const char *tz_id) 65 | { 66 | int dummy_error; 67 | 68 | return cached_tzfile_wrapper(tz_id, global.db, &dummy_error); 69 | } 70 | 71 | int main(int argc, char *argv[]) 72 | { 73 | timelib_sll ty; 74 | timelib_sll tw; 75 | timelib_sll td; 76 | timelib_sll th = 12; 77 | timelib_sll ti = 50; 78 | timelib_sll ts = 58; 79 | timelib_sll tus = 48 * 1000; 80 | const char *tz_id = "America/New_York"; 81 | timelib_time *t; 82 | timelib_tzinfo *tzi; 83 | 84 | if (argc < 4) { 85 | printf("Usage:\n\tdate-from-iso-parts isoyear isoweek isoday\n\tExample: ./date-from-iso-parts 2017 23 2\n\n"); 86 | exit(-1); 87 | } 88 | 89 | ty = atoll(argv[1]); 90 | tw = atoll(argv[2]); 91 | td = atoll(argv[3]); 92 | 93 | create_cache((timelib_tzdb*) timelib_builtin_db()); 94 | 95 | tzi = cached_fetch_tzinfo(tz_id); 96 | 97 | t = timelib_time_ctor(); 98 | timelib_date_from_isodate(ty, tw, td, &t->y, &t->m, &t->d); 99 | t->h = th; t->i = ti; t->s = ts; 100 | t->us = tus; 101 | 102 | timelib_update_ts(t, tzi); 103 | timelib_set_timezone(t, tzi); 104 | timelib_unixtime2gmt(t, t->sse); /* Note it says gmt in the function name */ 105 | 106 | 107 | #define LLABS(y) (y < 0 ? (y * -1) : y) 108 | 109 | /* Show parts Y/m/d */ 110 | { 111 | printf( 112 | "%s%04lld-%02lld-%02lld %02lld:%02lld:%02lld", 113 | t->y < 0 ? "-" : "", LLABS(t->y), 114 | t->m, t->d, t->h, t->i, t->s 115 | ); 116 | if (t->us > 0) { 117 | printf(".%06lld", t->us); 118 | } 119 | printf("\n"); 120 | } 121 | 122 | 123 | /* Show Unix timestamp */ 124 | { 125 | printf("Timestamp: %lld\n", t->sse); 126 | } 127 | 128 | 129 | timelib_tzinfo_dtor(tzi); 130 | timelib_time_dtor(t); 131 | 132 | cleanup_cache(); 133 | } 134 | -------------------------------------------------------------------------------- /docs/date-to-parts.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* 26 | * Example that shows how to convert a string and TZ identifier to its parts, 27 | * in both normal and ISO week date parts. 28 | * 29 | * Compile with: 30 | * gcc -ggdb3 -o date-to-parts date-to-parts.c ../timelib.a -lm 31 | */ 32 | 33 | #include 34 | #include 35 | #include "../timelib.h" 36 | 37 | struct { 38 | timelib_tzdb *db; 39 | /* cache *tz_cache; */ 40 | } global; 41 | 42 | void create_cache(timelib_tzdb *db) 43 | { 44 | global.db = db; 45 | 46 | /* Loop over all the entries and store in tz_cache */ 47 | } 48 | 49 | void cleanup_cache() 50 | { 51 | if (global.db != timelib_builtin_db()) { 52 | timelib_zoneinfo_dtor(global.db); 53 | } 54 | 55 | /* Loop over all the entries in tz_cache and free */ 56 | } 57 | 58 | timelib_tzinfo *cached_tzfile_wrapper(const char *tz_id, const timelib_tzdb *db, int *error) 59 | { 60 | /* return tz_cache[tzid]; (pseudo code) */ 61 | return timelib_parse_tzfile(tz_id, global.db, error); 62 | } 63 | 64 | timelib_tzinfo *cached_fetch_tzinfo(const char *tz_id) 65 | { 66 | int dummy_error; 67 | 68 | return cached_tzfile_wrapper(tz_id, global.db, &dummy_error); 69 | } 70 | 71 | int main(void) 72 | { 73 | const char *dt_string = "2017-06-05T11:30:09.123Z"; 74 | const char *tz_id = "Europe/London"; 75 | timelib_time *t; 76 | timelib_tzinfo *tzi; 77 | timelib_error_container *errors; 78 | 79 | create_cache((timelib_tzdb*) timelib_builtin_db()); 80 | 81 | tzi = cached_fetch_tzinfo(tz_id); 82 | 83 | /* Convert string to timelib_time, and hence its constituent parts */ 84 | t = timelib_strtotime( 85 | dt_string, strlen(dt_string), 86 | &errors, 87 | global.db, 88 | cached_tzfile_wrapper 89 | ); 90 | timelib_update_ts(t, tzi); 91 | timelib_set_timezone(t, tzi); 92 | timelib_unixtime2local(t, t->sse); 93 | 94 | 95 | #define LLABS(y) (y < 0 ? (y * -1) : y) 96 | 97 | /* Show parts Y/m/d */ 98 | { 99 | printf( 100 | "%s%04lld-%02lld-%02lld %02lld:%02lld:%02lld", 101 | t->y < 0 ? "-" : "", LLABS(t->y), 102 | t->m, t->d, t->h, t->i, t->s 103 | ); 104 | if (t->us > 0) { 105 | printf(".%03lld", (t->us / 1000)); 106 | } 107 | printf("\n"); 108 | } 109 | 110 | 111 | /* Show parts ISO */ 112 | { 113 | timelib_sll iso_year, iso_week, iso_dow; 114 | 115 | timelib_isodate_from_date(t->y, t->m, t->d, &iso_year, &iso_week, &iso_dow); 116 | printf( 117 | "%s%04lldW%02lldD%02lld %02lld:%02lld:%02lld", 118 | iso_year < 0 ? "-" : "", LLABS(iso_year), 119 | iso_week, iso_dow, 120 | t->h, t->i, t->s 121 | ); 122 | if (t->us > 0) { 123 | printf(".%03lld", (t->us / 1000)); 124 | } 125 | printf("\n"); 126 | } 127 | 128 | timelib_error_container_dtor(errors); 129 | timelib_tzinfo_dtor(tzi); 130 | timelib_time_dtor(t); 131 | 132 | cleanup_cache(); 133 | } 134 | -------------------------------------------------------------------------------- /astro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2019 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | /* This macro computes the length of the day, from sunrise to sunset. */ 26 | /* Sunrise/set is considered to occur when the Sun's upper limb is */ 27 | /* 35 arc minutes below the horizon (this accounts for the refraction */ 28 | /* of the Earth's atmosphere). */ 29 | #define day_length(year,month,day,lon,lat) \ 30 | __daylen__( year, month, day, lon, lat, -35.0/60.0, 1 ) 31 | 32 | /* This macro computes the length of the day, including civil twilight. */ 33 | /* Civil twilight starts/ends when the Sun's center is 6 degrees below */ 34 | /* the horizon. */ 35 | #define day_civil_twilight_length(year,month,day,lon,lat) \ 36 | __daylen__( year, month, day, lon, lat, -6.0, 0 ) 37 | 38 | /* This macro computes the length of the day, incl. nautical twilight. */ 39 | /* Nautical twilight starts/ends when the Sun's center is 12 degrees */ 40 | /* below the horizon. */ 41 | #define day_nautical_twilight_length(year,month,day,lon,lat) \ 42 | __daylen__( year, month, day, lon, lat, -12.0, 0 ) 43 | 44 | /* This macro computes the length of the day, incl. astronomical twilight. */ 45 | /* Astronomical twilight starts/ends when the Sun's center is 18 degrees */ 46 | /* below the horizon. */ 47 | #define day_astronomical_twilight_length(year,month,day,lon,lat) \ 48 | __daylen__( year, month, day, lon, lat, -18.0, 0 ) 49 | 50 | 51 | /* This macro computes times for sunrise/sunset. */ 52 | /* Sunrise/set is considered to occur when the Sun's upper limb is */ 53 | /* 35 arc minutes below the horizon (this accounts for the refraction */ 54 | /* of the Earth's atmosphere). */ 55 | #define timelib_astro_sun_rise_set(ts,lon,lat,hrise,hset,rise,set) \ 56 | timelib_astro_rise_set_altitude( ts, lon, lat, -35.0/60.0, 1, hrise, hset, rise, set ) 57 | 58 | /* This macro computes the start and end times of civil twilight. */ 59 | /* Civil twilight starts/ends when the Sun's center is 6 degrees below */ 60 | /* the horizon. */ 61 | #define civil_twilight(ts,lon,lat,start,end) \ 62 | timelib_astro_rise_set_altitude( ts, lon, lat, -6.0, 0, start, end ) 63 | 64 | /* This macro computes the start and end times of nautical twilight. */ 65 | /* Nautical twilight starts/ends when the Sun's center is 12 degrees */ 66 | /* below the horizon. */ 67 | #define nautical_twilight(ts,lon,lat,start,end) \ 68 | timelib_astro_rise_set_altitude( ts, lon, lat, -12.0, 0, start, end ) 69 | 70 | /* This macro computes the start and end times of astronomical twilight. */ 71 | /* Astronomical twilight starts/ends when the Sun's center is 18 degrees */ 72 | /* below the horizon. */ 73 | #define astronomical_twilight(ts,lon,lat,start,end) \ 74 | timelib_astro_rise_set_altitude( ts, lon, lat, -18.0, 0, start, end ) 75 | -------------------------------------------------------------------------------- /tests/c/timelib_decimal_hour.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | 4 | TEST_GROUP(decimal_hour) 5 | { 6 | }; 7 | 8 | TEST(decimal_hour, convertPositiveHMSToDecimal) 9 | { 10 | int hour = 2, min = 19, sec = 48; 11 | double d; 12 | 13 | timelib_hms_to_decimal_hour(hour, min, sec, &d); 14 | 15 | DOUBLES_EQUAL(2.33, d, 0.00001); 16 | } 17 | 18 | TEST(decimal_hour, convertZeroHMSToDecimal) 19 | { 20 | int hour = 0, min = 0, sec = 0; 21 | double d; 22 | 23 | timelib_hms_to_decimal_hour(hour, min, sec, &d); 24 | 25 | DOUBLES_EQUAL(0, d, 0.00001); 26 | } 27 | 28 | TEST(decimal_hour, convertNegativeHMSToDecimal) 29 | { 30 | int hour = -2, min = 20, sec = 0; 31 | double d; 32 | 33 | timelib_hms_to_decimal_hour(hour, min, sec, &d); 34 | 35 | DOUBLES_EQUAL(-2.333333, d, 0.000001); 36 | } 37 | 38 | TEST(decimal_hour, convertNegativeZeroHMSToDecimal) 39 | { 40 | int hour = -0, min = 0, sec = 0; 41 | double d; 42 | 43 | timelib_hms_to_decimal_hour(hour, min, sec, &d); 44 | 45 | DOUBLES_EQUAL(0, d, 0.00001); 46 | } 47 | 48 | TEST(decimal_hour, convertPositiveDecimalToHMS) 49 | { 50 | double d = 2.33; 51 | int hour, min, sec; 52 | 53 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 54 | 55 | LONGS_EQUAL(2, hour); 56 | LONGS_EQUAL(19, min); 57 | LONGS_EQUAL(48, sec); 58 | } 59 | 60 | TEST(decimal_hour, convertZeroDecimalToHMS) 61 | { 62 | double d = 0; 63 | int hour, min, sec; 64 | 65 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 66 | 67 | LONGS_EQUAL(0, hour); 68 | LONGS_EQUAL(0, min); 69 | LONGS_EQUAL(0, sec); 70 | } 71 | 72 | TEST(decimal_hour, convertNegativeDecimalToHMS) 73 | { 74 | double d = -2.33; 75 | int hour, min, sec; 76 | 77 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 78 | 79 | LONGS_EQUAL(-2, hour); 80 | LONGS_EQUAL(19, min); 81 | LONGS_EQUAL(48, sec); 82 | } 83 | 84 | TEST(decimal_hour, convertNegativeZeroDecimalToHMS) 85 | { 86 | double d = -0; 87 | int hour, min, sec; 88 | 89 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 90 | 91 | LONGS_EQUAL(-0, hour); 92 | LONGS_EQUAL(0, min); 93 | LONGS_EQUAL(0, sec); 94 | } 95 | 96 | 97 | TEST(decimal_hour, convertPositiveHMSFToDecimal) 98 | { 99 | int hour = 2, min = 19, sec = 48, usec = 250000; 100 | double d; 101 | 102 | timelib_hmsf_to_decimal_hour(hour, min, sec, usec, &d); 103 | 104 | DOUBLES_EQUAL(2.330069, d, 0.000001); 105 | } 106 | 107 | TEST(decimal_hour, convertPositiveHMSFToDecimalFullSec) 108 | { 109 | int hour = 2, min = 19, sec = 47, usec = 1000000; 110 | double d; 111 | 112 | timelib_hmsf_to_decimal_hour(hour, min, sec, usec, &d); 113 | 114 | DOUBLES_EQUAL(2.33, d, 0.000001); 115 | } 116 | 117 | TEST(decimal_hour, convertZeroHMSFToDecimal) 118 | { 119 | int hour = 0, min = 0, sec = 0, usec = 0; 120 | double d; 121 | 122 | timelib_hmsf_to_decimal_hour(hour, min, sec, usec, &d); 123 | 124 | DOUBLES_EQUAL(0, d, 0.00001); 125 | } 126 | 127 | TEST(decimal_hour, convertNegativeHMSFToDecimal) 128 | { 129 | int hour = -2, min = 20, sec = 0, usec = 50000; 130 | double d; 131 | 132 | timelib_hmsf_to_decimal_hour(hour, min, sec, usec, &d); 133 | 134 | DOUBLES_EQUAL(-2.333347, d, 0.000001); 135 | } 136 | 137 | TEST(decimal_hour, convertNegativeHMSFToDecimalSmall) 138 | { 139 | int hour = -2, min = 20, sec = 0, usec = 5; 140 | double base, d; 141 | 142 | timelib_hms_to_decimal_hour(hour, min, sec, &base); 143 | timelib_hmsf_to_decimal_hour(hour, min, sec, usec, &d); 144 | 145 | DOUBLES_EQUAL(-1.388889e-09, d - base, 0.00001); 146 | } 147 | 148 | TEST(decimal_hour, convertNegativeZeroHMSFToDecimal) 149 | { 150 | int hour = -0, min = 0, sec = 0, usec = 10; 151 | double d; 152 | 153 | timelib_hmsf_to_decimal_hour(hour, min, sec, usec, &d); 154 | 155 | DOUBLES_EQUAL(0, d, 0.00001); 156 | } 157 | 158 | TEST(decimal_hour, convertPositiveDecimalToHMSOverflow15) 159 | { 160 | double d = 9.333333333333333; 161 | int hour, min, sec; 162 | 163 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 164 | 165 | LONGS_EQUAL(59, sec); 166 | LONGS_EQUAL(19, min); 167 | LONGS_EQUAL( 9, hour); 168 | } 169 | 170 | TEST(decimal_hour, convertPositiveDecimalToHMSOverflow16) 171 | { 172 | double d = 9.3333333333333333; 173 | int hour, min, sec; 174 | 175 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 176 | 177 | LONGS_EQUAL( 0, sec); 178 | LONGS_EQUAL(20, min); 179 | LONGS_EQUAL( 9, hour); 180 | } 181 | 182 | TEST(decimal_hour, convertNegativeDecimalToHMSOverflow15) 183 | { 184 | double d = -9.333333333333333; 185 | int hour, min, sec; 186 | 187 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 188 | 189 | LONGS_EQUAL(59, sec); 190 | LONGS_EQUAL(19, min); 191 | LONGS_EQUAL(-9, hour); 192 | } 193 | 194 | TEST(decimal_hour, convertNegativeDecimalToHMSOverflow16) 195 | { 196 | double d = -9.3333333333333333; 197 | int hour, min, sec; 198 | 199 | timelib_decimal_hour_to_hms(d, &hour, &min, &sec); 200 | 201 | LONGS_EQUAL( 0, sec); 202 | LONGS_EQUAL(20, min); 203 | LONGS_EQUAL(-9, hour); 204 | } 205 | -------------------------------------------------------------------------------- /docs/date-from-string.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017 MongoDB, Inc. 5 | * Copyright (c) 2017 Derick Rethans 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Example that shows how to convert a date/time in its parts, to a 28 | * Unix timestamp. 29 | * 30 | * Compile with: 31 | * gcc -ggdb3 -o date-from-string date-from-string.c ../timelib.a -lm 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include "../timelib.h" 38 | 39 | struct { 40 | timelib_tzdb *db; 41 | /* cache *tz_cache; */ 42 | } global; 43 | 44 | void create_cache(timelib_tzdb *db) 45 | { 46 | global.db = db; 47 | 48 | /* Loop over all the entries and store in tz_cache */ 49 | } 50 | 51 | void cleanup_cache() 52 | { 53 | if (global.db != timelib_builtin_db()) { 54 | timelib_zoneinfo_dtor(global.db); 55 | } 56 | 57 | /* Loop over all the entries in tz_cache and free */ 58 | } 59 | 60 | timelib_tzinfo *cached_tzfile_wrapper(const char *tz_id, const timelib_tzdb *db, int *error) 61 | { 62 | /* return tz_cache[tzid]; (pseudo code) */ 63 | return timelib_parse_tzfile(tz_id, global.db, error); 64 | } 65 | 66 | timelib_tzinfo *cached_fetch_tzinfo(const char *tz_id) 67 | { 68 | int dummy_error; 69 | 70 | return cached_tzfile_wrapper(tz_id, global.db, &dummy_error); 71 | } 72 | 73 | int main(int argc, char *argv[]) 74 | { 75 | char *time_string = NULL; 76 | const char *tz_id = NULL; 77 | timelib_time *t; 78 | #if defined(PARTIAL) 79 | timelib_time *t_now; 80 | #endif 81 | timelib_tzinfo *tzi = NULL; 82 | timelib_tzinfo *tzi_utc = NULL; 83 | timelib_error_container *errors; 84 | 85 | if (argc < 2) { 86 | printf("Usage:\n\tdate-from-string string [tzExpression]\n\n"); 87 | exit(-1); 88 | } 89 | 90 | time_string = argv[1]; 91 | 92 | if (argc >= 3) { 93 | tz_id = argv[2]; 94 | } 95 | 96 | create_cache((timelib_tzdb*) timelib_builtin_db()); 97 | 98 | if (tz_id) { 99 | tzi = cached_fetch_tzinfo(tz_id); 100 | } 101 | tzi_utc = cached_fetch_tzinfo("UTC"); 102 | 103 | #if defined(PARTIAL) 104 | /* Doing this means you can also supply partial date/time strings, such as 105 | * "July 4th" */ 106 | 107 | /* Create "now" to fill in the holes */ 108 | t_now = timelib_time_ctor(); 109 | if (tzi) { 110 | timelib_set_timezone(t_now, tzi); 111 | } else { 112 | timelib_set_timezone(t_now, tzi_utc); 113 | } 114 | timelib_unixtime2gmt(t_now, time(NULL)); 115 | #endif 116 | 117 | /* Convert from string to timelib_t 118 | * 119 | * Passing in the "Z" at the end of the string, means the extra timezone gets ignored. 120 | * If you *don't* want that, then compile with -DDONT_IGNORE_TZ */ 121 | t = timelib_strtotime(time_string, strlen(time_string), &errors, global.db, cached_tzfile_wrapper); 122 | 123 | /* Error handling */ 124 | if (errors->warning_count) { 125 | printf("Warnings found while parsing '%s'\n", time_string); 126 | } 127 | if (errors->error_count) { 128 | printf("Errors found while parsing '%s'\n", time_string); 129 | } 130 | timelib_error_container_dtor(errors); 131 | 132 | #if defined(PARTIAL) 133 | /* Add missing fields */ 134 | timelib_fill_holes(t, t_now, TIMELIB_NO_CLONE); 135 | #endif 136 | 137 | if (tzi) { 138 | #if defined(DONT_IGNORE_TZ) 139 | timelib_set_timezone(t, tzi); 140 | #endif 141 | 142 | timelib_update_ts(t, tzi); 143 | timelib_unixtime2local(t, t->sse); 144 | timelib_dump_date(t, 1); 145 | } else { 146 | timelib_update_ts(t, tzi_utc); 147 | timelib_dump_date(t, 1); 148 | } 149 | 150 | timelib_set_timezone(t, tzi_utc); 151 | timelib_unixtime2local(t, t->sse); 152 | 153 | /* Show Unix timestamp */ 154 | timelib_dump_date(t, 1); 155 | printf("Timestamp: %lld\n", (t->sse * 1000) + (int) (t->us / 1000.0)); 156 | 157 | timelib_time_dtor(t); 158 | #if defined(PARTIAL) 159 | timelib_time_dtor(t_now); 160 | #endif 161 | if (tzi) { 162 | timelib_tzinfo_dtor(tzi); 163 | } 164 | timelib_tzinfo_dtor(tzi_utc); 165 | 166 | cleanup_cache(); 167 | } 168 | -------------------------------------------------------------------------------- /zones/create-timezonedb.php: -------------------------------------------------------------------------------- 1 | 3 ) { 21 | $comments = $fields[3]; 22 | } 23 | 24 | // format lang/lat 25 | if ( strlen( $coordinates ) == 11 ) 26 | { 27 | sscanf( $coordinates, '%c%2d%2d%c%3d%2d', $xSign, $xH, $xM, $ySign, $yH, $yM ); 28 | $xS = $yS = 0; 29 | } 30 | else 31 | { 32 | sscanf( $coordinates, '%c%2d%2d%2d%c%3d%2d%2d', $xSign, $xH, $xM, $xS, $ySign, $yH, $yM, $yS ); 33 | } 34 | $lat = $xH + ( $xM / 60 ) + ( $xS / 3600 ); 35 | $long = $yH + ( $yM / 60 ) + ( $yS / 3600 ); 36 | $lat = $xSign == '+' ? $lat : -$lat; 37 | $long = $ySign == '+' ? $long : -$long; 38 | 39 | $tab[$tzid] = [ 40 | 'cc' => $countryCode, 41 | 'lat' => ($lat + 90) * 100000, 42 | 'lng' => ($long + 180) * 100000, 43 | 'desc' => $comments ?: '', 44 | ]; 45 | } 46 | 47 | return $tab; 48 | } 49 | 50 | function readDataFile( string $tzid, array $tab ) : array 51 | { 52 | $originalHeaderSize = 20; 53 | 54 | // obtain data from tz files 55 | $fatData = file_get_contents( "code/data/{$tzid}", false, NULL, $originalHeaderSize ); 56 | $slimData = file_get_contents( "code/data-slim/{$tzid}", false, NULL, $originalHeaderSize ); 57 | 58 | $tabData = array_key_exists( $tzid, $tab ) ? $tab[$tzid] : null; 59 | 60 | if ( $tabData ) { 61 | $header = pack( 'a4ca2a13', 'PHP2', true, $tabData['cc'], '' ); 62 | $footer = pack( 'NNNa*', $tabData['lat'], $tabData['lng'], strlen( $tabData['desc'] ), $tabData['desc'] ); 63 | } else { 64 | $header = pack( 'a4ca2a13', 'PHP2', $tzid == 'UTC', '??', '' ); 65 | $footer = pack( 'NNNa*', 0, 0, 0, '' ); 66 | } 67 | 68 | $fatData = $header . $fatData . $footer; 69 | $slimData = $header . $slimData . $footer; 70 | 71 | return [ $fatData, $slimData ]; 72 | } 73 | 74 | function createDataAndIndex(array $tab) 75 | { 76 | $files = array_merge(glob("code/data/*"), glob("code/data/*/*"), glob("code/data/*/*/*")); 77 | usort($files, 'strcasecmp'); 78 | 79 | $fatIndex = []; 80 | $slimIndex = []; 81 | $fatData = $slimData = ''; 82 | 83 | foreach ($files as $fileName) { 84 | if (is_dir($fileName)) { 85 | continue; 86 | } 87 | 88 | $tzid = preg_replace('@code/data/@', '', $fileName); 89 | list($tzFatData, $tzSlimData) = readDataFile($tzid, $tab); 90 | 91 | $fatIndex[$tzid] = strlen($fatData); 92 | $slimIndex[$tzid] = strlen($slimData); 93 | 94 | $fatData .= $tzFatData; 95 | $slimData .= $tzSlimData; 96 | } 97 | 98 | return [$fatIndex, $fatData, $slimIndex, $slimData]; 99 | } 100 | 101 | function getVersion() : string 102 | { 103 | $version_info = file( 'version-info.txt' ); 104 | $version = trim( $version_info[1] ); 105 | 106 | return $version; 107 | } 108 | 109 | function addData(array $index, string $data) 110 | { 111 | $elements = count( $index ); 112 | $flipIndex = array_flip( $index ); 113 | $dataSize = strlen( $data ); 114 | 115 | $str = "const timelib_tzdb_index_entry timezonedb_idx_builtin[$elements] = {\n"; 116 | foreach ( $index as $tzid => $start ) 117 | { 118 | $str .= sprintf( "\t{ (char*) %-36s, 0x%06X },\n", "\"{$tzid}\"", $start ); 119 | } 120 | $str .= "};\n"; 121 | 122 | $pos = 0; 123 | 124 | $str .= "\n\nconst unsigned char timelib_timezone_db_data_builtin[{$dataSize}] = {"; 125 | for ( $i = 0; $i < $dataSize; $i++ ) 126 | { 127 | if ( isset( $flipIndex[$i] ) ) 128 | { 129 | $str .= "\n\n/* {$flipIndex[$i] } */\n"; 130 | $pos = 0; 131 | } 132 | $str .= sprintf( "0x%02X,", ord( $data[$i] ) ); 133 | if ( $pos % 16 == 15) { 134 | $str .= "\n"; 135 | } else { 136 | $str .= " "; 137 | } 138 | $pos++; 139 | } 140 | $str .= "\n};\n"; 141 | 142 | return $str; 143 | } 144 | 145 | function writeTimelibH(array $fatIndex, string $fatData, array $slimIndex, string $slimData ) 146 | { 147 | $elements = count( $fatIndex ); 148 | 149 | $data = "/* This is a generated file, do not modify */\n"; 150 | 151 | /* Error out if TIMELIB_SUPPORTS_V2DATA isn't set */ 152 | $data .= "#ifndef TIMELIB_SUPPORTS_V2DATA\n"; 153 | $data .= "#error Your version of timelib does not understand the timzonedb data format, please upgrade\n"; 154 | $data .= "#endif\n\n"; 155 | 156 | $data .= "#ifdef TIMELIB_SUPPORT_SLIM_FILE\n"; 157 | $data .= addData( $slimIndex, $slimData ); 158 | $data .= "#else\n"; 159 | $data .= addData( $fatIndex, $fatData ); 160 | $data .= "#endif\n\n"; 161 | 162 | $version = getVersion(); 163 | $data .= "const timelib_tzdb timezonedb_builtin = { \"{$version}\", {$elements}, timezonedb_idx_builtin, timelib_timezone_db_data_builtin };\n"; 164 | 165 | file_put_contents( 'timezonedb.h', $data ); 166 | } 167 | 168 | $tab = readZoneTab( 'code/zone.tab' ); 169 | list( $fatIndex, $fatData, $slimIndex, $slimData ) = createDataAndIndex( $tab ); 170 | writeTimelibH( $fatIndex, $fatData, $slimIndex, $slimData ); 171 | ?> 172 | -------------------------------------------------------------------------------- /zones/Makefile: -------------------------------------------------------------------------------- 1 | all: index 2 | 3 | download: 4 | -@rm -f tzdata20*tar.gz 5 | -@rm -f tzcode20*tar.gz 6 | @echo "Downloading latest Olson TZDB release..." 7 | $(eval VERSION := $(shell curl -s -o - https://www.iana.org/time-zones | grep \"version\" | sed 's/.*version">//' | sed 's/<\/span.*//')) 8 | curl -s -o tzdata$(VERSION).tar.gz https://data.iana.org/time-zones/releases/tzdata$(VERSION).tar.gz 9 | curl -s -o tzcode$(VERSION).tar.gz https://data.iana.org/time-zones/releases/tzcode$(VERSION).tar.gz 10 | @echo " done" 11 | 12 | clean: release-php-clean 13 | -rm -rf code 14 | -rm -f timezonedb.idx.php timezonedb.dta timezonedb.idx version-info.txt timezonedb-20[12]*.tgz timezonedb.tgz timezonedb*.zip 15 | 16 | tzdb: download 17 | -@rm -rf code 18 | @mkdir code 19 | @echo "Unpacking Olson TZDB release..." 20 | tar -C code -xzf tzdata*tar.gz 21 | tar -C code -xzf tzcode*tar.gz 22 | 23 | code/zone.tab: tzdb 24 | @make PACKRATDATA=backzone PACKRATLIST=zone.tab -C code 25 | @echo "Compiling tzdata files..." 26 | cat code/main.zi | ./code/zic -b fat -d code/data - 27 | cat code/main.zi | ./code/zic -b slim -d code/data-slim - 28 | 29 | version-info.txt: download 30 | find . -name tzdata*.tar.gz | sed 's/.*202/202/' | sed 's/\.tar.*//' > version-info.txt 31 | find . -name tzdata*.tar.gz | sed -E "s/^.*(20[0-9]{2}).*$$/\1/" | tr -d '\n' >> version-info.txt 32 | echo -n "." >> version-info.txt 33 | find . -name tzdata*.tar.gz | sed -E "s/^.*20[0-9]{2}(\w).*$$/\1/" | sed "s/\\n//" | od -A n -t d1 -N 1 | awk '{printf "%s", $$1 - 96}' >> version-info.txt 34 | 35 | timezonedb.zip: code/zone.tab version-info.txt 36 | @echo -n "Making archive..." 37 | -@mkdir code/timezonedb-$(VERSION); cp -r code/data/. code/timezonedb-$(VERSION)/; cp version-info.txt code/timezonedb-$(VERSION)/ 38 | -@cd code; zip --quiet -r ../timezonedb.zip timezonedb-$(VERSION) 39 | @cp timezonedb.zip timezonedb-$(VERSION).zip 40 | @echo " done" 41 | 42 | timezonedb.h: code/zone.tab version-info.txt create-timezonedb.php 43 | php create-timezonedb.php 44 | cp timezonedb.h .. 45 | 46 | index: timezonedb.h 47 | 48 | timezonedb.tgz: index 49 | $(eval VERSION := $(shell cat version-info.txt | tail -n 1)) 50 | cp timezonedb.h ~/dev/php/pecl-datetime-timezonedb 51 | php update-package-xml.php ~/dev/php/pecl-datetime-timezonedb 52 | php update-package-version.php ~/dev/php/pecl-datetime-timezonedb 53 | pecl package ~/dev/php/pecl-datetime-timezonedb/package.xml 54 | cp timezonedb-$(VERSION).tgz timezonedb.tgz 55 | 56 | release-pecl: timezonedb.tgz 57 | $(eval VERSION := $(shell cat version-info.txt | tail -n 1)) 58 | $(eval TZVERSION := $(shell cat version-info.txt | head -n 1)) 59 | cd ~/dev/php/pecl-datetime-timezonedb; git commit -m "Updated to version $(VERSION) ($(TZVERSION))" timezonedb.c timezonedb.h package.xml php_timezonedb.h 60 | cd ~/dev/php/pecl-datetime-timezonedb; git tag -s "$(VERSION)" -m "Go with $(VERSION)" 61 | cd ~/dev/php/pecl-datetime-timezonedb; git push origin master "$(VERSION)" 62 | 63 | release-docs: timezonedb.tgz 64 | $(eval VERSION := $(shell cat version-info.txt | tail -n 1)) 65 | $(eval TZVERSION := $(shell cat version-info.txt | head -n 1)) 66 | -pecl upgrade -f timezonedb.tgz 67 | cd ~/dev/php/phpdoc/en; git pull 68 | cd ~/dev/php/phpdoc/en/reference/datetime/; php -dextension=timezonedb.so ../../../doc-base/scripts/gen-phpdoc-tz-list.php > timezones.xml 69 | cd ~/dev/php/phpdoc/en/reference/datetime/; git commit -m "Updated to version $(VERSION) ($(TZVERSION))" timezones.xml && git push 70 | 71 | release-php-clean: 72 | -rm -rf /tmp/tz-tmp 73 | 74 | release-php-clone: release-php-clean 75 | git clone git@github.com:/php/php-src /tmp/tz-tmp 76 | 77 | release-php-commit-83: timezonedb.tgz 78 | $(eval VERSION := $(shell cat version-info.txt | tail -n 1)) 79 | $(eval TZVERSION := $(shell cat version-info.txt | head -n 1)) 80 | cd /tmp/tz-tmp; git checkout PHP-8.3 81 | cp timezonedb.h /tmp/tz-tmp/ext/date/lib 82 | cd /tmp/tz-tmp; git commit -m "Updated to version $(VERSION) ($(TZVERSION))" ext/date/lib/timezonedb.h 83 | 84 | release-php-commit-84: timezonedb.tgz release-php-commit-83 85 | $(eval VERSION := $(shell cat version-info.txt | tail -n 1)) 86 | $(eval TZVERSION := $(shell cat version-info.txt | head -n 1)) 87 | cd /tmp/tz-tmp; git checkout PHP-8.4 88 | cd /tmp/tz-tmp; git merge PHP-8.3 --strategy=ours -m "Empty merge" 89 | cp timezonedb.h /tmp/tz-tmp/ext/date/lib 90 | cd /tmp/tz-tmp; git commit -m "Updated to version $(VERSION) ($(TZVERSION))" ext/date/lib/timezonedb.h 91 | 92 | release-php-commit-master: timezonedb.tgz release-php-commit-84 93 | $(eval VERSION := $(shell cat version-info.txt | tail -n 1)) 94 | $(eval TZVERSION := $(shell cat version-info.txt | head -n 1)) 95 | cd /tmp/tz-tmp; git checkout master 96 | cd /tmp/tz-tmp; git merge PHP-8.4 --strategy=ours -m "Empty merge" 97 | cp timezonedb.h /tmp/tz-tmp/ext/date/lib 98 | cd /tmp/tz-tmp; git commit -m "Updated to version $(VERSION) ($(TZVERSION))" ext/date/lib/timezonedb.h 99 | 100 | release-php-commit: release-php-clone release-php-commit-83 release-php-commit-84 release-php-commit-master 101 | 102 | release-php-push: release-php-commit 103 | cd /tmp/tz-tmp; git push origin PHP-8.3 PHP-8.4 master 104 | 105 | release-php: release-php-clean release-php-clone release-php-commit release-php-push 106 | -------------------------------------------------------------------------------- /tests/c/timezones_same.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | 5 | TEST_GROUP(timezone_same) 6 | { 7 | int same; 8 | timelib_time *t1, *t2; 9 | timelib_tzinfo *tzi1, *tzi2; 10 | int dummy_error; 11 | 12 | TEST_SETUP() 13 | { 14 | same = 0; 15 | t1 = t2 = NULL; 16 | tzi1 = NULL; 17 | tzi2 = NULL; 18 | } 19 | 20 | void test_parse(const char *str1, const char *str2) 21 | { 22 | t1 = timelib_strtotime(str1, strlen(str1), NULL, timelib_builtin_db(), timelib_parse_tzfile); 23 | t2 = timelib_strtotime(str2, strlen(str2), NULL, timelib_builtin_db(), timelib_parse_tzfile); 24 | 25 | same = timelib_same_timezone(t1, t2); 26 | } 27 | 28 | void test_parse_with_tz(const char *str1, const char *str2, const char *tz1, const char *tz2) 29 | { 30 | t1 = timelib_strtotime(str1, strlen(str1), NULL, timelib_builtin_db(), timelib_parse_tzfile); 31 | t2 = timelib_strtotime(str2, strlen(str2), NULL, timelib_builtin_db(), timelib_parse_tzfile); 32 | 33 | if (tz1) { 34 | tzi1 = timelib_parse_tzfile((char*) tz1, timelib_builtin_db(), &dummy_error); 35 | timelib_update_ts(t1, tzi1); 36 | } 37 | if (tz2) { 38 | tzi2 = timelib_parse_tzfile((char*) tz2, timelib_builtin_db(), &dummy_error); 39 | timelib_update_ts(t2, tzi2); 40 | } 41 | 42 | same = timelib_same_timezone(t1, t2); 43 | } 44 | 45 | TEST_TEARDOWN() 46 | { 47 | if (t1) { 48 | timelib_time_dtor(t1); 49 | } 50 | if (t2) { 51 | timelib_time_dtor(t2); 52 | } 53 | if (tzi1) { 54 | timelib_tzinfo_dtor(tzi1); 55 | } 56 | if (tzi2) { 57 | timelib_tzinfo_dtor(tzi2); 58 | } 59 | } 60 | }; 61 | 62 | #define CHECK_SAME(s,ty1,ty2) \ 63 | LONGS_EQUAL(s, same); \ 64 | LONGS_EQUAL(ty1, t1->zone_type); \ 65 | LONGS_EQUAL(ty2, t2->zone_type); 66 | 67 | TEST(timezone_same, type1_type1_same1) 68 | { 69 | test_parse("2021-11-05 11:23:39 GMT+0100", "2021-11-05 11:24:07 GMT+0100"); 70 | CHECK_SAME(1, TIMELIB_ZONETYPE_OFFSET, TIMELIB_ZONETYPE_OFFSET); 71 | } 72 | 73 | TEST(timezone_same, type1_type1_notsame1) 74 | { 75 | test_parse("2021-11-05 11:23:39 GMT+0200", "2021-11-05 11:24:07 GMT+0100"); 76 | CHECK_SAME(0, TIMELIB_ZONETYPE_OFFSET, TIMELIB_ZONETYPE_OFFSET); 77 | } 78 | 79 | TEST(timezone_same, type1_type1_notsame2) 80 | { 81 | test_parse("2021-11-05 11:23:39 GMT+0100", "2021-11-05 11:24:07 GMT+0200"); 82 | CHECK_SAME(0, TIMELIB_ZONETYPE_OFFSET, TIMELIB_ZONETYPE_OFFSET); 83 | } 84 | 85 | 86 | TEST(timezone_same, type2_type2_same1) 87 | { 88 | test_parse("2021-11-05 11:23:39 CET", "2021-11-05 11:24:07 CET"); 89 | CHECK_SAME(1, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ABBR); 90 | } 91 | 92 | TEST(timezone_same, type2_type2_same2) 93 | { 94 | test_parse("2021-11-05 11:23:39 BST", "2021-11-05 11:24:07 CET"); 95 | CHECK_SAME(1, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ABBR); 96 | } 97 | 98 | TEST(timezone_same, type2_type2_same3) 99 | { 100 | test_parse("2021-11-05 11:23:39 CDT", "2021-11-05 11:24:07 EST"); 101 | CHECK_SAME(1, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ABBR); 102 | } 103 | 104 | TEST(timezone_same, type2_type2_same4) 105 | { 106 | test_parse("2021-11-05 11:23:39 EST", "2021-11-05 11:24:07 CDT"); 107 | CHECK_SAME(1, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ABBR); 108 | } 109 | 110 | TEST(timezone_same, type2_type2_notsame1) 111 | { 112 | test_parse("2021-11-05 11:23:39 EDT", "2021-11-05 11:24:07 CDT"); 113 | CHECK_SAME(0, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ABBR); 114 | } 115 | 116 | TEST(timezone_same, type2_type2_notsame2) 117 | { 118 | test_parse("2021-11-05 11:23:39 CET", "2021-11-05 11:24:07 CEST"); 119 | CHECK_SAME(0, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ABBR); 120 | } 121 | 122 | 123 | TEST(timezone_same, type3_type3_same1) 124 | { 125 | test_parse_with_tz("2021-11-05 11:23:39", "2021-11-05 11:24:07", "Europe/Amsterdam", "Europe/Amsterdam"); 126 | CHECK_SAME(1, TIMELIB_ZONETYPE_ID, TIMELIB_ZONETYPE_ID); 127 | } 128 | 129 | TEST(timezone_same, type3_type3_notsame1) 130 | { 131 | test_parse_with_tz("2021-11-05 11:23:39", "2021-11-05 11:24:07", "Europe/Amsterdam", "America/Chicago"); 132 | CHECK_SAME(0, TIMELIB_ZONETYPE_ID, TIMELIB_ZONETYPE_ID); 133 | } 134 | 135 | 136 | TEST(timezone_same, type1_type2) 137 | { 138 | test_parse_with_tz("2021-11-05 11:23:39 GMT+0100", "2021-11-05 11:24:07 BST", NULL, NULL); 139 | CHECK_SAME(0, TIMELIB_ZONETYPE_OFFSET, TIMELIB_ZONETYPE_ABBR); 140 | } 141 | 142 | TEST(timezone_same, type1_type3) 143 | { 144 | test_parse_with_tz("2021-11-05 11:23:39 GMT+0100", "2021-11-05 11:24:07", NULL, "Europe/Berlin"); 145 | CHECK_SAME(0, TIMELIB_ZONETYPE_OFFSET, TIMELIB_ZONETYPE_ID); 146 | } 147 | 148 | TEST(timezone_same, type2_type1) 149 | { 150 | test_parse_with_tz("2021-11-05 11:23:39 CEST", "2021-11-05 11:24:07 GMT+0200", NULL, NULL); 151 | CHECK_SAME(0, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_OFFSET); 152 | } 153 | 154 | TEST(timezone_same, type2_type3) 155 | { 156 | test_parse_with_tz("2021-11-05 11:23:39 CET", "2021-11-05 11:24:07", NULL, "Europe/Berlin"); 157 | CHECK_SAME(0, TIMELIB_ZONETYPE_ABBR, TIMELIB_ZONETYPE_ID); 158 | } 159 | 160 | TEST(timezone_same, type3_type1) 161 | { 162 | test_parse_with_tz("2021-11-05 11:23:39", "2021-11-05 11:24:07 GMT+0200", "Europe/Berlin", NULL); 163 | CHECK_SAME(0, TIMELIB_ZONETYPE_ID, TIMELIB_ZONETYPE_OFFSET); 164 | } 165 | 166 | TEST(timezone_same, type3_type2) 167 | { 168 | test_parse_with_tz("2021-11-05 11:23:39", "2021-11-05 11:24:07 CET", "Europe/Berlin", NULL); 169 | CHECK_SAME(0, TIMELIB_ZONETYPE_ID, TIMELIB_ZONETYPE_ABBR); 170 | } 171 | -------------------------------------------------------------------------------- /timelib_private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2019 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #ifndef __TIMELIB_PRIVATE_H__ 26 | #define __TIMELIB_PRIVATE_H__ 27 | 28 | #ifdef HAVE_TIMELIB_CONFIG_H 29 | # include "timelib_config.h" 30 | #endif 31 | 32 | #ifdef HAVE_SYS_TIME_H 33 | # include 34 | #endif 35 | 36 | #ifdef _WIN32 37 | # ifdef HAVE_WINSOCK2_H 38 | # include 39 | # endif 40 | #endif 41 | 42 | #include 43 | 44 | #ifdef HAVE_SYS_TYPES_H 45 | #include 46 | #endif 47 | 48 | #include 49 | 50 | #if defined(HAVE_UNISTD_H) 51 | # include 52 | #endif 53 | 54 | #if defined(HAVE_IO_H) 55 | # include 56 | #endif 57 | 58 | #if defined(HAVE_DIRENT_H) 59 | # include 60 | #endif 61 | 62 | #include 63 | #include 64 | 65 | #define TIMELIB_SECOND 1 66 | #define TIMELIB_MINUTE 2 67 | #define TIMELIB_HOUR 3 68 | #define TIMELIB_DAY 4 69 | #define TIMELIB_MONTH 5 70 | #define TIMELIB_YEAR 6 71 | #define TIMELIB_WEEKDAY 7 72 | #define TIMELIB_SPECIAL 8 73 | #define TIMELIB_MICROSEC 9 74 | 75 | #define TIMELIB_SPECIAL_WEEKDAY 0x01 76 | #define TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH 0x02 77 | #define TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH 0x03 78 | 79 | #define TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH 0x01 80 | #define TIMELIB_SPECIAL_LAST_DAY_OF_MONTH 0x02 81 | 82 | #define TIMELIB_TIME_PART_DONT_KEEP 0x00 83 | #define TIMELIB_TIME_PART_KEEP 0x01 84 | 85 | #define MINS_PER_HOUR 60 86 | #define SECS_PER_ERA TIMELIB_LL_CONST(12622780800) 87 | #define SECS_PER_DAY 86400 88 | #define SECS_PER_HOUR 3600 89 | #define USECS_PER_HOUR TIMELIB_LL_CONST(3600000000) 90 | 91 | #define DAYS_PER_WEEK 7 92 | #define DAYS_PER_YEAR 365 93 | #define DAYS_PER_LYEAR 366 94 | #define MONTHS_PER_YEAR 12 95 | /* 400*365 days + 97 leap days */ 96 | #define DAYS_PER_ERA 146097 97 | #define YEARS_PER_ERA 400 98 | #define HINNANT_EPOCH_SHIFT 719468 /* 0000-03-01 instead of 1970-01-01 */ 99 | 100 | #define TIMELIB_TZINFO_PHP 0x01 101 | #define TIMELIB_TZINFO_ZONEINFO 0x02 102 | 103 | #define timelib_is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) 104 | 105 | #define TIMELIB_DEBUG(s) if (0) { s } 106 | 107 | #define TIMELIB_TIME_FREE(m) \ 108 | if (m) { \ 109 | timelib_free(m); \ 110 | m = NULL; \ 111 | } 112 | 113 | #if defined (__GNUC__) 114 | # define TIMELIB_GNUC_CHECK_VERSION(major, minor) \ 115 | ((__GNUC__ > (major)) || \ 116 | ((__GNUC__ == (major)) && (__GNUC_MINOR__ >= (minor)))) 117 | #else 118 | # define TIMELIB_GNUC_CHECK_VERSION(major, minor) 0 119 | #endif 120 | 121 | #if TIMELIB_GNUC_CHECK_VERSION(7, 0) 122 | # define TIMELIB_BREAK_INTENTIONALLY_MISSING __attribute__ ((fallthrough)); 123 | #else 124 | # define TIMELIB_BREAK_INTENTIONALLY_MISSING 125 | #endif 126 | 127 | #if defined(__has_builtin) 128 | # if __has_builtin(__builtin_saddll_overflow) 129 | # define TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW 1 130 | # endif 131 | #endif 132 | 133 | #ifndef TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW 134 | # define TIMELIB_HAVE_BUILTIN_SADDLL_OVERFLOW 0 135 | #endif 136 | 137 | struct _ttinfo 138 | { 139 | int32_t offset; 140 | int isdst; 141 | unsigned int abbr_idx; 142 | 143 | unsigned int isstdcnt; 144 | unsigned int isgmtcnt; 145 | }; 146 | 147 | struct _tlinfo 148 | { 149 | int64_t trans; 150 | int32_t offset; 151 | }; 152 | 153 | 154 | #ifndef LONG_MAX 155 | #define LONG_MAX 2147483647L 156 | #endif 157 | 158 | #ifndef LONG_MIN 159 | #define LONG_MIN (- LONG_MAX - 1) 160 | #endif 161 | 162 | #ifdef __cplusplus 163 | extern "C" { 164 | #endif 165 | 166 | /* From unixtime2tm.c */ 167 | int timelib_apply_localtime(timelib_time *t, unsigned int localtime); 168 | 169 | /* From parse_posix.c */ 170 | timelib_sll timelib_ts_at_start_of_year(timelib_sll year); 171 | ttinfo* timelib_fetch_posix_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time); 172 | 173 | /* From parse_tz.c */ 174 | void timelib_time_tz_abbr_update(timelib_time* tm, const char* tz_abbr); 175 | ttinfo* timelib_fetch_timezone_offset(timelib_tzinfo *tz, timelib_sll ts, timelib_sll *transition_time); 176 | 177 | /* From timelib.c */ 178 | int timelib_strcasecmp(const char *s1, const char *s2); 179 | int timelib_strncasecmp(const char *s1, const char *s2, size_t n); 180 | 181 | #ifdef __cplusplus 182 | } /* extern "C" */ 183 | #endif 184 | 185 | #endif 186 | -------------------------------------------------------------------------------- /tests/c/parse_tz.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include "timelib_private.h" 4 | #include 5 | 6 | TEST_GROUP(parse_tz) 7 | { 8 | }; 9 | 10 | TEST(parse_tz, new_york) 11 | { 12 | int error; 13 | timelib_tzinfo *tzi = timelib_parse_tzfile("America/New_York", timelib_builtin_db(), &error); 14 | CHECK(tzi != NULL); 15 | CHECK(error == TIMELIB_ERROR_NO_ERROR); 16 | STRCMP_EQUAL("US", tzi->location.country_code); 17 | DOUBLES_EQUAL( 40, tzi->location.latitude, 1); 18 | DOUBLES_EQUAL(-74, tzi->location.longitude, 1); 19 | STRCMP_EQUAL("EST5EDT,M3.2.0,M11.1.0", tzi->posix_string); 20 | CHECK(!!tzi->bc); 21 | timelib_tzinfo_dtor(tzi); 22 | } 23 | 24 | TEST(parse_tz, utc) 25 | { 26 | int error; 27 | timelib_tzinfo *tzi = timelib_parse_tzfile("UTC", timelib_builtin_db(), &error); 28 | CHECK(tzi != NULL); 29 | CHECK(error == TIMELIB_ERROR_NO_ERROR); 30 | STRCMP_EQUAL("??", tzi->location.country_code); 31 | DOUBLES_EQUAL( -90, tzi->location.latitude, 1); 32 | DOUBLES_EQUAL( -180, tzi->location.longitude, 1); 33 | STRCMP_EQUAL("UTC0", tzi->posix_string); 34 | LONGS_EQUAL(1, tzi->bit64.typecnt); 35 | CHECK(!!tzi->bc); 36 | timelib_tzinfo_dtor(tzi); 37 | } 38 | 39 | TEST(parse_tz, us_samoa) 40 | { 41 | int error; 42 | timelib_tzinfo *tzi = timelib_parse_tzfile("US/Samoa", timelib_builtin_db(), &error); 43 | CHECK(tzi != NULL); 44 | CHECK(error == TIMELIB_ERROR_NO_ERROR); 45 | STRCMP_EQUAL("SST11", tzi->posix_string); 46 | CHECK(!tzi->bc); 47 | timelib_tzinfo_dtor(tzi); 48 | } 49 | 50 | TEST(parse_tz, petersburg) 51 | { 52 | int error; 53 | timelib_tzinfo *tzi = timelib_parse_tzfile("America/Indiana/Petersburg", timelib_builtin_db(), &error); 54 | CHECK(tzi != NULL); 55 | CHECK(error == TIMELIB_ERROR_NO_ERROR); 56 | STRCMP_EQUAL("EST5EDT,M3.2.0,M11.1.0", tzi->posix_string); 57 | LONGS_EQUAL(7, tzi->bit64.typecnt); 58 | LONGS_EQUAL(6, tzi->posix_info->type_index_dst_type); 59 | timelib_tzinfo_dtor(tzi); 60 | } 61 | 62 | TEST(parse_tz, beulah) 63 | { 64 | int error; 65 | timelib_tzinfo *tzi = timelib_parse_tzfile("America/North_Dakota/Beulah", timelib_builtin_db(), &error); 66 | CHECK(tzi != NULL); 67 | CHECK(error == TIMELIB_ERROR_NO_ERROR); 68 | STRCMP_EQUAL("CST6CDT,M3.2.0,M11.1.0", tzi->posix_string); 69 | LONGS_EQUAL(7, tzi->bit64.typecnt); 70 | LONGS_EQUAL(6, tzi->posix_info->type_index_dst_type); 71 | STRCMP_EQUAL("CDT", &tzi->timezone_abbr[tzi->type[tzi->posix_info->type_index_dst_type].abbr_idx]); 72 | timelib_tzinfo_dtor(tzi); 73 | } 74 | 75 | static timelib_tzinfo *test_date_parse_tzfile(const char *formal_tzname, const timelib_tzdb *tzdb, int *dummy_error_code) 76 | { 77 | return timelib_parse_tzfile(formal_tzname, tzdb, dummy_error_code); 78 | } 79 | 80 | TEST(parse_tz, cst6cdt) 81 | { 82 | timelib_long tll; 83 | timelib_time *t = timelib_time_ctor(); 84 | const char *tz_name = "CST6CDT"; 85 | int is_dst; 86 | int tz_not_found; 87 | 88 | tll = timelib_parse_zone(&tz_name, &is_dst, t, &tz_not_found, timelib_builtin_db(), test_date_parse_tzfile); 89 | 90 | CHECK(t->tz_info != NULL); 91 | STRCMP_EQUAL(t->tz_info->name, "CST6CDT"); 92 | LONGS_EQUAL(6, t->tz_info->bit64.typecnt); 93 | LONGS_EQUAL(1, t->tz_info->posix_info->type_index_dst_type); 94 | LONGS_EQUAL(0, tll); 95 | 96 | timelib_tzinfo_dtor(t->tz_info); 97 | timelib_time_dtor(t); 98 | } 99 | 100 | TEST(parse_tz, etc_gmt_1) 101 | { 102 | timelib_long tll; 103 | timelib_time *t = timelib_time_ctor(); 104 | const char *tz_name = "Etc/GMT+7"; 105 | int is_dst; 106 | int tz_not_found; 107 | 108 | tll = timelib_parse_zone(&tz_name, &is_dst, t, &tz_not_found, timelib_builtin_db(), test_date_parse_tzfile); 109 | 110 | CHECK(t->tz_info != NULL); 111 | STRCMP_EQUAL(t->tz_info->name, "Etc/GMT+7"); 112 | LONGS_EQUAL(1, t->tz_info->bit64.typecnt); 113 | LONGS_EQUAL(0, t->tz_info->posix_info->type_index_dst_type); 114 | LONGS_EQUAL(0, tll); 115 | 116 | timelib_tzinfo_dtor(t->tz_info); 117 | timelib_time_dtor(t); 118 | } 119 | 120 | TEST(parse_tz, etc_gmt_2) 121 | { 122 | timelib_long tll; 123 | timelib_time *t = timelib_time_ctor(); 124 | const char *tz_name = "Etc/GMT-7"; 125 | int is_dst; 126 | int tz_not_found; 127 | 128 | tll = timelib_parse_zone(&tz_name, &is_dst, t, &tz_not_found, timelib_builtin_db(), test_date_parse_tzfile); 129 | 130 | CHECK(t->tz_info != NULL); 131 | STRCMP_EQUAL(t->tz_info->name, "Etc/GMT-7"); 132 | LONGS_EQUAL(1, t->tz_info->bit64.typecnt); 133 | LONGS_EQUAL(0, t->tz_info->posix_info->type_index_dst_type); 134 | LONGS_EQUAL(0, tll); 135 | 136 | timelib_tzinfo_dtor(t->tz_info); 137 | timelib_time_dtor(t); 138 | } 139 | 140 | TEST(parse_tz, corrupt_transitions_dont_increase_01) 141 | { 142 | const timelib_tzdb_index_entry index[1] = { 143 | { (char*) "dummy", 0 } 144 | }; 145 | const unsigned char data[] = { 146 | 'P', 'H', 'P', '2', 0, 'X', 'X', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 147 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 148 | 'T', 'Z', 'i', 'f', '2', 149 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 151 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 152 | }; 153 | const timelib_tzdb corrupt_tzdb = { "1", 1, index, data }; 154 | 155 | int error; 156 | timelib_tzinfo *tzi = timelib_parse_tzfile("dummy", &corrupt_tzdb, &error); 157 | CHECK(tzi == NULL); 158 | CHECK(error == TIMELIB_ERROR_CORRUPT_TRANSITIONS_DONT_INCREASE); 159 | } 160 | 161 | TEST(parse_tz, corrupt_transitions_dont_increase_02) 162 | { 163 | int error_code; 164 | timelib_tzinfo *tzi; 165 | timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); 166 | 167 | tzi = timelib_parse_tzfile((char*) "NonContinuous", test_directory, &error_code); 168 | 169 | CHECK(tzi == NULL); 170 | LONGS_EQUAL(TIMELIB_ERROR_CORRUPT_TRANSITIONS_DONT_INCREASE, error_code); 171 | 172 | timelib_zoneinfo_dtor(test_directory); 173 | } 174 | 175 | TEST(parse_tz, parentheses) 176 | { 177 | timelib_time *t = timelib_time_ctor(); 178 | const char *tz_name = "((UTC)))"; 179 | int is_dst; 180 | int tz_not_found; 181 | 182 | timelib_parse_zone(&tz_name, &is_dst, t, &tz_not_found, timelib_builtin_db(), test_date_parse_tzfile); 183 | 184 | CHECK(t->tz_info != NULL); 185 | STRCMP_EQUAL(t->tz_info->name, "UTC"); 186 | BYTES_EQUAL(*tz_name, ')'); 187 | 188 | timelib_tzinfo_dtor(t->tz_info); 189 | timelib_time_dtor(t); 190 | } 191 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FLAGS=-O0 -ggdb3 \ 2 | -fPIC \ 3 | -Wall -Werror -Wextra \ 4 | -Wempty-body \ 5 | -Wenum-compare \ 6 | -Wformat-nonliteral \ 7 | -Wformat-security \ 8 | -Wimplicit-fallthrough \ 9 | -Winit-self \ 10 | -Wlogical-not-parentheses \ 11 | -Wlogical-op \ 12 | -Wmaybe-uninitialized \ 13 | -Wmissing-field-initializers \ 14 | -Wmissing-format-attribute \ 15 | -Wno-unused-parameter \ 16 | -Wparentheses \ 17 | -Wpointer-arith \ 18 | -Wshadow \ 19 | -Wsign-compare \ 20 | -Wsizeof-array-argument \ 21 | -Wundef \ 22 | -Wunused-but-set-variable \ 23 | -Wvariadic-macros \ 24 | -Wwrite-strings \ 25 | -fdiagnostics-show-option \ 26 | -fno-exceptions \ 27 | -fno-omit-frame-pointer \ 28 | -fno-optimize-sibling-calls \ 29 | -fstack-protector \ 30 | -pedantic \ 31 | -DHAVE_GETTIMEOFDAY -DHAVE_UNISTD_H -DHAVE_DIRENT_H -I.# -DDEBUG_PARSER 32 | 33 | CFLAGS_BASE=-Wdeclaration-after-statement ${FLAGS} 34 | CFLAGS=-fsanitize=address -fsanitize=undefined ${CFLAGS_BASE} 35 | 36 | CPPFLAGS=-fsanitize=address -fsanitize=undefined ${FLAGS} 37 | 38 | LDFLAGS_BASE=-lm 39 | LDFLAGS=${LDFLAGS_BASE} -fsanitize=undefined -l:libubsan.so 40 | 41 | TEST_LDFLAGS=-lCppUTest 42 | 43 | CC=gcc 44 | CXX=g++ 45 | 46 | FILES=parse_iso_intervals.o parse_date.o unixtime2tm.o tm2unixtime.o dow.o parse_tz.o parse_zoneinfo.o timelib.o astro.o interval.o parse_posix.o 47 | 48 | MANUAL_TESTS=tests/tester-parse-interval \ 49 | tests/tester-iso-week tests/test-abbr-to-id \ 50 | tests/enumerate-timezones tests/date_from_isodate 51 | AUTO_TESTS=tests/tester-parse-string tests/tester-parse-string-by-format \ 52 | tests/tester-create-ts tests/tester-render-ts tests/tester-render-ts-zoneinfo 53 | C_TESTS=tests/c/timelib_get_current_offset_test.o tests/c/timelib_decimal_hour.o \ 54 | tests/c/timelib_juliandate.o \ 55 | tests/c/issues.o tests/c/issue0120.o \ 56 | tests/c/astro_rise_set_altitude.o \ 57 | tests/c/parse_date_from_format_test.o tests/c/parse_intervals.o \ 58 | tests/c/warn_on_slim.o tests/c/parse_posix.o tests/c/transitions.o \ 59 | tests/c/parse_tz.o tests/c/render.o tests/c/create_ts_from_string.o \ 60 | tests/c/parse_date.o tests/c/php-rfc.o tests/c/diff.o tests/c/interval.o \ 61 | tests/c/timezones_same.o tests/c/diff_days.o \ 62 | tests/c/timelib_hmsf_to_decimal_hour.o tests/c/dow.o \ 63 | tests/c/timelib_get_offset_info_test.o tests/c/add.o tests/c/sub.o 64 | 65 | TEST_BINARIES=${MANUAL_TESTS} ${AUTO_TESTS} ctest 66 | 67 | EXAMPLE_BINARIES=docs/date-from-iso-parts docs/date-from-parts docs/date-from-string \ 68 | docs/date-to-parts docs/show-tzinfo 69 | 70 | all: parse_date.o tm2unixtime.o unixtime2tm.o dow.o astro.o interval.o timelib.a timelib.so \ 71 | ${TEST_BINARIES} ${EXAMPLE_BINARIES} 72 | 73 | parse_date.c: timezonemap.h parse_date.re 74 | re2c -d -b parse_date.re --output parse_date.c 75 | 76 | parse_iso_intervals.c: parse_iso_intervals.re 77 | re2c -d -b parse_iso_intervals.re --output parse_iso_intervals.c 78 | 79 | timelib.a: ${FILES} 80 | ar -rc timelib.a ${FILES} 81 | 82 | timelib.so: ${FILES} 83 | $(CC) $(CFLAGS_BASE) -shared -o timelib.so ${FILES} $(LDFLAGS_BASE) 84 | 85 | tests/tester-diff: timelib.a tests/tester-diff.c 86 | $(CC) $(CFLAGS) -o tests/tester-diff tests/tester-diff.c timelib.a $(LDFLAGS) 87 | 88 | tests/tester-parse-string: timelib.a tests/tester-parse-string.c 89 | $(CC) $(CFLAGS) -o tests/tester-parse-string tests/tester-parse-string.c timelib.a $(LDFLAGS) 90 | 91 | tests/tester-parse-interval: timelib.a tests/tester-parse-interval.c 92 | $(CC) $(CFLAGS) -o tests/tester-parse-interval tests/tester-parse-interval.c timelib.a $(LDFLAGS) 93 | 94 | tests/tester-parse-string-by-format: timelib.a tests/tester-parse-string-by-format.c 95 | $(CC) $(CFLAGS) -o tests/tester-parse-string-by-format tests/tester-parse-string-by-format.c timelib.a $(LDFLAGS) 96 | 97 | tests/tester-create-ts: timelib.a tests/tester-create-ts.c 98 | $(CC) $(CFLAGS) -o tests/tester-create-ts tests/tester-create-ts.c timelib.a $(LDFLAGS) 99 | 100 | tests/tester-render-ts: timelib.a tests/tester-render-ts.c 101 | $(CC) $(CFLAGS) -o tests/tester-render-ts tests/tester-render-ts.c timelib.a $(LDFLAGS) 102 | 103 | tests/tester-render-ts-zoneinfo: timelib.a tests/tester-render-ts-zoneinfo.c 104 | $(CC) $(CFLAGS) -o tests/tester-render-ts-zoneinfo tests/tester-render-ts-zoneinfo.c timelib.a $(LDFLAGS) 105 | 106 | tests/tester-iso-week: timelib.a tests/tester-iso-week.c 107 | $(CC) $(CFLAGS) -o tests/tester-iso-week tests/tester-iso-week.c timelib.a $(LDFLAGS) 108 | 109 | tests/test-abbr-to-id: timelib.a tests/test-abbr-to-id.c 110 | $(CC) $(CFLAGS) -o tests/test-abbr-to-id tests/test-abbr-to-id.c timelib.a $(LDFLAGS) 111 | 112 | tests/test-astro: timelib.a tests/test-astro.c 113 | $(CC) $(CFLAGS) -o tests/test-astro tests/test-astro.c timelib.a -lm $(LDFLAGS) 114 | 115 | tests/enumerate-timezones: timelib.a tests/enumerate-timezones.c 116 | $(CC) $(CFLAGS) -o tests/enumerate-timezones tests/enumerate-timezones.c timelib.a $(LDFLAGS) 117 | 118 | tests/date_from_isodate: timelib.a tests/date_from_isodate.c 119 | $(CC) $(CFLAGS) -o tests/date_from_isodate tests/date_from_isodate.c timelib.a $(LDFLAGS) 120 | 121 | 122 | docs/date-from-parts: timelib.a docs/date-from-parts.c 123 | $(CC) $(CFLAGS) -o docs/date-from-parts docs/date-from-parts.c timelib.a $(LDFLAGS) 124 | 125 | docs/date-from-iso-parts: timelib.a docs/date-from-iso-parts.c 126 | $(CC) $(CFLAGS) -o docs/date-from-iso-parts docs/date-from-iso-parts.c timelib.a $(LDFLAGS) 127 | 128 | docs/date-from-string: timelib.a docs/date-from-string.c 129 | $(CC) $(CFLAGS) -o docs/date-from-string docs/date-from-string.c timelib.a $(LDFLAGS) 130 | 131 | docs/date-to-parts: timelib.a docs/date-to-parts.c 132 | $(CC) $(CFLAGS) -o docs/date-to-parts docs/date-to-parts.c timelib.a $(LDFLAGS) 133 | 134 | docs/show-tzinfo: timelib.a docs/show-tzinfo.c 135 | $(CC) $(CFLAGS) -o docs/show-tzinfo docs/show-tzinfo.c timelib.a $(LDFLAGS) 136 | 137 | 138 | timezonemap.h: gettzmapping.php 139 | echo Generating timezone mapping file. 140 | php gettzmapping.php > timezonemap.h 141 | 142 | clean-all: clean 143 | rm -f timezonemap.h 144 | 145 | clean: 146 | rm -f parse_iso_intervals.c parse_date.c *.o tests/c/*.o timelib.a timelib.so ${TEST_BINARIES} 147 | 148 | #%.o: %.cpp timelib.a 149 | # $(CXX) -c $(CPPFLAGS) $(LDFLAGS) $< -o $@ 150 | 151 | ctest: tests/c/all_tests.cpp timelib.a ${C_TESTS} 152 | $(CXX) $(CPPFLAGS) $(LDFLAGS) tests/c/all_tests.cpp ${C_TESTS} $(TEST_LDFLAGS) timelib.a -o ctest 153 | 154 | test: ctest 155 | @./ctest -c 156 | 157 | package: clean 158 | tar -cvzf parse_date.tar.gz parse_date.re Makefile tests 159 | -------------------------------------------------------------------------------- /dow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2019 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include "timelib.h" 26 | #include "timelib_private.h" 27 | 28 | static int m_table_common[13] = { -1, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 }; /* 1 = jan */ 29 | static int m_table_leap[13] = { -1, 6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 }; /* 1 = jan */ 30 | 31 | static timelib_sll positive_mod(timelib_sll x, timelib_sll y) 32 | { 33 | timelib_sll tmp; 34 | 35 | tmp = x % y; 36 | if (tmp < 0) { 37 | tmp += y; 38 | } 39 | 40 | return tmp; 41 | } 42 | 43 | static timelib_sll century_value(timelib_sll j) 44 | { 45 | return 6 - positive_mod(j, 4) * 2; 46 | } 47 | 48 | static timelib_sll timelib_day_of_week_ex(timelib_sll y, timelib_sll m, timelib_sll d, int iso) 49 | { 50 | timelib_sll c1, y1, m1, dow; 51 | 52 | /* Only valid for Gregorian calendar, commented out as we don't handle 53 | * Julian calendar. We just return the 'wrong' day of week to be 54 | * consistent. */ 55 | c1 = century_value(positive_mod(y, 400) / 100); 56 | y1 = positive_mod(y, 100); 57 | m1 = timelib_is_leap(y) ? m_table_leap[m] : m_table_common[m]; 58 | dow = positive_mod((c1 + y1 + m1 + (y1 / 4) + d), 7); 59 | if (iso) { 60 | if (dow == 0) { 61 | dow = 7; 62 | } 63 | } 64 | return dow; 65 | } 66 | 67 | timelib_sll timelib_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d) 68 | { 69 | return timelib_day_of_week_ex(y, m, d, 0); 70 | } 71 | 72 | timelib_sll timelib_iso_day_of_week(timelib_sll y, timelib_sll m, timelib_sll d) 73 | { 74 | return timelib_day_of_week_ex(y, m, d, 1); 75 | } 76 | 77 | /* jan feb mar apr may jun jul aug sep oct nov dec */ 78 | static int d_table_common[13] = { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 79 | static int d_table_leap[13] = { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; 80 | static int ml_table_common[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 81 | static int ml_table_leap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 82 | 83 | timelib_sll timelib_day_of_year(timelib_sll y, timelib_sll m, timelib_sll d) 84 | { 85 | return (timelib_is_leap(y) ? d_table_leap[m] : d_table_common[m]) + d - 1; 86 | } 87 | 88 | timelib_sll timelib_days_in_month(timelib_sll y, timelib_sll m) 89 | { 90 | return timelib_is_leap(y) ? ml_table_leap[m] : ml_table_common[m]; 91 | } 92 | 93 | void timelib_isoweek_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iw, timelib_sll *iy) 94 | { 95 | int y_leap, prev_y_leap, doy, jan1weekday, weekday; 96 | 97 | y_leap = timelib_is_leap(y); 98 | prev_y_leap = timelib_is_leap(y-1); 99 | doy = timelib_day_of_year(y, m, d) + 1; 100 | if (y_leap && m > 2) { 101 | doy++; 102 | } 103 | jan1weekday = timelib_day_of_week(y, 1, 1); 104 | weekday = timelib_day_of_week(y, m, d); 105 | if (weekday == 0) weekday = 7; 106 | if (jan1weekday == 0) jan1weekday = 7; 107 | /* Find if Y M D falls in YearNumber Y-1, WeekNumber 52 or 53 */ 108 | if (doy <= (8 - jan1weekday) && jan1weekday > 4) { 109 | *iy = y - 1; 110 | if (jan1weekday == 5 || (jan1weekday == 6 && prev_y_leap)) { 111 | *iw = 53; 112 | } else { 113 | *iw = 52; 114 | } 115 | } else { 116 | *iy = y; 117 | } 118 | /* 8. Find if Y M D falls in YearNumber Y+1, WeekNumber 1 */ 119 | if (*iy == y) { 120 | int i; 121 | 122 | i = y_leap ? 366 : 365; 123 | if ((i - (doy - y_leap)) < (4 - weekday)) { 124 | *iy = y + 1; 125 | *iw = 1; 126 | return; 127 | } 128 | } 129 | /* 9. Find if Y M D falls in YearNumber Y, WeekNumber 1 through 53 */ 130 | if (*iy == y) { 131 | int j; 132 | 133 | j = doy + (7 - weekday) + (jan1weekday - 1); 134 | *iw = j / 7; 135 | if (jan1weekday > 4) { 136 | *iw -= 1; 137 | } 138 | } 139 | } 140 | 141 | void timelib_isodate_from_date(timelib_sll y, timelib_sll m, timelib_sll d, timelib_sll *iy, timelib_sll *iw, timelib_sll *id) 142 | { 143 | timelib_isoweek_from_date(y, m, d, iw, iy); 144 | *id = timelib_day_of_week_ex(y, m, d, 1); 145 | } 146 | 147 | timelib_sll timelib_daynr_from_weeknr(timelib_sll iy, timelib_sll iw, timelib_sll id) 148 | { 149 | timelib_sll dow, day; 150 | 151 | /* Figure out the dayofweek for y-1-1 */ 152 | dow = timelib_day_of_week(iy, 1, 1); 153 | /* then use that to figure out the offset for day 1 of week 1 */ 154 | day = 0 - (dow > 4 ? dow - 7 : dow); 155 | 156 | /* Add weeks and days */ 157 | return day + ((iw - 1) * 7) + id; 158 | } 159 | 160 | void timelib_date_from_isodate(timelib_sll iy, timelib_sll iw, timelib_sll id, timelib_sll *y, timelib_sll *m, timelib_sll *d) 161 | { 162 | timelib_sll daynr = timelib_daynr_from_weeknr(iy, iw, id) + 1; 163 | int *table; 164 | bool is_leap_year; 165 | 166 | // Invariant: is_leap_year == timelib_is_leap(*y) 167 | *y = iy; 168 | is_leap_year = timelib_is_leap(*y); 169 | 170 | // Establish invariant that daynr >= 0 171 | while (daynr <= 0) { 172 | *y -= 1; 173 | daynr += (is_leap_year = timelib_is_leap(*y)) ? 366 : 365; 174 | } 175 | 176 | // Establish invariant that daynr <= number of days in *yr 177 | while (daynr > (is_leap_year ? 366 : 365)) { 178 | daynr -= is_leap_year ? 366 : 365; 179 | *y += 1; 180 | is_leap_year = timelib_is_leap(*y); 181 | } 182 | 183 | table = is_leap_year ? ml_table_leap : ml_table_common; 184 | 185 | // Establish invariant that daynr <= number of days in *m 186 | *m = 1; 187 | while (daynr > table[*m]) { 188 | daynr -= table[*m]; 189 | *m += 1; 190 | } 191 | 192 | *d = daynr; 193 | } 194 | 195 | int timelib_valid_time(timelib_sll h, timelib_sll i, timelib_sll s) 196 | { 197 | if (h < 0 || h > 23 || i < 0 || i > 59 || s < 0 || s > 59) { 198 | return 0; 199 | } 200 | return 1; 201 | } 202 | 203 | int timelib_valid_date(timelib_sll y, timelib_sll m, timelib_sll d) 204 | { 205 | if (m < 1 || m > 12 || d < 1 || d > timelib_days_in_month(y, m)) { 206 | return 0; 207 | } 208 | return 1; 209 | } 210 | #if 0 211 | int main(void) 212 | { 213 | printf("dow = %d\n", timelib_day_of_week(1978, 12, 22)); /* 5 */ 214 | printf("dow = %d\n", timelib_day_of_week(2005, 2, 19)); /* 6 */ 215 | } 216 | #endif 217 | -------------------------------------------------------------------------------- /tests/c/render.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | 5 | extern timelib_tzdb *zoneinfo; 6 | 7 | TEST_GROUP(render) 8 | { 9 | }; 10 | 11 | TEST_GROUP(render_zoneinfo) 12 | { 13 | }; 14 | 15 | #define TIMELIB_LLABS(y) (y < 0 ? (y * -1) : y) 16 | 17 | char *render_date(timelib_time *d) 18 | { 19 | char *tmp = (char*) calloc(1, 1024); 20 | sprintf(tmp, "%s%04lld-%02lld-%02lld %02lld:%02lld:%02lld.%06lld %s", 21 | d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s, d->us, 22 | d->zone_type == TIMELIB_ZONETYPE_OFFSET 23 | ? "GMT" 24 | : (d->tz_abbr ? d->tz_abbr : "??") 25 | ); 26 | 27 | return tmp; 28 | } 29 | 30 | #define RENDER_TEST(n,s,ts,tzid) \ 31 | TEST(render, n) { \ 32 | int dummy_error; \ 33 | timelib_tzinfo *tzi = timelib_parse_tzfile(tzid, timelib_builtin_db(), &dummy_error); \ 34 | LONGS_EQUAL(TIMELIB_ERROR_NO_ERROR, dummy_error); \ 35 | CHECK(tzi != NULL); \ 36 | timelib_time *t = timelib_time_ctor(); \ 37 | CHECK(t != NULL); \ 38 | timelib_set_timezone(t, tzi); \ 39 | timelib_unixtime2local(t, ts); \ 40 | char *rendered = render_date(t); \ 41 | STRCMP_EQUAL(s, rendered); \ 42 | timelib_time_dtor(t); \ 43 | timelib_tzinfo_dtor(tzi); \ 44 | free(rendered); \ 45 | } \ 46 | \ 47 | TEST(render_zoneinfo, n) { \ 48 | int dummy_error; \ 49 | timelib_tzinfo *tzi = timelib_parse_tzfile(tzid, zoneinfo, &dummy_error); \ 50 | LONGS_EQUAL(TIMELIB_ERROR_NO_ERROR, dummy_error); \ 51 | CHECK(tzi != NULL); \ 52 | timelib_time *t = timelib_time_ctor(); \ 53 | CHECK(t != NULL); \ 54 | timelib_set_timezone(t, tzi); \ 55 | timelib_unixtime2local(t, ts); \ 56 | char *rendered = render_date(t); \ 57 | STRCMP_EQUAL(s, rendered); \ 58 | timelib_time_dtor(t); \ 59 | timelib_tzinfo_dtor(tzi); \ 60 | free(rendered); \ 61 | } \ 62 | 63 | RENDER_TEST(php_bug_30532_01, "2004-10-31 00:00:00.000000 EDT", 1099195200, "America/New_York") 64 | RENDER_TEST(php_bug_30532_02, "2004-10-31 01:00:00.000000 EDT", 1099198800, "America/New_York") 65 | RENDER_TEST(php_bug_30532_03, "2004-10-31 02:00:00.000000 EST", 1099206000, "America/New_York") 66 | 67 | RENDER_TEST(php_bug_32086_01, "2004-11-01 00:00:00.000000 -03", 1099278000, "America/Sao_Paulo") 68 | RENDER_TEST(php_bug_32086_02, "2004-11-01 23:00:00.000000 -03", 1099360800, "America/Sao_Paulo") 69 | RENDER_TEST(php_bug_32086_03, "2004-11-01 23:59:00.000000 -03", 1099364340, "America/Sao_Paulo") 70 | RENDER_TEST(php_bug_32086_04, "2004-11-02 01:00:00.000000 -02", 1099364400, "America/Sao_Paulo") 71 | RENDER_TEST(php_bug_32086_05, "2004-11-02 02:00:00.000000 -02", 1099368000, "America/Sao_Paulo") 72 | RENDER_TEST(php_bug_32086_06, "2005-02-18 23:00:00.000000 -02", 1108774800, "America/Sao_Paulo") 73 | RENDER_TEST(php_bug_32086_07, "2005-02-19 00:00:00.000000 -02", 1108778400, "America/Sao_Paulo") 74 | RENDER_TEST(php_bug_32086_08, "2005-02-19 01:00:00.000000 -02", 1108782000, "America/Sao_Paulo") 75 | RENDER_TEST(php_bug_32086_09, "2005-02-20 00:00:00.000000 -03", 1108868400, "America/Sao_Paulo") 76 | 77 | RENDER_TEST(php_bug_32555_01, "2005-04-02 02:30:00.000000 EST", 1112427000, "America/New_York") 78 | RENDER_TEST(php_bug_32555_02, "2005-04-02 00:00:00.000000 EST", 1112418000, "America/New_York") 79 | RENDER_TEST(php_bug_32555_03, "2005-04-03 00:00:00.000000 EST", 1112504400, "America/New_York") 80 | 81 | RENDER_TEST(php_bug_32588_01, "2005-04-02 00:00:00.000000 GMT", 1112400000, "GMT") 82 | RENDER_TEST(php_bug_32588_02, "2005-04-03 00:00:00.000000 GMT", 1112486400, "GMT") 83 | RENDER_TEST(php_bug_32588_03, "2005-04-04 00:00:00.000000 GMT", 1112572800, "GMT") 84 | 85 | RENDER_TEST(php_bug_73294_01, "-1900-06-22 00:00:00.000000 UTC", -122110502400, "UTC") 86 | RENDER_TEST(php_bug_73294_02, "-1916-06-22 00:00:00.000000 UTC", -122615337600, "UTC") 87 | 88 | RENDER_TEST(first_transition_01, "1884-08-06 06:39:33.000000 PST", -2695022427, "America/Los_Angeles") 89 | RENDER_TEST(first_transition_02, "1900-08-06 06:39:33.000000 PST", -2190187227, "America/Los_Angeles") 90 | RENDER_TEST(first_transition_03, "1901-08-06 06:39:33.000000 PST", -2158651227, "America/Los_Angeles") 91 | RENDER_TEST(first_transition_04, "1902-08-06 06:39:33.000000 PST", -2127115227, "America/Los_Angeles") 92 | RENDER_TEST(first_transition_05, "1918-02-06 06:39:33.000000 PST", -1637832027, "America/Los_Angeles") 93 | RENDER_TEST(first_transition_06, "1918-08-06 06:39:33.000000 PDT", -1622197227, "America/Los_Angeles") 94 | 95 | RENDER_TEST(issue0017_render_2369_01, "2369-12-31 00:00:00.000000 UTC", 12622694400, "UTC") 96 | 97 | RENDER_TEST(past_leap_01, "1965-01-01 00:00:00.000000 UTC", -157766400, "UTC") 98 | RENDER_TEST(past_leap_02, "1964-12-31 00:00:00.000000 UTC", -157852800, "UTC") 99 | RENDER_TEST(past_leap_03, "1964-01-31 00:00:00.000000 UTC", -186796800, "UTC") 100 | RENDER_TEST(past_leap_04, "1964-01-01 00:00:00.000000 UTC", -189388800, "UTC") 101 | RENDER_TEST(past_leap_05, "1963-12-31 00:00:00.000000 UTC", -189475200, "UTC") 102 | 103 | RENDER_TEST(render_01, "2005-05-26 23:11:59.000000 CEST", 1117141919, "Europe/Amsterdam") 104 | RENDER_TEST(render_02, "2005-05-26 22:11:59.000000 BST", 1117141919, "Europe/London") 105 | RENDER_TEST(render_03, "2005-05-27 07:11:59.000000 AEST", 1117141919, "Australia/Sydney") 106 | RENDER_TEST(render_04, "2005-10-30 00:00:00.000000 GMT", 1130630400, "GMT") 107 | RENDER_TEST(render_05, "2005-10-30 00:59:59.000000 GMT", 1130633999, "GMT") 108 | RENDER_TEST(render_06, "2005-10-30 01:00:00.000000 GMT", 1130634000, "GMT") 109 | RENDER_TEST(render_07, "2005-10-30 02:00:00.000000 CEST", 1130630400, "Europe/Oslo") 110 | RENDER_TEST(render_08, "2005-10-30 02:59:59.000000 CEST", 1130633999, "Europe/Oslo") 111 | RENDER_TEST(render_09, "2005-10-30 02:00:00.000000 CET", 1130634000, "Europe/Oslo") 112 | RENDER_TEST(render_10, "2005-10-30 01:00:00.000000 CEST", 1130626800, "Europe/Amsterdam") 113 | RENDER_TEST(render_11, "2005-10-30 02:00:00.000000 CEST", 1130630400, "Europe/Amsterdam") 114 | RENDER_TEST(render_12, "2005-10-30 02:00:00.000000 CET", 1130634000, "Europe/Amsterdam") 115 | RENDER_TEST(render_13, "2005-10-30 03:00:00.000000 CET", 1130637600, "Europe/Amsterdam") 116 | RENDER_TEST(render_14, "2005-10-30 04:00:00.000000 CET", 1130641200, "Europe/Amsterdam") 117 | RENDER_TEST(render_15, "2006-06-07 19:06:44.000000 CEST", 1149700004, "Europe/Amsterdam") 118 | 119 | RENDER_TEST(transition_01, "2008-03-30 01:00:00.000000 CET", 1206835200, "Europe/Amsterdam") 120 | RENDER_TEST(transition_02, "2008-03-30 01:59:59.000000 CET", 1206838799, "Europe/Amsterdam") 121 | RENDER_TEST(transition_03, "2008-03-30 03:00:00.000000 CEST", 1206838800, "Europe/Amsterdam") 122 | RENDER_TEST(transition_04, "2008-03-30 03:59:59.000000 CEST", 1206842399, "Europe/Amsterdam") 123 | RENDER_TEST(transition_05, "2008-03-30 03:00:00.000000 CEST", 1206838800, "Europe/Amsterdam") 124 | 125 | RENDER_TEST(weird_timezone_01, "1970-01-04 21:47:17.000000 -0830", 368237, "Pacific/Pitcairn") 126 | RENDER_TEST(weird_timezone_02, "1998-04-27 08:29:59.000000 UTC", 893665799, "UTC") 127 | RENDER_TEST(weird_timezone_03, "1998-04-27 08:30:00.000000 UTC", 893665800, "UTC") 128 | RENDER_TEST(weird_timezone_04, "1998-04-27 08:30:01.000000 UTC", 893665801, "UTC") 129 | RENDER_TEST(weird_timezone_05, "1998-04-26 23:59:59.000000 -0830", 893665799, "Pacific/Pitcairn") 130 | RENDER_TEST(weird_timezone_06, "1998-04-27 00:30:00.000000 -08", 893665800, "Pacific/Pitcairn") 131 | RENDER_TEST(weird_timezone_07, "1998-04-27 00:30:01.000000 -08", 893665801, "Pacific/Pitcairn") 132 | -------------------------------------------------------------------------------- /tests/c/transitions.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | 5 | TEST_GROUP(transitions) 6 | { 7 | }; 8 | 9 | #define TEST_EXISTS(n,tz) \ 10 | TEST(transitions, n) { \ 11 | return; \ 12 | int error; \ 13 | timelib_tzinfo *tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &error); \ 14 | CHECK(tzi != NULL); \ 15 | CHECK(error == TIMELIB_ERROR_NO_ERROR || error == TIMELIB_ERROR_SLIM_FILE); \ 16 | timelib_dump_tzinfo(tzi); \ 17 | timelib_tzinfo_dtor(tzi); \ 18 | } 19 | 20 | #define TEST_TRANSITION(n,tz,ts,eab,eoff,edst) \ 21 | TEST(transitions, n) { \ 22 | int error; \ 23 | timelib_time_offset *tto; \ 24 | timelib_tzinfo *tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &error); \ 25 | CHECK(tzi != NULL); \ 26 | tto = timelib_get_time_zone_info((ts), tzi); \ 27 | CHECK(tto != NULL); \ 28 | LONGS_EQUAL((eoff), tto->offset); \ 29 | STRCMP_EQUAL((eab), tto->abbr); \ 30 | LONGS_EQUAL((edst), tto->is_dst); \ 31 | timelib_time_offset_dtor(tto); \ 32 | timelib_tzinfo_dtor(tzi); \ 33 | } 34 | 35 | TEST_EXISTS(utc_00, "UTC") 36 | TEST_TRANSITION(utc_01, "UTC", INT64_MIN / 2, "UTC", 0, 0) 37 | TEST_TRANSITION(utc_02, "UTC", 0, "UTC", 0, 0) 38 | TEST_TRANSITION(utc_03, "UTC", INT64_MAX / 2, "UTC", 0, 0) 39 | 40 | TEST_EXISTS(tokyo_00, "Asia/Tokyo") 41 | TEST_TRANSITION(tokyo_01, "Asia/Tokyo", INT64_MIN, "LMT", 33539, 0) 42 | TEST_TRANSITION(tokyo_02, "Asia/Tokyo", -2587712401, "LMT", 33539, 0) 43 | TEST_TRANSITION(tokyo_03, "Asia/Tokyo", -2587712400, "JST", 32400, 0) 44 | TEST_TRANSITION(tokyo_04, "Asia/Tokyo", -2587712399, "JST", 32400, 0) 45 | TEST_TRANSITION(tokyo_05, "Asia/Tokyo", -577962001, "JDT", 36000, 1) 46 | TEST_TRANSITION(tokyo_06, "Asia/Tokyo", -577962000, "JST", 32400, 0) 47 | TEST_TRANSITION(tokyo_07, "Asia/Tokyo", -577961999, "JST", 32400, 0) 48 | TEST_TRANSITION(tokyo_08, "Asia/Tokyo", 0, "JST", 32400, 0) 49 | TEST_TRANSITION(tokyo_09, "Asia/Tokyo", INT64_MAX, "JST", 32400, 0) 50 | 51 | TEST_EXISTS(ams_00, "Europe/Amsterdam") 52 | TEST_TRANSITION(ams_01, "Europe/Amsterdam", INT64_MIN, "LMT", 1172, 0) 53 | TEST_TRANSITION(ams_02, "Europe/Amsterdam", -4260212372, "AMT", 1172, 0) 54 | TEST_TRANSITION(ams_03, "Europe/Amsterdam", -1025745573, "NST", 4772, 1) 55 | TEST_TRANSITION(ams_04, "Europe/Amsterdam", -1025745572, "+0120", 4800, 1) 56 | TEST_TRANSITION(ams_05, "Europe/Amsterdam", 811904399, "CEST", 7200, 1) 57 | TEST_TRANSITION(ams_06, "Europe/Amsterdam", 811904440, "CET", 3600, 0) 58 | TEST_TRANSITION(ams_07, "Europe/Amsterdam", 828234000, "CEST", 7200, 1) 59 | 60 | TEST_TRANSITION(ams_08, "Europe/Amsterdam", 846377999, "CEST", 7200, 1) 61 | TEST_TRANSITION(ams_09, "Europe/Amsterdam", 846378000, "CET", 3600, 0) 62 | TEST_TRANSITION(ams_10, "Europe/Amsterdam", 846378001, "CET", 3600, 0) 63 | 64 | TEST_TRANSITION(ams_11, "Europe/Amsterdam", 859683599, "CET", 3600, 0) 65 | TEST_TRANSITION(ams_12, "Europe/Amsterdam", 859683600, "CEST", 7200, 1) 66 | TEST_TRANSITION(ams_13, "Europe/Amsterdam", 859683600, "CEST", 7200, 1) 67 | 68 | TEST_EXISTS(can_00, "Australia/Canberra") 69 | TEST_TRANSITION(can_01, "Australia/Canberra", 1193500799, "AEST", 36000, 0) 70 | TEST_TRANSITION(can_02, "Australia/Canberra", 1193500800, "AEDT", 39600, 1) 71 | TEST_TRANSITION(can_03, "Australia/Canberra", 1193500801, "AEDT", 39600, 1) 72 | 73 | TEST_TRANSITION(can_04, "Australia/Canberra", 1207411199, "AEDT", 39600, 1) 74 | TEST_TRANSITION(can_05, "Australia/Canberra", 1207411200, "AEST", 36000, 0) 75 | TEST_TRANSITION(can_06, "Australia/Canberra", 1207411201, "AEST", 36000, 0) 76 | 77 | TEST_TRANSITION(can_07, "Australia/Canberra", 1223135999, "AEST", 36000, 0) 78 | TEST_TRANSITION(can_08, "Australia/Canberra", 1223136000, "AEDT", 39600, 1) 79 | TEST_TRANSITION(can_09, "Australia/Canberra", 1223136001, "AEDT", 39600, 1) 80 | 81 | TEST_TRANSITION(can_10, "Australia/Canberra", 1238860799, "AEDT", 39600, 1) 82 | TEST_TRANSITION(can_11, "Australia/Canberra", 1238860800, "AEST", 36000, 0) 83 | TEST_TRANSITION(can_12, "Australia/Canberra", 1238860801, "AEST", 36000, 0) 84 | 85 | 86 | TEST_EXISTS(lh_00, "Australia/Lord_Howe") 87 | TEST_TRANSITION(lh_01, "Australia/Lord_Howe", 1207407599, "+11", 39600, 1) 88 | TEST_TRANSITION(lh_02, "Australia/Lord_Howe", 1207407600, "+1030", 37800, 0) 89 | TEST_TRANSITION(lh_03, "Australia/Lord_Howe", 1207407601, "+1030", 37800, 0) 90 | 91 | TEST_TRANSITION(lh_04, "Australia/Lord_Howe", 1317482999, "+1030", 37800, 0) 92 | TEST_TRANSITION(lh_05, "Australia/Lord_Howe", 1317483000, "+11", 39600, 1) 93 | TEST_TRANSITION(lh_06, "Australia/Lord_Howe", 1317483001, "+11", 39600, 1) 94 | 95 | TEST_TRANSITION(lh_07, "Australia/Lord_Howe", 1365260399, "+11", 39600, 1) 96 | TEST_TRANSITION(lh_08, "Australia/Lord_Howe", 1365260400, "+1030", 37800, 0) 97 | TEST_TRANSITION(lh_09, "Australia/Lord_Howe", 1365260401, "+1030", 37800, 0) 98 | 99 | // local new year 2011 100 | TEST_TRANSITION(lh_10, "Australia/Lord_Howe", 1293800399, "+11", 39600, 1) 101 | TEST_TRANSITION(lh_11, "Australia/Lord_Howe", 1293800400, "+11", 39600, 1) 102 | TEST_TRANSITION(lh_12, "Australia/Lord_Howe", 1293800401, "+11", 39600, 1) 103 | 104 | // UT new year 2011 105 | TEST_TRANSITION(lh_13, "Australia/Lord_Howe", 1293839999, "+11", 39600, 1) 106 | TEST_TRANSITION(lh_14, "Australia/Lord_Howe", 1293840000, "+11", 39600, 1) 107 | TEST_TRANSITION(lh_15, "Australia/Lord_Howe", 1293840001, "+11", 39600, 1) 108 | 109 | 110 | // Fiji has a large time-of-effect hour of 99 111 | TEST_EXISTS(fiji_00, "Pacific/Fiji") 112 | TEST_TRANSITION(fiji_01, "Pacific/Fiji", 1608386399, "+12", 43200, 0) 113 | TEST_TRANSITION(fiji_02, "Pacific/Fiji", 1608386400, "+13", 46800, 1) 114 | TEST_TRANSITION(fiji_03, "Pacific/Fiji", 1608386401, "+13", 46800, 1) 115 | 116 | 117 | // Has a switch at a non-hour barrier 118 | TEST_EXISTS(chat_00, "Pacific/Chatham") 119 | TEST_TRANSITION(chat_01, "Pacific/Chatham", 1821880799, "+1245", 45900, 0) 120 | TEST_TRANSITION(chat_02, "Pacific/Chatham", 1821880800, "+1345", 49500, 1) 121 | TEST_TRANSITION(chat_03, "Pacific/Chatham", 1821880801, "+1345", 49500, 1) 122 | 123 | 124 | // Ireland has negative DST 125 | TEST_EXISTS(eire_00, "Europe/Dublin") 126 | TEST_TRANSITION(eire_01, "Europe/Dublin", 859683599, "GMT", 0, 1) 127 | TEST_TRANSITION(eire_02, "Europe/Dublin", 859683600, "IST", 3600, 0) 128 | TEST_TRANSITION(eire_03, "Europe/Dublin", 859683601, "IST", 3600, 0) 129 | 130 | #define TEST_TRANSITION_FROM_TEST_DIR(n,tz,ts,eab,eoff) \ 131 | TEST(transitions, n) { \ 132 | int error; \ 133 | timelib_time_offset *tto; \ 134 | timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); \ 135 | CHECK(test_directory != NULL); \ 136 | timelib_tzinfo *tzi = timelib_parse_tzfile(tz, test_directory, &error); \ 137 | CHECK(tzi != NULL); \ 138 | tto = timelib_get_time_zone_info((ts), tzi); \ 139 | CHECK(tto != NULL); \ 140 | LONGS_EQUAL((eoff), tto->offset); \ 141 | STRCMP_EQUAL((eab), tto->abbr); \ 142 | timelib_time_offset_dtor(tto); \ 143 | timelib_tzinfo_dtor(tzi); \ 144 | timelib_zoneinfo_dtor(test_directory); \ 145 | } 146 | 147 | // With a fake NY file, where I've modified the POSIX string to full year DST 148 | TEST_TRANSITION_FROM_TEST_DIR(ny_01, "New_York_mod_Full_Year_DST", 1615483094, "EDT", -14400) 149 | TEST_TRANSITION_FROM_TEST_DIR(ny_02, "New_York_mod_Full_Year_DST", 1609477199, "EDT", -14400) 150 | TEST_TRANSITION_FROM_TEST_DIR(ny_03, "New_York_mod_Full_Year_DST", 1609477200, "EDT", -14400) 151 | TEST_TRANSITION_FROM_TEST_DIR(ny_04, "New_York_mod_Full_Year_DST", 1609477201, "EDT", -14400) 152 | -------------------------------------------------------------------------------- /unixtime2tm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2021 Derick Rethans 5 | * Copyright (c) 2021 MongoDB 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #include "timelib.h" 27 | #include "timelib_private.h" 28 | 29 | void timelib_unixtime2date(timelib_sll ts, timelib_sll *y, timelib_sll *m, timelib_sll *d) 30 | { 31 | timelib_sll days, era, t; 32 | timelib_ull day_of_era, year_of_era, day_of_year, month_portion; 33 | 34 | /* Calculate days since algorithm's epoch (0000-03-01) */ 35 | days = ts / SECS_PER_DAY + HINNANT_EPOCH_SHIFT; 36 | 37 | /* Adjustment for a negative time portion */ 38 | t = ts % SECS_PER_DAY; 39 | days += (t < 0) ? -1 : 0; 40 | 41 | /* Calculate year, month, and day. Algorithm from: 42 | * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */ 43 | era = (days >= 0 ? days : days - DAYS_PER_ERA + 1) / DAYS_PER_ERA; 44 | day_of_era = days - era * DAYS_PER_ERA; 45 | year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / DAYS_PER_YEAR; 46 | *y = year_of_era + era * YEARS_PER_ERA; 47 | day_of_year = day_of_era - (DAYS_PER_YEAR * year_of_era + year_of_era / 4 - year_of_era / 100); 48 | month_portion = (5 * day_of_year + 2) / 153; 49 | *d = day_of_year - (153 * month_portion + 2) / 5 + 1; 50 | *m = month_portion + (month_portion < 10 ? 3 : -9); 51 | *y += (*m <= 2); 52 | 53 | TIMELIB_DEBUG(printf("A: ts=%lld, year=%lld, month=%lld, day=%lld,", ts, *y, *m, *d);); 54 | } 55 | 56 | /* Converts a Unix timestamp value into broken down time, in GMT */ 57 | void timelib_unixtime2gmt(timelib_time* tm, timelib_sll ts) 58 | { 59 | timelib_sll remainder; 60 | timelib_sll hours, minutes, seconds; 61 | 62 | timelib_unixtime2date(ts, &tm->y, &tm->m, &tm->d); 63 | remainder = ts % SECS_PER_DAY; 64 | remainder += (remainder < 0) * SECS_PER_DAY; 65 | 66 | /* That was the date, now we do the time */ 67 | hours = remainder / 3600; 68 | minutes = (remainder - hours * 3600) / 60; 69 | seconds = remainder % 60; 70 | TIMELIB_DEBUG(printf(" hour=%lld, minute=%lld, second=%lld\n", hours, minutes, seconds);); 71 | 72 | tm->h = hours; 73 | tm->i = minutes; 74 | tm->s = seconds; 75 | tm->z = 0; 76 | tm->dst = 0; 77 | tm->sse = ts; 78 | tm->sse_uptodate = 1; 79 | tm->tim_uptodate = 1; 80 | tm->is_localtime = 0; 81 | } 82 | 83 | void timelib_update_from_sse(timelib_time *tm) 84 | { 85 | timelib_sll sse; 86 | int z = tm->z; 87 | signed int dst = tm->dst; 88 | 89 | sse = tm->sse; 90 | 91 | switch (tm->zone_type) { 92 | case TIMELIB_ZONETYPE_ABBR: 93 | case TIMELIB_ZONETYPE_OFFSET: { 94 | timelib_unixtime2gmt(tm, tm->sse + tm->z + (tm->dst * 3600)); 95 | 96 | goto cleanup; 97 | } 98 | 99 | case TIMELIB_ZONETYPE_ID: { 100 | int32_t offset = 0; 101 | 102 | timelib_get_time_zone_offset_info(tm->sse, tm->tz_info, &offset, NULL, NULL); 103 | timelib_unixtime2gmt(tm, tm->sse + offset); 104 | 105 | goto cleanup; 106 | } 107 | 108 | default: 109 | timelib_unixtime2gmt(tm, tm->sse); 110 | goto cleanup; 111 | } 112 | cleanup: 113 | tm->sse = sse; 114 | tm->is_localtime = 1; 115 | tm->have_zone = 1; 116 | tm->z = z; 117 | tm->dst = dst; 118 | } 119 | 120 | void timelib_unixtime2local(timelib_time *tm, timelib_sll ts) 121 | { 122 | timelib_time_offset *gmt_offset; 123 | timelib_tzinfo *tz = tm->tz_info; 124 | 125 | switch (tm->zone_type) { 126 | case TIMELIB_ZONETYPE_ABBR: 127 | case TIMELIB_ZONETYPE_OFFSET: { 128 | int z = tm->z; 129 | signed int dst = tm->dst; 130 | 131 | timelib_unixtime2gmt(tm, ts + tm->z + (tm->dst * 3600)); 132 | 133 | tm->sse = ts; 134 | tm->z = z; 135 | tm->dst = dst; 136 | break; 137 | } 138 | 139 | case TIMELIB_ZONETYPE_ID: 140 | gmt_offset = timelib_get_time_zone_info(ts, tz); 141 | timelib_unixtime2gmt(tm, ts + gmt_offset->offset); 142 | 143 | /* we need to reset the sse here as unixtime2gmt modifies it */ 144 | tm->sse = ts; 145 | tm->dst = gmt_offset->is_dst; 146 | tm->z = gmt_offset->offset; 147 | tm->tz_info = tz; 148 | 149 | timelib_time_tz_abbr_update(tm, gmt_offset->abbr); 150 | timelib_time_offset_dtor(gmt_offset); 151 | break; 152 | 153 | default: 154 | tm->is_localtime = 0; 155 | tm->have_zone = 0; 156 | return; 157 | } 158 | 159 | tm->is_localtime = 1; 160 | tm->have_zone = 1; 161 | } 162 | 163 | void timelib_set_timezone_from_offset(timelib_time *t, timelib_sll utc_offset) 164 | { 165 | if (t->tz_abbr) { 166 | timelib_free(t->tz_abbr); 167 | } 168 | t->tz_abbr = NULL; 169 | 170 | t->z = utc_offset; 171 | t->have_zone = 1; 172 | t->zone_type = TIMELIB_ZONETYPE_OFFSET; 173 | t->dst = 0; 174 | t->tz_info = NULL; 175 | } 176 | 177 | void timelib_set_timezone_from_abbr(timelib_time *t, timelib_abbr_info abbr_info) 178 | { 179 | if (t->tz_abbr) { 180 | timelib_free(t->tz_abbr); 181 | } 182 | t->tz_abbr = timelib_strdup(abbr_info.abbr); 183 | 184 | t->z = abbr_info.utc_offset; 185 | t->have_zone = 1; 186 | t->zone_type = TIMELIB_ZONETYPE_ABBR; 187 | t->dst = abbr_info.dst; 188 | t->tz_info = NULL; 189 | } 190 | 191 | void timelib_set_timezone(timelib_time *t, timelib_tzinfo *tz) 192 | { 193 | timelib_time_offset *gmt_offset; 194 | 195 | gmt_offset = timelib_get_time_zone_info(t->sse, tz); 196 | t->z = gmt_offset->offset; 197 | /* 198 | if (t->dst != gmt_offset->is_dst) { 199 | printf("ERROR (%d, %d)\n", t->dst, gmt_offset->is_dst); 200 | exit(1); 201 | } 202 | */ 203 | t->dst = gmt_offset->is_dst; 204 | t->tz_info = tz; 205 | if (t->tz_abbr) { 206 | timelib_free(t->tz_abbr); 207 | } 208 | t->tz_abbr = timelib_strdup(gmt_offset->abbr); 209 | timelib_time_offset_dtor(gmt_offset); 210 | 211 | t->have_zone = 1; 212 | t->zone_type = TIMELIB_ZONETYPE_ID; 213 | } 214 | 215 | /* Converts the time stored in the struct to localtime if localtime = true, 216 | * otherwise it converts it to gmttime. This is only done when necessary 217 | * of course. */ 218 | int timelib_apply_localtime(timelib_time *t, unsigned int localtime) 219 | { 220 | if (localtime) { 221 | /* Converting from GMT time to local time */ 222 | TIMELIB_DEBUG(printf("Converting from GMT time to local time\n");); 223 | 224 | /* Check if TZ is set */ 225 | if (!t->tz_info) { 226 | TIMELIB_DEBUG(printf("E: No timezone configured, can't switch to local time\n");); 227 | return -1; 228 | } 229 | 230 | timelib_unixtime2local(t, t->sse); 231 | } else { 232 | /* Converting from local time to GMT time */ 233 | TIMELIB_DEBUG(printf("Converting from local time to GMT time\n");); 234 | 235 | timelib_unixtime2gmt(t, t->sse); 236 | } 237 | return 0; 238 | } 239 | -------------------------------------------------------------------------------- /tests/c/interval.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include 4 | #include 5 | 6 | 7 | TEST_GROUP(timelib_interval) 8 | { 9 | timelib_tzinfo *tzi; 10 | timelib_time *t; 11 | timelib_time *changed; 12 | timelib_time *b = NULL, *e = NULL; 13 | int r = 0; 14 | timelib_rel_time *p = NULL; 15 | timelib_error_container *errors; 16 | 17 | void setup(const char *base, const char *tzid, const char *interval, timelib_sll us, bool invert) 18 | { 19 | int dummy_error; 20 | 21 | tzi = timelib_parse_tzfile((char*) tzid, timelib_builtin_db(), &dummy_error); 22 | t = timelib_strtotime(base, strlen(base), NULL, timelib_builtin_db(), timelib_parse_tzfile); 23 | timelib_update_ts(t, tzi); 24 | timelib_set_timezone(t, tzi); 25 | timelib_update_from_sse(t); 26 | 27 | timelib_strtointerval(interval, strlen(interval), &b, &e, &p, &r, &errors); 28 | p->us = us; 29 | p->invert = invert; 30 | 31 | } 32 | 33 | void test_add_wall(const char *base, const char *interval, timelib_sll us, bool invert) 34 | { 35 | setup(base, "UTC", interval, us, invert); 36 | changed = timelib_add_wall(t, p); 37 | } 38 | 39 | void test_add_wall(const char *base, const char *tzid, const char *interval, timelib_sll us, bool invert) 40 | { 41 | setup(base, tzid, interval, us, invert); 42 | changed = timelib_add_wall(t, p); 43 | } 44 | 45 | void test_sub_wall(const char *base, const char *interval, timelib_sll us, bool invert) 46 | { 47 | setup(base, "UTC", interval, us, invert); 48 | changed = timelib_sub_wall(t, p); 49 | } 50 | 51 | TEST_TEARDOWN() 52 | { 53 | timelib_time_dtor(t); 54 | timelib_time_dtor(changed); 55 | timelib_tzinfo_dtor(tzi); 56 | timelib_rel_time_dtor(p); 57 | timelib_error_container_dtor(errors); 58 | } 59 | }; 60 | 61 | #define CHECKRES(e_y,e_m,e_d,e_h,e_i,e_s,e_us,e_sse) \ 62 | LONGS_EQUAL(e_y, changed->y); \ 63 | LONGS_EQUAL(e_m, changed->m); \ 64 | LONGS_EQUAL(e_d, changed->d); \ 65 | LONGS_EQUAL(e_h, changed->h); \ 66 | LONGS_EQUAL(e_i, changed->i); \ 67 | LONGS_EQUAL(e_s, changed->s); \ 68 | LONGS_EQUAL(e_us, changed->us); \ 69 | LONGS_EQUAL(e_sse, changed->sse); 70 | 71 | #define INVERT 1 72 | 73 | TEST(timelib_interval, php80998_1a) 74 | { 75 | test_add_wall("2021-04-05 14:00:00", "PT10799S", 999999L, INVERT); 76 | CHECKRES(2021, 4, 5, 11, 0, 0, 1, 1617620400); 77 | } 78 | 79 | TEST(timelib_interval, php80998_1b) 80 | { 81 | test_add_wall("2021-04-05 14:00:00", "PT10800S", 0L, INVERT); 82 | CHECKRES(2021, 4, 5, 11, 0, 0, 0, 1617620400); 83 | } 84 | 85 | TEST(timelib_interval, php80998_2a) 86 | { 87 | test_add_wall("2000-01-01 00:00:00", "PT1S", 500000L, INVERT); 88 | CHECKRES(1999, 12, 31, 23, 59, 58, 500000, 946684798); 89 | } 90 | 91 | TEST(timelib_interval, php80998_2b) 92 | { 93 | test_add_wall("2000-01-01 00:00:00", "PT1S", 0L, INVERT); 94 | CHECKRES(1999, 12, 31, 23, 59, 59, 0, 946684799); 95 | } 96 | 97 | TEST(timelib_interval, php80998_1a_sub) 98 | { 99 | test_sub_wall("2021-04-05 11:00:00", "PT2H59M59S", 999999L, INVERT); 100 | CHECKRES(2021, 4, 5, 13, 59, 59, 999999, 1617631199); 101 | } 102 | 103 | TEST(timelib_interval, php80998_1b_sub) 104 | { 105 | test_sub_wall("2021-04-05 11:00:00", "PT3H", 0L, INVERT); 106 | CHECKRES(2021, 4, 5, 14, 0, 0, 0, 1617631200); 107 | } 108 | 109 | TEST(timelib_interval, php80998_2a_sub) 110 | { 111 | test_sub_wall("2021-04-05 11:00:00", "PT2H59M59S", 999999L, 0); 112 | CHECKRES(2021, 4, 5, 8, 0, 0, 1, 1617609600); 113 | } 114 | 115 | TEST(timelib_interval, php80998_2b_sub) 116 | { 117 | test_sub_wall("2021-04-05 11:00:00", "PT3H", 0L, 0); 118 | CHECKRES(2021, 4, 5, 8, 0, 0, 0, 1617609600); 119 | } 120 | 121 | TEST(timelib_interval, gh8964a) 122 | { 123 | test_add_wall("2022-07-21 14:50:13", "PT0S", -5L, 0); 124 | CHECKRES(2022, 7, 21, 14, 50, 12, 999995L, 1658415012); 125 | } 126 | 127 | TEST(timelib_interval, gh8964b) 128 | { 129 | test_add_wall("2022-07-21 14:50:13", "PT0S", -5L, INVERT); 130 | CHECKRES(2022, 7, 21, 14, 50, 13, 5L, 1658415013); 131 | } 132 | 133 | TEST(timelib_interval, gh8964c) 134 | { 135 | test_sub_wall("2022-07-21 14:50:13", "PT0S", -5L, 0); 136 | CHECKRES(2022, 7, 21, 14, 50, 13, 5L, 1658415013); 137 | } 138 | 139 | TEST(timelib_interval, gh8964d) 140 | { 141 | test_sub_wall("2022-07-21 14:50:13", "PT0S", -5L, INVERT); 142 | CHECKRES(2022, 7, 21, 14, 50, 12, 999995L, 1658415012); 143 | } 144 | 145 | TEST(timelib_interval, gh8964e) 146 | { 147 | test_sub_wall("2022-07-21 15:00:10", "PT0S", 900000L, 0); 148 | CHECKRES(2022, 7, 21, 15, 0, 9, 100000L, 1658415609); 149 | } 150 | 151 | TEST(timelib_interval, gh8964f) 152 | { 153 | test_add_wall("2022-07-21 15:00:10", "PT0S", 900000L, 0); 154 | CHECKRES(2022, 7, 21, 15, 0, 10, 900000L, 1658415610); 155 | } 156 | 157 | TEST(timelib_interval, gh8964g) 158 | { 159 | test_sub_wall("2022-07-21 15:00:10", "PT0S", -900000L, 0); 160 | CHECKRES(2022, 7, 21, 15, 0, 10, 900000L, 1658415610); 161 | } 162 | 163 | TEST(timelib_interval, gh8964h) 164 | { 165 | test_add_wall("2022-07-21 15:00:10", "PT0S", -900000L, 0); 166 | CHECKRES(2022, 7, 21, 15, 0, 9, 100000L, 1658415609); 167 | } 168 | 169 | TEST(timelib_interval, gh8964i) 170 | { 171 | test_sub_wall("2022-07-21 15:00:10", "PT1S", -900000L, 0); 172 | CHECKRES(2022, 7, 21, 15, 0, 9, 900000L, 1658415609); 173 | } 174 | 175 | TEST(timelib_interval, gh8964j) 176 | { 177 | test_add_wall("2022-07-21 15:00:10", "PT1S", -900000L, 0); 178 | CHECKRES(2022, 7, 21, 15, 0, 10, 100000L, 1658415610); 179 | } 180 | 181 | TEST(timelib_interval, gh8964k) 182 | { 183 | test_sub_wall("2022-07-21 15:00:10", "PT2S", -900000L, 0); 184 | CHECKRES(2022, 7, 21, 15, 0, 8, 900000L, 1658415608); 185 | } 186 | 187 | TEST(timelib_interval, gh8964l) 188 | { 189 | test_add_wall("2022-07-21 15:00:10", "PT2S", -900000L, 0); 190 | CHECKRES(2022, 7, 21, 15, 0, 11, 100000L, 1658415611); 191 | } 192 | 193 | TEST(timelib_interval, gh8964m) 194 | { 195 | test_sub_wall("2022-07-21 15:00:09.100000", "PT0S", -900000L, 0); 196 | CHECKRES(2022, 7, 21, 15, 0, 10, 0L, 1658415610); 197 | } 198 | 199 | TEST(timelib_interval, gh9106a) 200 | { 201 | test_add_wall("2020-01-01 00:00:00.000000", "PT1S", 500000L, 0); 202 | CHECKRES(2020, 1, 1, 0, 0, 1, 500000L, 1577836801); 203 | } 204 | 205 | TEST(timelib_interval, gh9106b) 206 | { 207 | test_add_wall("2020-01-01 00:00:01.500000", "PT1S", 500000L, 0); 208 | CHECKRES(2020, 1, 1, 0, 0, 3, 0L, 1577836803); 209 | } 210 | 211 | TEST(timelib_interval, gh9106c) 212 | { 213 | test_add_wall("2020-01-01 00:00:03.600000", "PT1S", 500000L, 0); 214 | CHECKRES(2020, 1, 1, 0, 0, 5, 100000L, 1577836805); 215 | } 216 | 217 | TEST(timelib_interval, gh8860a) 218 | { 219 | test_add_wall("2022-10-30 01:00:00", "Europe/Amsterdam", "PT0H", 0, 0); 220 | CHECKRES(2022, 10, 30, 1, 0, 0, 0L, 1667084400); 221 | LONGS_EQUAL(changed->z, 7200); 222 | } 223 | 224 | TEST(timelib_interval, gh8860b) 225 | { 226 | test_add_wall("2022-10-30 01:00:00", "Europe/Amsterdam", "PT1H", 0, 0); 227 | CHECKRES(2022, 10, 30, 2, 0, 0, 0L, 1667088000); 228 | LONGS_EQUAL(changed->z, 7200); 229 | } 230 | 231 | TEST(timelib_interval, gh8860c) 232 | { 233 | test_add_wall("2022-10-30 02:00:00", "Europe/Amsterdam", "PT1H", 0, 0); 234 | CHECKRES(2022, 10, 30, 3, 0, 0, 0L, 1667095200); 235 | LONGS_EQUAL(changed->z, 3600); 236 | } 237 | 238 | TEST(timelib_interval, gh8860d) 239 | { 240 | test_add_wall("2022-10-30 02:00:00", "Europe/Amsterdam", "PT3600S", 0, 0); 241 | CHECKRES(2022, 10, 30, 3, 0, 0, 0L, 1667095200); 242 | LONGS_EQUAL(changed->z, 3600); 243 | } 244 | 245 | TEST(timelib_interval, long_positive_interval) 246 | { 247 | test_add_wall("2000-01-01 00:00:01.500000", "P135000D", 0, 0); 248 | CHECKRES(2369, 8, 14, 0, 0, 1, 500000L, 12610684801); 249 | } 250 | 251 | TEST(timelib_interval, long_negative_interval) 252 | { 253 | test_add_wall("2000-01-01 00:00:01.500000", "P135000D", 0, INVERT); 254 | CHECKRES(1630, 5, 20, 0, 0, 1, 500000L, -10717315199); 255 | } 256 | 257 | TEST(timelib_interval, leap_year_crossing_1) 258 | { 259 | test_add_wall("2000-02-28 00:00:01.500000", "P1D", 0, 0); 260 | CHECKRES(2000, 2, 29, 0, 0, 1, 500000L, 951782401); 261 | } 262 | 263 | TEST(timelib_interval, leap_year_crossing_2) 264 | { 265 | test_add_wall("2000-02-29 00:00:01.500000", "P1D", 0, INVERT); 266 | CHECKRES(2000, 2, 28, 0, 0, 1, 500000L, 951696001); 267 | } 268 | 269 | TEST(timelib_interval, leap_year_crossing_3) 270 | { 271 | test_add_wall("2000-01-28 00:00:01.500000", "P32D", 0, 0); 272 | CHECKRES(2000, 2, 29, 0, 0, 1, 500000L, 951782401); 273 | } 274 | 275 | TEST(timelib_interval, leap_year_crossing_4) 276 | { 277 | test_add_wall("2000-02-29 00:00:01.500000", "P32D", 0, INVERT); 278 | CHECKRES(2000, 1, 28, 0, 0, 1, 500000L, 949017601); 279 | } 280 | 281 | TEST(timelib_interval, non_leap_year_crossing_1) 282 | { 283 | test_add_wall("2001-02-28 00:00:01.500000", "P1D", 0, 0); 284 | CHECKRES(2001, 3, 1, 0, 0, 1, 500000L, 983404801); 285 | } 286 | 287 | TEST(timelib_interval, non_leap_year_crossing_2) 288 | { 289 | test_add_wall("2001-03-01 00:00:01.500000", "P1D", 0, INVERT); 290 | CHECKRES(2001, 2, 28, 0, 0, 1, 500000L, 983318401); 291 | } 292 | 293 | TEST(timelib_interval, non_leap_year_crossing_3) 294 | { 295 | test_add_wall("2001-01-28 00:00:01.500000", "P32D", 0, 0); 296 | CHECKRES(2001, 3, 1, 0, 0, 1, 500000L, 983404801); 297 | } 298 | 299 | TEST(timelib_interval, non_leap_year_crossing_4) 300 | { 301 | test_add_wall("2001-03-01 00:00:01.500000", "P32D", 0, INVERT); 302 | CHECKRES(2001, 1, 28, 0, 0, 1, 500000L, 980640001); 303 | } 304 | -------------------------------------------------------------------------------- /parse_zoneinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2017-2018 MongoDB, Inc. 5 | * Copyright (c) 2015 Red Hat, Inc. 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #include "timelib.h" 27 | #include "timelib_private.h" 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #ifdef _WIN32 34 | # include "win_dirent.h" 35 | #endif 36 | 37 | #ifndef MAXPATHLEN 38 | # ifdef _WIN32 39 | # define MAXPATHLEN 2048 40 | # elif PATH_MAX 41 | # define MAXPATHLEN PATH_MAX 42 | # elif defined(MAX_PATH) 43 | # define MAXPATHLEN MAX_PATH 44 | # else 45 | # define MAXPATHLEN 256 46 | # endif 47 | #endif 48 | 49 | #if defined(_MSC_VER) 50 | # define TIMELIB_DIR_SEPARATOR "\\" 51 | #else 52 | # define TIMELIB_DIR_SEPARATOR "/" 53 | #endif 54 | 55 | #define TIMELIB_NAME_SEPARATOR "/" 56 | 57 | /* Filter out some non-tzdata files and the posix/right databases, if 58 | * present. */ 59 | static int index_filter(const struct dirent *ent) 60 | { 61 | return strcmp(ent->d_name, ".") != 0 62 | && strcmp(ent->d_name, "..") != 0 63 | && strcmp(ent->d_name, "posix") != 0 64 | && strcmp(ent->d_name, "posixrules") != 0 65 | && strcmp(ent->d_name, "right") != 0 66 | && strcmp(ent->d_name, "localtime") != 0 67 | && strstr(ent->d_name, ".list") == NULL 68 | && strstr(ent->d_name, ".tab") == NULL; 69 | } 70 | 71 | static int timelib_alphasort(const struct dirent **a, const struct dirent **b) 72 | { 73 | return strcmp((*a)->d_name, (*b)->d_name); 74 | } 75 | 76 | 77 | static int sysdbcmp(const void *first, const void *second) 78 | { 79 | const timelib_tzdb_index_entry *alpha = first, *beta = second; 80 | 81 | return timelib_strcasecmp(alpha->id, beta->id); 82 | } 83 | 84 | /* Returns true if the passed-in stat structure describes a 85 | * probably-valid timezone file. */ 86 | static int is_valid_tzfile(const struct stat *st, int fd) 87 | { 88 | if (fd) { 89 | char buf[20]; 90 | if (read(fd, buf, 20) != 20) { 91 | return 0; 92 | } 93 | lseek(fd, SEEK_SET, 0); 94 | if (memcmp(buf, "TZif", 4)) { 95 | return 0; 96 | } 97 | } 98 | return S_ISREG(st->st_mode) && st->st_size > 20; 99 | } 100 | 101 | /* Return the contents of the tzfile if found, else NULL. On success, the 102 | * length of the mapped data is placed in *length. */ 103 | static char *read_tzfile(const char *directory, const char *timezone, size_t *length) 104 | { 105 | char *fname; 106 | size_t fname_len; 107 | char *buffer; 108 | struct stat st; 109 | int fd; 110 | #ifdef _WIN32 111 | int read_bytes; 112 | #else 113 | ssize_t read_bytes; 114 | #endif 115 | 116 | if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) { 117 | return NULL; 118 | } 119 | 120 | fname_len = strlen(directory) + strlen(TIMELIB_DIR_SEPARATOR) + strlen(timezone) + 1; 121 | fname = timelib_malloc(fname_len); 122 | if (snprintf(fname, fname_len, "%s%s%s", directory, TIMELIB_DIR_SEPARATOR, timezone) < 0) { 123 | timelib_free(fname); 124 | return NULL; 125 | } 126 | 127 | /* O_BINARY is required to properly read the file on windows */ 128 | #ifdef _WIN32 129 | fd = open(fname, O_RDONLY | O_BINARY); 130 | #else 131 | fd = open(fname, O_RDONLY); 132 | #endif 133 | timelib_free(fname); 134 | 135 | if (fd == -1) { 136 | return NULL; 137 | } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st, fd)) { 138 | close(fd); 139 | return NULL; 140 | } 141 | 142 | *length = st.st_size; 143 | 144 | buffer = timelib_calloc(1, *length + 1); 145 | read_bytes = read(fd, buffer, *length); 146 | close(fd); 147 | 148 | if (read_bytes == -1 || read_bytes != st.st_size) { 149 | return NULL; 150 | } 151 | 152 | return buffer; 153 | } 154 | 155 | static int timelib_scandir(const char *directory_name, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) 156 | { 157 | DIR *dir; 158 | struct dirent **entries = NULL; 159 | int entries_size = 0; 160 | int entries_count = 0; 161 | char entry_container[sizeof(struct dirent) + MAXPATHLEN]; 162 | struct dirent *entry = (struct dirent *)&entry_container; 163 | 164 | dir = opendir(directory_name); 165 | if (!dir) { 166 | return -1; 167 | } 168 | 169 | while ((entry = readdir(dir)) != NULL) { 170 | int new_entry_size = 0; 171 | struct dirent *new_entry = NULL; 172 | 173 | /* Skip if the filter matches */ 174 | if (filter && (*filter)(entry) == 0) { 175 | continue; 176 | } 177 | 178 | /* Make sure our entries array is large enough */ 179 | if (entries_count == entries_size) { 180 | struct dirent **new_entries; 181 | 182 | if (entries_size == 0) { 183 | entries_size = 16; 184 | } else { 185 | entries_size *= 2; 186 | } 187 | 188 | new_entries = (struct dirent **) timelib_realloc(entries, entries_size * sizeof(struct dirent *)); 189 | if (!new_entries) { 190 | return -1; 191 | } 192 | entries = new_entries; 193 | } 194 | 195 | /* Add our new entry to the list */ 196 | new_entry_size = sizeof(struct dirent) + (strlen(entry->d_name) + 1); 197 | new_entry = (struct dirent *) timelib_malloc(new_entry_size); 198 | 199 | if (new_entry == NULL) { 200 | goto cleanup; 201 | } 202 | 203 | entries[entries_count++] = (struct dirent *) memcpy(new_entry, entry, new_entry_size); 204 | } 205 | 206 | closedir(dir); 207 | 208 | *namelist = entries; 209 | 210 | if (compar) { 211 | qsort(*namelist, entries_count, sizeof(struct dirent *), (int (*) (const void *, const void *)) compar); 212 | } 213 | 214 | return entries_count; 215 | 216 | cleanup: 217 | while (entries_count-- > 0) { 218 | timelib_free(entries[entries_count]); 219 | } 220 | timelib_free(entries); 221 | return -1; 222 | } 223 | 224 | /* Create the zone identifier index by trawling the filesystem. */ 225 | static int create_zone_index(const char *directory, timelib_tzdb *db) 226 | { 227 | size_t dirstack_size, dirstack_top; 228 | size_t index_size, index_next; 229 | timelib_tzdb_index_entry *db_index; 230 | char **dirstack; 231 | size_t data_size = 0; 232 | unsigned char *tmp_data = NULL; 233 | 234 | /* LIFO stack to hold directory entries to scan; each slot is a 235 | * directory name relative to the zoneinfo prefix. */ 236 | dirstack_size = 32; 237 | dirstack = timelib_malloc(dirstack_size * sizeof(*dirstack)); 238 | dirstack_top = 1; 239 | dirstack[0] = timelib_strdup(""); 240 | 241 | /* Index array. */ 242 | index_size = 64; 243 | db_index = timelib_malloc(index_size * sizeof(timelib_tzdb_index_entry)); 244 | index_next = 0; 245 | 246 | do { 247 | struct dirent **ents; 248 | char name[PATH_MAX], *top; 249 | int count; 250 | 251 | /* Pop the top stack entry, and iterate through its contents. */ 252 | top = dirstack[--dirstack_top]; 253 | snprintf(name, sizeof(name), "%s/%s", directory, top); 254 | 255 | count = timelib_scandir(name, &ents, index_filter, timelib_alphasort); 256 | if (count == -1) { 257 | timelib_free(top); 258 | while (dirstack_top > 0) { 259 | timelib_free(dirstack[--dirstack_top]); 260 | } 261 | timelib_free(dirstack); 262 | timelib_free(db_index); 263 | return -errno; 264 | } 265 | 266 | while (count > 0) { 267 | struct stat st; 268 | const char *leaf = ents[count - 1]->d_name; 269 | 270 | snprintf(name, sizeof(name), "%s%s%s%s%s", directory, TIMELIB_NAME_SEPARATOR, top, TIMELIB_NAME_SEPARATOR, leaf); 271 | 272 | if (strlen(name) && stat(name, &st) == 0) { 273 | /* Name, relative to the zoneinfo prefix. */ 274 | const char *root = top; 275 | 276 | if (root[0] == '/') { 277 | root++; 278 | } 279 | 280 | snprintf(name, sizeof(name), "%s%s%s", root, *root ? TIMELIB_NAME_SEPARATOR : "", leaf); 281 | 282 | if (S_ISDIR(st.st_mode)) { 283 | if (dirstack_top == dirstack_size) { 284 | dirstack_size *= 2; 285 | dirstack = timelib_realloc(dirstack, dirstack_size * sizeof(*dirstack)); 286 | } 287 | dirstack[dirstack_top++] = timelib_strdup(name); 288 | } else { 289 | if (index_next == index_size) { 290 | index_size *= 2; 291 | db_index = timelib_realloc(db_index, index_size * sizeof(timelib_tzdb_index_entry)); 292 | } 293 | 294 | db_index[index_next].id = timelib_strdup(name); 295 | 296 | { 297 | size_t length; 298 | char *tzfile_data = read_tzfile(directory, name, &length); 299 | 300 | if (tzfile_data) { 301 | tmp_data = timelib_realloc(tmp_data, data_size + length); 302 | memcpy(tmp_data + data_size, tzfile_data, length); 303 | db_index[index_next].pos = data_size; 304 | data_size += length; 305 | timelib_free(tzfile_data); 306 | 307 | index_next++; 308 | } else { 309 | timelib_free(db_index[index_next].id); 310 | } 311 | } 312 | } 313 | } 314 | 315 | timelib_free(ents[--count]); 316 | } 317 | 318 | if (count != -1) { 319 | timelib_free(ents); 320 | } 321 | timelib_free(top); 322 | } while (dirstack_top); 323 | 324 | qsort(db_index, index_next, sizeof(*db_index), sysdbcmp); 325 | 326 | db->index = db_index; 327 | db->index_size = index_next; 328 | db->data = tmp_data; 329 | 330 | timelib_free(dirstack); 331 | 332 | return 0; 333 | } 334 | 335 | timelib_tzdb *timelib_zoneinfo(const char *directory) 336 | { 337 | timelib_tzdb *tmp = timelib_malloc(sizeof(timelib_tzdb)); 338 | 339 | tmp->version = "0.system"; 340 | tmp->data = NULL; 341 | if (create_zone_index(directory, tmp) < 0) { 342 | timelib_free(tmp); 343 | return NULL; 344 | } 345 | return tmp; 346 | } 347 | 348 | void timelib_zoneinfo_dtor(timelib_tzdb *tzdb) 349 | { 350 | int i; 351 | 352 | for (i = 0; i < tzdb->index_size; i++) { 353 | timelib_free(tzdb->index[i].id); 354 | } 355 | timelib_free((timelib_tzdb_index_entry*) tzdb->index); 356 | timelib_free((char*) tzdb->data); 357 | timelib_free(tzdb); 358 | } 359 | -------------------------------------------------------------------------------- /tests/c/php-rfc.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include "timelib_private.h" 4 | #include 5 | 6 | TEST_GROUP(php_rfc_interval) 7 | { 8 | int dummy_error; 9 | timelib_time *changed; 10 | timelib_tzinfo *tzi; 11 | timelib_time *t; 12 | timelib_rel_time *p = NULL; 13 | timelib_error_container *errors; 14 | 15 | TEST_SETUP() 16 | { 17 | t = changed = NULL; 18 | } 19 | 20 | void test_add_wall(const char *str, const char *interval, const char *tzid) 21 | { 22 | timelib_time *b = NULL, *e = NULL; 23 | int r = 0; 24 | 25 | tzi = timelib_parse_tzfile((char*) tzid, timelib_builtin_db(), &dummy_error); 26 | t = timelib_strtotime(str, strlen(str), NULL, timelib_builtin_db(), timelib_parse_tzfile); 27 | // printf("\n"); 28 | timelib_update_ts(t, tzi); 29 | timelib_set_timezone(t, tzi); 30 | timelib_update_from_sse(t); 31 | 32 | // timelib_dump_date(t, 3); 33 | 34 | timelib_strtointerval(interval, strlen(interval), &b, &e, &p, &r, &errors); 35 | 36 | changed = timelib_add_wall(t, p); 37 | // timelib_dump_date(changed, 3); 38 | } 39 | 40 | void test_sub_wall(const char *str, const char *interval, const char *tzid) 41 | { 42 | timelib_time *b = NULL, *e = NULL; 43 | int r = 0; 44 | 45 | tzi = timelib_parse_tzfile((char*) tzid, timelib_builtin_db(), &dummy_error); 46 | t = timelib_strtotime(str, strlen(str), NULL, timelib_builtin_db(), timelib_parse_tzfile); 47 | // printf("\n"); 48 | timelib_update_ts(t, tzi); 49 | timelib_set_timezone(t, tzi); 50 | timelib_update_from_sse(t); 51 | 52 | // timelib_dump_date(t, 3); 53 | 54 | timelib_strtointerval(interval, strlen(interval), &b, &e, &p, &r, &errors); 55 | 56 | changed = timelib_sub_wall(t, p); 57 | // timelib_dump_date(changed, 3); 58 | } 59 | 60 | TEST_TEARDOWN() 61 | { 62 | timelib_tzinfo_dtor(tzi); 63 | timelib_time_dtor(t); 64 | timelib_time_dtor(changed); 65 | timelib_rel_time_dtor(p); 66 | timelib_error_container_dtor(errors); 67 | } 68 | }; 69 | 70 | 71 | TEST(php_rfc_interval, ba1) 72 | { 73 | test_add_wall("@1289109599", "PT1S", "America/New_York"); 74 | 75 | LONGS_EQUAL(1289109600, changed->sse); 76 | LONGS_EQUAL(2010, changed->y); 77 | LONGS_EQUAL(11, changed->m); 78 | LONGS_EQUAL( 7, changed->d); 79 | LONGS_EQUAL(1, changed->h); 80 | LONGS_EQUAL(0, changed->i); 81 | LONGS_EQUAL(0, changed->s); 82 | LONGS_EQUAL(0, changed->dst); 83 | STRCMP_EQUAL("EST", changed->tz_abbr); 84 | } 85 | 86 | TEST(php_rfc_interval, ba2) 87 | { 88 | test_add_wall("2010-11-06 04:30:00", "P1D", "America/New_York"); 89 | 90 | LONGS_EQUAL(2010, changed->y); 91 | LONGS_EQUAL(11, changed->m); 92 | LONGS_EQUAL( 7, changed->d); 93 | LONGS_EQUAL( 4, changed->h); 94 | LONGS_EQUAL(30, changed->i); 95 | LONGS_EQUAL( 0, changed->s); 96 | LONGS_EQUAL(0, changed->dst); 97 | STRCMP_EQUAL("EST", changed->tz_abbr); 98 | } 99 | 100 | TEST(php_rfc_interval, ba3) 101 | { 102 | test_add_wall("2010-11-06 04:30:00", "PT24H", "America/New_York"); 103 | 104 | LONGS_EQUAL(2010, changed->y); 105 | LONGS_EQUAL(11, changed->m); 106 | LONGS_EQUAL( 7, changed->d); 107 | LONGS_EQUAL( 3, changed->h); 108 | LONGS_EQUAL(30, changed->i); 109 | LONGS_EQUAL( 0, changed->s); 110 | LONGS_EQUAL(0, changed->dst); 111 | STRCMP_EQUAL("EST", changed->tz_abbr); 112 | } 113 | 114 | TEST(php_rfc_interval, ba4) 115 | { 116 | test_add_wall("2010-11-06 04:30:00", "PT23H", "America/New_York"); 117 | 118 | LONGS_EQUAL(2010, changed->y); 119 | LONGS_EQUAL(11, changed->m); 120 | LONGS_EQUAL( 7, changed->d); 121 | LONGS_EQUAL( 2, changed->h); 122 | LONGS_EQUAL(30, changed->i); 123 | LONGS_EQUAL( 0, changed->s); 124 | LONGS_EQUAL(0, changed->dst); 125 | STRCMP_EQUAL("EST", changed->tz_abbr); 126 | } 127 | 128 | TEST(php_rfc_interval, ba5) 129 | { 130 | test_add_wall("2010-11-06 04:30:00", "PT22H", "America/New_York"); 131 | 132 | LONGS_EQUAL(2010, changed->y); 133 | LONGS_EQUAL(11, changed->m); 134 | LONGS_EQUAL( 7, changed->d); 135 | LONGS_EQUAL( 1, changed->h); 136 | LONGS_EQUAL(30, changed->i); 137 | LONGS_EQUAL( 0, changed->s); 138 | LONGS_EQUAL(0, changed->dst); 139 | STRCMP_EQUAL("EST", changed->tz_abbr); 140 | } 141 | 142 | TEST(php_rfc_interval, ba6) 143 | { 144 | test_add_wall("2010-11-06 04:30:00", "PT21H", "America/New_York"); 145 | 146 | LONGS_EQUAL(2010, changed->y); 147 | LONGS_EQUAL(11, changed->m); 148 | LONGS_EQUAL( 7, changed->d); 149 | LONGS_EQUAL( 1, changed->h); 150 | LONGS_EQUAL(30, changed->i); 151 | LONGS_EQUAL( 0, changed->s); 152 | LONGS_EQUAL(1, changed->dst); 153 | STRCMP_EQUAL("EDT", changed->tz_abbr); 154 | } 155 | 156 | TEST(php_rfc_interval, ba7) 157 | { 158 | test_add_wall("2010-11-06 01:30:00", "P1D", "America/New_York"); 159 | 160 | LONGS_EQUAL(2010, changed->y); 161 | LONGS_EQUAL(11, changed->m); 162 | LONGS_EQUAL( 7, changed->d); 163 | LONGS_EQUAL( 1, changed->h); 164 | LONGS_EQUAL(30, changed->i); 165 | LONGS_EQUAL( 0, changed->s); 166 | LONGS_EQUAL(1, changed->dst); 167 | STRCMP_EQUAL("EDT", changed->tz_abbr); 168 | } 169 | 170 | TEST(php_rfc_interval, ba8) 171 | { 172 | test_add_wall("2010-11-06 01:30:00", "P1DT1H", "America/New_York"); 173 | 174 | LONGS_EQUAL(2010, changed->y); 175 | LONGS_EQUAL(11, changed->m); 176 | LONGS_EQUAL( 7, changed->d); 177 | LONGS_EQUAL( 1, changed->h); 178 | LONGS_EQUAL(30, changed->i); 179 | LONGS_EQUAL( 0, changed->s); 180 | LONGS_EQUAL(0, changed->dst); 181 | STRCMP_EQUAL("EST", changed->tz_abbr); 182 | } 183 | 184 | TEST(php_rfc_interval, ba9) 185 | { 186 | test_add_wall("2010-11-06 04:30:00", "PT25H", "America/New_York"); 187 | 188 | LONGS_EQUAL(2010, changed->y); 189 | LONGS_EQUAL(11, changed->m); 190 | LONGS_EQUAL( 7, changed->d); 191 | LONGS_EQUAL( 4, changed->h); 192 | LONGS_EQUAL(30, changed->i); 193 | LONGS_EQUAL( 0, changed->s); 194 | LONGS_EQUAL(0, changed->dst); 195 | STRCMP_EQUAL("EST", changed->tz_abbr); 196 | } 197 | 198 | TEST(php_rfc_interval, ba10) 199 | { 200 | test_add_wall("2010-11-06 03:30:00", "P1D", "America/New_York"); 201 | 202 | LONGS_EQUAL(2010, changed->y); 203 | LONGS_EQUAL(11, changed->m); 204 | LONGS_EQUAL( 7, changed->d); 205 | LONGS_EQUAL( 3, changed->h); 206 | LONGS_EQUAL(30, changed->i); 207 | LONGS_EQUAL( 0, changed->s); 208 | LONGS_EQUAL(0, changed->dst); 209 | STRCMP_EQUAL("EST", changed->tz_abbr); 210 | } 211 | 212 | TEST(php_rfc_interval, ba11) 213 | { 214 | test_add_wall("2010-11-06 02:30:00", "P1D", "America/New_York"); 215 | 216 | LONGS_EQUAL(2010, changed->y); 217 | LONGS_EQUAL(11, changed->m); 218 | LONGS_EQUAL( 7, changed->d); 219 | LONGS_EQUAL( 2, changed->h); 220 | LONGS_EQUAL(30, changed->i); 221 | LONGS_EQUAL( 0, changed->s); 222 | LONGS_EQUAL(0, changed->dst); 223 | STRCMP_EQUAL("EST", changed->tz_abbr); 224 | } 225 | 226 | TEST(php_rfc_interval, bs1) 227 | { 228 | test_sub_wall("@1289109600", "PT1S", "America/New_York"); 229 | 230 | LONGS_EQUAL(2010, changed->y); 231 | LONGS_EQUAL(11, changed->m); 232 | LONGS_EQUAL( 7, changed->d); 233 | LONGS_EQUAL( 1, changed->h); 234 | LONGS_EQUAL(59, changed->i); 235 | LONGS_EQUAL(59, changed->s); 236 | LONGS_EQUAL(1, changed->dst); 237 | STRCMP_EQUAL("EDT", changed->tz_abbr); 238 | } 239 | 240 | TEST(php_rfc_interval, bs2) 241 | { 242 | test_sub_wall("2010-11-07 04:30:00", "P1D", "America/New_York"); 243 | 244 | LONGS_EQUAL(2010, changed->y); 245 | LONGS_EQUAL(11, changed->m); 246 | LONGS_EQUAL( 6, changed->d); 247 | LONGS_EQUAL( 4, changed->h); 248 | LONGS_EQUAL(30, changed->i); 249 | LONGS_EQUAL( 0, changed->s); 250 | LONGS_EQUAL(1, changed->dst); 251 | STRCMP_EQUAL("EDT", changed->tz_abbr); 252 | } 253 | 254 | TEST(php_rfc_interval, bs3) 255 | { 256 | test_sub_wall("2010-11-07 03:30:00", "PT24H", "America/New_York"); 257 | 258 | LONGS_EQUAL(2010, changed->y); 259 | LONGS_EQUAL(11, changed->m); 260 | LONGS_EQUAL( 6, changed->d); 261 | LONGS_EQUAL( 4, changed->h); 262 | LONGS_EQUAL(30, changed->i); 263 | LONGS_EQUAL( 0, changed->s); 264 | LONGS_EQUAL(1, changed->dst); 265 | STRCMP_EQUAL("EDT", changed->tz_abbr); 266 | } 267 | 268 | TEST(php_rfc_interval, bs4) 269 | { 270 | test_sub_wall("2010-11-07 02:30:00", "PT23H", "America/New_York"); 271 | 272 | LONGS_EQUAL(2010, changed->y); 273 | LONGS_EQUAL(11, changed->m); 274 | LONGS_EQUAL( 6, changed->d); 275 | LONGS_EQUAL( 4, changed->h); 276 | LONGS_EQUAL(30, changed->i); 277 | LONGS_EQUAL( 0, changed->s); 278 | LONGS_EQUAL(1, changed->dst); 279 | STRCMP_EQUAL("EDT", changed->tz_abbr); 280 | } 281 | 282 | TEST(php_rfc_interval, bs5) 283 | { 284 | test_sub_wall("@1289111400", "PT22H", "America/New_York"); 285 | 286 | LONGS_EQUAL(2010, changed->y); 287 | LONGS_EQUAL(11, changed->m); 288 | LONGS_EQUAL( 6, changed->d); 289 | LONGS_EQUAL( 4, changed->h); 290 | LONGS_EQUAL(30, changed->i); 291 | LONGS_EQUAL( 0, changed->s); 292 | LONGS_EQUAL(1, changed->dst); 293 | STRCMP_EQUAL("EDT", changed->tz_abbr); 294 | } 295 | 296 | TEST(php_rfc_interval, bs6) 297 | { 298 | test_sub_wall("2010-11-07 01:30:00", "PT21H", "America/New_York"); 299 | 300 | LONGS_EQUAL(2010, changed->y); 301 | LONGS_EQUAL(11, changed->m); 302 | LONGS_EQUAL( 6, changed->d); 303 | LONGS_EQUAL( 4, changed->h); 304 | LONGS_EQUAL(30, changed->i); 305 | LONGS_EQUAL( 0, changed->s); 306 | LONGS_EQUAL(1, changed->dst); 307 | STRCMP_EQUAL("EDT", changed->tz_abbr); 308 | } 309 | 310 | TEST(php_rfc_interval, bs7) 311 | { 312 | test_sub_wall("2010-11-07 01:30:00", "P1D", "America/New_York"); 313 | 314 | LONGS_EQUAL(2010, changed->y); 315 | LONGS_EQUAL(11, changed->m); 316 | LONGS_EQUAL( 6, changed->d); 317 | LONGS_EQUAL( 1, changed->h); 318 | LONGS_EQUAL(30, changed->i); 319 | LONGS_EQUAL( 0, changed->s); 320 | LONGS_EQUAL(1, changed->dst); 321 | STRCMP_EQUAL("EDT", changed->tz_abbr); 322 | } 323 | 324 | TEST(php_rfc_interval, bs8) 325 | { 326 | test_sub_wall("@1289111400", "P1DT1H", "America/New_York"); 327 | 328 | LONGS_EQUAL(2010, changed->y); 329 | LONGS_EQUAL(11, changed->m); 330 | LONGS_EQUAL( 6, changed->d); 331 | LONGS_EQUAL( 0, changed->h); 332 | LONGS_EQUAL(30, changed->i); 333 | LONGS_EQUAL( 0, changed->s); 334 | LONGS_EQUAL(1, changed->dst); 335 | STRCMP_EQUAL("EDT", changed->tz_abbr); 336 | } 337 | 338 | TEST(php_rfc_interval, bs9) 339 | { 340 | test_sub_wall("2010-11-07 03:30:00", "P1D", "America/New_York"); 341 | 342 | LONGS_EQUAL(2010, changed->y); 343 | LONGS_EQUAL(11, changed->m); 344 | LONGS_EQUAL( 6, changed->d); 345 | LONGS_EQUAL( 3, changed->h); 346 | LONGS_EQUAL(30, changed->i); 347 | LONGS_EQUAL( 0, changed->s); 348 | LONGS_EQUAL(1, changed->dst); 349 | STRCMP_EQUAL("EDT", changed->tz_abbr); 350 | } 351 | 352 | TEST(php_rfc_interval, bs10) 353 | { 354 | test_sub_wall("2010-11-07 02:30:00", "P1D", "America/New_York"); 355 | 356 | LONGS_EQUAL(2010, changed->y); 357 | LONGS_EQUAL(11, changed->m); 358 | LONGS_EQUAL( 6, changed->d); 359 | LONGS_EQUAL( 2, changed->h); 360 | LONGS_EQUAL(30, changed->i); 361 | LONGS_EQUAL( 0, changed->s); 362 | LONGS_EQUAL(1, changed->dst); 363 | STRCMP_EQUAL("EDT", changed->tz_abbr); 364 | } 365 | 366 | -------------------------------------------------------------------------------- /tests/c/issue0120.cpp: -------------------------------------------------------------------------------- 1 | #include "CppUTest/TestHarness.h" 2 | #include "timelib.h" 3 | #include "timelib_private.h" 4 | #include 5 | 6 | TEST_GROUP(issue120) 7 | { 8 | int dummy_error; 9 | timelib_time *t; 10 | timelib_tzinfo *tzi; 11 | 12 | TEST_SETUP() 13 | { 14 | dummy_error = 0; 15 | t = NULL; 16 | tzi = NULL; 17 | } 18 | 19 | void test_set_timestamp(timelib_sll ts, const char *tzid) 20 | { 21 | t = timelib_strtotime("now", sizeof("now"), NULL, timelib_builtin_db(), timelib_parse_tzfile); 22 | tzi = timelib_parse_tzfile((char*) tzid, timelib_builtin_db(), &dummy_error); 23 | t->tz_info = tzi; 24 | t->zone_type = TIMELIB_ZONETYPE_ID; 25 | timelib_unixtime2local(t, ts); 26 | timelib_update_ts(t, NULL); 27 | } 28 | 29 | void test_set_timezone(const char *ts, const char *tzid) 30 | { 31 | t = timelib_parse_from_format("U", ts, sizeof(ts), NULL, timelib_builtin_db(), timelib_parse_tzfile); 32 | tzi = timelib_parse_tzfile((char*) tzid, timelib_builtin_db(), &dummy_error); 33 | timelib_set_timezone(t, tzi); 34 | timelib_unixtime2local(t, t->sse); 35 | timelib_update_ts(t, NULL); 36 | } 37 | 38 | TEST_TEARDOWN() 39 | { 40 | timelib_time_dtor(t); 41 | timelib_tzinfo_dtor(tzi); 42 | } 43 | }; 44 | 45 | #define ISSUE120_TESTS(n,ts,tzid,exp_offset) \ 46 | TEST(issue120, n ## _set_timestamp) \ 47 | { \ 48 | test_set_timestamp(ts, tzid); \ 49 | LONGS_EQUAL(exp_offset, t->z); \ 50 | LONGS_EQUAL(ts, t->sse); \ 51 | } \ 52 | \ 53 | TEST(issue120, n ## _set_timezone) \ 54 | { \ 55 | test_set_timezone(#ts, tzid); \ 56 | LONGS_EQUAL(exp_offset, t->z); \ 57 | LONGS_EQUAL(ts, t->sse); \ 58 | } \ 59 | 60 | // around transition to DST 61 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_01, 1615687199, "America/Los_Angeles", -28800) 62 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_02, 1615687200, "America/Los_Angeles", -28800) 63 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_03, 1615687201, "America/Los_Angeles", -28800) 64 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_04, 1615690799, "America/Los_Angeles", -28800) 65 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_05, 1615690800, "America/Los_Angeles", -28800) 66 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_06, 1615690801, "America/Los_Angeles", -28800) 67 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_07, 1615708799, "America/Los_Angeles", -28800) 68 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_08, 1615708800, "America/Los_Angeles", -28800) 69 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_09, 1615708801, "America/Los_Angeles", -28800) 70 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_10, 1615712399, "America/Los_Angeles", -28800) 71 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_11, 1615712400, "America/Los_Angeles", -28800) 72 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_12, 1615712401, "America/Los_Angeles", -28800) 73 | ISSUE120_TESTS(php_bug_68549_la_before_trans_to_dst_13, 1615715999, "America/Los_Angeles", -28800) 74 | ISSUE120_TESTS(php_bug_68549_la_after_trans_to_dst_1, 1615716000, "America/Los_Angeles", -25200) 75 | ISSUE120_TESTS(php_bug_68549_la_after_trans_to_dst_2, 1615716001, "America/Los_Angeles", -25200) 76 | ISSUE120_TESTS(php_bug_68549_la_after_trans_to_dst_3, 1615719599, "America/Los_Angeles", -25200) 77 | ISSUE120_TESTS(php_bug_68549_la_after_trans_to_dst_4, 1615719600, "America/Los_Angeles", -25200) 78 | ISSUE120_TESTS(php_bug_68549_la_after_trans_to_dst_5, 1615719601, "America/Los_Angeles", -25200) 79 | 80 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_to_dst_1, 1616889599, "Atlantic/Azores", -3600) 81 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_to_dst_2, 1616889600, "Atlantic/Azores", -3600) 82 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_to_dst_3, 1616889601, "Atlantic/Azores", -3600) 83 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_to_dst_4, 1616893199, "Atlantic/Azores", -3600) 84 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_to_dst_1, 1616893200, "Atlantic/Azores", 0) 85 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_to_dst_2, 1616893201, "Atlantic/Azores", 0) 86 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_to_dst_3, 1616896799, "Atlantic/Azores", 0) 87 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_to_dst_4, 1616896800, "Atlantic/Azores", 0) 88 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_to_dst_5, 1616896801, "Atlantic/Azores", 0) 89 | 90 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_to_dst_1, 1616889599, "Europe/London", 0) 91 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_to_dst_2, 1616889600, "Europe/London", 0) 92 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_to_dst_3, 1616889601, "Europe/London", 0) 93 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_to_dst_4, 1616893199, "Europe/London", 0) 94 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_to_dst_1, 1616893200, "Europe/London", 3600) 95 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_to_dst_2, 1616893201, "Europe/London", 3600) 96 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_to_dst_3, 1616896799, "Europe/London", 3600) 97 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_to_dst_4, 1616896800, "Europe/London", 3600) 98 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_to_dst_5, 1616896801, "Europe/London", 3600) 99 | 100 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_1, 1616885999, "Europe/Amsterdam", 3600) 101 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_2, 1616886000, "Europe/Amsterdam", 3600) 102 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_3, 1616886001, "Europe/Amsterdam", 3600) 103 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_4, 1616889599, "Europe/Amsterdam", 3600) 104 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_5, 1616889600, "Europe/Amsterdam", 3600) 105 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_6, 1616889601, "Europe/Amsterdam", 3600) 106 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_to_dst_7, 1616893199, "Europe/Amsterdam", 3600) 107 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_1, 1616893200, "Europe/Amsterdam", 7200) 108 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_2, 1616893201, "Europe/Amsterdam", 7200) 109 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_3, 1616896799, "Europe/Amsterdam", 7200) 110 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_4, 1616896800, "Europe/Amsterdam", 7200) 111 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_5, 1616896801, "Europe/Amsterdam", 7200) 112 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_6, 1616900399, "Europe/Amsterdam", 7200) 113 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_7, 1616900400, "Europe/Amsterdam", 7200) 114 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_to_dst_8, 1616900401, "Europe/Amsterdam", 7200) 115 | 116 | // around transition back from DST 117 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_01, 1636246799, "America/Los_Angeles", -25200) 118 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_02, 1636246800, "America/Los_Angeles", -25200) 119 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_03, 1636246801, "America/Los_Angeles", -25200) 120 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_04, 1636250399, "America/Los_Angeles", -25200) 121 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_05, 1636250400, "America/Los_Angeles", -25200) 122 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_06, 1636250401, "America/Los_Angeles", -25200) 123 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_07, 1636268399, "America/Los_Angeles", -25200) 124 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_08, 1636268400, "America/Los_Angeles", -25200) 125 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_09, 1636268401, "America/Los_Angeles", -25200) 126 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_10, 1636271999, "America/Los_Angeles", -25200) 127 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_11, 1636272000, "America/Los_Angeles", -25200) 128 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_12, 1636272001, "America/Los_Angeles", -25200) 129 | ISSUE120_TESTS(php_bug_68549_la_before_trans_from_dst_13, 1636275599, "America/Los_Angeles", -25200) 130 | ISSUE120_TESTS(php_bug_68549_la_after_trans_from_dst_01, 1636275600, "America/Los_Angeles", -28800) 131 | ISSUE120_TESTS(php_bug_68549_la_after_trans_from_dst_02, 1636275601, "America/Los_Angeles", -28800) 132 | ISSUE120_TESTS(php_bug_68549_la_after_trans_from_dst_03, 1636279199, "America/Los_Angeles", -28800) 133 | ISSUE120_TESTS(php_bug_68549_la_after_trans_from_dst_04, 1636279200, "America/Los_Angeles", -28800) 134 | ISSUE120_TESTS(php_bug_68549_la_after_trans_from_dst_05, 1636279201, "America/Los_Angeles", -28800) 135 | 136 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_from_dst_01, 1635638399, "Atlantic/Azores", 0) 137 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_from_dst_02, 1635638400, "Atlantic/Azores", 0) 138 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_from_dst_03, 1635638401, "Atlantic/Azores", 0) 139 | ISSUE120_TESTS(php_bug_68549_azo_before_trans_from_dst_04, 1635641999, "Atlantic/Azores", 0) 140 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_from_dst_01, 1635642000, "Atlantic/Azores", -3600) 141 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_from_dst_02, 1635642001, "Atlantic/Azores", -3600) 142 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_from_dst_03, 1635645599, "Atlantic/Azores", -3600) 143 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_from_dst_04, 1635645600, "Atlantic/Azores", -3600) 144 | ISSUE120_TESTS(php_bug_68549_azo_after_trans_from_dst_05, 1635645601, "Atlantic/Azores", -3600) 145 | 146 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_from_dst_01, 1635638399, "Europe/London", 3600) 147 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_from_dst_02, 1635638400, "Europe/London", 3600) 148 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_from_dst_03, 1635638401, "Europe/London", 3600) 149 | ISSUE120_TESTS(php_bug_68549_ldn_before_trans_from_dst_04, 1635641999, "Europe/London", 3600) 150 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_from_dst_01, 1635642000, "Europe/London", 0) 151 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_from_dst_02, 1635642001, "Europe/London", 0) 152 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_from_dst_03, 1635645599, "Europe/London", 0) 153 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_from_dst_04, 1635645600, "Europe/London", 0) 154 | ISSUE120_TESTS(php_bug_68549_ldn_after_trans_from_dst_05, 1635645601, "Europe/London", 0) 155 | 156 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_01, 1635631199, "Europe/Amsterdam", 7200) 157 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_02, 1635631200, "Europe/Amsterdam", 7200) 158 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_03, 1635631201, "Europe/Amsterdam", 7200) 159 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_04, 1635638399, "Europe/Amsterdam", 7200) 160 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_05, 1635638400, "Europe/Amsterdam", 7200) 161 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_06, 1635638401, "Europe/Amsterdam", 7200) 162 | ISSUE120_TESTS(php_bug_68549_ams_before_trans_from_dst_07, 1635641999, "Europe/Amsterdam", 7200) 163 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_01, 1635642000, "Europe/Amsterdam", 3600) 164 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_02, 1635642001, "Europe/Amsterdam", 3600) 165 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_03, 1635645599, "Europe/Amsterdam", 3600) 166 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_04, 1635645600, "Europe/Amsterdam", 3600) 167 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_05, 1635645601, "Europe/Amsterdam", 3600) 168 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_06, 1635649199, "Europe/Amsterdam", 3600) 169 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_07, 1635649200, "Europe/Amsterdam", 3600) 170 | ISSUE120_TESTS(php_bug_68549_ams_after_trans_from_dst_08, 1635649201, "Europe/Amsterdam", 3600) 171 | -------------------------------------------------------------------------------- /parse_iso_intervals.re: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2019 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | #include "timelib.h" 26 | #include "timelib_private.h" 27 | 28 | #include 29 | 30 | #define EOI 257 31 | 32 | #define TIMELIB_PERIOD 260 33 | #define TIMELIB_ISO_DATE 261 34 | #define TIMELIB_ERROR 999 35 | 36 | typedef unsigned char uchar; 37 | 38 | #define BSIZE 8192 39 | 40 | #define YYCTYPE uchar 41 | #define YYCURSOR cursor 42 | #define YYLIMIT s->lim 43 | #define YYMARKER s->ptr 44 | #define YYFILL(n) return EOI; 45 | 46 | #define RET(i) {s->cur = cursor; return i;} 47 | 48 | #define timelib_string_free timelib_free 49 | 50 | #define TIMELIB_INIT s->cur = cursor; str = timelib_string(s); ptr = str 51 | #define TIMELIB_DEINIT timelib_string_free(str) 52 | 53 | #ifdef DEBUG_PARSER 54 | #define DEBUG_OUTPUT(s) printf("%s\n", s); 55 | #define YYDEBUG(s,c) { if (s != -1) { printf("state: %d ", s); printf("[%c]\n", c); } } 56 | #else 57 | #define DEBUG_OUTPUT(s) 58 | #define YYDEBUG(s,c) 59 | #endif 60 | 61 | typedef struct _Scanner { 62 | int fd; 63 | uchar *lim, *str, *ptr, *cur, *tok, *pos; 64 | unsigned int line, len; 65 | timelib_error_container *errors; 66 | 67 | timelib_time *begin; 68 | timelib_time *end; 69 | timelib_rel_time *period; 70 | int recurrences; 71 | 72 | int have_period; 73 | int have_recurrences; 74 | int have_date; 75 | int have_begin_date; 76 | int have_end_date; 77 | } Scanner; 78 | 79 | static void add_error(Scanner *s, const char *error) 80 | { 81 | s->errors->error_count++; 82 | s->errors->error_messages = timelib_realloc(s->errors->error_messages, s->errors->error_count * sizeof(timelib_error_message)); 83 | s->errors->error_messages[s->errors->error_count - 1].position = s->tok ? s->tok - s->str : 0; 84 | s->errors->error_messages[s->errors->error_count - 1].character = s->tok ? *s->tok : 0; 85 | s->errors->error_messages[s->errors->error_count - 1].message = timelib_strdup(error); 86 | } 87 | 88 | static char *timelib_string(Scanner *s) 89 | { 90 | char *tmp = timelib_calloc(1, s->cur - s->tok + 1); 91 | memcpy(tmp, s->tok, s->cur - s->tok); 92 | 93 | return tmp; 94 | } 95 | 96 | static timelib_sll timelib_get_nr(const char **ptr, int max_length) 97 | { 98 | const char *begin, *end; 99 | char *str; 100 | timelib_sll tmp_nr = TIMELIB_UNSET; 101 | int len = 0; 102 | 103 | while ((**ptr < '0') || (**ptr > '9')) { 104 | if (**ptr == '\0') { 105 | return TIMELIB_UNSET; 106 | } 107 | ++*ptr; 108 | } 109 | begin = *ptr; 110 | while ((**ptr >= '0') && (**ptr <= '9') && len < max_length) { 111 | ++*ptr; 112 | ++len; 113 | } 114 | end = *ptr; 115 | str = timelib_calloc(1, end - begin + 1); 116 | memcpy(str, begin, end - begin); 117 | tmp_nr = strtoll(str, NULL, 10); 118 | timelib_free(str); 119 | return tmp_nr; 120 | } 121 | 122 | static timelib_ull timelib_get_unsigned_nr(const char **ptr, int max_length) 123 | { 124 | timelib_ull dir = 1; 125 | 126 | while (((**ptr < '0') || (**ptr > '9')) && (**ptr != '+') && (**ptr != '-')) { 127 | if (**ptr == '\0') { 128 | return TIMELIB_UNSET; 129 | } 130 | ++*ptr; 131 | } 132 | 133 | while (**ptr == '+' || **ptr == '-') 134 | { 135 | if (**ptr == '-') { 136 | dir *= -1; 137 | } 138 | ++*ptr; 139 | } 140 | return dir * timelib_get_nr(ptr, max_length); 141 | } 142 | 143 | #define timelib_split_free(arg) { \ 144 | int i; \ 145 | for (i = 0; i < arg.c; i++) { \ 146 | timelib_free(arg.v[i]); \ 147 | } \ 148 | if (arg.v) { \ 149 | timelib_free(arg.v); \ 150 | } \ 151 | } 152 | 153 | /* date parser's scan function too large for VC6 - VC7.x 154 | drop the optimization solves the problem */ 155 | #ifdef PHP_WIN32 156 | #pragma optimize( "", off ) 157 | #endif 158 | static int scan(Scanner *s) 159 | { 160 | uchar *cursor = s->cur; 161 | char *str; 162 | const char *ptr = NULL; 163 | 164 | std: 165 | s->tok = cursor; 166 | s->len = 0; 167 | /*!re2c 168 | 169 | /* */ 170 | any = [\000-\377]; 171 | number = [0-9]+; 172 | 173 | hour24lz = [01][0-9] | "2"[0-4]; 174 | minutelz = [0-5][0-9]; 175 | monthlz = "0" [1-9] | "1" [0-2]; 176 | monthlzz = "0" [0-9] | "1" [0-2]; 177 | daylz = "0" [1-9] | [1-2][0-9] | "3" [01]; 178 | daylzz = "0" [0-9] | [1-2][0-9] | "3" [01]; 179 | secondlz = minutelz; 180 | year4 = [0-9]{4}; 181 | weekofyear = "0"[1-9] | [1-4][0-9] | "5"[0-3]; 182 | 183 | space = [ \t]+; 184 | datetimebasic = year4 monthlz daylz "T" hour24lz minutelz secondlz "Z"; 185 | datetimeextended = year4 "-" monthlz "-" daylz "T" hour24lz ':' minutelz ':' secondlz "Z"; 186 | period = "P" (number "Y")? (number "M")? (number "W")? (number "D")? ("T" (number "H")? (number "M")? (number "S")?)?; 187 | combinedrep = "P" year4 "-" monthlzz "-" daylzz "T" hour24lz ':' minutelz ':' secondlz; 188 | 189 | recurrences = "R" number; 190 | 191 | isoweekday = year4 "-"? "W" weekofyear "-"? [0-7]; 192 | isoweek = year4 "-"? "W" weekofyear; 193 | 194 | */ 195 | 196 | /*!re2c 197 | /* so that vim highlights correctly */ 198 | recurrences 199 | { 200 | DEBUG_OUTPUT("recurrences"); 201 | TIMELIB_INIT; 202 | ptr++; 203 | s->recurrences = timelib_get_unsigned_nr(&ptr, 9); 204 | TIMELIB_DEINIT; 205 | s->have_recurrences = 1; 206 | return TIMELIB_PERIOD; 207 | } 208 | 209 | datetimebasic| datetimeextended 210 | { 211 | timelib_time *current; 212 | 213 | if (s->have_date || s->have_period) { 214 | current = s->end; 215 | s->have_end_date = 1; 216 | } else { 217 | current = s->begin; 218 | s->have_begin_date = 1; 219 | } 220 | DEBUG_OUTPUT("datetimebasic | datetimeextended"); 221 | TIMELIB_INIT; 222 | current->y = timelib_get_nr(&ptr, 4); 223 | current->m = timelib_get_nr(&ptr, 2); 224 | current->d = timelib_get_nr(&ptr, 2); 225 | current->h = timelib_get_nr(&ptr, 2); 226 | current->i = timelib_get_nr(&ptr, 2); 227 | current->s = timelib_get_nr(&ptr, 2); 228 | s->have_date = 1; 229 | TIMELIB_DEINIT; 230 | return TIMELIB_ISO_DATE; 231 | } 232 | 233 | period 234 | { 235 | timelib_sll nr; 236 | int in_time = 0; 237 | DEBUG_OUTPUT("period"); 238 | TIMELIB_INIT; 239 | ptr++; 240 | do { 241 | if ( *ptr == 'T' ) { 242 | in_time = 1; 243 | ptr++; 244 | } 245 | if ( *ptr == '\0' ) { 246 | add_error(s, "Missing expected time part"); 247 | break; 248 | } 249 | 250 | nr = timelib_get_unsigned_nr(&ptr, 12); 251 | switch (*ptr) { 252 | case 'Y': s->period->y = nr; break; 253 | case 'W': s->period->d += nr * 7; break; 254 | case 'D': s->period->d += nr; break; 255 | case 'H': s->period->h = nr; break; 256 | case 'S': s->period->s = nr; break; 257 | case 'M': 258 | if (in_time) { 259 | s->period->i = nr; 260 | } else { 261 | s->period->m = nr; 262 | } 263 | break; 264 | default: 265 | add_error(s, "Undefined period specifier"); 266 | break; 267 | } 268 | ptr++; 269 | } while (!s->errors->error_count && *ptr); 270 | s->have_period = 1; 271 | TIMELIB_DEINIT; 272 | return TIMELIB_PERIOD; 273 | } 274 | 275 | combinedrep 276 | { 277 | DEBUG_OUTPUT("combinedrep"); 278 | TIMELIB_INIT; 279 | s->period->y = timelib_get_unsigned_nr(&ptr, 4); 280 | ptr++; 281 | s->period->m = timelib_get_unsigned_nr(&ptr, 2); 282 | ptr++; 283 | s->period->d = timelib_get_unsigned_nr(&ptr, 2); 284 | ptr++; 285 | s->period->h = timelib_get_unsigned_nr(&ptr, 2); 286 | ptr++; 287 | s->period->i = timelib_get_unsigned_nr(&ptr, 2); 288 | ptr++; 289 | s->period->s = timelib_get_unsigned_nr(&ptr, 2); 290 | s->have_period = 1; 291 | TIMELIB_DEINIT; 292 | return TIMELIB_PERIOD; 293 | } 294 | 295 | [ .,\t/] 296 | { 297 | goto std; 298 | } 299 | 300 | "\000"|"\n" 301 | { 302 | s->pos = cursor; s->line++; 303 | goto std; 304 | } 305 | 306 | any 307 | { 308 | add_error(s, "Unexpected character"); 309 | goto std; 310 | } 311 | */ 312 | } 313 | #ifdef PHP_WIN32 314 | #pragma optimize( "", on ) 315 | #endif 316 | 317 | /*!max:re2c */ 318 | 319 | void timelib_strtointerval(const char *s, size_t len, 320 | timelib_time **begin, timelib_time **end, 321 | timelib_rel_time **period, int *recurrences, 322 | timelib_error_container **errors) 323 | { 324 | Scanner in; 325 | int t; 326 | const char *e = s + len - 1; 327 | 328 | memset(&in, 0, sizeof(in)); 329 | in.errors = timelib_malloc(sizeof(timelib_error_container)); 330 | in.errors->warning_count = 0; 331 | in.errors->warning_messages = NULL; 332 | in.errors->error_count = 0; 333 | in.errors->error_messages = NULL; 334 | 335 | if (len > 0) { 336 | while (isspace(*s) && s < e) { 337 | s++; 338 | } 339 | while (isspace(*e) && e > s) { 340 | e--; 341 | } 342 | } 343 | if (e - s < 0) { 344 | add_error(&in, "Empty string"); 345 | if (errors) { 346 | *errors = in.errors; 347 | } else { 348 | timelib_error_container_dtor(in.errors); 349 | } 350 | return; 351 | } 352 | e++; 353 | 354 | /* init cursor */ 355 | in.str = timelib_malloc((e - s) + YYMAXFILL); 356 | memset(in.str, 0, (e - s) + YYMAXFILL); 357 | memcpy(in.str, s, (e - s)); 358 | in.lim = in.str + (e - s) + YYMAXFILL; 359 | in.cur = in.str; 360 | 361 | /* init value containers */ 362 | in.begin = timelib_time_ctor(); 363 | in.begin->y = TIMELIB_UNSET; 364 | in.begin->d = TIMELIB_UNSET; 365 | in.begin->m = TIMELIB_UNSET; 366 | in.begin->h = TIMELIB_UNSET; 367 | in.begin->i = TIMELIB_UNSET; 368 | in.begin->s = TIMELIB_UNSET; 369 | in.begin->us = 0; 370 | in.begin->z = 0; 371 | in.begin->dst = 0; 372 | in.begin->is_localtime = 0; 373 | in.begin->zone_type = TIMELIB_ZONETYPE_OFFSET; 374 | 375 | in.end = timelib_time_ctor(); 376 | in.end->y = TIMELIB_UNSET; 377 | in.end->d = TIMELIB_UNSET; 378 | in.end->m = TIMELIB_UNSET; 379 | in.end->h = TIMELIB_UNSET; 380 | in.end->i = TIMELIB_UNSET; 381 | in.end->s = TIMELIB_UNSET; 382 | in.end->us = 0; 383 | in.end->z = 0; 384 | in.end->dst = 0; 385 | in.end->is_localtime = 0; 386 | in.end->zone_type = TIMELIB_ZONETYPE_OFFSET; 387 | 388 | in.period = timelib_rel_time_ctor(); 389 | in.period->y = 0; 390 | in.period->d = 0; 391 | in.period->m = 0; 392 | in.period->h = 0; 393 | in.period->i = 0; 394 | in.period->s = 0; 395 | in.period->weekday = 0; 396 | in.period->weekday_behavior = 0; 397 | in.period->first_last_day_of = 0; 398 | in.period->days = TIMELIB_UNSET; 399 | 400 | in.recurrences = 1; 401 | 402 | do { 403 | t = scan(&in); 404 | #ifdef DEBUG_PARSER 405 | printf("%d\n", t); 406 | #endif 407 | } while(t != EOI); 408 | 409 | timelib_free(in.str); 410 | if (errors) { 411 | *errors = in.errors; 412 | } else { 413 | timelib_error_container_dtor(in.errors); 414 | } 415 | if (in.have_begin_date) { 416 | *begin = in.begin; 417 | } else { 418 | timelib_time_dtor(in.begin); 419 | } 420 | if (in.have_end_date) { 421 | *end = in.end; 422 | } else { 423 | timelib_time_dtor(in.end); 424 | } 425 | if (in.have_period) { 426 | *period = in.period; 427 | } else { 428 | timelib_rel_time_dtor(in.period); 429 | } 430 | if (in.have_recurrences) { 431 | *recurrences = in.recurrences; 432 | } 433 | } 434 | 435 | 436 | /* 437 | * vim: syntax=c 438 | */ 439 | -------------------------------------------------------------------------------- /timelib.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2019 Derick Rethans 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | * Portions copyright (c) 1998-2017 Zend Technologies Ltd. 25 | * 26 | * The timelib_strcasecmp and timelib_strncasecmp are taken from PHP's 27 | * Zend/zend_operators.[hc] source files. 28 | * 29 | */ 30 | 31 | #include "timelib.h" 32 | #include "timelib_private.h" 33 | #include 34 | #include 35 | 36 | #define TIMELIB_LLABS(y) (y < 0 ? (y * -1) : y) 37 | 38 | const char *timelib_error_messages[10] = { 39 | "No error", 40 | "Cannot allocate buffer for parsing", 41 | "Corrupt tzfile: The transitions in the file don't always increase", 42 | "Corrupt tzfile: The expected 64-bit preamble is missing", 43 | "Corrupt tzfile: No abbreviation could be found for a transition", 44 | "The version used in this timezone identifier is unsupported", 45 | "No timezone with this name could be found", 46 | "A 'slim' timezone file has been detected", 47 | "The embedded POSIX string is not valid", 48 | "The embedded POSIX string is empty" 49 | }; 50 | 51 | const char *timelib_get_error_message(int error_code) 52 | { 53 | int entries = sizeof(timelib_error_messages) / sizeof(char*); 54 | 55 | if (error_code >= 0 && error_code < entries) { 56 | return timelib_error_messages[error_code]; 57 | } 58 | return "Unknown error code"; 59 | } 60 | 61 | timelib_time* timelib_time_ctor(void) 62 | { 63 | timelib_time *t; 64 | t = timelib_calloc(1, sizeof(timelib_time)); 65 | 66 | return t; 67 | } 68 | 69 | void timelib_time_dtor(timelib_time* t) 70 | { 71 | TIMELIB_TIME_FREE(t->tz_abbr); 72 | TIMELIB_TIME_FREE(t); 73 | } 74 | 75 | int timelib_time_compare(timelib_time *t1, timelib_time *t2) 76 | { 77 | if (t1->sse == t2->sse) { 78 | if (t1->us == t2->us) { 79 | return 0; 80 | } 81 | 82 | return (t1->us < t2->us) ? -1 : 1; 83 | } 84 | 85 | return (t1->sse < t2->sse) ? -1 : 1; 86 | } 87 | 88 | timelib_time* timelib_time_clone(timelib_time *orig) 89 | { 90 | timelib_time *tmp = timelib_time_ctor(); 91 | memcpy(tmp, orig, sizeof(timelib_time)); 92 | if (orig->tz_abbr) { 93 | tmp->tz_abbr = timelib_strdup(orig->tz_abbr); 94 | } 95 | if (orig->tz_info) { 96 | tmp->tz_info = orig->tz_info; 97 | } 98 | return tmp; 99 | } 100 | 101 | timelib_rel_time* timelib_rel_time_ctor(void) 102 | { 103 | timelib_rel_time *t; 104 | t = timelib_calloc(1, sizeof(timelib_rel_time)); 105 | 106 | return t; 107 | } 108 | 109 | void timelib_rel_time_dtor(timelib_rel_time* t) 110 | { 111 | TIMELIB_TIME_FREE(t); 112 | } 113 | 114 | timelib_rel_time* timelib_rel_time_clone(timelib_rel_time *rel) 115 | { 116 | timelib_rel_time *tmp = timelib_rel_time_ctor(); 117 | memcpy(tmp, rel, sizeof(timelib_rel_time)); 118 | return tmp; 119 | } 120 | 121 | void timelib_time_tz_abbr_update(timelib_time* tm, const char* tz_abbr) 122 | { 123 | unsigned int i; 124 | size_t tz_abbr_len = strlen(tz_abbr); 125 | 126 | TIMELIB_TIME_FREE(tm->tz_abbr); 127 | tm->tz_abbr = timelib_strdup(tz_abbr); 128 | for (i = 0; i < tz_abbr_len; i++) { 129 | tm->tz_abbr[i] = toupper(tz_abbr[i]); 130 | } 131 | } 132 | 133 | timelib_time_offset* timelib_time_offset_ctor(void) 134 | { 135 | timelib_time_offset *t; 136 | t = timelib_calloc(1, sizeof(timelib_time_offset)); 137 | 138 | return t; 139 | } 140 | 141 | void timelib_time_offset_dtor(timelib_time_offset* t) 142 | { 143 | TIMELIB_TIME_FREE(t->abbr); 144 | TIMELIB_TIME_FREE(t); 145 | } 146 | 147 | char *timelib_get_tz_abbr_ptr(timelib_time *t) 148 | { 149 | if (!t->sse_uptodate) { 150 | timelib_update_ts(t, NULL); 151 | }; 152 | return t->tz_abbr; 153 | } 154 | 155 | void timelib_error_container_dtor(timelib_error_container *errors) 156 | { 157 | int i; 158 | 159 | for (i = 0; i < errors->warning_count; i++) { 160 | timelib_free(errors->warning_messages[i].message); 161 | } 162 | timelib_free(errors->warning_messages); 163 | for (i = 0; i < errors->error_count; i++) { 164 | timelib_free(errors->error_messages[i].message); 165 | } 166 | timelib_free(errors->error_messages); 167 | timelib_free(errors); 168 | } 169 | 170 | timelib_long timelib_date_to_int(timelib_time *d, int *error) 171 | { 172 | timelib_sll ts; 173 | 174 | ts = d->sse; 175 | 176 | if (ts < TIMELIB_LONG_MIN || ts > TIMELIB_LONG_MAX) { 177 | if (error) { 178 | *error = 1; 179 | } 180 | return 0; 181 | } 182 | if (error) { 183 | *error = 0; 184 | } 185 | return (timelib_long) d->sse; 186 | } 187 | 188 | void timelib_decimal_hour_to_hms(double h, int *hour, int *min, int *sec) 189 | { 190 | bool swap = false; 191 | int seconds; 192 | 193 | if (h < 0) { 194 | swap = true; 195 | h = fabs(h); 196 | } 197 | 198 | *hour = floor(h); 199 | seconds = floor((h - *hour) * 3600); 200 | 201 | *min = seconds / 60; 202 | *sec = seconds % 60; 203 | 204 | if (swap) { 205 | *hour = 0 - *hour; 206 | } 207 | } 208 | 209 | void timelib_hms_to_decimal_hour(int hour, int min, int sec, double *h) 210 | { 211 | if (hour >= 0) { 212 | *h = ((double)hour + (double)min / 60 + (double)sec / 3600); 213 | } else { 214 | *h = ((double)hour - (double)min / 60 - (double)sec / 3600); 215 | } 216 | } 217 | 218 | void timelib_hmsf_to_decimal_hour(int hour, int min, int sec, int us, double *h) 219 | { 220 | if (hour >= 0) { 221 | *h = ((double)hour + (double)min / MINS_PER_HOUR + (double)sec / SECS_PER_HOUR) + (double)us / USECS_PER_HOUR; 222 | } else { 223 | *h = ((double)hour - (double)min / MINS_PER_HOUR - (double)sec / SECS_PER_HOUR) - (double)us / USECS_PER_HOUR; 224 | } 225 | } 226 | 227 | timelib_sll timelib_hms_to_seconds(timelib_sll h, timelib_sll m, timelib_sll s) 228 | { 229 | return (h * SECS_PER_HOUR) + (m * 60) + s; 230 | } 231 | 232 | static const unsigned char timelib_tolower_map[256] = { 233 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 234 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 235 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 236 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 237 | 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 238 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 239 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 240 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 241 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 242 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 243 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 244 | 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 245 | 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 246 | 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 247 | 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 248 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff 249 | }; 250 | 251 | #define timelib_tolower(c) (timelib_tolower_map[(unsigned char)(c)]) 252 | #undef MIN 253 | #undef MAX 254 | #define MAX(a, b) (((a)>(b))?(a):(b)) 255 | #define MIN(a, b) (((a)<(b))?(a):(b)) 256 | 257 | int timelib_strcasecmp(const char *s1, const char *s2) 258 | { 259 | size_t len; 260 | size_t len1 = strlen(s1); 261 | size_t len2 = strlen(s2); 262 | int c1, c2; 263 | 264 | if (s1 == s2) { 265 | return 0; 266 | } 267 | 268 | len = MIN(len1, len2); 269 | while (len--) { 270 | c1 = timelib_tolower(*(unsigned char *)s1++); 271 | c2 = timelib_tolower(*(unsigned char *)s2++); 272 | if (c1 != c2) { 273 | return c1 - c2; 274 | } 275 | } 276 | 277 | return (int)(len1 - len2); 278 | } 279 | 280 | int timelib_strncasecmp(const char *s1, const char *s2, size_t length) 281 | { 282 | size_t len; 283 | size_t len1 = strlen(s1); 284 | size_t len2 = strlen(s2); 285 | int c1, c2; 286 | 287 | if (s1 == s2) { 288 | return 0; 289 | } 290 | len = MIN(length, MIN(len1, len2)); 291 | while (len--) { 292 | c1 = timelib_tolower(*(unsigned char *)s1++); 293 | c2 = timelib_tolower(*(unsigned char *)s2++); 294 | if (c1 != c2) { 295 | return c1 - c2; 296 | } 297 | } 298 | 299 | return (int)(MIN(length, len1) - MIN(length, len2)); 300 | } 301 | 302 | #undef MIN 303 | #undef MAX 304 | 305 | void timelib_dump_date(timelib_time *d, int options) 306 | { 307 | if ((options & 2) == 2) { 308 | printf("TYPE: %d ", d->zone_type); 309 | } 310 | printf("TS: %lld | %s%04lld-%02lld-%02lld %02lld:%02lld:%02lld", 311 | d->sse, d->y < 0 ? "-" : "", TIMELIB_LLABS(d->y), d->m, d->d, d->h, d->i, d->s); 312 | if (d->us > 0) { 313 | printf(" 0.%06lld", d->us); 314 | } 315 | 316 | if (d->is_localtime) { 317 | switch (d->zone_type) { 318 | case TIMELIB_ZONETYPE_OFFSET: /* Only offset */ 319 | printf(" GMT %05d%s", d->z, d->dst == 1 ? " (DST)" : ""); 320 | break; 321 | case TIMELIB_ZONETYPE_ID: /* Timezone struct */ 322 | /* Show abbreviation if wanted */ 323 | if (d->tz_abbr) { 324 | printf(" %s", d->tz_abbr); 325 | } 326 | /* Do we have a TimeZone struct? */ 327 | if (d->tz_info) { 328 | printf(" %s", d->tz_info->name); 329 | } 330 | break; 331 | case TIMELIB_ZONETYPE_ABBR: 332 | printf(" %s", d->tz_abbr); 333 | printf(" %05d%s", d->z, d->dst == 1 ? " (DST)" : ""); 334 | break; 335 | } 336 | } 337 | 338 | if ((options & 1) == 1) { 339 | if (d->have_relative) { 340 | printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS", 341 | d->relative.y, d->relative.m, d->relative.d, d->relative.h, d->relative.i, d->relative.s); 342 | if (d->relative.us) { 343 | printf(" 0.%06lld", d->relative.us); 344 | } 345 | if (d->relative.first_last_day_of != 0) { 346 | switch (d->relative.first_last_day_of) { 347 | case 1: 348 | printf(" / first day of"); 349 | break; 350 | case 2: 351 | printf(" / last day of"); 352 | break; 353 | } 354 | } 355 | if (d->relative.have_weekday_relative) { 356 | printf(" / %d.%d", d->relative.weekday, d->relative.weekday_behavior); 357 | } 358 | if (d->relative.have_special_relative) { 359 | switch (d->relative.special.type) { 360 | case TIMELIB_SPECIAL_WEEKDAY: 361 | printf(" / %lld weekday", d->relative.special.amount); 362 | break; 363 | case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH: 364 | printf(" / x y of z month"); 365 | break; 366 | case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH: 367 | printf(" / last y of z month"); 368 | break; 369 | } 370 | } 371 | } 372 | } 373 | printf("\n"); 374 | } 375 | 376 | void timelib_dump_rel_time(timelib_rel_time *d) 377 | { 378 | printf("%3lldY %3lldM %3lldD / %3lldH %3lldM %3lldS (days: %lld)%s", 379 | d->y, d->m, d->d, d->h, d->i, d->s, d->days, d->invert ? " inverted" : ""); 380 | if (d->first_last_day_of != 0) { 381 | switch (d->first_last_day_of) { 382 | case 1: 383 | printf(" / first day of"); 384 | break; 385 | case 2: 386 | printf(" / last day of"); 387 | break; 388 | } 389 | } 390 | printf("\n"); 391 | } 392 | --------------------------------------------------------------------------------