├── .github └── workflows │ └── c-cpp.yml ├── .travis.yml ├── t ├── nth_dow.c ├── easter_western.c ├── easter_orthodox.c ├── add_months.c ├── days_in_quarter.c ├── days_in_year.c ├── end_of_month.c ├── end_of_quarter.c ├── start_of_quarter.c ├── next_weekday.c ├── prev_weekday.c ├── start_of_month.c ├── zone.c ├── days_in_month.c ├── ywd.c ├── add_years.c ├── end_of_week.c ├── start_of_week.c ├── delta_yqd.c ├── tm.c ├── add_quarters.c ├── next_dow.c ├── prev_dow.c ├── nth_weekday_in_month.c ├── parse_iso_zone.c ├── end_of_year.c ├── start_of_year.c ├── nth_weekday_in_quarter.c ├── nth_weekday_in_year.c ├── char.c ├── ymd_epochs.c ├── is_holiday.c ├── is_workday.c ├── yd.c ├── parse_iso_zone_lenient.c ├── roll_workday.c ├── end_of_quarter.h ├── start_of_quarter.h ├── parse_iso_date.c ├── yqd.c ├── delta_yd.c ├── ymd.c ├── add_workdays.c ├── tap.h ├── delta_ymd.c ├── delta_weekdays.c └── delta_workdays.c ├── LICENCE ├── dt_tm.h ├── dt_length.h ├── dt_easter.h ├── dt_valid.h ├── dt_util.h ├── dt_search.h ├── dt.h ├── dt_length.c ├── dt_tm.c ├── dt_accessor.h ├── dt_valid.c ├── dt_navigate.h ├── dt_search.c ├── dt_easter.c ├── dt_config.h ├── dt_parse_iso.h ├── dt_zone.h ├── dt_arithmetic.h ├── dt_accessor.c ├── dt_util.c ├── dt_core.h ├── dt_weekday.h ├── dt_navigate.c ├── dt_dow.h ├── dt_workday.h ├── tools ├── zone.pl └── weekday.pl ├── dt_char.h ├── dt_zone.c ├── dt_dow.c ├── dt_core.c ├── dt_arithmetic.c └── Makefile /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Build 16 | run: make 17 | - name: Test 18 | run: make test 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | install: "perl -V" 3 | before_script: 4 | - "cpanm -n Test::Harness" 5 | - "cpanm -n Devel::Cover::Report::Coveralls" 6 | script: "make test" 7 | after_success: 8 | - "make clean" 9 | - "make cover" 10 | - "cover --no-gcov -report coveralls" 11 | notifications: 12 | recipients: 13 | - chansen@cpan.org 14 | email: 15 | on_success: change 16 | on_failure: always 17 | -------------------------------------------------------------------------------- /t/nth_dow.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "nth_dow.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 15 | dt_t got = dt_nth_dow(src, t.nth, t.dow); 16 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 17 | cmp_ok(got, "==", exp, "dt_nth_dow(%d (%.4d-%.2d-%.2d), %d, %d)", 18 | src, t.y, t.m, t.d, t.nth, t.dow); 19 | } 20 | 21 | } 22 | done_testing(); 23 | } 24 | -------------------------------------------------------------------------------- /t/easter_western.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "easter_western.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t exp = dt_from_ymd(t.y, t.m, t.d); 15 | dt_t got = dt_from_easter(t.y, DT_WESTERN); 16 | cmp_ok(got, "==", exp, "dt_from_easter(%d, DT_WESTERN)", t.y); 17 | } 18 | } 19 | 20 | { 21 | dt_t got = dt_from_easter(-1, DT_WESTERN); 22 | cmp_ok(got, "==", 0, "dt_from_easter(-1, DT_WESTERN)"); 23 | } 24 | 25 | { 26 | dt_t got = dt_from_easter(0, DT_WESTERN); 27 | cmp_ok(got, "==", 0, "dt_from_easter(0, DT_WESTERN)"); 28 | } 29 | 30 | done_testing(); 31 | } 32 | -------------------------------------------------------------------------------- /t/easter_orthodox.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "easter_orthodox.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t exp = dt_from_ymd(t.y, t.m, t.d); 15 | dt_t got = dt_from_easter(t.y, DT_ORTHODOX); 16 | cmp_ok(got, "==", exp, "dt_from_easter(%d, DT_ORTHODOX)", t.y); 17 | } 18 | } 19 | 20 | { 21 | dt_t got = dt_from_easter(-1, DT_ORTHODOX); 22 | cmp_ok(got, "==", 0, "dt_from_easter(-1, DT_ORTHODOX)"); 23 | } 24 | 25 | { 26 | dt_t got = dt_from_easter(0, DT_ORTHODOX); 27 | cmp_ok(got, "==", 0, "dt_from_easter(0, DT_ORTHODOX)"); 28 | } 29 | 30 | done_testing(); 31 | } 32 | -------------------------------------------------------------------------------- /t/add_months.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "add_months.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 15 | dt_t got = dt_add_months(src, t.delta, t.adjust); 16 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 17 | cmp_ok(got, "==", exp, "dt_add_months(%d (%.4d-%.2d-%.2d), %d, %d)", 18 | src, t.y, t.m, t.d, t.delta, t.adjust); 19 | } 20 | 21 | if (t.adjust != DT_EXCESS) { 22 | dt_t dt1 = dt_from_ymd(t.y, t.m, t.d); 23 | dt_t dt2 = dt_add_months(dt1, t.delta, t.adjust); 24 | int got = dt_delta_months(dt1, dt2, 0); 25 | cmp_ok(got, "==", t.delta, "dt_delta_months(%d, %d, false)", dt1, dt2); 26 | } 27 | } 28 | done_testing(); 29 | } 30 | -------------------------------------------------------------------------------- /t/days_in_quarter.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int q; 7 | int d; 8 | } tests[] = { 9 | { 2100, 1, 90 }, 10 | { 2100, 2, 91 }, 11 | { 2100, 3, 92 }, 12 | { 2100, 4, 92 }, 13 | { 2000, 1, 91 }, 14 | { 2000, 2, 91 }, 15 | { 2000, 3, 92 }, 16 | { 2000, 4, 92 }, 17 | { 2000, 5, 0 }, 18 | { 2000, 0, 0 }, 19 | { 2000, -1, 0 }, 20 | { 2000, 10, 0 }, 21 | }; 22 | 23 | int 24 | main() { 25 | int i, ntests; 26 | 27 | ntests = sizeof(tests) / sizeof(*tests); 28 | for (i = 0; i < ntests; i++) { 29 | const struct test t = tests[i]; 30 | 31 | { 32 | int got = dt_days_in_quarter(t.y, t.q); 33 | cmp_ok(got, "==", t.d, "dt_days_in_quarter(%d, %d)", t.y, t.q); 34 | } 35 | 36 | if (t.d != 0) { 37 | int got = dt_length_of_quarter(dt_from_yqd(t.y, t.q, 1)); 38 | cmp_ok(got, "==", t.d, "dt_length_of_quarter(%.4d-Q%d-01)", t.y, t.q); 39 | } 40 | } 41 | done_testing(); 42 | } 43 | -------------------------------------------------------------------------------- /t/days_in_year.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | bool leap; 7 | } tests[] = { 8 | { 400, 1 }, 9 | { 401, 0 }, 10 | { 800, 1 }, 11 | { 801, 0 }, 12 | { 1200, 1 }, 13 | { 1201, 0 }, 14 | { 1600, 1 }, 15 | { 1601, 0 }, 16 | { 1700, 0 }, 17 | { 1800, 0 }, 18 | { 1900, 0 }, 19 | { 2000, 1 }, 20 | { 2004, 1 }, 21 | { 2100, 0 } 22 | }; 23 | 24 | int 25 | main() { 26 | int i, ntests; 27 | 28 | ntests = sizeof(tests) / sizeof(*tests); 29 | for (i = 0; i < ntests; i++) { 30 | const struct test t = tests[i]; 31 | 32 | { 33 | int got = dt_days_in_year(t.y); 34 | cmp_ok(got, "==", 365 + t.leap, "dt_days_in_year(%d)", t.y); 35 | } 36 | 37 | { 38 | int got = dt_leap_year(t.y); 39 | cmp_ok(got, "==", (int)t.leap, "dt_leap_year(%d)", t.y); 40 | } 41 | 42 | { 43 | int got = dt_length_of_year(dt_from_yd(t.y, 1)); 44 | cmp_ok(got, "==", 365 + t.leap, "dt_length_of_year(%.4d-001)", t.y); 45 | } 46 | } 47 | done_testing(); 48 | } 49 | -------------------------------------------------------------------------------- /t/end_of_month.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "end_of_month.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 15 | dt_t got = dt_end_of_month(src, t.delta); 16 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 17 | cmp_ok(got, "==", exp, "dt_end_of_month(%d, %d)", src, t.delta); 18 | } 19 | 20 | { 21 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 22 | dt_t dt1 = dt_end_of_month(src, t.delta); 23 | dt_t dt2 = dt_start_of_month(src, t.delta + 1) - 1; 24 | cmp_ok(dt1, "==", dt2, "dt_end_of_month(%d, %d) == dt_start_of_month(%d, %d + 1) - 1", 25 | src, t.y, src, t.y); 26 | } 27 | 28 | { 29 | dt_t dt1 = dt_from_ymd(t.y, t.m, t.d); 30 | dt_t dt2 = dt_end_of_month(dt1, t.delta); 31 | int got = dt_delta_months(dt1, dt2, 0); 32 | cmp_ok(got, "==", t.delta, "dt_delta_months(%d, %d, false)", dt1, dt2); 33 | } 34 | } 35 | done_testing(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /t/end_of_quarter.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "end_of_quarter.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t src = dt_from_yqd(t.y, t.q, t.d); 15 | dt_t got = dt_end_of_quarter(src, t.delta); 16 | dt_t exp = dt_from_yqd(t.ey, t.eq, t.ed); 17 | cmp_ok(got, "==", exp, "dt_end_of_quarter(%d, %d)", src, t.delta); 18 | } 19 | 20 | { 21 | dt_t src = dt_from_yqd(t.y, t.q, t.d); 22 | dt_t dt1 = dt_end_of_quarter(src, t.delta); 23 | dt_t dt2 = dt_start_of_quarter(src, t.delta + 1) - 1; 24 | cmp_ok(dt1, "==", dt2, "dt_end_of_quarter(%d, %d) == dt_start_of_quarter(%d, %d + 1) - 1", 25 | src, t.y, src, t.y); 26 | } 27 | 28 | { 29 | dt_t dt1 = dt_from_yqd(t.y, t.q, t.d); 30 | dt_t dt2 = dt_end_of_quarter(dt1, t.delta); 31 | int got = dt_delta_quarters(dt1, dt2, 0); 32 | cmp_ok(got, "==", t.delta, "dt_delta_quarters(%d, %d, false)", dt1, dt2); 33 | } 34 | } 35 | done_testing(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /t/start_of_quarter.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "start_of_quarter.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t src = dt_from_yqd(t.y, t.q, t.d); 15 | dt_t got = dt_start_of_quarter(src, t.delta); 16 | dt_t exp = dt_from_yqd(t.ey, t.eq, t.ed); 17 | cmp_ok(got, "==", exp, "dt_start_of_quarter(%d, %d)", src, t.delta); 18 | } 19 | 20 | { 21 | dt_t src = dt_from_yqd(t.y, t.q, t.d); 22 | dt_t dt1 = dt_start_of_quarter(src, t.delta); 23 | dt_t dt2 = dt_end_of_quarter(src, t.delta - 1) + 1; 24 | cmp_ok(dt1, "==", dt2, "dt_start_of_quarter(%d, %d) == dt_end_of_quarter(%d, %d - 1) + 1", 25 | src, t.y, src, t.y); 26 | } 27 | 28 | { 29 | dt_t dt1 = dt_from_yqd(t.y, t.q, t.d); 30 | dt_t dt2 = dt_start_of_quarter(dt1, t.delta); 31 | int got = dt_delta_quarters(dt1, dt2, 0); 32 | cmp_ok(got, "==", t.delta, "dt_delta_quarters(%d, %d, false)", dt1, dt2); 33 | } 34 | } 35 | done_testing(); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /t/next_weekday.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int w; 7 | int d; 8 | bool current; 9 | int ey; 10 | int ew; 11 | int ed; 12 | } tests[] = { 13 | {2012, 10, 1, 1, 2012, 10, 1}, 14 | {2012, 10, 2, 1, 2012, 10, 2}, 15 | {2012, 10, 3, 1, 2012, 10, 3}, 16 | {2012, 10, 4, 1, 2012, 10, 4}, 17 | {2012, 10, 5, 1, 2012, 10, 5}, 18 | {2012, 10, 6, 1, 2012, 11, 1}, 19 | {2012, 10, 7, 1, 2012, 11, 1}, 20 | {2012, 10, 1, 0, 2012, 10, 2}, 21 | {2012, 10, 2, 0, 2012, 10, 3}, 22 | {2012, 10, 3, 0, 2012, 10, 4}, 23 | {2012, 10, 4, 0, 2012, 10, 5}, 24 | {2012, 10, 5, 0, 2012, 11, 1}, 25 | {2012, 10, 6, 0, 2012, 11, 1}, 26 | {2012, 10, 7, 0, 2012, 11, 1}, 27 | }; 28 | 29 | int 30 | main() { 31 | int i, ntests; 32 | 33 | ntests = sizeof(tests) / sizeof(*tests); 34 | for (i = 0; i < ntests; i++) { 35 | const struct test t = tests[i]; 36 | 37 | { 38 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 39 | dt_t got = dt_next_weekday(src, t.current); 40 | dt_t exp = dt_from_ywd(t.ey, t.ew, t.ed); 41 | cmp_ok(got, "==", exp, "dt_next_weekday(%d, %d)", src, t.current); 42 | } 43 | 44 | } 45 | done_testing(); 46 | } 47 | -------------------------------------------------------------------------------- /t/prev_weekday.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int w; 7 | int d; 8 | bool current; 9 | int ey; 10 | int ew; 11 | int ed; 12 | } tests[] = { 13 | {2012, 10, 1, 1, 2012, 10, 1}, 14 | {2012, 10, 2, 1, 2012, 10, 2}, 15 | {2012, 10, 3, 1, 2012, 10, 3}, 16 | {2012, 10, 4, 1, 2012, 10, 4}, 17 | {2012, 10, 5, 1, 2012, 10, 5}, 18 | {2012, 10, 6, 1, 2012, 10, 5}, 19 | {2012, 10, 7, 1, 2012, 10, 5}, 20 | {2012, 10, 1, 0, 2012, 9, 5}, 21 | {2012, 10, 2, 0, 2012, 10, 1}, 22 | {2012, 10, 3, 0, 2012, 10, 2}, 23 | {2012, 10, 4, 0, 2012, 10, 3}, 24 | {2012, 10, 5, 0, 2012, 10, 4}, 25 | {2012, 10, 6, 0, 2012, 10, 5}, 26 | {2012, 10, 7, 0, 2012, 10, 5}, 27 | }; 28 | 29 | int 30 | main() { 31 | int i, ntests; 32 | 33 | ntests = sizeof(tests) / sizeof(*tests); 34 | for (i = 0; i < ntests; i++) { 35 | const struct test t = tests[i]; 36 | 37 | { 38 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 39 | dt_t got = dt_prev_weekday(src, t.current); 40 | dt_t exp = dt_from_ywd(t.ey, t.ew, t.ed); 41 | cmp_ok(got, "==", exp, "dt_prev_weekday(%d, %d)", src, t.current); 42 | } 43 | 44 | } 45 | done_testing(); 46 | } 47 | -------------------------------------------------------------------------------- /t/start_of_month.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "start_of_month.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 15 | dt_t got = dt_start_of_month(src, t.delta); 16 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 17 | cmp_ok(got, "==", exp, "dt_start_of_month(%d (%.4d-%.2d-%.2d), %d)", 18 | src, t.y, t.m, t.d, t.delta); 19 | } 20 | 21 | { 22 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 23 | dt_t dt1 = dt_start_of_month(src, t.delta); 24 | dt_t dt2 = dt_end_of_month(src, t.delta - 1) + 1; 25 | cmp_ok(dt1, "==", dt2, "dt_start_of_month(%d, %d) == dt_end_of_month(%d, %d - 1) + 1", 26 | src, t.y, src, t.y); 27 | } 28 | 29 | { 30 | dt_t dt1 = dt_from_ymd(t.y, t.m, t.d); 31 | dt_t dt2 = dt_start_of_month(dt1, t.delta); 32 | int got = dt_delta_months(dt1, dt2, 0); 33 | cmp_ok(got, "==", t.delta, "dt_delta_months(%d, %d, false)", dt1, dt2); 34 | } 35 | } 36 | done_testing(); 37 | } 38 | 39 | -------------------------------------------------------------------------------- /t/zone.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | int 5 | main() { 6 | const dt_zone_t *zone; 7 | size_t len; 8 | 9 | { 10 | len = dt_zone_lookup("cst", 3, &zone); 11 | cmp_ok(len, "==", 3, "dt_zone_lookup(cst)"); 12 | is(dt_zone_name(zone), "CST", "name is CST"); 13 | ok(dt_zone_is_rfc(zone), "CST is RFC"); 14 | ok(dt_zone_is_ambiguous(zone), "CST is ambiguous"); 15 | ok(!dt_zone_is_utc(zone), "CST is not universal"); 16 | ok(!dt_zone_is_military(zone), "CST is not military"); 17 | cmp_ok(dt_zone_offset(zone), "==", -6*60, "CST offset"); 18 | } 19 | 20 | { 21 | len = dt_zone_lookup("UTC", 3, &zone); 22 | cmp_ok(len, "==", 3, "dt_zone_lookup(UTC)"); 23 | is(dt_zone_name(zone), "UTC", "name is UTC"); 24 | ok(dt_zone_is_rfc(zone), "UTC is RFC"); 25 | ok(!dt_zone_is_ambiguous(zone), "UTC is not ambiguous"); 26 | ok(dt_zone_is_utc(zone), "UTC is universal"); 27 | ok(!dt_zone_is_military(zone), "UTC is not military"); 28 | cmp_ok(dt_zone_offset(zone), "==", 0, "UTC offset"); 29 | } 30 | 31 | { 32 | len = dt_zone_lookup("YeKsT", 5, &zone); 33 | cmp_ok(len, "==", 5, "dt_zone_lookup(YeKsT)"); 34 | is(dt_zone_name(zone), "YEKST", "name is YEKST"); 35 | } 36 | 37 | done_testing(); 38 | } 39 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015 Christian Hansen 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /t/days_in_month.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int d; 8 | } tests[] = { 9 | { 2100, 1, 31 }, 10 | { 2100, 2, 28 }, 11 | { 2100, 3, 31 }, 12 | { 2100, 4, 30 }, 13 | { 2100, 5, 31 }, 14 | { 2100, 6, 30 }, 15 | { 2100, 7, 31 }, 16 | { 2100, 8, 31 }, 17 | { 2100, 9, 30 }, 18 | { 2100, 10, 31 }, 19 | { 2100, 11, 30 }, 20 | { 2100, 12, 31 }, 21 | { 2000, 1, 31 }, 22 | { 2000, 2, 29 }, 23 | { 2000, 3, 31 }, 24 | { 2000, 4, 30 }, 25 | { 2000, 5, 31 }, 26 | { 2000, 6, 30 }, 27 | { 2000, 7, 31 }, 28 | { 2000, 8, 31 }, 29 | { 2000, 9, 30 }, 30 | { 2000, 10, 31 }, 31 | { 2000, 11, 30 }, 32 | { 2000, 12, 31 }, 33 | { 2000, 13, 0 }, 34 | { 2000, 0, 0 }, 35 | { 2000, -1, 0 }, 36 | { 2000, 50, 0 }, 37 | }; 38 | 39 | int 40 | main() { 41 | int i, ntests; 42 | 43 | ntests = sizeof(tests) / sizeof(*tests); 44 | for (i = 0; i < ntests; i++) { 45 | const struct test t = tests[i]; 46 | 47 | { 48 | int got = dt_days_in_month(t.y, t.m); 49 | cmp_ok(got, "==", t.d, "dt_days_in_month(%d, %d)", t.y, t.m); 50 | } 51 | 52 | if (t.d != 0) { 53 | int got = dt_length_of_month(dt_from_ymd(t.y, t.m, 1)); 54 | cmp_ok(got, "==", t.d, "dt_length_of_month(%.4d-%.2d-01)", t.y, t.m); 55 | } 56 | } 57 | done_testing(); 58 | } 59 | -------------------------------------------------------------------------------- /t/ywd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include "ywd.h" 4 | 5 | int 6 | main() { 7 | int i, ntests; 8 | 9 | ntests = sizeof(tests) / sizeof(*tests); 10 | for (i = 0; i < ntests; i++) { 11 | const struct test t = tests[i]; 12 | 13 | { 14 | dt_t got = dt_rdn(dt_from_ywd(t.y, t.w, t.d)); 15 | cmp_ok(got, "==", t.rdn, "dt_rdn(dt_from_ywd(%d, %d, %d))", t.y, t.w, t.d); 16 | } 17 | 18 | { 19 | int y, w, d; 20 | dt_to_ywd(dt_from_rdn(t.rdn), &y, &w, &d); 21 | if (!ok((y == t.y && w == t.w && d == t.d), "dt_to_ywd(dt_from_rdn(%d))", t.rdn)) { 22 | diag(" got: %.4d-W%.2d-%d", y, w, d); 23 | diag(" exp: %.4d-W%.2d-%d", t.y, t.w, t.d); 24 | } 25 | } 26 | 27 | { 28 | int got = dt_woy(dt_from_rdn(t.rdn)); 29 | cmp_ok(got, "==", t.w, "dt_woy(dt_from_rdn(%d))", t.rdn); 30 | } 31 | 32 | { 33 | int got = dt_yow(dt_from_rdn(t.rdn)); 34 | cmp_ok(got, "==", t.y, "dt_yow(dt_from_rdn(%d))", t.rdn); 35 | } 36 | 37 | { 38 | int got = dt_dow(dt_from_rdn(t.rdn)); 39 | cmp_ok(got, "==", t.d, "dt_dow(dt_from_rdn(%d))", t.rdn); 40 | } 41 | 42 | { 43 | ok(dt_valid_ywd(t.y, t.w, t.d), "dt_valid_ywd(%d, %d, %d)", t.y, t.w, t.d); 44 | } 45 | 46 | } 47 | done_testing(); 48 | } 49 | -------------------------------------------------------------------------------- /t/add_years.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int d; 7 | int delta; 8 | dt_adjust_t adjust; 9 | int ey; 10 | int ed; 11 | } tests[] = { 12 | {2000, 1, 1, DT_EXCESS, 2001, 1}, 13 | {2000, 365, 1, DT_EXCESS, 2001, 365}, 14 | {2000, 366, 1, DT_EXCESS, 2002, 1}, 15 | {2000, 366, 1, DT_LIMIT, 2001, 365}, 16 | {2001, 365, 3, DT_LIMIT, 2004, 365}, 17 | {2001, 365, 3, DT_EXCESS, 2004, 365}, 18 | {2001, 365, 3, DT_SNAP, 2004, 366}, 19 | {2000, 366, 1, DT_SNAP, 2001, 365}, 20 | }; 21 | 22 | int 23 | main() { 24 | int i, ntests; 25 | 26 | ntests = sizeof(tests) / sizeof(*tests); 27 | for (i = 0; i < ntests; i++) { 28 | const struct test t = tests[i]; 29 | 30 | { 31 | dt_t src = dt_from_yd(t.y, t.d); 32 | dt_t got = dt_add_years(src, t.delta, t.adjust); 33 | dt_t exp = dt_from_yd(t.ey, t.ed); 34 | cmp_ok(got, "==", exp, "dt_add_years(%d (%.4d-%.3d), %d, %d)", 35 | src, t.y, t.d, t.delta, t.adjust); 36 | } 37 | 38 | if (t.adjust != DT_EXCESS) { 39 | dt_t dt1 = dt_from_yd(t.y, t.d); 40 | dt_t dt2 = dt_add_years(dt1, t.delta, t.adjust); 41 | int got = dt_delta_years(dt1, dt2, 0); 42 | cmp_ok(got, "==", t.delta, "dt_delta_years(%d, %d, false)", dt1, dt2); 43 | } 44 | } 45 | done_testing(); 46 | } 47 | 48 | -------------------------------------------------------------------------------- /t/end_of_week.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int w; 7 | int d; 8 | int dow; 9 | int ey; 10 | int ew; 11 | int ed; 12 | } tests[] = { 13 | {2012, 10, 1, 1, 2012, 10, 7}, 14 | {2012, 10, 1, 2, 2012, 10, 1}, 15 | {2012, 10, 1, 3, 2012, 10, 2}, 16 | {2012, 10, 1, 4, 2012, 10, 3}, 17 | {2012, 10, 1, 5, 2012, 10, 4}, 18 | {2012, 10, 1, 6, 2012, 10, 5}, 19 | {2012, 10, 1, 7, 2012, 10, 6}, 20 | {2012, 10, 1, 1, 2012, 10, 7}, 21 | {2012, 10, 2, 2, 2012, 11, 1}, 22 | {2012, 10, 3, 3, 2012, 11, 2}, 23 | {2012, 10, 4, 4, 2012, 11, 3}, 24 | {2012, 10, 5, 5, 2012, 11, 4}, 25 | {2012, 10, 6, 6, 2012, 11, 5}, 26 | {2012, 10, 7, 7, 2012, 11, 6}, 27 | }; 28 | 29 | int 30 | main() { 31 | int i, ntests; 32 | 33 | ntests = sizeof(tests) / sizeof(*tests); 34 | for (i = 0; i < ntests; i++) { 35 | const struct test t = tests[i]; 36 | 37 | { 38 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 39 | dt_t got = dt_end_of_week(src, t.dow); 40 | dt_t exp = dt_from_ywd(t.ey, t.ew, t.ed); 41 | cmp_ok(got, "==", exp, "dt_end_of_week(%d, %d)", src, t.dow); 42 | } 43 | 44 | { 45 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 46 | dt_t dt1 = dt_end_of_week(src, t.dow); 47 | dt_t dt2 = dt_start_of_week(src, t.dow) + 6; 48 | cmp_ok(dt1, "==", dt2, "dt_end_of_week(%d, %d) == dt_start_of_week(%d, %d) + 6", 49 | src, t.dow, src, t.dow); 50 | } 51 | 52 | } 53 | done_testing(); 54 | } 55 | -------------------------------------------------------------------------------- /t/start_of_week.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int w; 7 | int d; 8 | int dow; 9 | int ey; 10 | int ew; 11 | int ed; 12 | } tests[] = { 13 | {2012, 10, 7, 1, 2012, 10, 1}, 14 | {2012, 10, 7, 2, 2012, 10, 2}, 15 | {2012, 10, 7, 3, 2012, 10, 3}, 16 | {2012, 10, 7, 4, 2012, 10, 4}, 17 | {2012, 10, 7, 5, 2012, 10, 5}, 18 | {2012, 10, 7, 6, 2012, 10, 6}, 19 | {2012, 10, 7, 7, 2012, 10, 7}, 20 | {2012, 11, 1, 7, 2012, 10, 7}, 21 | {2012, 10, 1, 1, 2012, 10, 1}, 22 | {2012, 10, 2, 2, 2012, 10, 2}, 23 | {2012, 10, 3, 3, 2012, 10, 3}, 24 | {2012, 10, 4, 4, 2012, 10, 4}, 25 | {2012, 10, 5, 5, 2012, 10, 5}, 26 | {2012, 10, 6, 6, 2012, 10, 6}, 27 | {2012, 10, 7, 7, 2012, 10, 7}, 28 | }; 29 | 30 | int 31 | main() { 32 | int i, ntests; 33 | 34 | ntests = sizeof(tests) / sizeof(*tests); 35 | for (i = 0; i < ntests; i++) { 36 | const struct test t = tests[i]; 37 | 38 | { 39 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 40 | dt_t got = dt_start_of_week(src, t.dow); 41 | dt_t exp = dt_from_ywd(t.ey, t.ew, t.ed); 42 | cmp_ok(got, "==", exp, "dt_start_of_week(%d, %d)", src, t.dow); 43 | } 44 | 45 | { 46 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 47 | dt_t dt1 = dt_start_of_week(src, t.dow); 48 | dt_t dt2 = dt_end_of_week(src, t.dow) - 6; 49 | cmp_ok(dt1, "==", dt2, "dt_start_of_week(%d, %d) == dt_end_of_week(%d, %d) - 6", 50 | src, t.dow, src, t.dow); 51 | } 52 | 53 | } 54 | done_testing(); 55 | } 56 | -------------------------------------------------------------------------------- /t/delta_yqd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y1; 6 | int q1; 7 | int d1; 8 | int y2; 9 | int q2; 10 | int d2; 11 | int ny; 12 | int nq; 13 | int nd; 14 | } tests[] = { 15 | {2010, 1, 1, 2010, 1, 1, 0, 0, 0}, 16 | {2010, 1, 1, 2010, 2, 1, 0, 1, 0}, 17 | {2010, 1, 1, 2010, 2, 10, 0, 1, 9}, 18 | {2012, 4, 92, 2016, 4, 92, 4, 0, 0}, 19 | }; 20 | 21 | int 22 | main() { 23 | int i, ntests; 24 | 25 | ntests = sizeof(tests) / sizeof(*tests); 26 | for (i = 0; i < ntests; i++) { 27 | const struct test t = tests[i]; 28 | 29 | { 30 | int ny, nq, nd; 31 | dt_t dt1 = dt_from_yqd(t.y1, t.q1, t.d1); 32 | dt_t dt2 = dt_from_yqd(t.y2, t.q2, t.d2); 33 | 34 | dt_delta_yqd(dt1, dt2, &ny, &nq, &nd); 35 | if (!ok((ny == t.ny && nq == t.nq && nd == t.nd), 36 | "dt_delta_yqd(%.4d-Q%d-%.2d, %.4d-Q%d-%.2d)", 37 | t.y1, t.q1, t.d1, t.y2, t.q2, t.d2)) { 38 | diag(" got: Y:%d, Q:%d, D:%d", ny, nq, nd); 39 | diag(" exp: Y:%d, Q:%d, D:%d", t.ny, t.nq, t.nd); 40 | } 41 | } 42 | 43 | { 44 | int nq, eq; 45 | dt_t dt1 = dt_from_yqd(t.y1, t.q1, t.d1); 46 | dt_t dt2 = dt_from_yqd(t.y2, t.q2, t.d2); 47 | 48 | nq = dt_delta_quarters(dt1, dt2, 1); 49 | eq = t.nq + (t.ny * 4); 50 | cmp_ok(nq, "==", eq, "dt_delta_quarters(%.4d-Q%d-%.2d, %.4d-Q%d-%.2d, true)", 51 | t.y1, t.q1, t.d1, t.y2, t.q2, t.d2); 52 | } 53 | } 54 | done_testing(); 55 | } 56 | 57 | -------------------------------------------------------------------------------- /t/tm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dt.h" 3 | #include "tap.h" 4 | 5 | const struct test { 6 | int year; 7 | int mon; 8 | int mday; 9 | int wday; 10 | int yday; 11 | int rdn; 12 | } tests[] = { 13 | { 70, 0, 1, 4, 0, 719163}, 14 | {110, 11, 31, 5, 364, 734137}, 15 | {120, 11, 31, 4, 365, 737790}, 16 | }; 17 | 18 | int 19 | main() { 20 | int i, ntests; 21 | 22 | ntests = sizeof(tests) / sizeof(*tests); 23 | for (i = 0; i < ntests; i++) { 24 | const struct test t = tests[i]; 25 | 26 | { 27 | struct tm tm; 28 | int rdn; 29 | 30 | memset(&tm, 0, sizeof(tm)); 31 | tm.tm_year = t.year; 32 | tm.tm_mon = t.mon; 33 | tm.tm_mday = t.mday; 34 | rdn = dt_rdn(dt_from_struct_tm(&tm)); 35 | cmp_ok(rdn, "==", t.rdn, "dt_rdn(dt_from_struct_tm(tm->tm_year: %d, tm->tm_mon: %d, tm->tm_mday: %d))", 36 | tm.tm_year, tm.tm_mon, tm.tm_mday); 37 | } 38 | 39 | { 40 | struct tm tm; 41 | 42 | memset(&tm, 0, sizeof(tm)); 43 | dt_to_struct_tm(dt_from_rdn(t.rdn), &tm); 44 | cmp_ok(tm.tm_year, "==", t.year, "dt_to_struct_tm(dt_from_rdn(%d), &tm) tm_year", t.rdn); 45 | cmp_ok(tm.tm_mon, "==", t.mon, "dt_to_struct_tm(dt_from_rdn(%d), &tm) tm_mon", t.rdn); 46 | cmp_ok(tm.tm_mday, "==", t.mday, "dt_to_struct_tm(dt_from_rdn(%d), &tm) tm_mday", t.rdn); 47 | cmp_ok(tm.tm_wday, "==", t.wday, "dt_to_struct_tm(dt_from_rdn(%d), &tm) tm_wday", t.rdn); 48 | cmp_ok(tm.tm_yday, "==", t.yday, "dt_to_struct_tm(dt_from_rdn(%d), &tm) tm_yday", t.rdn); 49 | } 50 | } 51 | done_testing(); 52 | } 53 | -------------------------------------------------------------------------------- /t/add_quarters.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int q; 7 | int d; 8 | int delta; 9 | dt_adjust_t adjust; 10 | int ey; 11 | int eq; 12 | int ed; 13 | } tests[] = { 14 | {2000, 1, 1, 1, DT_EXCESS, 2000, 2, 1}, 15 | {2000, 1, 1, 2, DT_EXCESS, 2000, 3, 1}, 16 | {2000, 1, 1, 3, DT_EXCESS, 2000, 4, 1}, 17 | {2000, 1, 1, 4, DT_EXCESS, 2001, 1, 1}, 18 | {2000, 1, 1, 4, DT_EXCESS, 2001, 1, 1}, 19 | {2000, 1, 91, 1, DT_SNAP, 2000, 2, 91}, 20 | {2000, 1, 91, 2, DT_SNAP, 2000, 3, 92}, 21 | {2000, 1, 91, 3, DT_SNAP, 2000, 4, 92}, 22 | {2001, 1, 90, 1, DT_SNAP, 2001, 2, 91}, 23 | {2001, 1, 90, 2, DT_SNAP, 2001, 3, 92}, 24 | {2001, 1, 90, 3, DT_SNAP, 2001, 4, 92}, 25 | {2000, 4, 92, -3, DT_LIMIT, 2000, 1, 91}, 26 | {2001, 4, 92, -3, DT_LIMIT, 2001, 1, 90}, 27 | }; 28 | 29 | int 30 | main() { 31 | int i, ntests; 32 | 33 | ntests = sizeof(tests) / sizeof(*tests); 34 | for (i = 0; i < ntests; i++) { 35 | const struct test t = tests[i]; 36 | 37 | { 38 | dt_t src = dt_from_yqd(t.y, t.q, t.d); 39 | dt_t got = dt_add_quarters(src, t.delta, t.adjust); 40 | dt_t exp = dt_from_yqd(t.ey, t.eq, t.ed); 41 | cmp_ok(got, "==", exp, "dt_add_quarters(%d (%.4d-Q%d-%.2d), %d, %d)", 42 | src, t.y, t.q, t.d, t.delta, t.adjust); 43 | } 44 | 45 | if (t.adjust != DT_EXCESS) { 46 | dt_t dt1 = dt_from_yqd(t.y, t.q, t.d); 47 | dt_t dt2 = dt_add_quarters(dt1, t.delta, t.adjust); 48 | int got = dt_delta_quarters(dt1, dt2, 0); 49 | cmp_ok(got, "==", t.delta, "dt_delta_quarters(%d, %d, false)", dt1, dt2); 50 | } 51 | } 52 | done_testing(); 53 | } 54 | 55 | -------------------------------------------------------------------------------- /dt_tm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_TM_H__ 27 | #define __DT_TM_H__ 28 | #include 29 | #include "dt_core.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | dt_t dt_from_struct_tm (const struct tm *tm); 36 | 37 | void dt_to_struct_tm (dt_t dt, struct tm *tm); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /dt_length.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_LENGTH_H__ 27 | #define __DT_LENGTH_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | int dt_length_of_year (dt_t dt); 35 | int dt_length_of_quarter (dt_t dt); 36 | int dt_length_of_month (dt_t dt); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /dt_easter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_EASTER_H__ 27 | #define __DT_EASTER_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | typedef enum { 35 | DT_ORTHODOX, 36 | DT_WESTERN 37 | } dt_computus_t; 38 | 39 | dt_t dt_from_easter (int y, dt_computus_t computus); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /t/next_dow.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int w; 7 | int d; 8 | int dow; 9 | bool current; 10 | int ey; 11 | int ew; 12 | int ed; 13 | } tests[] = { 14 | {2012, 10, 1, 1, 1, 2012, 10, 1}, 15 | {2012, 10, 2, 2, 1, 2012, 10, 2}, 16 | {2012, 10, 3, 3, 1, 2012, 10, 3}, 17 | {2012, 10, 4, 4, 1, 2012, 10, 4}, 18 | {2012, 10, 5, 5, 1, 2012, 10, 5}, 19 | {2012, 10, 6, 6, 1, 2012, 10, 6}, 20 | {2012, 10, 7, 7, 1, 2012, 10, 7}, 21 | {2012, 10, 1, 2, 1, 2012, 10, 2}, 22 | {2012, 10, 2, 3, 1, 2012, 10, 3}, 23 | {2012, 10, 3, 4, 1, 2012, 10, 4}, 24 | {2012, 10, 4, 5, 1, 2012, 10, 5}, 25 | {2012, 10, 5, 6, 1, 2012, 10, 6}, 26 | {2012, 10, 6, 7, 1, 2012, 10, 7}, 27 | {2012, 10, 7, 1, 1, 2012, 11, 1}, 28 | {2012, 10, 1, 1, 0, 2012, 11, 1}, 29 | {2012, 10, 2, 2, 0, 2012, 11, 2}, 30 | {2012, 10, 3, 3, 0, 2012, 11, 3}, 31 | {2012, 10, 4, 4, 0, 2012, 11, 4}, 32 | {2012, 10, 5, 5, 0, 2012, 11, 5}, 33 | {2012, 10, 6, 6, 0, 2012, 11, 6}, 34 | {2012, 10, 7, 7, 0, 2012, 11, 7}, 35 | {2012, 10, 1, 2, 0, 2012, 10, 2}, 36 | {2012, 10, 2, 3, 0, 2012, 10, 3}, 37 | {2012, 10, 3, 4, 0, 2012, 10, 4}, 38 | {2012, 10, 4, 5, 0, 2012, 10, 5}, 39 | {2012, 10, 5, 6, 0, 2012, 10, 6}, 40 | {2012, 10, 6, 7, 0, 2012, 10, 7}, 41 | {2012, 10, 7, 1, 0, 2012, 11, 1}, 42 | }; 43 | 44 | int 45 | main() { 46 | int i, ntests; 47 | 48 | ntests = sizeof(tests) / sizeof(*tests); 49 | for (i = 0; i < ntests; i++) { 50 | const struct test t = tests[i]; 51 | 52 | { 53 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 54 | dt_t got = dt_next_dow(src, t.dow, t.current); 55 | dt_t exp = dt_from_ywd(t.ey, t.ew, t.ed); 56 | cmp_ok(got, "==", exp, "dt_next_dow(%d, %d, %d)", src, t.dow, t.current); 57 | } 58 | 59 | } 60 | done_testing(); 61 | } 62 | -------------------------------------------------------------------------------- /t/prev_dow.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int w; 7 | int d; 8 | int dow; 9 | bool current; 10 | int ey; 11 | int ew; 12 | int ed; 13 | } tests[] = { 14 | {2012, 10, 1, 1, 1, 2012, 10, 1}, 15 | {2012, 10, 2, 2, 1, 2012, 10, 2}, 16 | {2012, 10, 3, 3, 1, 2012, 10, 3}, 17 | {2012, 10, 4, 4, 1, 2012, 10, 4}, 18 | {2012, 10, 5, 5, 1, 2012, 10, 5}, 19 | {2012, 10, 6, 6, 1, 2012, 10, 6}, 20 | {2012, 10, 7, 7, 1, 2012, 10, 7}, 21 | {2012, 10, 1, 2, 1, 2012, 9, 2}, 22 | {2012, 10, 2, 3, 1, 2012, 9, 3}, 23 | {2012, 10, 3, 4, 1, 2012, 9, 4}, 24 | {2012, 10, 4, 5, 1, 2012, 9, 5}, 25 | {2012, 10, 5, 6, 1, 2012, 9, 6}, 26 | {2012, 10, 6, 7, 1, 2012, 9, 7}, 27 | {2012, 10, 7, 1, 1, 2012, 10, 1}, 28 | {2012, 10, 1, 1, 0, 2012, 9, 1}, 29 | {2012, 10, 2, 2, 0, 2012, 9, 2}, 30 | {2012, 10, 3, 3, 0, 2012, 9, 3}, 31 | {2012, 10, 4, 4, 0, 2012, 9, 4}, 32 | {2012, 10, 5, 5, 0, 2012, 9, 5}, 33 | {2012, 10, 6, 6, 0, 2012, 9, 6}, 34 | {2012, 10, 7, 7, 0, 2012, 9, 7}, 35 | {2012, 10, 1, 2, 0, 2012, 9, 2}, 36 | {2012, 10, 2, 3, 0, 2012, 9, 3}, 37 | {2012, 10, 3, 4, 0, 2012, 9, 4}, 38 | {2012, 10, 4, 5, 0, 2012, 9, 5}, 39 | {2012, 10, 5, 6, 0, 2012, 9, 6}, 40 | {2012, 10, 6, 7, 0, 2012, 9, 7}, 41 | {2012, 10, 7, 1, 0, 2012, 10, 1}, 42 | }; 43 | 44 | int 45 | main() { 46 | int i, ntests; 47 | 48 | ntests = sizeof(tests) / sizeof(*tests); 49 | for (i = 0; i < ntests; i++) { 50 | const struct test t = tests[i]; 51 | 52 | { 53 | dt_t src = dt_from_ywd(t.y, t.w, t.d); 54 | dt_t got = dt_prev_dow(src, t.dow, t.current); 55 | dt_t exp = dt_from_ywd(t.ey, t.ew, t.ed); 56 | cmp_ok(got, "==", exp, "dt_prev_dow(%d, %d, %d)", src, t.dow, t.current); 57 | } 58 | 59 | } 60 | done_testing(); 61 | } 62 | -------------------------------------------------------------------------------- /dt_valid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_VALID_H__ 27 | #define __DT_VALID_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | bool dt_valid_yd (int y, int d); 35 | bool dt_valid_ymd (int y, int m, int d); 36 | bool dt_valid_yqd (int y, int q, int d); 37 | bool dt_valid_ywd (int y, int w, int d); 38 | 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /dt_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_UTIL_H__ 27 | #define __DT_UTIL_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | bool dt_leap_year (int y); 35 | int dt_days_in_year (int y); 36 | int dt_days_in_quarter (int y, int q); 37 | int dt_days_in_month (int y, int m); 38 | int dt_weeks_in_year (int y); 39 | 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /dt_search.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_SEARCH_H__ 27 | #define __DT_SEARCH_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | const dt_t * dt_lower_bound (dt_t dt, const dt_t *first, const dt_t *last); 35 | const dt_t * dt_upper_bound (dt_t dt, const dt_t *first, const dt_t *last); 36 | bool dt_binary_search (dt_t dt, const dt_t *first, const dt_t *last); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | #endif 42 | 43 | -------------------------------------------------------------------------------- /dt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2014 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_H__ 27 | #define __DT_H__ 28 | #include "dt_accessor.h" 29 | #include "dt_arithmetic.h" 30 | #include "dt_char.h" 31 | #include "dt_core.h" 32 | #include "dt_dow.h" 33 | #include "dt_easter.h" 34 | #include "dt_length.h" 35 | #include "dt_navigate.h" 36 | #include "dt_parse_iso.h" 37 | #include "dt_search.h" 38 | #include "dt_tm.h" 39 | #include "dt_util.h" 40 | #include "dt_valid.h" 41 | #include "dt_weekday.h" 42 | #include "dt_workday.h" 43 | #include "dt_zone.h" 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /t/nth_weekday_in_month.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int nth; 8 | int ey; 9 | int em; 10 | int ed; 11 | } tests[] = { 12 | {2010, 1, 1, 2010, 1, 1}, 13 | {2011, 1, -1, 2011, 1, 31}, 14 | {2012, 1, 1, 2012, 1, 2}, 15 | {2014, 12, 1, 2014, 12, 1}, 16 | {2014, 12, 6, 2014, 12, 8}, 17 | {2014, 12, 11, 2014, 12, 15}, 18 | {2014, 12, 16, 2014, 12, 22}, 19 | {2014, 12, 21, 2014, 12, 29}, 20 | {2014, 12, 22, 2014, 12, 30}, 21 | {2014, 12, 23, 2014, 12, 31}, 22 | {2014, 12, -1, 2014, 12, 31}, 23 | {2014, 12, -2, 2014, 12, 30}, 24 | {2014, 12, -22, 2014, 12, 2}, 25 | {2014, 12, -23, 2014, 12, 1}, 26 | {2014, 3, -1, 2014, 3, 31}, 27 | {2014, 3, -6, 2014, 3, 24}, 28 | {2014, 3, -11, 2014, 3, 17}, 29 | {2014, 3, -16, 2014, 3, 10}, 30 | {2014, 3, -21, 2014, 3, 3}, 31 | }; 32 | 33 | int 34 | main() { 35 | int i, ntests; 36 | 37 | ntests = sizeof(tests) / sizeof(*tests); 38 | for (i = 0; i < ntests; i++) { 39 | const struct test t = tests[i]; 40 | 41 | { 42 | dt_t got = dt_from_nth_weekday_in_month(t.y, t.m, t.nth); 43 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 44 | cmp_ok(got, "==", exp, "dt_from_nth_weekday_in_month(%d, %d, %d)", t.y, t.m, t.nth); 45 | } 46 | 47 | { 48 | dt_t src = dt_from_ymd(t.y, t.m, 1); 49 | dt_t got = dt_nth_weekday_in_month(src, t.nth); 50 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 51 | cmp_ok(got, "==", exp, "dt_nth_weekday_in_month(%d, %d)", src, t.nth); 52 | } 53 | 54 | { 55 | dt_t src = dt_from_nth_weekday_in_month(t.y, t.m, t.nth); 56 | int got = dt_weekday_in_month(src, t.nth < 0); 57 | cmp_ok(got, "==", t.nth, "dt_weekday_in_month(%d, %s)", src, (t.nth < 0 ? "true" : "false")); 58 | } 59 | 60 | } 61 | done_testing(); 62 | } 63 | -------------------------------------------------------------------------------- /dt_length.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include 27 | #include "dt_core.h" 28 | #include "dt_util.h" 29 | 30 | int 31 | dt_length_of_year(dt_t dt) { 32 | int y; 33 | dt_to_yd(dt, &y, NULL); 34 | return dt_days_in_year(y); 35 | } 36 | 37 | int 38 | dt_length_of_quarter(dt_t dt) { 39 | int y, q; 40 | dt_to_yqd(dt, &y, &q, NULL); 41 | return dt_days_in_quarter(y, q); 42 | } 43 | 44 | int 45 | dt_length_of_month(dt_t dt) { 46 | int y, m; 47 | dt_to_ymd(dt, &y, &m, NULL); 48 | return dt_days_in_month(y, m); 49 | } 50 | 51 | -------------------------------------------------------------------------------- /dt_tm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include "dt_core.h" 27 | #include "dt_tm.h" 28 | 29 | dt_t 30 | dt_from_struct_tm(const struct tm *tm) { 31 | return dt_from_ymd(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); 32 | } 33 | 34 | void 35 | dt_to_struct_tm(dt_t dt, struct tm *tm) { 36 | int y, m, d; 37 | 38 | dt_to_ymd(dt, &y, &m, &d); 39 | tm->tm_year = y - 1900; 40 | tm->tm_mon = m - 1; /* [0=Jan, 11=Dec] */ 41 | tm->tm_mday = d; 42 | tm->tm_wday = dt_dow(dt) % 7; /* [0=Sun, 6=Sat] */ 43 | tm->tm_yday = dt - dt_from_yd(y, 1); /* [0, 365] */ 44 | } 45 | 46 | -------------------------------------------------------------------------------- /dt_accessor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_ACCESSOR_H__ 27 | #define __DT_ACCESSOR_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | dt_t dt_from_cjdn (int n); 35 | 36 | int dt_cjdn (dt_t dt); 37 | 38 | int dt_year (dt_t dt); 39 | int dt_quarter (dt_t dt); 40 | int dt_month (dt_t dt); 41 | 42 | int dt_doy (dt_t dt); 43 | int dt_doq (dt_t dt); 44 | int dt_dom (dt_t dt); 45 | 46 | int dt_woy (dt_t dt); 47 | int dt_yow (dt_t dt); 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /t/parse_iso_zone.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include 4 | 5 | const struct good_t { 6 | int offset; 7 | const char *str; 8 | size_t elen; 9 | } good[] = { 10 | { 0, "Z", 1 }, 11 | { 0, "+00", 3 }, 12 | { 1380, "+23", 3 }, 13 | { 60, "+01", 3 }, 14 | { -1380, "-23", 3 }, 15 | { -60, "-01", 3 }, 16 | { 0, "+00:00", 6 }, 17 | { 59, "+00:59", 6 }, 18 | { -59, "-00:59", 6 }, 19 | { 1439, "+23:59", 6 }, 20 | { -1439, "-23:59", 6 }, 21 | { 0, "+0000", 5 }, 22 | { 59, "+0059", 5 }, 23 | { -59, "-0059", 5 }, 24 | { 1439, "+2359", 5 }, 25 | { -1439, "-2359", 5 }, 26 | }; 27 | 28 | const struct bad_t { 29 | const char *str; 30 | } bad[] = { 31 | { "" }, 32 | { "0" }, 33 | { "+0" }, 34 | { "-0" }, 35 | { "+00:0" }, 36 | { "-00:0" }, 37 | { "+00:60" }, 38 | { "-00:60" }, 39 | { "+24:00" }, 40 | { "+00:000" }, 41 | { "-00:000" }, 42 | { "+0060" }, 43 | { "-0060" }, 44 | { "-0060" }, 45 | { "+2400" }, 46 | { "+00000" }, 47 | { "-00000" }, 48 | }; 49 | 50 | int 51 | main() { 52 | int i, ntests; 53 | 54 | ntests = sizeof(good) / sizeof(*good); 55 | for (i = 0; i < ntests; i++) { 56 | const struct good_t t = good[i]; 57 | 58 | { 59 | int offset = 0; 60 | size_t glen; 61 | 62 | glen = dt_parse_iso_zone(t.str, strlen(t.str), &offset); 63 | ok(glen == t.elen, "dt_parse_iso_zone(%s) size_t: %d", t.str, (int)glen); 64 | cmp_ok(offset, "==", t.offset, "dt_parse_iso_zone(%s)", t.str); 65 | } 66 | } 67 | 68 | ntests = sizeof(bad) / sizeof(*bad); 69 | for (i = 0; i < ntests; i++) { 70 | const struct bad_t t = bad[i]; 71 | 72 | { 73 | int offset = 0; 74 | size_t glen; 75 | 76 | glen = dt_parse_iso_zone(t.str, strlen(t.str), &offset); 77 | ok(glen == 0, "dt_parse_iso_zone(%s) size_t: %d", t.str, (int)glen); 78 | } 79 | } 80 | done_testing(); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /dt_valid.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include "dt_core.h" 27 | #include "dt_util.h" 28 | 29 | bool 30 | dt_valid_yd(int y, int d) { 31 | return (d >= 1 && (d <= 365 || d == dt_days_in_year(y))); 32 | } 33 | 34 | bool 35 | dt_valid_ymd(int y, int m, int d) { 36 | return ((m >= 1 && m <= 12) && 37 | (d >= 1 && (d <= 28 || d <= dt_days_in_month(y, m)))); 38 | } 39 | 40 | bool 41 | dt_valid_yqd(int y, int q, int d) { 42 | return ((q >= 1 && q <= 4) && 43 | (d >= 1 && (d <= 90 || d <= dt_days_in_quarter(y, q)))); 44 | } 45 | 46 | bool 47 | dt_valid_ywd(int y, int w, int d) { 48 | return ((d >= 1 && d <= 7) && 49 | (w >= 1 && (w <= 52 || w == dt_weeks_in_year(y)))); 50 | } 51 | 52 | -------------------------------------------------------------------------------- /dt_navigate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_NAVIGATE_H__ 27 | #define __DT_NAVIGATE_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | dt_t dt_start_of_year (dt_t dt, int offset); 35 | dt_t dt_start_of_quarter (dt_t dt, int offset); 36 | dt_t dt_start_of_month (dt_t dt, int offset); 37 | dt_t dt_start_of_week (dt_t dt, dt_dow_t first); 38 | 39 | dt_t dt_end_of_year (dt_t dt, int offset); 40 | dt_t dt_end_of_quarter (dt_t dt, int offset); 41 | dt_t dt_end_of_month (dt_t dt, int offset); 42 | dt_t dt_end_of_week (dt_t dt, dt_dow_t first); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /t/end_of_year.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int d; 8 | int delta; 9 | int ey; 10 | int em; 11 | int ed; 12 | } tests[] = { 13 | {2000, 2, 15, -19, 1981, 12, 31}, 14 | {2001, 7, 27, -10, 1991, 12, 31}, 15 | {2002, 6, 3, 14, 2016, 12, 31}, 16 | {2003, 7, 12, -21, 1982, 12, 31}, 17 | {2004, 8, 4, 9, 2013, 12, 31}, 18 | {2005, 5, 15, -24, 1981, 12, 31}, 19 | {2006, 4, 11, 3, 2009, 12, 31}, 20 | {2007, 5, 5, -11, 1996, 12, 31}, 21 | {2008, 11, 30, 3, 2011, 12, 31}, 22 | {2009, 12, 16, 1, 2010, 12, 31}, 23 | {2010, 10, 26, -20, 1990, 12, 31}, 24 | {2011, 2, 28, 17, 2028, 12, 31}, 25 | {2012, 11, 12, -5, 2007, 12, 31}, 26 | {2013, 11, 3, -20, 1993, 12, 31}, 27 | {2014, 7, 29, 16, 2030, 12, 31}, 28 | {2015, 6, 8, 2, 2017, 12, 31}, 29 | {2016, 11, 9, -22, 1994, 12, 31}, 30 | {2017, 6, 10, 2, 2019, 12, 31}, 31 | {2018, 1, 20, -20, 1998, 12, 31}, 32 | {2019, 3, 28, 0, 2019, 12, 31}, 33 | {2020, 11, 15, 19, 2039, 12, 31}, 34 | }; 35 | 36 | int 37 | main() { 38 | int i, ntests; 39 | 40 | ntests = sizeof(tests) / sizeof(*tests); 41 | for (i = 0; i < ntests; i++) { 42 | const struct test t = tests[i]; 43 | 44 | { 45 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 46 | dt_t got = dt_end_of_year(src, t.delta); 47 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 48 | cmp_ok(got, "==", exp, "dt_end_of_year(%d, %d)", src, t.delta); 49 | } 50 | 51 | { 52 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 53 | dt_t dt1 = dt_end_of_year(src, t.delta); 54 | dt_t dt2 = dt_start_of_year(src, t.delta + 1) - 1; 55 | cmp_ok(dt1, "==", dt2, "dt_end_of_year(%d, %d) == dt_start_of_year(%d, %d + 1) - 1", 56 | src, t.y, src, t.y); 57 | } 58 | 59 | { 60 | dt_t dt1 = dt_from_ymd(t.y, t.m, t.d); 61 | dt_t dt2 = dt_end_of_year(dt1, t.delta); 62 | int got = dt_delta_years(dt1, dt2, 0); 63 | cmp_ok(got, "==", t.delta, "dt_delta_years(%d, %d, false)", dt1, dt2); 64 | } 65 | } 66 | done_testing(); 67 | } 68 | 69 | -------------------------------------------------------------------------------- /t/start_of_year.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int d; 8 | int delta; 9 | int ey; 10 | int em; 11 | int ed; 12 | } tests[] = { 13 | {2000, 2, 15, -19, 1981, 1, 1}, 14 | {2001, 7, 27, -10, 1991, 1, 1}, 15 | {2002, 6, 3, 14, 2016, 1, 1}, 16 | {2003, 7, 12, -21, 1982, 1, 1}, 17 | {2004, 8, 4, 9, 2013, 1, 1}, 18 | {2005, 5, 15, -24, 1981, 1, 1}, 19 | {2006, 4, 11, 3, 2009, 1, 1}, 20 | {2007, 5, 5, -11, 1996, 1, 1}, 21 | {2008, 11, 30, 3, 2011, 1, 1}, 22 | {2009, 12, 16, 1, 2010, 1, 1}, 23 | {2010, 10, 26, -20, 1990, 1, 1}, 24 | {2011, 2, 28, 17, 2028, 1, 1}, 25 | {2012, 11, 12, -5, 2007, 1, 1}, 26 | {2013, 11, 3, -20, 1993, 1, 1}, 27 | {2014, 7, 29, 16, 2030, 1, 1}, 28 | {2015, 6, 8, 2, 2017, 1, 1}, 29 | {2016, 11, 9, -22, 1994, 1, 1}, 30 | {2017, 6, 10, 2, 2019, 1, 1}, 31 | {2018, 1, 20, -20, 1998, 1, 1}, 32 | {2019, 3, 28, 0, 2019, 1, 1}, 33 | {2020, 11, 15, 19, 2039, 1, 1}, 34 | }; 35 | 36 | int 37 | main() { 38 | int i, ntests; 39 | 40 | ntests = sizeof(tests) / sizeof(*tests); 41 | for (i = 0; i < ntests; i++) { 42 | const struct test t = tests[i]; 43 | 44 | { 45 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 46 | dt_t got = dt_start_of_year(src, t.delta); 47 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 48 | cmp_ok(got, "==", exp, "dt_start_of_year(%d, %d)", src, t.delta); 49 | } 50 | 51 | { 52 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 53 | dt_t dt1 = dt_start_of_year(src, t.delta); 54 | dt_t dt2 = dt_end_of_year(src, t.delta - 1) + 1; 55 | cmp_ok(dt1, "==", dt2, "dt_start_of_year(%d, %d) == dt_end_of_year(%d, %d - 1) + 1", 56 | src, t.y, src, t.y); 57 | } 58 | 59 | { 60 | dt_t dt1 = dt_from_ymd(t.y, t.m, t.d); 61 | dt_t dt2 = dt_start_of_year(dt1, t.delta); 62 | int got = dt_delta_years(dt1, dt2, 0); 63 | cmp_ok(got, "==", t.delta, "dt_delta_years(%d, %d, false)", dt1, dt2); 64 | } 65 | } 66 | done_testing(); 67 | } 68 | 69 | -------------------------------------------------------------------------------- /dt_search.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include "dt_core.h" 27 | 28 | const dt_t * 29 | dt_lower_bound(dt_t dt, const dt_t *lo, const dt_t *hi) { 30 | const dt_t *mid; 31 | 32 | while (lo < hi) { 33 | mid = lo + (hi - lo) / 2; 34 | if (*mid < dt) 35 | lo = mid + 1; 36 | else 37 | hi = mid; 38 | } 39 | return lo; 40 | } 41 | 42 | const dt_t * 43 | dt_upper_bound(dt_t dt, const dt_t *lo, const dt_t *hi) { 44 | const dt_t *mid; 45 | 46 | while (lo < hi) { 47 | mid = lo + (hi - lo) / 2; 48 | if (*mid <= dt) 49 | lo = mid + 1; 50 | else 51 | hi = mid; 52 | } 53 | return lo; 54 | } 55 | 56 | bool 57 | dt_binary_search(dt_t dt, const dt_t *lo, const dt_t *hi) { 58 | lo = dt_lower_bound(dt, lo, hi); 59 | return (lo != hi && !(dt < *lo)); 60 | } 61 | 62 | -------------------------------------------------------------------------------- /t/nth_weekday_in_quarter.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int q; 7 | int nth; 8 | int ey; 9 | int eq; 10 | int ed; 11 | } tests[] = { 12 | {2010, 1, 1, 2010, 1, 1}, 13 | {2010, 1, -1, 2010, 1, 90}, 14 | {2014, 1, 1, 2014, 1, 1}, 15 | {2014, 1, 6, 2014, 1, 8}, 16 | {2014, 1, 11, 2014, 1, 15}, 17 | {2014, 1, 16, 2014, 1, 22}, 18 | {2014, 1, 21, 2014, 1, 29}, 19 | {2014, 1, 26, 2014, 1, 36}, 20 | {2014, 1, 31, 2014, 1, 43}, 21 | {2014, 1, 36, 2014, 1, 50}, 22 | {2014, 1, 41, 2014, 1, 57}, 23 | {2014, 1, 46, 2014, 1, 64}, 24 | {2014, 1, 51, 2014, 1, 71}, 25 | {2014, 1, 56, 2014, 1, 78}, 26 | {2014, 1, 61, 2014, 1, 85}, 27 | {2014, 1, -1, 2014, 1, 90}, 28 | {2014, 1, -6, 2014, 1, 83}, 29 | {2014, 1, -11, 2014, 1, 76}, 30 | {2014, 1, -16, 2014, 1, 69}, 31 | {2014, 1, -21, 2014, 1, 62}, 32 | {2014, 1, -26, 2014, 1, 55}, 33 | {2014, 1, -31, 2014, 1, 48}, 34 | {2014, 1, -36, 2014, 1, 41}, 35 | {2014, 1, -41, 2014, 1, 34}, 36 | {2014, 1, -46, 2014, 1, 27}, 37 | {2014, 1, -51, 2014, 1, 20}, 38 | {2014, 1, -56, 2014, 1, 13}, 39 | {2014, 1, -61, 2014, 1, 6}, 40 | }; 41 | 42 | int 43 | main() { 44 | int i, ntests; 45 | 46 | ntests = sizeof(tests) / sizeof(*tests); 47 | for (i = 0; i < ntests; i++) { 48 | const struct test t = tests[i]; 49 | 50 | { 51 | dt_t got = dt_from_nth_weekday_in_quarter(t.y, t.q, t.nth); 52 | dt_t exp = dt_from_yqd(t.ey, t.eq, t.ed); 53 | cmp_ok(got, "==", exp, "dt_from_nth_weekday_in_quarter(%d, %d, %d)", t.y, t.q, t.nth); 54 | } 55 | 56 | { 57 | dt_t src = dt_from_yqd(t.y, t.q, 1); 58 | dt_t got = dt_nth_weekday_in_quarter(src, t.nth); 59 | dt_t exp = dt_from_yqd(t.ey, t.eq, t.ed); 60 | cmp_ok(got, "==", exp, "dt_nth_weekday_in_quarter(%d, %d)", src, t.nth); 61 | } 62 | 63 | { 64 | dt_t src = dt_from_nth_weekday_in_quarter(t.y, t.q, t.nth); 65 | int got = dt_weekday_in_quarter(src, t.nth < 0); 66 | cmp_ok(got, "==", t.nth, "dt_weekday_in_quarter(%d, %s)", src, (t.nth < 0 ? "true" : "false")); 67 | } 68 | 69 | } 70 | done_testing(); 71 | } 72 | -------------------------------------------------------------------------------- /t/nth_weekday_in_year.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int nth; 7 | int ey; 8 | int ed; 9 | } tests[] = { 10 | {2014, 1, 2014, 1}, 11 | {2014, 16, 2014, 22}, 12 | {2014, 31, 2014, 43}, 13 | {2014, 46, 2014, 64}, 14 | {2014, 61, 2014, 85}, 15 | {2014, 76, 2014, 106}, 16 | {2014, 91, 2014, 127}, 17 | {2014, 106, 2014, 148}, 18 | {2014, 121, 2014, 169}, 19 | {2014, 136, 2014, 190}, 20 | {2014, 151, 2014, 211}, 21 | {2014, 166, 2014, 232}, 22 | {2014, 181, 2014, 253}, 23 | {2014, 196, 2014, 274}, 24 | {2014, 211, 2014, 295}, 25 | {2014, 226, 2014, 316}, 26 | {2014, 241, 2014, 337}, 27 | {2014, 256, 2014, 358}, 28 | {2014, -1, 2014, 365}, 29 | {2014, -16, 2014, 344}, 30 | {2014, -31, 2014, 323}, 31 | {2014, -46, 2014, 302}, 32 | {2014, -61, 2014, 281}, 33 | {2014, -76, 2014, 260}, 34 | {2014, -91, 2014, 239}, 35 | {2014, -106, 2014, 218}, 36 | {2014, -121, 2014, 197}, 37 | {2014, -136, 2014, 176}, 38 | {2014, -151, 2014, 155}, 39 | {2014, -166, 2014, 134}, 40 | {2014, -181, 2014, 113}, 41 | {2014, -196, 2014, 92}, 42 | {2014, -211, 2014, 71}, 43 | {2014, -226, 2014, 50}, 44 | {2014, -241, 2014, 29}, 45 | {2014, -256, 2014, 8}, 46 | }; 47 | 48 | int 49 | main() { 50 | int i, ntests; 51 | 52 | ntests = sizeof(tests) / sizeof(*tests); 53 | for (i = 0; i < ntests; i++) { 54 | const struct test t = tests[i]; 55 | 56 | { 57 | dt_t got = dt_from_nth_weekday_in_year(t.y, t.nth); 58 | dt_t exp = dt_from_yd(t.ey, t.ed); 59 | cmp_ok(got, "==", exp, "dt_from_nth_weekday_in_year(%d, %d)", t.y, t.nth); 60 | } 61 | 62 | { 63 | dt_t src = dt_from_yd(t.y, 1); 64 | dt_t got = dt_nth_weekday_in_year(src, t.nth); 65 | dt_t exp = dt_from_yd(t.ey, t.ed); 66 | cmp_ok(got, "==", exp, "dt_nth_weekday_in_year(%d, %d)", src, t.nth); 67 | } 68 | 69 | { 70 | dt_t src = dt_from_nth_weekday_in_year(t.y, t.nth); 71 | int got = dt_weekday_in_year(src, t.nth < 0); 72 | cmp_ok(got, "==", t.nth, "dt_weekday_in_year(%d, %s)", src, (t.nth < 0 ? "true" : "false")); 73 | } 74 | 75 | } 76 | done_testing(); 77 | } 78 | -------------------------------------------------------------------------------- /dt_easter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include "dt_core.h" 27 | #include "dt_easter.h" 28 | 29 | /* 30 | * Easter algorithms by Al Petrofsky, San Mateo County, California, U.S.A. 31 | * 32 | */ 33 | static int 34 | easter_gregorian(unsigned int y) { 35 | unsigned int a, b; 36 | a = y/100 * 1483 - y/400 * 2225 + 2613; 37 | b = ((y % 19 * 3510 + a/25 * 319) / 330) % 29; 38 | return 56 - b - ((y * 5/4) + a - b) % 7; 39 | } 40 | 41 | static int 42 | easter_julian(unsigned int y) { 43 | unsigned int a; 44 | a = (y % 19 * 19 + 15) % 30; 45 | return 28 + a - ((y * 5/4) + a) % 7; 46 | } 47 | 48 | dt_t 49 | dt_from_easter(int y, dt_computus_t computus) { 50 | if (y < 1) 51 | return 0; 52 | if (computus == DT_WESTERN) 53 | return dt_from_ymd(y, 3, easter_gregorian(y)); 54 | else 55 | return dt_from_ymd(y, 3, easter_julian(y) + y/100 - y/400 - 2); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /dt_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_CONFIG_H__ 27 | #define __DT_CONFIG_H__ 28 | 29 | #if !defined(_MSC_VER) 30 | # include 31 | #endif 32 | 33 | #if !defined(__cplusplus) && !defined(__bool_true_false_are_defined) 34 | typedef char _Bool; 35 | # define bool _Bool 36 | # define true 1 37 | # define false 0 38 | # define __bool_true_false_are_defined 1 39 | #endif 40 | 41 | /* Chronological Julian Date, January 1, 4713 BC, Monday 42 | #define DT_EPOCH_OFFSET 1721425 43 | */ 44 | 45 | /* Network Time Protocol (NTP), January 1, 1900, Monday 46 | #define DT_EPOCH_OFFSET -693596 47 | */ 48 | 49 | /* Unix, January 1, 1970, Thursday 50 | #define DT_EPOCH_OFFSET -719163 51 | */ 52 | 53 | /* Rata Die, January 1, 0001, Monday (as Day 1) */ 54 | #define DT_EPOCH_OFFSET 0 55 | 56 | #ifdef __cplusplus 57 | extern "C" { 58 | #endif 59 | 60 | typedef int dt_t; 61 | 62 | #ifdef __cplusplus 63 | } 64 | #endif 65 | #endif 66 | -------------------------------------------------------------------------------- /dt_parse_iso.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_PARSE_H__ 27 | #define __DT_PARSE_H__ 28 | #include 29 | #include "dt_core.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | size_t dt_parse_iso_date (const char *str, size_t len, dt_t *dt); 36 | 37 | size_t dt_parse_iso_time (const char *str, size_t len, int *sod, int *nsec); 38 | size_t dt_parse_iso_time_basic (const char *str, size_t len, int *sod, int *nsec); 39 | size_t dt_parse_iso_time_extended (const char *str, size_t len, int *sod, int *nsec); 40 | 41 | size_t dt_parse_iso_zone (const char *str, size_t len, int *offset); 42 | size_t dt_parse_iso_zone_basic (const char *str, size_t len, int *offset); 43 | size_t dt_parse_iso_zone_extended (const char *str, size_t len, int *offset); 44 | size_t dt_parse_iso_zone_lenient (const char *str, size_t len, int *offset); 45 | 46 | #ifdef __cplusplus 47 | } 48 | #endif 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /dt_zone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_ZONE_H__ 27 | #define __DT_ZONE_H__ 28 | #include 29 | #include "dt_core.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | enum { 36 | DT_ZONE_UTC = 0x01, 37 | DT_ZONE_RFC = 0x02, 38 | DT_ZONE_MILITARY = 0x04, 39 | DT_ZONE_AMBIGUOUS = 0x08, 40 | }; 41 | 42 | typedef struct { 43 | char name[8]; 44 | unsigned short flags; 45 | short offset; 46 | } dt_zone_t; 47 | 48 | size_t dt_zone_lookup (const char *str, size_t len, const dt_zone_t **zone); 49 | const char * dt_zone_name (const dt_zone_t *zone); 50 | short dt_zone_offset (const dt_zone_t *zone); 51 | bool dt_zone_is_utc (const dt_zone_t *zone); 52 | bool dt_zone_is_rfc (const dt_zone_t *zone); 53 | bool dt_zone_is_military (const dt_zone_t *zone); 54 | bool dt_zone_is_ambiguous (const dt_zone_t *zone); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | #endif 60 | -------------------------------------------------------------------------------- /t/char.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "dt.h" 3 | #include "tap.h" 4 | 5 | int 6 | main() { 7 | 8 | #define DT_CHAR(F) ("DT_CHAR_"#F), (DT_CHAR_##F) 9 | { 10 | const struct test { 11 | const char *name; 12 | unsigned char flag; 13 | size_t exp; 14 | const char *str; 15 | } tests[] = { 16 | { DT_CHAR(HSPACE), 2, " \t" }, 17 | { DT_CHAR(VSPACE), 2, "\r\n" }, 18 | { DT_CHAR(SPACE), 4, " \t\r\n" }, 19 | { DT_CHAR(PUNCT), 32, "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" }, 20 | { DT_CHAR(DIGIT), 10, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" }, 21 | { DT_CHAR(ALNUM), 36, "0123456789ABCDEFGHIJKLMnopqrstuvwxyz" }, 22 | { DT_CHAR(ALPHA), 26, "ABCDEFGHIJKLMnopqrstuvwxyz0123456789" }, 23 | { DT_CHAR(UPPER), 26, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij" }, 24 | { DT_CHAR(LOWER), 26, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJ" }, 25 | }; 26 | size_t i, ntests, got; 27 | 28 | ntests = sizeof(tests) / sizeof(*tests); 29 | for (i = 0; i < ntests; i++) { 30 | const struct test t = tests[i]; 31 | 32 | got = dt_char_span((const unsigned char *)t.str, strlen(t.str), t.flag); 33 | cmp_ok(got, "==", t.exp, "dt_char_span(\"%s\", %s)", t.str, t.name); 34 | } 35 | } 36 | 37 | { 38 | const struct test { 39 | const char *name; 40 | unsigned char flag; 41 | size_t exp; 42 | const char *str; 43 | } tests[] = { 44 | { DT_CHAR(ALPHA), 10, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" }, 45 | { DT_CHAR(LOWER), 23, "0123456789ABCDEFGHIJKLMnopqrstuvwxyz" }, 46 | { DT_CHAR(UPPER), 26, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJ" }, 47 | { DT_CHAR(DIGIT), 26, "ABCDEFGHIJKLMnopqrstuvwxyz0123456789" }, 48 | { DT_CHAR(ALPHA), 0, "ABCDEFGHIJKLMnopqrstuvwxyz0123456789" }, 49 | }; 50 | size_t i, ntests, got; 51 | 52 | ntests = sizeof(tests) / sizeof(*tests); 53 | for (i = 0; i < ntests; i++) { 54 | const struct test t = tests[i]; 55 | 56 | got = dt_char_span_until((const unsigned char *)t.str, strlen(t.str), t.flag); 57 | cmp_ok(got, "==", t.exp, "dt_char_span_until(\"%s\", %s)", t.str, t.name); 58 | } 59 | } 60 | 61 | done_testing(); 62 | } 63 | -------------------------------------------------------------------------------- /dt_arithmetic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_ARITHMETIC_H__ 27 | #define __DT_ARITHMETIC_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | typedef enum { 35 | DT_EXCESS, 36 | DT_LIMIT, 37 | DT_SNAP 38 | } dt_adjust_t; 39 | 40 | dt_t dt_add_years (dt_t dt, int delta, dt_adjust_t adjust); 41 | dt_t dt_add_quarters (dt_t dt, int delta, dt_adjust_t adjust); 42 | dt_t dt_add_months (dt_t dt, int delta, dt_adjust_t adjust); 43 | 44 | void dt_delta_yd (dt_t start, dt_t end, int *y, int *d); 45 | void dt_delta_ymd (dt_t start, dt_t end, int *y, int *m, int *d); 46 | void dt_delta_yqd (dt_t start, dt_t end, int *y, int *q, int *d); 47 | 48 | int dt_delta_years (dt_t start, dt_t end, bool complete); 49 | int dt_delta_quarters (dt_t start, dt_t end, bool complete); 50 | int dt_delta_months (dt_t start, dt_t end, bool complete); 51 | int dt_delta_weeks (dt_t start, dt_t end); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | #endif 57 | 58 | -------------------------------------------------------------------------------- /t/ymd_epochs.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int d; 8 | int rdn; 9 | } tests[] = { 10 | /* Test cases from Calendrical Calculations, Third Edition, Table 1.2 */ 11 | { -4713, 11, 24, -1721425 }, /* Chronological Modified Julian Date */ 12 | { -3760, 9, 7, -1373427 }, /* Hebrew */ 13 | { -3113, 8, 11, -1137142 }, /* Mayan */ 14 | { -3101, 1, 23, -1132959 }, /* Hinu (Kali Yuga) */ 15 | { -2636, 2, 15, -963099 }, /* Chinese */ 16 | { -746, 2, 18, -272787 }, /* Egyptian */ 17 | { -127, 12, 7, -46410 }, /* Tibetan */ 18 | { 0, 12, 30, -1 }, /* Julian */ 19 | { 1, 1, 1, 1 }, /* Gregorian */ 20 | { 8, 8, 27, 2796 }, /* Ethiopic */ 21 | { 284, 8, 29, 103605 }, /* Coptic */ 22 | { 552, 7, 13, 201443 }, /* Armenian */ 23 | { 622, 3, 22, 226896 }, /* Persian */ 24 | { 622, 7, 19, 227015 }, /* Islamic */ 25 | { 632, 6, 19, 230638 }, /* Zoroastrian */ 26 | { 1792, 9, 22, 654415 }, /* French Revolutionary */ 27 | { 1844, 3, 21, 673222 }, /* Bahá'í */ 28 | { 1858, 11, 17, 678576 }, /* Modified Julian Day Number */ 29 | }; 30 | 31 | int 32 | main() { 33 | int i, ntests; 34 | 35 | ntests = sizeof(tests) / sizeof(*tests); 36 | for (i = 0; i < ntests; i++) { 37 | const struct test t = tests[i]; 38 | 39 | { 40 | int rdn = dt_rdn(dt_from_ymd(t.y, t.m, t.d)); 41 | cmp_ok(rdn, "==", t.rdn, "dt_rdn(dt_from_ymd(%d, %d, %d))", t.y, t.m, t.d); 42 | } 43 | 44 | { 45 | int y, m, d; 46 | dt_to_ymd(dt_from_rdn(t.rdn), &y, &m, &d); 47 | if (!ok((y == t.y && m == t.m && d == t.d), "dt_to_ymd(dt_from_rdn(%d))", t.rdn)) { 48 | diag(" got: %.4d-%.2d-%.2d", y, m, d); 49 | diag(" exp: %.4d-%.2d-%.2d", t.y, t.m, t.d); 50 | } 51 | } 52 | 53 | { 54 | int got = dt_year(dt_from_rdn(t.rdn)); 55 | cmp_ok(got, "==", t.y, "dt_year(dt_from_rdn(%d))", t.rdn); 56 | } 57 | 58 | { 59 | int got = dt_month(dt_from_rdn(t.rdn)); 60 | cmp_ok(got, "==", t.m, "dt_month(dt_from_rdn(%d))", t.rdn); 61 | } 62 | 63 | { 64 | int got = dt_dom(dt_from_rdn(t.rdn)); 65 | cmp_ok(got, "==", t.d, "dt_dom(dt_from_rdn(%d))", t.rdn); 66 | } 67 | 68 | { 69 | ok(dt_valid_ymd(t.y, t.m, t.d), "dt_valid_ymd(%d, %d, %d)", t.y, t.m, t.d); 70 | } 71 | 72 | } 73 | done_testing(); 74 | } 75 | -------------------------------------------------------------------------------- /dt_accessor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include 27 | #include "dt_core.h" 28 | 29 | dt_t 30 | dt_from_cjdn(int n) { 31 | return dt_from_rdn(n - 1721425); 32 | } 33 | 34 | int 35 | dt_cjdn(dt_t dt) { 36 | return dt_rdn(dt) + 1721425; 37 | } 38 | 39 | int 40 | dt_year(dt_t dt) { 41 | int y; 42 | dt_to_yd(dt, &y, NULL); 43 | return y; 44 | } 45 | 46 | int 47 | dt_quarter(dt_t dt) { 48 | int q; 49 | dt_to_yqd(dt, NULL, &q, NULL); 50 | return q; 51 | } 52 | 53 | int 54 | dt_month(dt_t dt) { 55 | int m; 56 | dt_to_ymd(dt, NULL, &m, NULL); 57 | return m; 58 | } 59 | 60 | int 61 | dt_doy(dt_t dt) { 62 | int d; 63 | dt_to_yd(dt, NULL, &d); 64 | return d; 65 | } 66 | 67 | int 68 | dt_doq(dt_t dt) { 69 | int d; 70 | dt_to_yqd(dt, NULL, NULL, &d); 71 | return d; 72 | } 73 | 74 | int 75 | dt_dom(dt_t dt) { 76 | int d; 77 | dt_to_ymd(dt, NULL, NULL, &d); 78 | return d; 79 | } 80 | 81 | int 82 | dt_woy(dt_t dt) { 83 | int w; 84 | dt_to_ywd(dt, NULL, &w, NULL); 85 | return w; 86 | } 87 | 88 | int 89 | dt_yow(dt_t dt) { 90 | int y; 91 | dt_to_ywd(dt, &y, NULL, NULL); 92 | return y; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /dt_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include "dt_core.h" 27 | 28 | bool 29 | dt_leap_year(int y) { 30 | return ((y & 3) == 0 && (y % 100 != 0 || y % 400 == 0)); 31 | } 32 | 33 | int 34 | dt_days_in_year(int y) { 35 | return dt_leap_year(y) ? 366 : 365; 36 | } 37 | 38 | int 39 | dt_days_in_quarter(int y, int q) { 40 | static const int days_in_quarter[2][5] = { 41 | { 0, 90, 91, 92, 92 }, 42 | { 0, 91, 91, 92, 92 } 43 | }; 44 | if (q < 1 || q > 4) 45 | return 0; 46 | return days_in_quarter[dt_leap_year(y)][q]; 47 | } 48 | 49 | int 50 | dt_days_in_month(int y, int m) { 51 | static const int days_in_month[2][13] = { 52 | { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 53 | { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 54 | }; 55 | if (m < 1 || m > 12) 56 | return 0; 57 | return days_in_month[dt_leap_year(y)][m]; 58 | } 59 | 60 | int 61 | dt_weeks_in_year(int year) { 62 | unsigned int y, d; 63 | if (year < 1) 64 | year += 400 * (1 - year/400); 65 | y = year - 1; 66 | d = (y + y/4 - y/100 + y/400) % 7; /* [0=Mon, 6=Sun]*/ 67 | return (d == 3 || (d == 2 && dt_leap_year(year))) ? 53 : 52; 68 | } 69 | 70 | -------------------------------------------------------------------------------- /dt_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_CORE_H__ 27 | #define __DT_CORE_H__ 28 | #include "dt_config.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | typedef enum { 35 | DT_MON = 1, 36 | DT_MONDAY = 1, 37 | DT_TUE = 2, 38 | DT_TUESDAY = 2, 39 | DT_WED = 3, 40 | DT_WEDNESDAY = 3, 41 | DT_THU = 4, 42 | DT_THURSDAY = 4, 43 | DT_FRI = 5, 44 | DT_FRIDAY = 5, 45 | DT_SAT = 6, 46 | DT_SATURDAY = 6, 47 | DT_SUN = 7, 48 | DT_SUNDAY = 7, 49 | } dt_dow_t; 50 | 51 | dt_t dt_from_rdn (int n); 52 | dt_t dt_from_yd (int y, int d); 53 | dt_t dt_from_ymd (int y, int m, int d); 54 | dt_t dt_from_yqd (int y, int q, int d); 55 | dt_t dt_from_ywd (int y, int w, int d); 56 | 57 | void dt_to_yd (dt_t dt, int *y, int *d); 58 | void dt_to_ymd (dt_t dt, int *y, int *m, int *d); 59 | void dt_to_yqd (dt_t dt, int *y, int *q, int *d); 60 | void dt_to_ywd (dt_t dt, int *y, int *w, int *d); 61 | 62 | int dt_rdn (dt_t dt); 63 | dt_dow_t dt_dow (dt_t dt); 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /dt_weekday.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2014 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_WEEKDAY_H__ 27 | #define __DT_WEEKDAY_H__ 28 | #include "dt_core.h" 29 | #include "dt_weekday.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | dt_t dt_from_nth_weekday_in_year (int y, int nth); 36 | dt_t dt_from_nth_weekday_in_quarter (int y, int q, int nth); 37 | dt_t dt_from_nth_weekday_in_month (int y, int m, int nth); 38 | 39 | dt_t dt_nth_weekday_in_year (dt_t dt, int nth); 40 | dt_t dt_nth_weekday_in_quarter (dt_t dt, int nth); 41 | dt_t dt_nth_weekday_in_month (dt_t dt, int nth); 42 | 43 | int dt_weekday_in_year (dt_t dt, bool end); 44 | int dt_weekday_in_quarter (dt_t dt, bool end); 45 | int dt_weekday_in_month (dt_t dt, bool end); 46 | 47 | bool dt_is_weekday (dt_t dt); 48 | 49 | dt_t dt_next_weekday (dt_t dt, bool current); 50 | dt_t dt_prev_weekday (dt_t dt, bool current); 51 | 52 | dt_t dt_add_weekdays (dt_t dt, int delta); 53 | 54 | int dt_delta_weekdays (dt_t start, dt_t end, bool inclusive); 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /dt_navigate.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include 27 | #include "dt_core.h" 28 | 29 | dt_t 30 | dt_start_of_year(dt_t dt, int offset) { 31 | int y; 32 | dt_to_yd(dt, &y, NULL); 33 | return dt_from_yd(y + offset, 1); 34 | } 35 | 36 | dt_t 37 | dt_start_of_quarter(dt_t dt, int offset) { 38 | int y, q; 39 | dt_to_yqd(dt, &y, &q, NULL); 40 | return dt_from_yqd(y, q + offset, 1); 41 | } 42 | 43 | dt_t 44 | dt_start_of_month(dt_t dt, int offset) { 45 | int y, m; 46 | dt_to_ymd(dt, &y, &m, NULL); 47 | return dt_from_ymd(y, m + offset, 1); 48 | } 49 | 50 | dt_t 51 | dt_start_of_week(dt_t dt, dt_dow_t first) { 52 | return dt - (dt_dow(dt) - first + 7) % 7; 53 | } 54 | 55 | dt_t 56 | dt_end_of_year(dt_t dt, int offset) { 57 | int y; 58 | dt_to_yd(dt, &y, NULL); 59 | return dt_from_yd(y + offset + 1, 0); 60 | } 61 | 62 | dt_t 63 | dt_end_of_quarter(dt_t dt, int offset) { 64 | int y, q; 65 | dt_to_yqd(dt, &y, &q, NULL); 66 | return dt_from_yqd(y, q + offset + 1, 0); 67 | } 68 | 69 | dt_t 70 | dt_end_of_month(dt_t dt, int offset) { 71 | int y, m; 72 | dt_to_ymd(dt, &y, &m, NULL); 73 | return dt_from_ymd(y, m + offset + 1, 0); 74 | } 75 | 76 | dt_t 77 | dt_end_of_week(dt_t dt, dt_dow_t first) { 78 | return dt + (first - dt_dow(dt) + 6) % 7; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /dt_dow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_DOW_H__ 27 | #define __DT_DOW_H__ 28 | #include "dt_core.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | dt_t dt_from_nth_dow_in_year (int y, int nth, dt_dow_t day); 35 | dt_t dt_from_nth_dow_in_quarter (int y, int q, int nth, dt_dow_t day); 36 | dt_t dt_from_nth_dow_in_month (int y, int m, int nth, dt_dow_t day); 37 | 38 | dt_t dt_nth_dow (dt_t dt, int nth, dt_dow_t day); 39 | dt_t dt_nth_dow_in_year (dt_t dt, int nth, dt_dow_t day); 40 | dt_t dt_nth_dow_in_quarter (dt_t dt, int nth, dt_dow_t day); 41 | dt_t dt_nth_dow_in_month (dt_t dt, int nth, dt_dow_t day); 42 | 43 | dt_t dt_next_dow (dt_t dt, dt_dow_t day, bool current); 44 | dt_t dt_prev_dow (dt_t dt, dt_dow_t day, bool current); 45 | 46 | int dt_dow_in_year (dt_t dt, bool end); 47 | int dt_dow_in_quarter (dt_t dt, bool end); 48 | int dt_dow_in_month (dt_t dt, bool end); 49 | 50 | bool dt_is_nth_dow_in_year (dt_t dt, int nth, dt_dow_t day); 51 | bool dt_is_nth_dow_in_quarter (dt_t dt, int nth, dt_dow_t day); 52 | bool dt_is_nth_dow_in_month (dt_t dt, int nth, dt_dow_t day); 53 | 54 | #ifdef __cplusplus 55 | } 56 | #endif 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /t/is_holiday.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | struct ymd { 5 | int y; 6 | int m; 7 | int d; 8 | }; 9 | 10 | const struct ymd days[] = { 11 | {2013, 1, 1}, /* Tuesday, Nyårsdagen (New Year's Day) */ 12 | {2013, 3, 29}, /* Friday, Långfredagen (Good Friday) */ 13 | {2013, 4, 1}, /* Monday, Annandag påsk (Easter Monday) */ 14 | {2013, 5, 1}, /* Wednesday, Första maj (First of May) */ 15 | {2013, 5, 9}, /* Thursday, Kristi himmelsfärds dag (Ascension Day) */ 16 | {2013, 6, 6}, /* Thursday, Sveriges nationaldag (National Day of Sweden) */ 17 | {2013, 6, 21}, /* Friday, Midsommarafton (Midsummer's Eve) */ 18 | {2013, 12, 24}, /* Tuesday, Julafton (Christmas Eve) */ 19 | {2013, 12, 25}, /* Wednesday, Juldagen (Christmas Day) */ 20 | {2013, 12, 26}, /* Thursday, Annandag jul (Boxing Day) */ 21 | {2013, 12, 31}, /* Tuesday, Nyårsafton (New Year's Eve) */ 22 | }; 23 | 24 | const struct ymd weekdays[] = { 25 | {2013, 1, 2}, /* Wednesday */ 26 | {2013, 3, 28}, /* Thursday */ 27 | {2013, 4, 2}, /* Tuesday */ 28 | {2013, 5, 2}, /* Thursday */ 29 | {2013, 12, 30}, /* Monday */ 30 | }; 31 | 32 | 33 | const struct ymd weekends[] = { 34 | {2013, 1, 6}, /* Sunday */ 35 | {2013, 2, 9}, /* Saturday */ 36 | {2013, 4, 28}, /* Sunday */ 37 | {2013, 5, 5}, /* Sunday */ 38 | {2013, 12, 28}, /* Saturday */ 39 | }; 40 | 41 | int 42 | main() { 43 | int ntests, nholidays; 44 | int i; 45 | dt_t holidays[20]; 46 | 47 | nholidays = sizeof(days) / sizeof(*days); 48 | for (i = 0; i < nholidays; i++) { 49 | const struct ymd date = days[i]; 50 | holidays[i] = dt_from_ymd(date.y, date.m, date.d); 51 | } 52 | 53 | ntests = sizeof(days) / sizeof(*days); 54 | for (i = 0; i < ntests; i++) { 55 | const struct ymd t = days[i]; 56 | 57 | { 58 | dt_t dt = dt_from_ymd(t.y, t.m, t.d); 59 | ok(dt_is_holiday(dt, holidays, nholidays), "dt_is_holiday(%.4d-%.2d-%.2d)", 60 | t.y, t.m, t.d); 61 | } 62 | } 63 | 64 | ntests = sizeof(weekdays) / sizeof(*weekdays); 65 | for (i = 0; i < ntests; i++) { 66 | const struct ymd t = weekdays[i]; 67 | 68 | { 69 | dt_t dt = dt_from_ymd(t.y, t.m, t.d); 70 | ok(!dt_is_holiday(dt, holidays, nholidays), "dt_is_holiday(%.4d-%.2d-%.2d)", 71 | t.y, t.m, t.d); 72 | } 73 | } 74 | 75 | ntests = sizeof(weekends) / sizeof(*weekends); 76 | for (i = 0; i < ntests; i++) { 77 | const struct ymd t = weekends[i]; 78 | 79 | { 80 | dt_t dt = dt_from_ymd(t.y, t.m, t.d); 81 | ok(!dt_is_holiday(dt, holidays, nholidays), "dt_is_holiday(%.4d-%.2d-%.2d)", 82 | t.y, t.m, t.d); 83 | } 84 | } 85 | done_testing(); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /t/is_workday.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | struct ymd { 5 | int y; 6 | int m; 7 | int d; 8 | }; 9 | 10 | const struct ymd days[] = { 11 | {2013, 1, 1}, /* Tuesday, Nyårsdagen (New Year's Day) */ 12 | {2013, 3, 29}, /* Friday, Långfredagen (Good Friday) */ 13 | {2013, 4, 1}, /* Monday, Annandag påsk (Easter Monday) */ 14 | {2013, 5, 1}, /* Wednesday, Första maj (First of May) */ 15 | {2013, 5, 9}, /* Thursday, Kristi himmelsfärds dag (Ascension Day) */ 16 | {2013, 6, 6}, /* Thursday, Sveriges nationaldag (National Day of Sweden) */ 17 | {2013, 6, 21}, /* Friday, Midsommarafton (Midsummer's Eve) */ 18 | {2013, 12, 24}, /* Tuesday, Julafton (Christmas Eve) */ 19 | {2013, 12, 25}, /* Wednesday, Juldagen (Christmas Day) */ 20 | {2013, 12, 26}, /* Thursday, Annandag jul (Boxing Day) */ 21 | {2013, 12, 31}, /* Tuesday, Nyårsafton (New Year's Eve) */ 22 | }; 23 | 24 | const struct ymd weekdays[] = { 25 | {2013, 1, 2}, /* Wednesday */ 26 | {2013, 3, 28}, /* Thursday */ 27 | {2013, 4, 2}, /* Tuesday */ 28 | {2013, 5, 2}, /* Thursday */ 29 | {2013, 12, 30}, /* Monday */ 30 | }; 31 | 32 | 33 | const struct ymd weekends[] = { 34 | {2013, 1, 6}, /* Sunday */ 35 | {2013, 2, 9}, /* Saturday */ 36 | {2013, 4, 28}, /* Sunday */ 37 | {2013, 5, 5}, /* Sunday */ 38 | {2013, 12, 28}, /* Saturday */ 39 | }; 40 | 41 | int 42 | main() { 43 | int ntests, nholidays; 44 | int i; 45 | dt_t holidays[20]; 46 | 47 | nholidays = sizeof(days) / sizeof(*days); 48 | for (i = 0; i < nholidays; i++) { 49 | const struct ymd date = days[i]; 50 | holidays[i] = dt_from_ymd(date.y, date.m, date.d); 51 | } 52 | 53 | ntests = sizeof(days) / sizeof(*days); 54 | for (i = 0; i < ntests; i++) { 55 | const struct ymd t = days[i]; 56 | 57 | { 58 | dt_t dt = dt_from_ymd(t.y, t.m, t.d); 59 | ok(!dt_is_workday(dt, holidays, nholidays), "dt_is_workday(%.4d-%.2d-%.2d)", 60 | t.y, t.m, t.d); 61 | } 62 | } 63 | 64 | ntests = sizeof(weekends) / sizeof(*weekends); 65 | for (i = 0; i < ntests; i++) { 66 | const struct ymd t = weekends[i]; 67 | 68 | { 69 | dt_t dt = dt_from_ymd(t.y, t.m, t.d); 70 | ok(!dt_is_workday(dt, holidays, nholidays), "dt_is_workday(%.4d-%.2d-%.2d)", 71 | t.y, t.m, t.d); 72 | } 73 | } 74 | 75 | ntests = sizeof(weekdays) / sizeof(*weekdays); 76 | for (i = 0; i < ntests; i++) { 77 | const struct ymd t = weekdays[i]; 78 | 79 | { 80 | dt_t dt = dt_from_ymd(t.y, t.m, t.d); 81 | ok(dt_is_workday(dt, holidays, nholidays), "dt_is_workday(%.4d-%.2d-%.2d)", 82 | t.y, t.m, t.d); 83 | } 84 | } 85 | 86 | done_testing(); 87 | } 88 | 89 | -------------------------------------------------------------------------------- /t/yd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int d; 7 | int rdn; 8 | } tests[] = { 9 | {1970, 195, 719357}, 10 | {1971, 256, 719783}, 11 | {1972, 199, 720091}, 12 | {1973, 331, 720589}, 13 | {1974, 249, 720872}, 14 | {1975, 30, 721018}, 15 | {1976, 300, 721653}, 16 | {1977, 217, 721936}, 17 | {1978, 114, 722198}, 18 | {1979, 38, 722487}, 19 | {1980, 278, 723092}, 20 | {1981, 300, 723480}, 21 | {1982, 58, 723603}, 22 | {1983, 222, 724132}, 23 | {1984, 185, 724460}, 24 | {1985, 324, 724965}, 25 | {1986, 93, 725099}, 26 | {1987, 338, 725709}, 27 | {1988, 174, 725910}, 28 | {1989, 350, 726452}, 29 | {1990, 170, 726637}, 30 | {1991, 291, 727123}, 31 | {1992, 251, 727448}, 32 | {1993, 214, 727777}, 33 | {1994, 107, 728035}, 34 | {1995, 94, 728387}, 35 | {1996, 360, 729018}, 36 | {1997, 164, 729188}, 37 | {1998, 45, 729434}, 38 | {1999, 360, 730114}, 39 | {2000, 341, 730460}, 40 | {2001, 133, 730618}, 41 | {2002, 140, 730990}, 42 | {2003, 140, 731355}, 43 | {2004, 233, 731813}, 44 | {2005, 22, 731968}, 45 | {2006, 80, 732391}, 46 | {2007, 340, 733016}, 47 | {2008, 365, 733406}, 48 | {2009, 254, 733661}, 49 | {2010, 116, 733888}, 50 | {2011, 301, 734438}, 51 | {2012, 143, 734645}, 52 | {2013, 106, 734974}, 53 | {2014, 102, 735335}, 54 | {2015, 347, 735945}, 55 | {2016, 210, 736173}, 56 | {2017, 277, 736606}, 57 | {2018, 139, 736833}, 58 | {2019, 99, 737158}, 59 | {2020, 282, 737706}, 60 | }; 61 | 62 | int 63 | main() { 64 | int i, ntests; 65 | 66 | ntests = sizeof(tests) / sizeof(*tests); 67 | for (i = 0; i < ntests; i++) { 68 | const struct test t = tests[i]; 69 | 70 | { 71 | int rdn = dt_rdn(dt_from_yd(t.y, t.d)); 72 | cmp_ok(rdn, "==", t.rdn, "dt_rdn(dt_from_yd(%d, %d))", t.y, t.d); 73 | } 74 | 75 | { 76 | int y, d; 77 | dt_to_yd(dt_from_rdn(t.rdn), &y, &d); 78 | if (!ok((y == t.y && d == t.d), "dt_to_yd(dt_from_rdn(%d))", t.rdn)) { 79 | diag(" got: %.4d-%.3d", y, d); 80 | diag(" exp: %.4d-%.3d", t.y, t.d); 81 | } 82 | } 83 | 84 | { 85 | int got = dt_year(dt_from_rdn(t.rdn)); 86 | cmp_ok(got, "==", t.y, "dt_year(dt_from_rdn(%d))", t.rdn); 87 | } 88 | 89 | { 90 | int got = dt_doy(dt_from_rdn(t.rdn)); 91 | cmp_ok(got, "==", t.d, "dt_doy(dt_from_rdn(%d))", t.rdn); 92 | } 93 | 94 | { 95 | ok(dt_valid_yd(t.y, t.d), "dt_valid_yd(%d, %d)", t.y, t.d); 96 | } 97 | 98 | } 99 | done_testing(); 100 | } 101 | -------------------------------------------------------------------------------- /t/parse_iso_zone_lenient.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include 4 | 5 | const struct good_t { 6 | int offset; 7 | const char *str; 8 | size_t elen; 9 | } good[] = { 10 | { 0, "Z", 1 }, 11 | { 0, "z", 1 }, 12 | { 0, "GMT", 3 }, 13 | { 180, "GMT+03", 6 }, 14 | { -180, "GMT-03", 6 }, 15 | { 180, "GMT+3", 5 }, 16 | { -180, "GMT-3", 5 }, 17 | { 0, "UTC", 3 }, 18 | { 180, "UTC+03", 6 }, 19 | { -180, "UTC-03", 6 }, 20 | { 180, "UTC+3", 5 }, 21 | { -180, "UTC-3", 5 }, 22 | { 0, "+0", 2 }, 23 | { 0, "-0", 2 }, 24 | { 60, "+1", 2 }, 25 | { -60, "-1", 2 }, 26 | { 60, "+1:00", 5 }, 27 | { -60, "-1:00", 5 }, 28 | { 0, "+00", 3 }, 29 | { 1380, "+23", 3 }, 30 | { 60, "+01", 3 }, 31 | { -1380, "-23", 3 }, 32 | { -60, "-01", 3 }, 33 | { 0, "+00:00", 6 }, 34 | { 59, "+00:59", 6 }, 35 | { -59, "-00:59", 6 }, 36 | { 1439, "+23:59", 6 }, 37 | { -1439, "-23:59", 6 }, 38 | { 0, "+0000", 5 }, 39 | { 59, "+0059", 5 }, 40 | { -59, "-0059", 5 }, 41 | { 1439, "+2359", 5 }, 42 | { -1439, "-2359", 5 }, 43 | }; 44 | 45 | const struct bad_t { 46 | const char *str; 47 | } bad[] = { 48 | { "" }, 49 | { "0" }, 50 | { "+00:0" }, 51 | { "-00:0" }, 52 | { "+00:60" }, 53 | { "-00:60" }, 54 | { "+24:00" }, 55 | { "+00:000" }, 56 | { "-00:000" }, 57 | { "+0060" }, 58 | { "-0060" }, 59 | { "-0060" }, 60 | { "+2400" }, 61 | { "+00000" }, 62 | { "-00000" }, 63 | { "UT" }, 64 | { "UTc" }, 65 | { "UTC+" }, 66 | { "UTC-" }, 67 | { "GM" }, 68 | { "GMt" }, 69 | { "GMT+" }, 70 | { "GMT-" }, 71 | }; 72 | 73 | int 74 | main() { 75 | int i, ntests; 76 | 77 | ntests = sizeof(good) / sizeof(*good); 78 | for (i = 0; i < ntests; i++) { 79 | const struct good_t t = good[i]; 80 | 81 | { 82 | int offset = 0; 83 | size_t glen; 84 | 85 | glen = dt_parse_iso_zone_lenient(t.str, strlen(t.str), &offset); 86 | ok(glen == t.elen, "dt_parse_iso_zone_lenient(%s) size_t: %d", t.str, (int)glen); 87 | cmp_ok(offset, "==", t.offset, "dt_parse_iso_zone_lenient(%s)", t.str); 88 | } 89 | } 90 | 91 | ntests = sizeof(bad) / sizeof(*bad); 92 | for (i = 0; i < ntests; i++) { 93 | const struct bad_t t = bad[i]; 94 | 95 | { 96 | int offset = 0; 97 | size_t glen; 98 | 99 | glen = dt_parse_iso_zone_lenient(t.str, strlen(t.str), &offset); 100 | ok(glen == 0, "dt_parse_iso_zone_lenient(%s) size_t: %d", t.str, (int)glen); 101 | } 102 | } 103 | done_testing(); 104 | } 105 | 106 | -------------------------------------------------------------------------------- /dt_workday.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_WORKDAY_H__ 27 | #define __DT_WORKDAY_H__ 28 | #include 29 | #include "dt_core.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | typedef enum { 36 | DT_UNADJUSTED, 37 | DT_FOLLOWING, 38 | DT_MODIFIED_FOLLOWING, 39 | DT_PRECEDING, 40 | DT_MODIFIED_PRECEDING, 41 | } dt_bdc_t; 42 | 43 | dt_t dt_from_nth_workday_in_year (int y, int nth, const dt_t *holidays, size_t n); 44 | dt_t dt_from_nth_workday_in_quarter (int y, int q, int nth, const dt_t *holidays, size_t n); 45 | dt_t dt_from_nth_workday_in_month (int y, int m, int nth, const dt_t *holidays, size_t n); 46 | 47 | dt_t dt_nth_workday_in_year (dt_t dt, int nth, const dt_t *holidays, size_t n); 48 | dt_t dt_nth_workday_in_quarter (dt_t dt, int nth, const dt_t *holidays, size_t n); 49 | dt_t dt_nth_workday_in_month (dt_t dt, int nth, const dt_t *holidays, size_t n); 50 | 51 | bool dt_is_holiday (dt_t dt, const dt_t *holidays, size_t n); 52 | bool dt_is_workday (dt_t dt, const dt_t *holidays, size_t n); 53 | 54 | dt_t dt_next_workday (dt_t dt, bool current, const dt_t *holidays, size_t n); 55 | dt_t dt_prev_workday (dt_t dt, bool current, const dt_t *holidays, size_t n); 56 | 57 | dt_t dt_add_workdays (dt_t dt, int delta, const dt_t *holidays, size_t n); 58 | 59 | int dt_delta_workdays (dt_t start, dt_t end, bool inclusive, const dt_t *holidays, size_t n); 60 | 61 | dt_t dt_roll_workday (dt_t dt, dt_bdc_t convention, const dt_t *holidays, size_t n); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | #endif 67 | 68 | -------------------------------------------------------------------------------- /tools/zone.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | 5 | sub encode { 6 | my ($string) = @_; 7 | my $v = 0; 8 | foreach my $c (split //, $string) { 9 | $v = ($v << 5) | ((ord($c) | 0x20) ^ 0x60); 10 | } 11 | return $v; 12 | } 13 | 14 | my %Universal = ( 15 | GMT => 0, 16 | UTC => 0, 17 | UT => 0, 18 | ); 19 | 20 | my %Military = ( 21 | A => 1*60, B => 2*60, C => 3*60, 22 | D => 4*60, E => 5*60, F => 6*60, 23 | G => 7*60, H => 8*60, I => 9*60, 24 | K => 10*60, L => 11*60, M => 12*60, 25 | 26 | N => -1*60, O => -2*60, P => -3*60, 27 | Q => -4*60, R => -5*60, S => -6*60, 28 | T => -7*60, U => -8*60, V => -9*60, 29 | W => -10*60, X => -11*60, Y => -12*60, 30 | 31 | Z => 0, 32 | ); 33 | 34 | my %Rfc = ( 35 | UT => 0, 36 | UTC => 0, 37 | GMT => 0, 38 | EST => -5*60, EDT => -4*60, 39 | CST => -6*60, CDT => -5*60, 40 | MST => -7*60, MDT => -6*60, 41 | PST => -8*60, PDT => -7*60, 42 | ); 43 | 44 | my %FlagName; 45 | 46 | BEGIN { 47 | my %flags = ( 48 | DT_ZONE_UTC => 0x01, 49 | DT_ZONE_RFC => 0x02, 50 | DT_ZONE_MILITARY => 0x04, 51 | DT_ZONE_AMBIGUOUS => 0x08, 52 | ); 53 | require constant; constant->import(\%flags); 54 | %FlagName = reverse %flags; 55 | } 56 | 57 | my $EntryRx = do { 58 | my $offset = '[+-] (?: [01][0-9] | [2][0-3]) [:] [0-5][0-9]'; 59 | my $name = '[A-Z][A-Za-z]{0,4}'; 60 | qr< ($name) \s+ ($offset) (?: \s+ / \s+ ($offset))? \s+ #>xo 61 | }; 62 | 63 | open(my $fh, '<:raw', 'tools/zone.txt') 64 | or die qq; 65 | 66 | my %Zone; 67 | 68 | while (<$fh>) { 69 | next if /\A \s* \# /x; 70 | 71 | my ($name, $offset1, $offset2) = m< \A $EntryRx >x 72 | or die qq/Could not parse zone entry at line: $./; 73 | 74 | my $encoded = encode($name); 75 | my $offset = 0; 76 | my $flags = 0; 77 | 78 | if ($offset2) { 79 | $flags = DT_ZONE_AMBIGUOUS; 80 | } 81 | else { 82 | my ($h, $m) = split /[:]/, $offset1; 83 | $offset = $h * 60 + $m; 84 | } 85 | 86 | if (exists $Zone{$encoded}) { 87 | $flags |= DT_ZONE_AMBIGUOUS; 88 | $offset = 0; 89 | } 90 | 91 | if (exists $Universal{$name}) { 92 | $flags |= DT_ZONE_UTC; 93 | $offset = 0; 94 | } 95 | 96 | if (exists $Military{$name}) { 97 | $flags |= DT_ZONE_MILITARY; 98 | $offset = $Military{$name}; 99 | } 100 | 101 | if (exists $Rfc{$name}) { 102 | $flags |= DT_ZONE_RFC; 103 | $offset = $Rfc{$name}; 104 | } 105 | 106 | $Zone{$encoded} = [$flags, $offset, $name]; 107 | } 108 | 109 | printf "/* Automatically generated by tools/zone.pl */\n"; 110 | foreach my $encoded (sort { $a <=> $b } keys %Zone) { 111 | my ($flags, $offset, $name) = @{ $Zone{$encoded} }; 112 | 113 | if ($flags != 0) { 114 | my @names; 115 | 116 | while ((my $flag = $flags & -$flags) != 0) { 117 | push @names, $FlagName{$flag} || die sprintf '0x%.8X', $flag; 118 | $flags &= ~$flag; 119 | } 120 | 121 | $flags = join '|', @names; 122 | } 123 | 124 | printf "DT_ZONE_ENTRY(0x%.8X, %4d, \"%s\", %s)\n", 125 | $encoded, $offset, $name, $flags; 126 | } 127 | -------------------------------------------------------------------------------- /t/roll_workday.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct ymd { 5 | int y; 6 | int m; 7 | int d; 8 | } days[] = { 9 | {2013, 1, 1}, /* Tuesday, Nyårsdagen (New Year's Day) */ 10 | {2013, 3, 29}, /* Friday, Långfredagen (Good Friday) */ 11 | {2013, 4, 1}, /* Monday, Annandag påsk (Easter Monday) */ 12 | {2013, 5, 1}, /* Wednesday, Första maj (First of May) */ 13 | {2013, 5, 9}, /* Thursday, Kristi himmelsfärds dag (Ascension Day) */ 14 | {2013, 6, 6}, /* Thursday, Sveriges nationaldag (National Day of Sweden) */ 15 | {2013, 6, 21}, /* Friday, Midsommarafton (Midsummer's Eve) */ 16 | {2013, 12, 24}, /* Tuesday, Julafton (Christmas Eve) */ 17 | {2013, 12, 25}, /* Wednesday, Juldagen (Christmas Day) */ 18 | {2013, 12, 26}, /* Thursday, Annandag jul (Boxing Day) */ 19 | {2013, 12, 31}, /* Tuesday, Nyårsafton (New Year's Eve) */ 20 | }; 21 | 22 | const struct test { 23 | int y; 24 | int m; 25 | int d; 26 | dt_bdc_t convention; 27 | int ey; 28 | int em; 29 | int ed; 30 | } tests[] = { 31 | {2013, 12, 23, DT_FOLLOWING, 2013, 12, 23}, 32 | {2013, 12, 23, DT_MODIFIED_FOLLOWING, 2013, 12, 23}, 33 | {2013, 12, 24, DT_FOLLOWING, 2013, 12, 27}, 34 | {2013, 12, 24, DT_MODIFIED_FOLLOWING, 2013, 12, 27}, 35 | {2013, 12, 30, DT_UNADJUSTED, 2013, 12, 30}, 36 | {2013, 12, 30, DT_FOLLOWING, 2013, 12, 30}, 37 | {2013, 12, 30, DT_MODIFIED_FOLLOWING, 2013, 12, 30}, 38 | {2013, 12, 31, DT_FOLLOWING, 2014, 1, 1}, 39 | {2013, 12, 31, DT_MODIFIED_FOLLOWING, 2013, 12, 30}, 40 | {2013, 1, 1, DT_PRECEDING, 2012, 12, 31}, 41 | {2013, 1, 1, DT_MODIFIED_PRECEDING, 2013, 1, 2}, 42 | {2013, 1, 2, DT_PRECEDING, 2013, 1, 2}, 43 | {2013, 1, 2, DT_MODIFIED_PRECEDING, 2013, 1, 2}, 44 | {2013, 4, 1, DT_UNADJUSTED, 2013, 4, 1}, 45 | {2013, 4, 1, DT_PRECEDING, 2013, 3, 28}, 46 | {2013, 4, 1, DT_MODIFIED_PRECEDING, 2013, 4, 2}, 47 | {2013, 4, 1, DT_UNADJUSTED, 2013, 4, 1}, 48 | }; 49 | 50 | static const char * 51 | convention_name(dt_bdc_t convention) { 52 | switch (convention) { 53 | case DT_UNADJUSTED: 54 | return "DT_UNADJUSTED"; 55 | case DT_FOLLOWING: 56 | return "DT_FOLLOWING"; 57 | case DT_MODIFIED_FOLLOWING: 58 | return "DT_MODIFIED_FOLLOWING"; 59 | case DT_PRECEDING: 60 | return "DT_PRECEDING"; 61 | case DT_MODIFIED_PRECEDING: 62 | return "DT_MODIFIED_PRECEDING"; 63 | } 64 | return "UNKNOWN CONVENTION"; 65 | } 66 | 67 | int 68 | main() { 69 | int ntests, nholidays; 70 | int i; 71 | dt_t holidays[20]; 72 | 73 | nholidays = sizeof(days) / sizeof(*days); 74 | for (i = 0; i < nholidays; i++) { 75 | const struct ymd date = days[i]; 76 | holidays[i] = dt_from_ymd(date.y, date.m, date.d); 77 | } 78 | 79 | ntests = sizeof(tests) / sizeof(*tests); 80 | for (i = 0; i < ntests; i++) { 81 | const struct test t = tests[i]; 82 | 83 | { 84 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 85 | dt_t got = dt_roll_workday(src, t.convention, holidays, nholidays); 86 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 87 | cmp_ok(got, "==", exp, "dt_roll(%.4d-%.2d-%.2d, %s)", 88 | t.y, t.m, t.d, convention_name(t.convention)); 89 | } 90 | } 91 | done_testing(); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /t/end_of_quarter.h: -------------------------------------------------------------------------------- 1 | 2 | const struct test { 3 | int y; 4 | int q; 5 | int d; 6 | int delta; 7 | int ey; 8 | int eq; 9 | int ed; 10 | } tests[] = { 11 | {2000, 1, 71, -1, 1999, 4, 92}, 12 | {2000, 2, 63, 3, 2001, 1, 90}, 13 | {2000, 3, 49, -15, 1996, 4, 92}, 14 | {2000, 4, 86, -6, 1999, 2, 91}, 15 | {2001, 1, 48, 2, 2001, 3, 92}, 16 | {2001, 2, 63, 17, 2005, 3, 92}, 17 | {2001, 3, 25, -21, 1996, 2, 91}, 18 | {2001, 4, 67, 15, 2005, 3, 92}, 19 | {2002, 1, 22, 2, 2002, 3, 92}, 20 | {2002, 2, 39, 8, 2004, 2, 91}, 21 | {2002, 3, 68, -10, 2000, 1, 91}, 22 | {2002, 4, 78, 10, 2005, 2, 91}, 23 | {2003, 1, 43, -16, 1999, 1, 90}, 24 | {2003, 2, 51, -17, 1999, 1, 90}, 25 | {2003, 3, 78, -24, 1997, 3, 92}, 26 | {2003, 4, 50, -16, 1999, 4, 92}, 27 | {2004, 1, 69, -16, 2000, 1, 91}, 28 | {2004, 2, 89, 21, 2009, 3, 92}, 29 | {2004, 3, 10, 9, 2006, 4, 92}, 30 | {2004, 4, 75, -15, 2001, 1, 90}, 31 | {2005, 1, 13, 20, 2010, 1, 90}, 32 | {2005, 2, 64, -17, 2001, 1, 90}, 33 | {2005, 3, 35, 14, 2009, 1, 90}, 34 | {2005, 4, 48, 3, 2006, 3, 92}, 35 | {2006, 1, 67, 19, 2010, 4, 92}, 36 | {2006, 2, 8, 9, 2008, 3, 92}, 37 | {2006, 3, 42, -23, 2000, 4, 92}, 38 | {2006, 4, 24, -3, 2006, 1, 90}, 39 | {2007, 1, 10, -22, 2001, 3, 92}, 40 | {2007, 2, 41, 18, 2011, 4, 92}, 41 | {2007, 3, 74, 21, 2012, 4, 92}, 42 | {2007, 4, 55, -13, 2004, 3, 92}, 43 | {2008, 1, 62, 4, 2009, 1, 90}, 44 | {2008, 2, 21, -17, 2004, 1, 91}, 45 | {2008, 3, 73, 3, 2009, 2, 91}, 46 | {2008, 4, 2, -23, 2003, 1, 90}, 47 | {2009, 1, 75, 23, 2014, 4, 92}, 48 | {2009, 2, 56, 16, 2013, 2, 91}, 49 | {2009, 3, 69, 9, 2011, 4, 92}, 50 | {2009, 4, 67, -9, 2007, 3, 92}, 51 | {2010, 1, 73, -8, 2008, 1, 91}, 52 | {2010, 2, 31, 21, 2015, 3, 92}, 53 | {2010, 3, 92, 18, 2015, 1, 90}, 54 | {2010, 4, 91, -8, 2008, 4, 92}, 55 | {2011, 1, 29, 7, 2012, 4, 92}, 56 | {2011, 2, 73, -3, 2010, 3, 92}, 57 | {2011, 3, 13, -11, 2008, 4, 92}, 58 | {2011, 4, 72, -9, 2009, 3, 92}, 59 | {2012, 1, 72, -17, 2007, 4, 92}, 60 | {2012, 2, 36, -24, 2006, 2, 91}, 61 | {2012, 3, 12, 12, 2015, 3, 92}, 62 | {2012, 4, 3, 8, 2014, 4, 92}, 63 | {2013, 1, 74, 16, 2017, 1, 90}, 64 | {2013, 2, 34, -22, 2007, 4, 92}, 65 | {2013, 3, 69, -19, 2008, 4, 92}, 66 | {2013, 4, 35, -22, 2008, 2, 91}, 67 | {2014, 1, 44, 12, 2017, 1, 90}, 68 | {2014, 2, 37, 15, 2018, 1, 90}, 69 | {2014, 3, 31, 22, 2020, 1, 91}, 70 | {2014, 4, 27, 19, 2019, 3, 92}, 71 | {2015, 1, 9, 20, 2020, 1, 91}, 72 | {2015, 2, 32, -16, 2011, 2, 91}, 73 | {2015, 3, 23, 18, 2020, 1, 91}, 74 | {2015, 4, 4, -18, 2011, 2, 91}, 75 | {2016, 1, 32, 15, 2019, 4, 92}, 76 | {2016, 2, 33, 9, 2018, 3, 92}, 77 | {2016, 3, 60, 22, 2022, 1, 90}, 78 | {2016, 4, 56, -21, 2011, 3, 92}, 79 | {2017, 1, 42, 20, 2022, 1, 90}, 80 | {2017, 2, 25, 3, 2018, 1, 90}, 81 | {2017, 3, 77, 0, 2017, 3, 92}, 82 | {2017, 4, 25, -15, 2014, 1, 90}, 83 | {2018, 1, 84, -8, 2016, 1, 91}, 84 | {2018, 2, 26, 18, 2022, 4, 92}, 85 | {2018, 3, 9, 3, 2019, 2, 91}, 86 | {2018, 4, 50, -14, 2015, 2, 91}, 87 | {2019, 1, 83, 7, 2020, 4, 92}, 88 | {2019, 2, 80, -6, 2017, 4, 92}, 89 | {2019, 3, 32, 3, 2020, 2, 91}, 90 | {2019, 4, 50, 5, 2021, 1, 90}, 91 | {2020, 1, 59, -18, 2015, 3, 92}, 92 | {2020, 2, 42, 11, 2023, 1, 90}, 93 | {2020, 3, 85, -16, 2016, 3, 92}, 94 | {2020, 4, 33, -13, 2017, 3, 92}, 95 | }; 96 | -------------------------------------------------------------------------------- /t/start_of_quarter.h: -------------------------------------------------------------------------------- 1 | 2 | const struct test { 3 | int y; 4 | int q; 5 | int d; 6 | int delta; 7 | int ey; 8 | int eq; 9 | int ed; 10 | } tests[] = { 11 | {2000, 1, 5, 15, 2003, 4, 1}, 12 | {2000, 2, 53, -7, 1998, 3, 1}, 13 | {2000, 3, 54, -4, 1999, 3, 1}, 14 | {2000, 4, 68, 5, 2002, 1, 1}, 15 | {2001, 1, 21, 7, 2002, 4, 1}, 16 | {2001, 2, 26, -18, 1996, 4, 1}, 17 | {2001, 3, 13, 11, 2004, 2, 1}, 18 | {2001, 4, 74, -15, 1998, 1, 1}, 19 | {2002, 1, 34, 23, 2007, 4, 1}, 20 | {2002, 2, 82, -24, 1996, 2, 1}, 21 | {2002, 3, 72, 21, 2007, 4, 1}, 22 | {2002, 4, 90, -6, 2001, 2, 1}, 23 | {2003, 1, 10, -5, 2001, 4, 1}, 24 | {2003, 2, 72, -24, 1997, 2, 1}, 25 | {2003, 3, 52, -2, 2003, 1, 1}, 26 | {2003, 4, 22, -3, 2003, 1, 1}, 27 | {2004, 1, 22, 13, 2007, 2, 1}, 28 | {2004, 2, 86, 21, 2009, 3, 1}, 29 | {2004, 3, 90, 9, 2006, 4, 1}, 30 | {2004, 4, 75, 8, 2006, 4, 1}, 31 | {2005, 1, 44, -1, 2004, 4, 1}, 32 | {2005, 2, 87, -23, 1999, 3, 1}, 33 | {2005, 3, 68, -17, 2001, 2, 1}, 34 | {2005, 4, 29, 20, 2010, 4, 1}, 35 | {2006, 1, 4, 5, 2007, 2, 1}, 36 | {2006, 2, 38, 14, 2009, 4, 1}, 37 | {2006, 3, 48, 0, 2006, 3, 1}, 38 | {2006, 4, 72, 11, 2009, 3, 1}, 39 | {2007, 1, 18, 13, 2010, 2, 1}, 40 | {2007, 2, 55, -3, 2006, 3, 1}, 41 | {2007, 3, 79, 5, 2008, 4, 1}, 42 | {2007, 4, 7, 21, 2013, 1, 1}, 43 | {2008, 1, 74, 5, 2009, 2, 1}, 44 | {2008, 2, 75, -24, 2002, 2, 1}, 45 | {2008, 3, 44, -24, 2002, 3, 1}, 46 | {2008, 4, 82, 7, 2010, 3, 1}, 47 | {2009, 1, 48, -1, 2008, 4, 1}, 48 | {2009, 2, 10, 17, 2013, 3, 1}, 49 | {2009, 3, 40, 6, 2011, 1, 1}, 50 | {2009, 4, 65, 3, 2010, 3, 1}, 51 | {2010, 1, 80, -6, 2008, 3, 1}, 52 | {2010, 2, 16, -18, 2005, 4, 1}, 53 | {2010, 3, 33, 1, 2010, 4, 1}, 54 | {2010, 4, 23, -21, 2005, 3, 1}, 55 | {2011, 1, 82, 14, 2014, 3, 1}, 56 | {2011, 2, 61, 3, 2012, 1, 1}, 57 | {2011, 3, 72, -9, 2009, 2, 1}, 58 | {2011, 4, 29, -24, 2005, 4, 1}, 59 | {2012, 1, 56, 13, 2015, 2, 1}, 60 | {2012, 2, 56, 21, 2017, 3, 1}, 61 | {2012, 3, 37, 18, 2017, 1, 1}, 62 | {2012, 4, 3, 0, 2012, 4, 1}, 63 | {2013, 1, 47, 23, 2018, 4, 1}, 64 | {2013, 2, 33, 5, 2014, 3, 1}, 65 | {2013, 3, 58, -10, 2011, 1, 1}, 66 | {2013, 4, 78, 6, 2015, 2, 1}, 67 | {2014, 1, 8, 6, 2015, 3, 1}, 68 | {2014, 2, 32, -4, 2013, 2, 1}, 69 | {2014, 3, 47, -15, 2010, 4, 1}, 70 | {2014, 4, 65, 6, 2016, 2, 1}, 71 | {2015, 1, 57, -16, 2011, 1, 1}, 72 | {2015, 2, 85, 24, 2021, 2, 1}, 73 | {2015, 3, 19, 12, 2018, 3, 1}, 74 | {2015, 4, 19, -10, 2013, 2, 1}, 75 | {2016, 1, 51, -18, 2011, 3, 1}, 76 | {2016, 2, 59, -5, 2015, 1, 1}, 77 | {2016, 3, 2, 6, 2018, 1, 1}, 78 | {2016, 4, 51, -9, 2014, 3, 1}, 79 | {2017, 1, 59, 2, 2017, 3, 1}, 80 | {2017, 2, 66, 15, 2021, 1, 1}, 81 | {2017, 3, 72, -2, 2017, 1, 1}, 82 | {2017, 4, 32, 18, 2022, 2, 1}, 83 | {2018, 1, 84, -21, 2012, 4, 1}, 84 | {2018, 2, 80, -9, 2016, 1, 1}, 85 | {2018, 3, 78, 21, 2023, 4, 1}, 86 | {2018, 4, 55, -18, 2014, 2, 1}, 87 | {2019, 1, 19, -6, 2017, 3, 1}, 88 | {2019, 2, 32, -7, 2017, 3, 1}, 89 | {2019, 3, 80, -19, 2014, 4, 1}, 90 | {2019, 4, 23, 6, 2021, 2, 1}, 91 | {2020, 1, 50, -10, 2017, 3, 1}, 92 | {2020, 2, 65, 2, 2020, 4, 1}, 93 | {2020, 3, 62, 17, 2024, 4, 1}, 94 | {2020, 4, 59, -9, 2018, 3, 1}, 95 | }; 96 | -------------------------------------------------------------------------------- /dt_char.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef __DT_CHAR_H__ 27 | #define __DT_CHAR_H__ 28 | #include 29 | #include "dt_core.h" 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | enum { 36 | DT_CHAR_HSPACE = 0x01, /* [\t ] */ 37 | DT_CHAR_VSPACE = 0x02, /* [\r\n] */ 38 | DT_CHAR_PUNCT = 0x04, /* [:punct:] */ 39 | DT_CHAR_DIGIT = 0x08, /* [0-9] */ 40 | DT_CHAR_UPPER = 0x10, /* [A-Z] */ 41 | DT_CHAR_LOWER = 0x20, /* [a-z] */ 42 | DT_CHAR_OTHER = 0x80, /* */ 43 | }; 44 | 45 | enum { 46 | DT_CHAR_ALPHA = DT_CHAR_UPPER | DT_CHAR_LOWER, 47 | DT_CHAR_ALNUM = DT_CHAR_ALPHA | DT_CHAR_DIGIT, 48 | DT_CHAR_SPACE = DT_CHAR_HSPACE | DT_CHAR_VSPACE, 49 | DT_CHAR_BLANK = DT_CHAR_HSPACE, 50 | }; 51 | 52 | bool dt_char_is_of (unsigned char c, unsigned char mask); 53 | 54 | bool dt_char_is_space (unsigned char c); 55 | bool dt_char_is_blank (unsigned char c); 56 | bool dt_char_is_punct (unsigned char c); 57 | bool dt_char_is_digit (unsigned char c); 58 | bool dt_char_is_lower (unsigned char c); 59 | bool dt_char_is_upper (unsigned char c); 60 | bool dt_char_is_alpha (unsigned char c); 61 | bool dt_char_is_alnum (unsigned char c); 62 | 63 | unsigned char dt_char_to_upper (unsigned char c); 64 | unsigned char dt_char_to_lower (unsigned char c); 65 | 66 | size_t dt_char_span (const unsigned char *src, size_t len, unsigned char mask); 67 | size_t dt_char_span_until (const unsigned char *src, size_t len, unsigned char mask); 68 | 69 | size_t dt_char_skip (const unsigned char **src, size_t len, unsigned char mask); 70 | size_t dt_char_skip_until (const unsigned char **src, size_t len, unsigned char mask); 71 | 72 | size_t dt_char_span_digit (const unsigned char *src, size_t len); 73 | size_t dt_char_span_alpha (const unsigned char *src, size_t len); 74 | size_t dt_char_span_alnum (const unsigned char *src, size_t len); 75 | 76 | #ifdef __cplusplus 77 | } 78 | #endif 79 | #endif 80 | -------------------------------------------------------------------------------- /t/parse_iso_date.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | #include 4 | 5 | const struct good_t { 6 | int ey; 7 | int em; 8 | int ed; 9 | const char *str; 10 | size_t elen; 11 | } good[] = { 12 | {2012, 12, 24, "20121224", 8 }, 13 | {2012, 12, 24, "20121224 Foo bar", 8 }, 14 | {2012, 12, 24, "2012-12-24", 10 }, 15 | {2012, 12, 24, "2012-12-24 23:59:59", 10 }, 16 | {2012, 12, 24, "2012-12-24T00:00:00+00:00", 10 }, 17 | {2012, 12, 24, "2012359", 7 }, 18 | {2012, 12, 24, "2012359T235959+0130", 7 }, 19 | {2012, 12, 24, "2012-359", 8 }, 20 | {2012, 12, 24, "2012W521", 8 }, 21 | {2012, 12, 24, "2012-W52-1", 10 }, 22 | {2012, 12, 24, "2012Q485", 8 }, 23 | {2012, 12, 24, "2012-Q4-85", 10 }, 24 | { 1, 1, 1, "0001-Q1-01", 10 }, 25 | { 1, 1, 1, "0001-W01-1", 10 }, 26 | { 1, 1, 1, "0001-01-01", 10 }, 27 | { 1, 1, 1, "0001-001", 8 }, 28 | }; 29 | 30 | const struct bad_t { 31 | const char *str; 32 | } bad[] = { 33 | {"20121232" }, /* Invalid day of month */ 34 | {"2012-12-310" }, /* Invalid day of month */ 35 | {"2012-13-24" }, /* Invalid month */ 36 | {"2012367" }, /* Invalid day of year */ 37 | {"2012-000" }, /* Invalid day of year */ 38 | {"2012W533" }, /* Invalid week of year */ 39 | {"2012-W52-8" }, /* Invalid day of week */ 40 | {"2012Q495" }, /* Invalid day of quarter */ 41 | {"2012-Q5-85" }, /* Invalid quarter */ 42 | {"20123670" }, /* Trailing digit */ 43 | {"201212320" }, /* Trailing digit */ 44 | {"2012-12" }, /* Reduced accuracy */ 45 | {"2012-Q4" }, /* Reduced accuracy */ 46 | {"2012-Q42" }, /* Invalid */ 47 | {"2012-Q1-1" }, /* Invalid day of quarter */ 48 | {"2012Q--420" }, /* Invalid */ 49 | {"2012-Q-420" }, /* Invalid */ 50 | {"2012Q11" }, /* Incomplete */ 51 | {"2012Q1234" }, /* Trailing digit */ 52 | {"2012W12" }, /* Incomplete */ 53 | {"2012W1234" }, /* Trailing digit */ 54 | {"2012W-123" }, /* Invalid */ 55 | {"2012-W12" }, /* Incomplete */ 56 | {"2012-W12-12" }, /* Trailing digit */ 57 | {"2012U1234" }, /* Invalid */ 58 | {"2012-1234" }, /* Invalid */ 59 | {"2012-X1234" }, /* Invalid */ 60 | {"0000-Q1-01" }, /* Year less than 0001 */ 61 | {"0000-W01-1" }, /* Year less than 0001 */ 62 | {"0000-01-01" }, /* Year less than 0001 */ 63 | {"0000-001" }, /* Year less than 0001 */ 64 | }; 65 | 66 | int 67 | main() { 68 | int i, ntests; 69 | 70 | ntests = sizeof(good) / sizeof(*good); 71 | for (i = 0; i < ntests; i++) { 72 | const struct good_t t = good[i]; 73 | 74 | { 75 | dt_t got = 0, exp = 0; 76 | size_t glen; 77 | 78 | glen = dt_parse_iso_date(t.str, strlen(t.str), &got); 79 | ok(glen == t.elen, "dt_parse_iso_date(%s) size_t: %d", t.str, (int)glen); 80 | exp = dt_from_ymd(t.ey, t.em, t.ed); 81 | cmp_ok(got, "==", exp, "dt_parse_iso_date(%s)", t.str); 82 | } 83 | } 84 | 85 | ntests = sizeof(bad) / sizeof(*bad); 86 | for (i = 0; i < ntests; i++) { 87 | const struct bad_t t = bad[i]; 88 | 89 | { 90 | dt_t got = 0; 91 | size_t glen; 92 | 93 | glen = dt_parse_iso_date(t.str, strlen(t.str), &got); 94 | ok(glen == 0, "dt_parse_iso_date(%s) size_t: %d", t.str, (int)glen); 95 | } 96 | } 97 | done_testing(); 98 | } 99 | 100 | -------------------------------------------------------------------------------- /dt_zone.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include 27 | #include 28 | #include "dt_core.h" 29 | #include "dt_zone.h" 30 | #include "dt_char.h" 31 | 32 | const char * 33 | dt_zone_name(const dt_zone_t *zone) { 34 | return zone->name; 35 | } 36 | 37 | short 38 | dt_zone_offset(const dt_zone_t *zone) { 39 | return zone->offset; 40 | } 41 | 42 | bool 43 | dt_zone_is_utc(const dt_zone_t *zone) { 44 | return (zone->flags & DT_ZONE_UTC) != 0; 45 | } 46 | 47 | bool 48 | dt_zone_is_rfc(const dt_zone_t *zone) { 49 | return (zone->flags & DT_ZONE_RFC) != 0; 50 | } 51 | 52 | bool 53 | dt_zone_is_military(const dt_zone_t *zone) { 54 | return (zone->flags & DT_ZONE_MILITARY) != 0; 55 | } 56 | 57 | bool 58 | dt_zone_is_ambiguous(const dt_zone_t *zone) { 59 | return (zone->flags & DT_ZONE_AMBIGUOUS) != 0; 60 | } 61 | 62 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) 63 | 64 | static const dt_zone_t kZoneEntry[] = { 65 | #define DT_ZONE_ENTRY(enc, offset, name, flags) \ 66 | { name, flags, offset }, 67 | #include "dt_zone_entries.h" 68 | #undef DT_ZONE_ENTRY 69 | }; 70 | 71 | static bool 72 | dt_zone_lookup_enc(unsigned int enc, const dt_zone_t **zone) { 73 | static const unsigned int T[] = { 74 | #define DT_ZONE_ENTRY(enc, offset, name, flags) \ 75 | enc, 76 | #include "dt_zone_entries.h" 77 | #undef DT_ZONE_ENTRY 78 | }; 79 | size_t lo, hi, mid; 80 | 81 | assert(ARRAY_SIZE(T) > 0); 82 | 83 | lo = 0; 84 | hi = ARRAY_SIZE(T) - 1; 85 | while (lo < hi) { 86 | mid = (lo + hi) / 2; 87 | if (T[mid] < enc) 88 | lo = mid + 1; 89 | else 90 | hi = mid; 91 | } 92 | 93 | if (lo == hi && T[lo] == enc) { 94 | if (zone) 95 | *zone = &kZoneEntry[lo]; 96 | return true; 97 | } 98 | return false; 99 | } 100 | 101 | size_t 102 | dt_zone_lookup(const char *str, size_t len, const dt_zone_t **zone) { 103 | const unsigned char *p = (const unsigned char *)str; 104 | unsigned int enc; 105 | size_t i; 106 | 107 | len = dt_char_span_alpha(p, len); 108 | if (!len || len > 5) 109 | return 0; 110 | 111 | enc = 0; 112 | for (i = 0; i < len; i++) 113 | enc = (enc << 5) | ((p[i] | 0x20) ^ 0x60); 114 | 115 | if (dt_zone_lookup_enc(enc, zone)) 116 | return len; 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /t/yqd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int q; 7 | int d; 8 | int rdn; 9 | } tests[] = { 10 | {2000, 1, 38, 730157}, 11 | {2000, 2, 21, 730231}, 12 | {2000, 3, 82, 730383}, 13 | {2000, 4, 11, 730404}, 14 | {2001, 1, 60, 730545}, 15 | {2001, 2, 67, 730642}, 16 | {2001, 3, 35, 730701}, 17 | {2001, 4, 52, 730810}, 18 | {2002, 1, 14, 730864}, 19 | {2002, 2, 55, 730995}, 20 | {2002, 3, 50, 731081}, 21 | {2002, 4, 47, 731170}, 22 | {2003, 1, 38, 731253}, 23 | {2003, 2, 25, 731330}, 24 | {2003, 3, 28, 731424}, 25 | {2003, 4, 2, 731490}, 26 | {2004, 1, 69, 731649}, 27 | {2004, 2, 13, 731684}, 28 | {2004, 3, 91, 731853}, 29 | {2004, 4, 79, 731933}, 30 | {2005, 1, 41, 731987}, 31 | {2005, 2, 16, 732052}, 32 | {2005, 3, 20, 732147}, 33 | {2005, 4, 81, 732300}, 34 | {2006, 1, 20, 732331}, 35 | {2006, 2, 11, 732412}, 36 | {2006, 3, 12, 732504}, 37 | {2006, 4, 55, 732639}, 38 | {2007, 1, 68, 732744}, 39 | {2007, 2, 27, 732793}, 40 | {2007, 3, 81, 732938}, 41 | {2007, 4, 21, 732970}, 42 | {2008, 1, 54, 733095}, 43 | {2008, 2, 27, 733159}, 44 | {2008, 3, 81, 733304}, 45 | {2008, 4, 66, 733381}, 46 | {2009, 1, 74, 733481}, 47 | {2009, 2, 30, 733527}, 48 | {2009, 3, 17, 733605}, 49 | {2009, 4, 85, 733765}, 50 | {2010, 1, 84, 733856}, 51 | {2010, 2, 85, 733947}, 52 | {2010, 3, 6, 733959}, 53 | {2010, 4, 51, 734096}, 54 | {2011, 1, 51, 734188}, 55 | {2011, 2, 39, 734266}, 56 | {2011, 3, 2, 734320}, 57 | {2011, 4, 62, 734472}, 58 | {2012, 1, 17, 734519}, 59 | {2012, 2, 56, 734649}, 60 | {2012, 3, 89, 734773}, 61 | {2012, 4, 2, 734778}, 62 | {2013, 1, 21, 734889}, 63 | {2013, 2, 38, 734996}, 64 | {2013, 3, 90, 735139}, 65 | {2013, 4, 14, 735155}, 66 | {2014, 1, 24, 735257}, 67 | {2014, 2, 39, 735362}, 68 | {2014, 3, 49, 735463}, 69 | {2014, 4, 11, 735517}, 70 | {2015, 1, 87, 735685}, 71 | {2015, 2, 80, 735768}, 72 | {2015, 3, 72, 735851}, 73 | {2015, 4, 34, 735905}, 74 | {2016, 1, 59, 736022}, 75 | {2016, 2, 23, 736077}, 76 | {2016, 3, 35, 736180}, 77 | {2016, 4, 60, 736297}, 78 | {2017, 1, 7, 736336}, 79 | {2017, 2, 32, 736451}, 80 | {2017, 3, 8, 736518}, 81 | {2017, 4, 4, 736606}, 82 | {2018, 1, 12, 736706}, 83 | {2018, 2, 91, 736875}, 84 | {2018, 3, 17, 736892}, 85 | {2018, 4, 42, 737009}, 86 | {2019, 1, 35, 737094}, 87 | {2019, 2, 16, 737165}, 88 | {2019, 3, 81, 737321}, 89 | {2019, 4, 77, 737409}, 90 | {2020, 1, 82, 737506}, 91 | {2020, 2, 12, 737527}, 92 | {2020, 3, 12, 737618}, 93 | {2020, 4, 60, 737758}, 94 | }; 95 | 96 | int 97 | main() { 98 | int i, ntests; 99 | 100 | ntests = sizeof(tests) / sizeof(*tests); 101 | for (i = 0; i < ntests; i++) { 102 | const struct test t = tests[i]; 103 | 104 | { 105 | dt_t got = dt_rdn(dt_from_yqd(t.y, t.q, t.d)); 106 | cmp_ok(got, "==", t.rdn, "dt_from_yqd(%d, %d, %d)", t.y, t.q, t.d); 107 | } 108 | 109 | { 110 | int y, q, d; 111 | dt_to_yqd(dt_from_rdn(t.rdn), &y, &q, &d); 112 | if (!ok((y == t.y && q == t.q && d == t.d), "dt_to_yqd(dt_from_rdn(%d))", t.rdn)) { 113 | diag(" got: %.4d-Q%d-%.2d", y, q, d); 114 | diag(" exp: %.4d-Q%d-%.2d", t.y, t.q, t.d); 115 | } 116 | } 117 | 118 | { 119 | int got = dt_year(dt_from_rdn(t.rdn)); 120 | cmp_ok(got, "==", t.y, "dt_year(dt_from_rdn(%d))", t.rdn); 121 | } 122 | 123 | { 124 | int got = dt_quarter(dt_from_rdn(t.rdn)); 125 | cmp_ok(got, "==", t.q, "dt_quarter(dt_from_rdn(%d))", t.rdn); 126 | } 127 | 128 | { 129 | int got = dt_doq(dt_from_rdn(t.rdn)); 130 | cmp_ok(got, "==", t.d, "dt_doq(dt_from_rdn(%d))", t.rdn); 131 | } 132 | 133 | { 134 | ok(dt_valid_yqd(t.y, t.q, t.d), "dt_valid_yqd(%d, %d, %d)", t.y, t.q, t.d); 135 | } 136 | 137 | } 138 | done_testing(); 139 | } 140 | -------------------------------------------------------------------------------- /t/delta_yd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y1; 6 | int d1; 7 | int y2; 8 | int d2; 9 | int ny; 10 | int nd; 11 | } tests[] = { 12 | {2010, 1, 2010, 1, 0, 0}, 13 | {2010, 1, 2010, 2, 0, 1}, 14 | {2010, 1, 2010, 31, 0, 30}, 15 | {2010, 1, 2010, 32, 0, 31}, 16 | {2010, 1, 2010, 59, 0, 58}, 17 | {2010, 1, 2010, 60, 0, 59}, 18 | {2010, 1, 2010, 365, 0, 364}, 19 | {2010, 1, 2011, 1, 1, 0}, 20 | {2010, 1, 2011, 365, 1, 364}, 21 | {2010, 1, 2012, 1, 2, 0}, 22 | {2010, 10, 2010, 1, 0, -9}, 23 | {2010, 10, 2010, 2, 0, -8}, 24 | {2010, 10, 2010, 9, 0, -1}, 25 | {2010, 10, 2010, 10, 0, 0}, 26 | {2010, 10, 2010, 11, 0, 1}, 27 | {2010, 10, 2010, 31, 0, 21}, 28 | {2010, 10, 2010, 32, 0, 22}, 29 | {2010, 10, 2010, 40, 0, 30}, 30 | {2010, 10, 2010, 41, 0, 31}, 31 | {2010, 10, 2010, 59, 0, 49}, 32 | {2010, 10, 2010, 60, 0, 50}, 33 | {2010, 10, 2010, 68, 0, 58}, 34 | {2010, 10, 2010, 69, 0, 59}, 35 | {2010, 10, 2010, 365, 0, 355}, 36 | {2010, 10, 2011, 1, 0, 356}, 37 | {2010, 10, 2011, 9, 0, 364}, 38 | {2010, 10, 2011, 10, 1, 0}, 39 | {2010, 89, 2011, 121, 1, 32}, 40 | {2010, 120, 2011, 121, 1, 1}, 41 | {2010, 59, 2012, 58, 1, 364}, 42 | {2010, 59, 2012, 59, 2, 0}, 43 | {2010, 59, 2012, 60, 2, 1}, 44 | {2012, 59, 2014, 58, 1, 364}, 45 | {2012, 59, 2014, 59, 2, 0}, 46 | {2012, 59, 2014, 60, 2, 1}, 47 | {2012, 60, 2014, 59, 1, 364}, 48 | {2012, 60, 2014, 60, 2, 0}, 49 | {2012, 60, 2014, 61, 2, 1}, 50 | {2012, 60, 2016, 59, 3, 364}, 51 | {2012, 60, 2016, 60, 4, 0}, 52 | {2012, 60, 2016, 61, 4, 1}, 53 | {2010, 1, 2009, 365, 0, -1}, 54 | {2010, 1, 2009, 364, 0, -2}, 55 | {2010, 1, 2009, 336, 0, -30}, 56 | {2010, 1, 2009, 335, 0, -31}, 57 | {2010, 1, 2009, 334, 0, -32}, 58 | {2010, 1, 2009, 306, 0, -60}, 59 | {2010, 1, 2009, 305, 0, -61}, 60 | {2010, 1, 2009, 2, 0, -364}, 61 | {2010, 1, 2009, 1, -1, 0}, 62 | {2010, 15, 2010, 15, 0, 0}, 63 | {2010, 15, 2010, 14, 0, -1}, 64 | {2010, 15, 2010, 1, 0, -14}, 65 | {2010, 15, 2009, 365, 0, -15}, 66 | {2010, 15, 2009, 350, 0, -30}, 67 | {2010, 15, 2009, 349, 0, -31}, 68 | {2010, 15, 2009, 348, 0, -32}, 69 | {2010, 59, 2009, 60, 0, -364}, 70 | {2010, 59, 2009, 59, -1, 0}, 71 | {2010, 59, 2009, 58, -1, -1}, 72 | {2010, 59, 2008, 60, -1, -365}, 73 | {2010, 59, 2008, 59, -2, 0}, 74 | {2010, 59, 2008, 58, -2, -1}, 75 | {2012, 60, 2009, 60, -3, 0}, 76 | {2012, 60, 2009, 59, -3, -1}, 77 | {2012, 60, 2009, 58, -3, -2}, 78 | {2012, 60, 2008, 61, -3, -365}, 79 | {2012, 60, 2008, 60, -4, 0}, 80 | {2012, 60, 2008, 59, -4, -1}, 81 | }; 82 | 83 | int 84 | main() { 85 | int i, ntests; 86 | 87 | ntests = sizeof(tests) / sizeof(*tests); 88 | for (i = 0; i < ntests; i++) { 89 | const struct test t = tests[i]; 90 | 91 | { 92 | int ny, nd; 93 | dt_t dt1 = dt_from_yd(t.y1, t.d1); 94 | dt_t dt2 = dt_from_yd(t.y2, t.d2); 95 | 96 | dt_delta_yd(dt1, dt2, &ny, &nd); 97 | if (!ok((ny == t.ny && nd == t.nd), 98 | "dt_delta_yd(%.4d-%.3d, %.4d-%.3d)", t.y1, t.d1, t.y2, t.d2)) { 99 | diag(" got: Y:%d, D:%d", ny, nd); 100 | diag(" exp: Y:%d, D:%d", t.ny, t.nd); 101 | } 102 | } 103 | 104 | { 105 | int got; 106 | dt_t dt1 = dt_from_yd(t.y1, t.d1); 107 | dt_t dt2 = dt_from_yd(t.y2, t.d2); 108 | 109 | got = dt_delta_years(dt1, dt2, 1); 110 | cmp_ok(got, "==", t.ny, "dt_delta_years(%.4d-%.3d, %.4d-%.3d, true)", 111 | t.y1, t.d1, t.y2, t.d2); 112 | } 113 | } 114 | done_testing(); 115 | } 116 | 117 | -------------------------------------------------------------------------------- /t/ymd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int d; 8 | int dow; 9 | int rdn; 10 | int cjdn; 11 | } tests[] = { 12 | /* Test cases extracted from */ 13 | {1700, 1, 11, 1, 620558, 2341983}, 14 | {1700, 2, 28, 7, 620606, 2342031}, 15 | {1700, 3, 1, 1, 620607, 2342032}, 16 | {1700, 3, 11, 4, 620617, 2342042}, 17 | {1700, 3, 12, 5, 620618, 2342043}, 18 | {1701, 1, 12, 3, 620924, 2342349}, 19 | {1701, 3, 11, 5, 620982, 2342407}, 20 | {1701, 3, 12, 6, 620983, 2342408}, 21 | {1748, 1, 12, 5, 638090, 2359515}, 22 | {1748, 3, 10, 7, 638148, 2359573}, 23 | {1748, 3, 11, 1, 638149, 2359574}, 24 | {1748, 3, 12, 2, 638150, 2359575}, 25 | {1748, 4, 4, 4, 638173, 2359598}, 26 | {1748, 4, 5, 5, 638174, 2359599}, 27 | {1749, 1, 12, 7, 638456, 2359881}, 28 | {1750, 1, 12, 1, 638821, 2360246}, 29 | {1751, 1, 11, 1, 639185, 2360610}, 30 | {1751, 1, 12, 2, 639186, 2360611}, 31 | {1751, 4, 4, 7, 639268, 2360693}, 32 | {1751, 4, 5, 1, 639269, 2360694}, 33 | {1751, 4, 12, 1, 639276, 2360701}, 34 | {1752, 1, 11, 2, 639550, 2360975}, 35 | {1752, 1, 12, 3, 639551, 2360976}, 36 | {1752, 2, 29, 2, 639599, 2361024}, 37 | {1752, 3, 1, 3, 639600, 2361025}, 38 | {1752, 3, 10, 5, 639609, 2361034}, 39 | {1752, 3, 11, 6, 639610, 2361035}, 40 | {1752, 3, 12, 7, 639611, 2361036}, 41 | {1752, 9, 12, 2, 639795, 2361220}, 42 | {1752, 9, 13, 3, 639796, 2361221}, 43 | {1752, 9, 14, 4, 639797, 2361222}, 44 | {1753, 1, 1, 1, 639906, 2361331}, 45 | {1800, 1, 1, 3, 657072, 2378497}, 46 | {1858, 11, 17, 3, 678576, 2400001}, 47 | {1900, 1, 1, 1, 693596, 2415021}, 48 | {1900, 2, 28, 3, 693654, 2415079}, 49 | {1900, 3, 1, 4, 693655, 2415080}, 50 | {1901, 1, 1, 2, 693961, 2415386}, 51 | {1910, 1, 1, 6, 697248, 2418673}, 52 | {1920, 1, 1, 4, 700900, 2422325}, 53 | {1930, 1, 1, 3, 704553, 2425978}, 54 | {1940, 1, 1, 1, 708205, 2429630}, 55 | {1950, 1, 1, 7, 711858, 2433283}, 56 | {1960, 1, 1, 5, 715510, 2436935}, 57 | {1970, 1, 1, 4, 719163, 2440588}, 58 | {1980, 1, 1, 2, 722815, 2444240}, 59 | {1990, 1, 1, 1, 726468, 2447893}, 60 | {1995, 10, 10, 2, 728576, 2450001}, 61 | {2000, 1, 1, 6, 730120, 2451545}, 62 | {2010, 1, 1, 5, 733773, 2455198}, 63 | {2020, 1, 1, 3, 737425, 2458850}, 64 | {2030, 1, 1, 2, 741078, 2462503}, 65 | {2040, 1, 1, 7, 744730, 2466155}, 66 | {2050, 1, 1, 6, 748383, 2469808}, 67 | {2132, 8, 31, 7, 778575, 2500000}, 68 | {2400, 12, 31, 7, 876582, 2598007}, 69 | {2404, 12, 31, 5, 878043, 2599468}, 70 | }; 71 | 72 | int 73 | main() { 74 | int i, ntests; 75 | 76 | ntests = sizeof(tests) / sizeof(*tests); 77 | for (i = 0; i < ntests; i++) { 78 | const struct test t = tests[i]; 79 | 80 | { 81 | int got = dt_rdn(dt_from_ymd(t.y, t.m, t.d)); 82 | cmp_ok(got, "==", t.rdn, "dt_rdn(dt_from_ymd(%d, %d, %d))", t.y, t.m, t.d); 83 | } 84 | 85 | { 86 | int got = dt_rdn(dt_from_cjdn(t.cjdn)); 87 | cmp_ok(got, "==", t.rdn, "dt_rdn(dt_from_cjdn(%d))", t.cjdn); 88 | } 89 | 90 | { 91 | int got = dt_cjdn(dt_from_rdn(t.rdn)); 92 | cmp_ok(got, "==", t.cjdn, "dt_cjdn(dt_from_rdn(%d))", t.rdn); 93 | } 94 | 95 | { 96 | int y, m, d; 97 | dt_to_ymd(dt_from_rdn(t.rdn), &y, &m, &d); 98 | if (!ok((y == t.y && m == t.m && d == t.d), "dt_to_ymd(dt_from_rdn(%d))", t.rdn)) { 99 | diag(" got: %.4d-%.2d-%.2d", y, m, d); 100 | diag(" exp: %.4d-%.2d-%.2d", t.y, t.m, t.d); 101 | } 102 | } 103 | 104 | { 105 | int got = dt_year(dt_from_rdn(t.rdn)); 106 | cmp_ok(got, "==", t.y, "dt_year(dt_from_rdn(%d))", t.rdn); 107 | } 108 | 109 | { 110 | int got = dt_month(dt_from_rdn(t.rdn)); 111 | cmp_ok(got, "==", t.m, "dt_month(dt_from_rdn(%d))", t.rdn); 112 | } 113 | 114 | { 115 | int got = dt_dom(dt_from_rdn(t.rdn)); 116 | cmp_ok(got, "==", t.d, "dt_dom(dt_from_rdn(%d))", t.rdn); 117 | } 118 | 119 | { 120 | int got = dt_dow(dt_from_rdn(t.rdn)); 121 | cmp_ok(got, "==", t.dow, "dt_dow(dt_from_rdn(%d))", t.rdn); 122 | } 123 | 124 | { 125 | ok(dt_valid_ymd(t.y, t.m, t.d), "dt_valid_ymd(%d, %d, %d)", t.y, t.m, t.d); 126 | } 127 | } 128 | done_testing(); 129 | } 130 | -------------------------------------------------------------------------------- /t/add_workdays.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y; 6 | int m; 7 | int d; 8 | int delta; 9 | int ey; 10 | int em; 11 | int ed; 12 | } tests[] = { 13 | {2013, 1, 1, 1, 2013, 1, 2}, 14 | {2013, 3, 28, 1, 2013, 4, 2}, 15 | {2013, 3, 28, 2, 2013, 4, 3}, 16 | {2013, 3, 28, 3, 2013, 4, 4}, 17 | {2013, 3, 28, 4, 2013, 4, 5}, 18 | {2013, 3, 29, 1, 2013, 4, 2}, 19 | {2013, 3, 29, 2, 2013, 4, 3}, 20 | {2013, 3, 29, 3, 2013, 4, 4}, 21 | {2013, 3, 29, 4, 2013, 4, 5}, 22 | {2013, 3, 30, 1, 2013, 4, 2}, 23 | {2013, 3, 30, 2, 2013, 4, 3}, 24 | {2013, 3, 30, 3, 2013, 4, 4}, 25 | {2013, 3, 30, 4, 2013, 4, 5}, 26 | {2013, 3, 31, 1, 2013, 4, 2}, 27 | {2013, 3, 31, 2, 2013, 4, 3}, 28 | {2013, 3, 31, 3, 2013, 4, 4}, 29 | {2013, 3, 31, 4, 2013, 4, 5}, 30 | {2013, 4, 1, 1, 2013, 4, 2}, 31 | {2013, 4, 1, 2, 2013, 4, 3}, 32 | {2013, 4, 1, 3, 2013, 4, 4}, 33 | {2013, 4, 1, 4, 2013, 4, 5}, 34 | {2013, 4, 2, 1, 2013, 4, 3}, 35 | {2013, 4, 2, 2, 2013, 4, 4}, 36 | {2013, 4, 2, 3, 2013, 4, 5}, 37 | {2013, 4, 2, 4, 2013, 4, 8}, 38 | {2013, 4, 2, -1, 2013, 3, 28}, 39 | {2013, 4, 2, -2, 2013, 3, 27}, 40 | {2013, 4, 2, -3, 2013, 3, 26}, 41 | {2013, 4, 2, -4, 2013, 3, 25}, 42 | {2013, 4, 1, -1, 2013, 3, 28}, 43 | {2013, 4, 1, -2, 2013, 3, 27}, 44 | {2013, 4, 1, -3, 2013, 3, 26}, 45 | {2013, 4, 1, -4, 2013, 3, 25}, 46 | {2013, 3, 31, -1, 2013, 3, 28}, 47 | {2013, 3, 31, -2, 2013, 3, 27}, 48 | {2013, 3, 31, -3, 2013, 3, 26}, 49 | {2013, 3, 31, -4, 2013, 3, 25}, 50 | {2013, 3, 31, -1, 2013, 3, 28}, 51 | {2013, 3, 30, -1, 2013, 3, 28}, 52 | {2013, 3, 30, -2, 2013, 3, 27}, 53 | {2013, 3, 30, -3, 2013, 3, 26}, 54 | {2013, 3, 30, -4, 2013, 3, 25}, 55 | {2013, 3, 29, -1, 2013, 3, 28}, 56 | {2013, 3, 29, -2, 2013, 3, 27}, 57 | {2013, 3, 29, -3, 2013, 3, 26}, 58 | {2013, 3, 29, -4, 2013, 3, 25}, 59 | {2013, 3, 28, -1, 2013, 3, 27}, 60 | {2013, 3, 28, -2, 2013, 3, 26}, 61 | {2013, 3, 28, -3, 2013, 3, 25}, 62 | {2013, 3, 28, -4, 2013, 3, 22}, 63 | {2013, 6, 1, 4, 2013, 6, 7}, 64 | {2013, 12, 20, 4, 2014, 1, 1}, 65 | {2013, 12, 23, 1, 2013, 12, 27}, 66 | {2013, 12, 24, 1, 2013, 12, 27}, 67 | {2013, 12, 25, -1, 2013, 12, 23}, 68 | {2013, 12, 26, -1, 2013, 12, 23}, 69 | {2013, 12, 27, -1, 2013, 12, 23}, 70 | {2013, 12, 27, 1, 2013, 12, 30}, 71 | {2013, 1, 1, 250, 2013, 12, 30}, 72 | {2013, 1, 1, 251, 2014, 1, 1}, 73 | {2013, 1, 2, 250, 2014, 1, 1}, 74 | {2013, 1, 1, -200, 2012, 3, 27}, 75 | {2013, 12, 30, -250, 2012, 12, 31}, 76 | {2013, 12, 31, -250, 2013, 1, 2}, 77 | {2011, 6, 1, 771, 2014, 5, 30}, 78 | {2014, 5, 30, -771, 2011, 6, 1}, 79 | {2013, 6, 1, 0, 2013, 6, 1}, 80 | {2013, 6, 6, 0, 2013, 6, 6}, 81 | }; 82 | 83 | const struct ymd { 84 | int y; 85 | int m; 86 | int d; 87 | } days[] = { 88 | {2013, 1, 1}, /* Tuesday, Nyårsdagen (New Year's Day) */ 89 | {2013, 3, 29}, /* Friday, Långfredagen (Good Friday) */ 90 | {2013, 4, 1}, /* Monday, Annandag påsk (Easter Monday) */ 91 | {2013, 5, 1}, /* Wednesday, Första maj (First of May) */ 92 | {2013, 5, 9}, /* Thursday, Kristi himmelsfärds dag (Ascension Day) */ 93 | {2013, 6, 6}, /* Thursday, Sveriges nationaldag (National Day of Sweden) */ 94 | {2013, 6, 21}, /* Friday, Midsommarafton (Midsummer's Eve) */ 95 | {2013, 12, 24}, /* Tuesday, Julafton (Christmas Eve) */ 96 | {2013, 12, 25}, /* Wednesday, Juldagen (Christmas Day) */ 97 | {2013, 12, 26}, /* Thursday, Annandag jul (Boxing Day) */ 98 | {2013, 12, 31}, /* Tuesday, Nyårsafton (New Year's Eve) */ 99 | }; 100 | 101 | int 102 | main() { 103 | int ntests, nholidays; 104 | int i; 105 | dt_t holidays[20]; 106 | 107 | nholidays = sizeof(days) / sizeof(*days); 108 | for (i = 0; i < nholidays; i++) { 109 | const struct ymd date = days[i]; 110 | holidays[i] = dt_from_ymd(date.y, date.m, date.d); 111 | } 112 | 113 | ntests = sizeof(tests) / sizeof(*tests); 114 | for (i = 0; i < ntests; i++) { 115 | const struct test t = tests[i]; 116 | 117 | { 118 | dt_t src = dt_from_ymd(t.y, t.m, t.d); 119 | dt_t got = dt_add_workdays(src, t.delta, holidays, nholidays); 120 | dt_t exp = dt_from_ymd(t.ey, t.em, t.ed); 121 | cmp_ok(got, "==", exp, "dt_add_workdays(%.4d-%.2d-%.2d, %d)", 122 | src, t.y, t.m, t.d, t.delta); 123 | } 124 | } 125 | done_testing(); 126 | } 127 | 128 | -------------------------------------------------------------------------------- /t/tap.h: -------------------------------------------------------------------------------- 1 | /* 2 | libtap - Write tests in C 3 | Copyright (C) 2011 Jake Gelbman 4 | This file is licensed under the GPL v3 5 | */ 6 | 7 | #ifndef __TAP_H__ 8 | #define __TAP_H__ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #ifndef va_copy 15 | #ifdef __va_copy 16 | #define va_copy __va_copy 17 | #else 18 | #define va_copy(d, s) ((d) = (s)) 19 | #endif 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | int vok_at_loc (const char *file, int line, int test, const char *fmt, 27 | va_list args); 28 | int ok_at_loc (const char *file, int line, int test, const char *fmt, 29 | ...); 30 | int is_at_loc (const char *file, int line, const char *got, 31 | const char *expected, const char *fmt, ...); 32 | int isnt_at_loc (const char *file, int line, const char *got, 33 | const char *expected, const char *fmt, ...); 34 | int cmp_ok_at_loc (const char *file, int line, int a, const char *op, 35 | int b, const char *fmt, ...); 36 | int bail_out (int ignore, const char *fmt, ...); 37 | void cplan (int tests, const char *fmt, ...); 38 | int diag (const char *fmt, ...); 39 | int note (const char *fmt, ...); 40 | int exit_status (void); 41 | void skippy (int n, const char *fmt, ...); 42 | void ctodo (int ignore, const char *fmt, ...); 43 | void cendtodo (void); 44 | 45 | #define NO_PLAN -1 46 | #define SKIP_ALL -2 47 | #define ok(...) ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) 48 | #define is(...) is_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) 49 | #define isnt(...) isnt_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) 50 | #define cmp_ok(...) cmp_ok_at_loc(__FILE__, __LINE__, __VA_ARGS__, NULL) 51 | #define plan(...) cplan(__VA_ARGS__, NULL) 52 | #define done_testing() return exit_status() 53 | #define BAIL_OUT(...) bail_out(0, "" __VA_ARGS__, NULL) 54 | #define pass(...) ok(1, "" __VA_ARGS__) 55 | #define fail(...) ok(0, "" __VA_ARGS__) 56 | 57 | #define skip(test, ...) do {if (test) {skippy(__VA_ARGS__, NULL); break;} 58 | #define endskip } while (0) 59 | 60 | #define todo(...) ctodo(0, "" __VA_ARGS__, NULL) 61 | #define endtodo cendtodo() 62 | 63 | #define dies_ok(...) dies_ok_common(1, __VA_ARGS__) 64 | #define lives_ok(...) dies_ok_common(0, __VA_ARGS__) 65 | 66 | #ifdef _WIN32 67 | #define like(...) skippy(1, "like is not implemented on MSWin32") 68 | #define unlike like 69 | #define dies_ok_common(...) \ 70 | skippy(1, "Death detection is not supported on MSWin32") 71 | #else 72 | #define like(...) like_at_loc(1, __FILE__, __LINE__, __VA_ARGS__, NULL) 73 | #define unlike(...) like_at_loc(0, __FILE__, __LINE__, __VA_ARGS__, NULL) 74 | int like_at_loc (int for_match, const char *file, int line, 75 | const char *got, const char *expected, 76 | const char *fmt, ...); 77 | #include 78 | #include 79 | #include 80 | int tap_test_died (int status); 81 | #define dies_ok_common(for_death, code, ...) \ 82 | do { \ 83 | int cpid; \ 84 | int it_died; \ 85 | tap_test_died(1); \ 86 | cpid = fork(); \ 87 | switch (cpid) { \ 88 | case -1: \ 89 | perror("fork error"); \ 90 | exit(1); \ 91 | case 0: \ 92 | close(1); \ 93 | close(2); \ 94 | code \ 95 | tap_test_died(0); \ 96 | exit(0); \ 97 | } \ 98 | if (waitpid(cpid, NULL, 0) < 0) { \ 99 | perror("waitpid error"); \ 100 | exit(1); \ 101 | } \ 102 | it_died = tap_test_died(0); \ 103 | if (!it_died) \ 104 | {code} \ 105 | ok(for_death ? it_died : !it_died, "" __VA_ARGS__); \ 106 | } while (0) 107 | #endif 108 | 109 | #ifdef __cplusplus 110 | } 111 | #endif 112 | 113 | #endif 114 | 115 | -------------------------------------------------------------------------------- /t/delta_ymd.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y1; 6 | int m1; 7 | int d1; 8 | int y2; 9 | int m2; 10 | int d2; 11 | int ny; 12 | int nm; 13 | int nd; 14 | } tests[] = { 15 | {2010, 1, 1, 2010, 1, 1, 0, 0, 0}, 16 | {2010, 1, 1, 2010, 1, 2, 0, 0, 1}, 17 | {2010, 1, 1, 2010, 1, 31, 0, 0, 30}, 18 | {2010, 1, 1, 2010, 2, 1, 0, 1, 0}, 19 | {2010, 1, 1, 2010, 2, 28, 0, 1, 27}, 20 | {2010, 1, 1, 2010, 3, 1, 0, 2, 0}, 21 | {2010, 1, 1, 2010, 12, 31, 0, 11, 30}, 22 | {2010, 1, 1, 2011, 1, 1, 1, 0, 0}, 23 | {2010, 1, 1, 2011, 12, 31, 1, 11, 30}, 24 | {2010, 1, 1, 2012, 1, 1, 2, 0, 0}, 25 | {2010, 1, 10, 2010, 1, 1, 0, 0, -9}, 26 | {2010, 1, 10, 2010, 1, 2, 0, 0, -8}, 27 | {2010, 1, 10, 2010, 1, 9, 0, 0, -1}, 28 | {2010, 1, 10, 2010, 1, 10, 0, 0, 0}, 29 | {2010, 1, 10, 2010, 1, 11, 0, 0, 1}, 30 | {2010, 1, 10, 2010, 1, 31, 0, 0, 21}, 31 | {2010, 1, 10, 2010, 2, 1, 0, 0, 22}, 32 | {2010, 1, 10, 2010, 2, 9, 0, 0, 30}, 33 | {2010, 1, 10, 2010, 2, 10, 0, 1, 0}, 34 | {2010, 1, 10, 2010, 2, 28, 0, 1, 18}, 35 | {2010, 1, 10, 2010, 3, 1, 0, 1, 19}, 36 | {2010, 1, 10, 2010, 3, 9, 0, 1, 27}, 37 | {2010, 1, 10, 2010, 3, 10, 0, 2, 0}, 38 | {2010, 1, 10, 2010, 12, 31, 0, 11, 21}, 39 | {2010, 1, 10, 2011, 1, 1, 0, 11, 22}, 40 | {2010, 1, 10, 2011, 1, 9, 0, 11, 30}, 41 | {2010, 1, 10, 2011, 1, 10, 1, 0, 0}, 42 | {2010, 3, 30, 2011, 5, 1, 1, 1, 1}, 43 | {2010, 4, 30, 2011, 5, 1, 1, 0, 1}, 44 | {2010, 2, 28, 2012, 2, 27, 1, 11, 30}, 45 | {2010, 2, 28, 2012, 2, 28, 2, 0, 0}, 46 | {2010, 2, 28, 2012, 2, 29, 2, 0, 1}, 47 | {2012, 2, 28, 2014, 2, 27, 1, 11, 30}, 48 | {2012, 2, 28, 2014, 2, 28, 2, 0, 0}, 49 | {2012, 2, 28, 2014, 3, 1, 2, 0, 1}, 50 | {2012, 2, 29, 2014, 2, 28, 1, 11, 30}, 51 | {2012, 2, 29, 2014, 3, 1, 2, 0, 1}, 52 | {2012, 2, 29, 2014, 3, 2, 2, 0, 2}, 53 | {2012, 2, 29, 2016, 2, 28, 3, 11, 30}, 54 | {2012, 2, 29, 2016, 2, 29, 4, 0, 0}, 55 | {2012, 2, 29, 2016, 3, 1, 4, 0, 1}, 56 | {2010, 1, 1, 2009, 12, 31, 0, 0, -1}, 57 | {2010, 1, 1, 2009, 12, 30, 0, 0, -2}, 58 | {2010, 1, 1, 2009, 12, 2, 0, 0, -30}, 59 | {2010, 1, 1, 2009, 12, 1, 0, -1, 0}, 60 | {2010, 1, 1, 2009, 11, 30, 0, -1, -1}, 61 | {2010, 1, 1, 2009, 11, 2, 0, -1, -29}, 62 | {2010, 1, 1, 2009, 11, 1, 0, -2, 0}, 63 | {2010, 1, 1, 2009, 1, 2, 0, -11, -30}, 64 | {2010, 1, 1, 2009, 1, 1, -1, 0, 0}, 65 | {2010, 1, 15, 2010, 1, 15, 0, 0, 0}, 66 | {2010, 1, 15, 2010, 1, 14, 0, 0, -1}, 67 | {2010, 1, 15, 2010, 1, 1, 0, 0, -14}, 68 | {2010, 1, 15, 2009, 12, 31, 0, 0, -15}, 69 | {2010, 1, 15, 2009, 12, 16, 0, 0, -30}, 70 | {2010, 1, 15, 2009, 12, 15, 0, -1, 0}, 71 | {2010, 1, 15, 2009, 12, 14, 0, -1, -1}, 72 | {2010, 2, 28, 2009, 3, 1, 0, -11, -27}, 73 | {2010, 2, 28, 2009, 2, 28, -1, 0, 0}, 74 | {2010, 2, 28, 2009, 2, 27, -1, 0, -1}, 75 | {2010, 2, 28, 2008, 2, 29, -1, -11, -28}, 76 | {2010, 2, 28, 2008, 2, 28, -2, 0, 0}, 77 | {2010, 2, 28, 2008, 2, 27, -2, 0, -1}, 78 | {2012, 2, 29, 2009, 3, 1, -2, -11, -28}, 79 | {2012, 2, 29, 2009, 2, 28, -3, 0, -1}, 80 | {2012, 2, 29, 2009, 2, 27, -3, 0, -2}, 81 | {2012, 2, 29, 2008, 3, 1, -3, -11, -28}, 82 | {2012, 2, 29, 2008, 2, 29, -4, 0, 0}, 83 | {2012, 2, 29, 2008, 2, 28, -4, 0, -1}, 84 | }; 85 | 86 | int 87 | main() { 88 | int i, ntests; 89 | 90 | ntests = sizeof(tests) / sizeof(*tests); 91 | for (i = 0; i < ntests; i++) { 92 | const struct test t = tests[i]; 93 | 94 | { 95 | int ny, nm, nd; 96 | dt_t dt1 = dt_from_ymd(t.y1, t.m1, t.d1); 97 | dt_t dt2 = dt_from_ymd(t.y2, t.m2, t.d2); 98 | 99 | dt_delta_ymd(dt1, dt2, &ny, &nm, &nd); 100 | if (!ok((ny == t.ny && nm == t.nm && nd == t.nd), 101 | "dt_delta_ymd(%.4d-%.2d-%.2d, %.4d-%.2d-%.2d)", 102 | t.y1, t.m1, t.d1, t.y2, t.m2, t.d2)) { 103 | diag(" got: Y:%d, M:%d, D:%d", ny, nm, nd); 104 | diag(" exp: Y:%d, M:%d, D:%d", t.ny, t.nm, t.nd); 105 | } 106 | } 107 | 108 | { 109 | int nm, em; 110 | dt_t dt1 = dt_from_ymd(t.y1, t.m1, t.d1); 111 | dt_t dt2 = dt_from_ymd(t.y2, t.m2, t.d2); 112 | 113 | nm = dt_delta_months(dt1, dt2, 1); 114 | em = t.nm + (t.ny * 12); 115 | cmp_ok(nm, "==", em, "dt_delta_months(%.4d-%.2d-%.2d, %.4d-%.2d-%.2d, true)", 116 | t.y1, t.m1, t.d1, t.y2, t.m2, t.d2); 117 | } 118 | } 119 | done_testing(); 120 | } 121 | 122 | -------------------------------------------------------------------------------- /t/delta_weekdays.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y1; 6 | int m1; 7 | int d1; 8 | int y2; 9 | int m2; 10 | int d2; 11 | int exp; 12 | bool inclusive; 13 | } tests[] = { 14 | {2012, 1, 2, 2012, 1, 2, 1, 1}, 15 | {2012, 1, 2, 2012, 1, 3, 2, 1}, 16 | {2012, 1, 2, 2012, 1, 4, 3, 1}, 17 | {2012, 1, 2, 2012, 1, 5, 4, 1}, 18 | {2012, 1, 2, 2012, 1, 6, 5, 1}, 19 | {2012, 1, 2, 2012, 1, 7, 5, 1}, 20 | {2012, 1, 2, 2012, 1, 8, 5, 1}, 21 | {2012, 1, 2, 2011, 11, 7, -41, 1}, 22 | {2012, 1, 2, 2011, 11, 8, -40, 1}, 23 | {2012, 1, 2, 2011, 11, 9, -39, 1}, 24 | {2012, 1, 2, 2011, 11, 10, -38, 1}, 25 | {2012, 1, 2, 2011, 11, 11, -37, 1}, 26 | {2012, 1, 2, 2011, 11, 12, -36, 1}, 27 | {2012, 1, 2, 2011, 11, 13, -36, 1}, 28 | {2012, 1, 3, 2012, 1, 2, -2, 1}, 29 | {2012, 1, 3, 2012, 1, 3, 1, 1}, 30 | {2012, 1, 3, 2012, 1, 4, 2, 1}, 31 | {2012, 1, 3, 2012, 1, 5, 3, 1}, 32 | {2012, 1, 3, 2012, 1, 6, 4, 1}, 33 | {2012, 1, 3, 2012, 1, 7, 4, 1}, 34 | {2012, 1, 3, 2012, 1, 8, 4, 1}, 35 | {2012, 1, 3, 2011, 11, 7, -42, 1}, 36 | {2012, 1, 3, 2011, 11, 8, -41, 1}, 37 | {2012, 1, 3, 2011, 11, 9, -40, 1}, 38 | {2012, 1, 3, 2011, 11, 10, -39, 1}, 39 | {2012, 1, 3, 2011, 11, 11, -38, 1}, 40 | {2012, 1, 3, 2011, 11, 12, -37, 1}, 41 | {2012, 1, 3, 2011, 11, 13, -37, 1}, 42 | {2012, 1, 4, 2012, 1, 2, -3, 1}, 43 | {2012, 1, 4, 2012, 1, 3, -2, 1}, 44 | {2012, 1, 4, 2012, 1, 4, 1, 1}, 45 | {2012, 1, 4, 2012, 1, 5, 2, 1}, 46 | {2012, 1, 4, 2012, 1, 6, 3, 1}, 47 | {2012, 1, 4, 2012, 1, 7, 3, 1}, 48 | {2012, 1, 4, 2012, 1, 8, 3, 1}, 49 | {2012, 1, 4, 2011, 11, 7, -43, 1}, 50 | {2012, 1, 4, 2011, 11, 8, -42, 1}, 51 | {2012, 1, 4, 2011, 11, 9, -41, 1}, 52 | {2012, 1, 4, 2011, 11, 10, -40, 1}, 53 | {2012, 1, 4, 2011, 11, 11, -39, 1}, 54 | {2012, 1, 4, 2011, 11, 12, -38, 1}, 55 | {2012, 1, 4, 2011, 11, 13, -38, 1}, 56 | {2012, 1, 5, 2012, 1, 2, -4, 1}, 57 | {2012, 1, 5, 2012, 1, 3, -3, 1}, 58 | {2012, 1, 5, 2012, 1, 4, -2, 1}, 59 | {2012, 1, 5, 2012, 1, 5, 1, 1}, 60 | {2012, 1, 5, 2012, 1, 6, 2, 1}, 61 | {2012, 1, 5, 2012, 1, 7, 2, 1}, 62 | {2012, 1, 5, 2012, 1, 8, 2, 1}, 63 | {2012, 1, 5, 2011, 11, 7, -44, 1}, 64 | {2012, 1, 5, 2011, 11, 8, -43, 1}, 65 | {2012, 1, 5, 2011, 11, 9, -42, 1}, 66 | {2012, 1, 5, 2011, 11, 10, -41, 1}, 67 | {2012, 1, 5, 2011, 11, 11, -40, 1}, 68 | {2012, 1, 5, 2011, 11, 12, -39, 1}, 69 | {2012, 1, 5, 2011, 11, 13, -39, 1}, 70 | {2012, 1, 6, 2012, 1, 2, -5, 1}, 71 | {2012, 1, 6, 2012, 1, 3, -4, 1}, 72 | {2012, 1, 6, 2012, 1, 4, -3, 1}, 73 | {2012, 1, 6, 2012, 1, 5, -2, 1}, 74 | {2012, 1, 6, 2012, 1, 6, 1, 1}, 75 | {2012, 1, 6, 2012, 1, 7, 1, 1}, 76 | {2012, 1, 6, 2012, 1, 8, 1, 1}, 77 | {2012, 1, 6, 2011, 11, 7, -45, 1}, 78 | {2012, 1, 6, 2011, 11, 8, -44, 1}, 79 | {2012, 1, 6, 2011, 11, 9, -43, 1}, 80 | {2012, 1, 6, 2011, 11, 10, -42, 1}, 81 | {2012, 1, 6, 2011, 11, 11, -41, 1}, 82 | {2012, 1, 6, 2011, 11, 12, -40, 1}, 83 | {2012, 1, 6, 2011, 11, 13, -40, 1}, 84 | {2012, 1, 7, 2012, 1, 2, -5, 1}, 85 | {2012, 1, 7, 2012, 1, 3, -4, 1}, 86 | {2012, 1, 7, 2012, 1, 4, -3, 1}, 87 | {2012, 1, 7, 2012, 1, 5, -2, 1}, 88 | {2012, 1, 7, 2012, 1, 6, -1, 1}, 89 | {2012, 1, 7, 2012, 1, 7, 0, 1}, 90 | {2012, 1, 7, 2012, 1, 8, 0, 1}, 91 | {2012, 1, 7, 2011, 11, 7, -45, 1}, 92 | {2012, 1, 7, 2011, 11, 8, -44, 1}, 93 | {2012, 1, 7, 2011, 11, 9, -43, 1}, 94 | {2012, 1, 7, 2011, 11, 10, -42, 1}, 95 | {2012, 1, 7, 2011, 11, 11, -41, 1}, 96 | {2012, 1, 7, 2011, 11, 12, -40, 1}, 97 | {2012, 1, 7, 2011, 11, 13, -40, 1}, 98 | {2012, 1, 8, 2012, 1, 2, -5, 1}, 99 | {2012, 1, 8, 2012, 1, 3, -4, 1}, 100 | {2012, 1, 8, 2012, 1, 4, -3, 1}, 101 | {2012, 1, 8, 2012, 1, 5, -2, 1}, 102 | {2012, 1, 8, 2012, 1, 6, -1, 1}, 103 | {2012, 1, 8, 2012, 1, 7, 0, 1}, 104 | {2012, 1, 8, 2012, 1, 8, 0, 1}, 105 | {2012, 1, 8, 2011, 11, 7, -45, 1}, 106 | {2012, 1, 8, 2011, 11, 8, -44, 1}, 107 | {2012, 1, 8, 2011, 11, 9, -43, 1}, 108 | {2012, 1, 8, 2011, 11, 10, -42, 1}, 109 | {2012, 1, 8, 2011, 11, 11, -41, 1}, 110 | {2012, 1, 8, 2011, 11, 12, -40, 1}, 111 | {2012, 1, 8, 2011, 11, 13, -40, 1}, 112 | }; 113 | 114 | int 115 | main() { 116 | int i, ntests; 117 | 118 | ntests = sizeof(tests) / sizeof(*tests); 119 | for (i = 0; i < ntests; i++) { 120 | const struct test t = tests[i]; 121 | 122 | { 123 | int got; 124 | dt_t dt1 = dt_from_ymd(t.y1, t.m1, t.d1); 125 | dt_t dt2 = dt_from_ymd(t.y2, t.m2, t.d2); 126 | 127 | got = dt_delta_weekdays(dt1, dt2, t.inclusive); 128 | cmp_ok(got, "==", t.exp, "dt_delta_weekdays(%.4d-%.2d-%.2d, %.4d-%.2d-%.2d, %s)", 129 | t.y1, t.m1, t.d1, t.y2, t.m2, t.d2, (t.inclusive ? "true" : "false")); 130 | } 131 | } 132 | done_testing(); 133 | } 134 | 135 | -------------------------------------------------------------------------------- /dt_dow.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include 27 | #include "dt_core.h" 28 | #include "dt_util.h" 29 | #include "dt_dow.h" 30 | 31 | dt_t 32 | dt_from_nth_dow_in_year(int y, int nth, dt_dow_t day) { 33 | dt_t dt; 34 | 35 | if (nth > 0) { 36 | dt = dt_nth_dow(dt_from_yd(y, 1), nth, day); 37 | if (nth <= 52 || dt <= dt_from_yd(y + 1, 0)) 38 | return dt; 39 | } 40 | else if (nth < 0) { 41 | dt = dt_nth_dow(dt_from_yd(y + 1, 0), nth, day); 42 | if (nth >= -52 || dt >= dt_from_yd(y, 1)) 43 | return dt; 44 | } 45 | return 0; 46 | } 47 | 48 | dt_t 49 | dt_from_nth_dow_in_quarter(int y, int q, int nth, dt_dow_t day) { 50 | dt_t dt; 51 | 52 | if (nth > 0) { 53 | dt = dt_nth_dow(dt_from_yqd(y, q, 1), nth, day); 54 | if (nth <= 12 || dt <= dt_from_yqd(y, q + 1, 0)) 55 | return dt; 56 | } 57 | else if (nth < 0) { 58 | dt = dt_nth_dow(dt_from_yqd(y, q + 1, 0), nth, day); 59 | if (nth >= -12 || dt >= dt_from_yqd(y, q, 1)) 60 | return dt; 61 | } 62 | return 0; 63 | } 64 | 65 | dt_t 66 | dt_from_nth_dow_in_month(int y, int m, int nth, dt_dow_t day) { 67 | dt_t dt; 68 | 69 | if (nth > 0) { 70 | dt = dt_nth_dow(dt_from_ymd(y, m, 1), nth, day); 71 | if (nth <= 4 || dt <= dt_from_ymd(y, m + 1, 0)) 72 | return dt; 73 | } 74 | else if (nth < 0) { 75 | dt = dt_nth_dow(dt_from_ymd(y, m + 1, 0), nth, day); 76 | if (nth >= -4 || dt >= dt_from_ymd(y, m, 1)) 77 | return dt; 78 | } 79 | return 0; 80 | } 81 | 82 | dt_t 83 | dt_nth_dow(dt_t dt, int nth, dt_dow_t day) { 84 | if (nth > 0) nth--, dt += (day - dt_dow(dt) + 7) % 7; 85 | else if (nth < 0) nth++, dt -= (dt_dow(dt) - day + 7) % 7; 86 | return dt + nth * 7; 87 | } 88 | 89 | dt_t 90 | dt_nth_dow_in_year(dt_t dt, int nth, dt_dow_t day) { 91 | int y; 92 | dt_to_yd(dt, &y, NULL); 93 | return dt_from_nth_dow_in_year(y, nth, day); 94 | } 95 | 96 | dt_t 97 | dt_nth_dow_in_quarter(dt_t dt, int nth, dt_dow_t day) { 98 | int y, q; 99 | dt_to_yqd(dt, &y, &q, NULL); 100 | return dt_from_nth_dow_in_quarter(y, q, nth, day); 101 | } 102 | 103 | dt_t 104 | dt_nth_dow_in_month(dt_t dt, int nth, dt_dow_t day) { 105 | int y, m; 106 | dt_to_ymd(dt, &y, &m, NULL); 107 | return dt_from_nth_dow_in_month(y, m, nth, day); 108 | } 109 | 110 | dt_t 111 | dt_next_dow(dt_t dt, dt_dow_t day, bool current) { 112 | if (current) return dt + (day - dt_dow(dt) + 7) % 7; 113 | else return dt + (day - dt_dow(dt) + 6) % 7 + 1; 114 | } 115 | 116 | dt_t 117 | dt_prev_dow(dt_t dt, dt_dow_t day, bool current) { 118 | if (current) return dt - (dt_dow(dt) - day + 7) % 7; 119 | else return dt - (dt_dow(dt) - day + 6) % 7 - 1; 120 | } 121 | 122 | int 123 | dt_dow_in_year(dt_t dt, bool end) { 124 | int y, d; 125 | 126 | dt_to_yd(dt, &y, &d); 127 | if (end) return (d - dt_days_in_year(y) - 7) / 7; 128 | else return (d + 6) / 7; 129 | } 130 | 131 | int 132 | dt_dow_in_quarter(dt_t dt, bool end) { 133 | int y, q, d; 134 | 135 | dt_to_yqd(dt, &y, &q, &d); 136 | if (end) return (d - dt_days_in_quarter(y, q) - 7) / 7; 137 | else return (d + 6) / 7; 138 | } 139 | 140 | int 141 | dt_dow_in_month(dt_t dt, bool end) { 142 | int y, m, d; 143 | 144 | dt_to_ymd(dt, &y, &m, &d); 145 | if (end) return (d - dt_days_in_month(y, m) - 7) / 7; 146 | else return (d + 6) / 7; 147 | } 148 | 149 | bool 150 | dt_is_nth_dow_in_year(dt_t dt, int nth, dt_dow_t day) { 151 | const bool end = (nth < 0); 152 | return (dt_dow(dt) == day && 153 | dt_dow_in_year(dt, end) == nth); 154 | } 155 | 156 | bool 157 | dt_is_nth_dow_in_quarter(dt_t dt, int nth, dt_dow_t day) { 158 | const bool end = (nth < 0); 159 | return (dt_dow(dt) == day && 160 | dt_dow_in_quarter(dt, end) == nth); 161 | } 162 | 163 | bool 164 | dt_is_nth_dow_in_month(dt_t dt, int nth, dt_dow_t day) { 165 | const bool end = (nth < 0); 166 | return (dt_dow(dt) == day && 167 | dt_dow_in_month(dt, end) == nth); 168 | } 169 | 170 | -------------------------------------------------------------------------------- /t/delta_workdays.c: -------------------------------------------------------------------------------- 1 | #include "dt.h" 2 | #include "tap.h" 3 | 4 | const struct test { 5 | int y1; 6 | int m1; 7 | int d1; 8 | int y2; 9 | int m2; 10 | int d2; 11 | int exp; 12 | bool inclusive; 13 | } tests[] = { 14 | {2013, 1, 1, 2013, 1, 2, 1, 1}, 15 | {2013, 3, 28, 2013, 4, 2, 2, 1}, 16 | {2013, 3, 28, 2013, 4, 3, 3, 1}, 17 | {2013, 3, 28, 2013, 4, 4, 4, 1}, 18 | {2013, 3, 28, 2013, 4, 5, 5, 1}, 19 | {2013, 3, 29, 2013, 4, 2, 1, 1}, 20 | {2013, 3, 29, 2013, 4, 3, 2, 1}, 21 | {2013, 3, 29, 2013, 4, 4, 3, 1}, 22 | {2013, 3, 29, 2013, 4, 5, 4, 1}, 23 | {2013, 3, 30, 2013, 4, 2, 1, 1}, 24 | {2013, 3, 30, 2013, 4, 3, 2, 1}, 25 | {2013, 3, 30, 2013, 4, 4, 3, 1}, 26 | {2013, 3, 30, 2013, 4, 5, 4, 1}, 27 | {2013, 3, 31, 2013, 4, 2, 1, 1}, 28 | {2013, 3, 31, 2013, 4, 3, 2, 1}, 29 | {2013, 3, 31, 2013, 4, 4, 3, 1}, 30 | {2013, 3, 31, 2013, 4, 5, 4, 1}, 31 | {2013, 4, 1, 2013, 4, 2, 1, 1}, 32 | {2013, 4, 1, 2013, 4, 3, 2, 1}, 33 | {2013, 4, 1, 2013, 4, 4, 3, 1}, 34 | {2013, 4, 1, 2013, 4, 5, 4, 1}, 35 | {2013, 4, 2, 2013, 4, 3, 2, 1}, 36 | {2013, 4, 2, 2013, 4, 4, 3, 1}, 37 | {2013, 4, 2, 2013, 4, 5, 4, 1}, 38 | {2013, 4, 2, 2013, 4, 8, 5, 1}, 39 | {2013, 4, 2, 2013, 3, 28, -2, 1}, 40 | {2013, 4, 2, 2013, 3, 27, -3, 1}, 41 | {2013, 4, 2, 2013, 3, 26, -4, 1}, 42 | {2013, 4, 2, 2013, 3, 25, -5, 1}, 43 | {2013, 4, 1, 2013, 3, 28, -1, 1}, 44 | {2013, 4, 1, 2013, 3, 27, -2, 1}, 45 | {2013, 4, 1, 2013, 3, 26, -3, 1}, 46 | {2013, 4, 1, 2013, 3, 25, -4, 1}, 47 | {2013, 3, 31, 2013, 3, 28, -1, 1}, 48 | {2013, 3, 31, 2013, 3, 27, -2, 1}, 49 | {2013, 3, 31, 2013, 3, 26, -3, 1}, 50 | {2013, 3, 31, 2013, 3, 25, -4, 1}, 51 | {2013, 3, 31, 2013, 3, 28, -1, 1}, 52 | {2013, 3, 30, 2013, 3, 28, -1, 1}, 53 | {2013, 3, 30, 2013, 3, 27, -2, 1}, 54 | {2013, 3, 30, 2013, 3, 26, -3, 1}, 55 | {2013, 3, 30, 2013, 3, 25, -4, 1}, 56 | {2013, 3, 29, 2013, 3, 28, -1, 1}, 57 | {2013, 3, 29, 2013, 3, 27, -2, 1}, 58 | {2013, 3, 29, 2013, 3, 26, -3, 1}, 59 | {2013, 3, 29, 2013, 3, 25, -4, 1}, 60 | {2013, 3, 28, 2013, 3, 27, -2, 1}, 61 | {2013, 3, 28, 2013, 3, 26, -3, 1}, 62 | {2013, 3, 28, 2013, 3, 25, -4, 1}, 63 | {2013, 3, 28, 2013, 3, 22, -5, 1}, 64 | {2013, 6, 1, 2013, 6, 7, 4, 1}, 65 | {2013, 12, 20, 2014, 1, 1, 5, 1}, 66 | {2013, 12, 23, 2013, 12, 27, 2, 1}, 67 | {2013, 12, 24, 2013, 12, 27, 1, 1}, 68 | {2013, 12, 25, 2013, 12, 23, -1, 1}, 69 | {2013, 12, 26, 2013, 12, 23, -1, 1}, 70 | {2013, 12, 27, 2013, 12, 23, -2, 1}, 71 | {2013, 12, 27, 2013, 12, 30, 2, 1}, 72 | {2013, 1, 1, 2013, 12, 30, 250, 1}, 73 | {2013, 1, 1, 2014, 1, 1, 251, 1}, 74 | {2013, 1, 2, 2014, 1, 1, 251, 1}, 75 | {2013, 1, 1, 2012, 3, 27, -200, 1}, 76 | {2013, 12, 30, 2012, 12, 31, -251, 1}, 77 | {2013, 12, 31, 2013, 1, 2, -250, 1}, 78 | {2011, 6, 1, 2014, 5, 30, 772, 1}, 79 | {2014, 5, 30, 2011, 6, 1, -772, 1}, 80 | {2013, 3, 28, 2013, 4, 1, 1, 1}, 81 | {2013, 3, 29, 2013, 4, 1, 0, 1}, 82 | {2013, 3, 30, 2013, 4, 1, 0, 1}, 83 | {2013, 3, 31, 2013, 4, 1, 0, 1}, 84 | {2013, 4, 1, 2013, 3, 28, -1, 1}, 85 | {2013, 4, 1, 2013, 3, 29, 0, 1}, 86 | {2013, 4, 1, 2013, 3, 30, 0, 1}, 87 | {2013, 4, 1, 2013, 3, 31, 0, 1}, 88 | {2013, 3, 28, 2013, 4, 2, 2, 1}, 89 | {2013, 3, 29, 2013, 4, 2, 1, 1}, 90 | {2013, 3, 30, 2013, 4, 2, 1, 1}, 91 | {2013, 3, 31, 2013, 4, 2, 1, 1}, 92 | {2013, 12, 24, 2013, 12, 26, 0, 1}, 93 | {2013, 12, 26, 2013, 12, 24, 0, 1}, 94 | {2011, 6, 1, 2014, 6, 1, 772, 1}, 95 | {2014, 6, 1, 2011, 6, 1, -772, 1}, 96 | }; 97 | 98 | const struct ymd { 99 | int y; 100 | int m; 101 | int d; 102 | } days[] = { 103 | {2013, 1, 1}, /* Tuesday, Nyårsdagen (New Year's Day) */ 104 | {2013, 3, 29}, /* Friday, Långfredagen (Good Friday) */ 105 | {2013, 4, 1}, /* Monday, Annandag påsk (Easter Monday) */ 106 | {2013, 5, 1}, /* Wednesday, Första maj (First of May) */ 107 | {2013, 5, 9}, /* Thursday, Kristi himmelsfärds dag (Ascension Day) */ 108 | {2013, 6, 6}, /* Thursday, Sveriges nationaldag (National Day of Sweden) */ 109 | {2013, 6, 21}, /* Friday, Midsommarafton (Midsummer's Eve) */ 110 | {2013, 12, 24}, /* Tuesday, Julafton (Christmas Eve) */ 111 | {2013, 12, 25}, /* Wednesday, Juldagen (Christmas Day) */ 112 | {2013, 12, 26}, /* Thursday, Annandag jul (Boxing Day) */ 113 | {2013, 12, 31}, /* Tuesday, Nyårsafton (New Year's Eve) */ 114 | }; 115 | 116 | int 117 | main() { 118 | int ntests, nholidays; 119 | int i; 120 | dt_t holidays[20]; 121 | 122 | nholidays = sizeof(days) / sizeof(*days); 123 | for (i = 0; i < nholidays; i++) { 124 | const struct ymd date = days[i]; 125 | holidays[i] = dt_from_ymd(date.y, date.m, date.d); 126 | } 127 | 128 | ntests = sizeof(tests) / sizeof(*tests); 129 | for (i = 0; i < ntests; i++) { 130 | const struct test t = tests[i]; 131 | 132 | { 133 | int got; 134 | dt_t dt1 = dt_from_ymd(t.y1, t.m1, t.d1); 135 | dt_t dt2 = dt_from_ymd(t.y2, t.m2, t.d2); 136 | 137 | got = dt_delta_workdays(dt1, dt2, t.inclusive, holidays, nholidays); 138 | cmp_ok(got, "==", t.exp, "dt_delta_workdays(%.4d-%.2d-%.2d, %.4d-%.2d-%.2d, %s)", 139 | t.y1, t.m1, t.d1, t.y2, t.m2, t.d2, (t.inclusive ? "true" : "false")); 140 | } 141 | } 142 | done_testing(); 143 | } 144 | 145 | -------------------------------------------------------------------------------- /tools/weekday.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # Copyright (c) 2012-2014 Christian Hansen 3 | # 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # 1. Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 2. Redistributions in binary form must reproduce the above copyright notice, 12 | # this list of conditions and the following disclaimer in the documentation 13 | # and/or other materials provided with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | # 26 | # This script computes the matrices that are used in dt_weekday.c 27 | # 28 | use strict; 29 | use warnings; 30 | 31 | my @DoW = qw(M T W T F S S); 32 | my @WeekDay = (1, 1, 1, 1, 1, 0, 0); 33 | my $WeekDayCount = grep { $_ == 1 } @WeekDay; 34 | 35 | (@WeekDay == 7) 36 | or die q/@WeekDay must contain 7 elements/; 37 | 38 | (!grep { $_ > 1 || $_ < 0 } @WeekDay) 39 | or die q/Elements of @WeekDay must be either 1 or 0/; 40 | 41 | ($WeekDayCount > 0) 42 | or die q/$WeekDayCount must be > 0/; 43 | 44 | sub delta_matrix { 45 | @_ == 1 || die q/Usage: delta_matrix($inclusive)/; 46 | my ($inclusive) = @_; 47 | 48 | my @T; 49 | for my $d1 (0..6) { 50 | for my $d2 (0..6) { 51 | my $c; 52 | if ($d1 <= $d2) { 53 | $c = $inclusive ? $WeekDay[$d1] : 0; 54 | for (; $d1 < $d2; $d2--) { 55 | $c += $WeekDay[$d2]; 56 | } 57 | } 58 | else { 59 | $c = $WeekDayCount + ($inclusive ? $WeekDay[$d2] : 0); 60 | for (; $d1 > $d2; $d2++) { 61 | $c -= $WeekDay[$d2]; 62 | } 63 | } 64 | push @T, $c; 65 | } 66 | } 67 | 68 | (@T == 7*7) 69 | or die q/@T != 7*7/; 70 | 71 | return @T; 72 | } 73 | 74 | sub add_matrix { 75 | @_ == 1 || die q/Usage: add_matrix($direction)/; 76 | my ($direction) = @_; 77 | 78 | ($direction == 1 || $direction == -1) 79 | or die q/Parameter 'direction' must be -1 or +1/; 80 | 81 | my @T; 82 | for my $d1 (0..6) { 83 | my ($c, $d2); 84 | 85 | $c = 0; 86 | $d2 = $d1; 87 | while (!$WeekDay[$d2]) { 88 | $c--; 89 | $d2 = ($d1 + $c * $direction) % 7; 90 | } 91 | push @T, $c; 92 | for ($c = 1, my $i = 1; $i < $WeekDayCount; $i++, $c++) { 93 | $d2 = ($d1 + $c * $direction) % 7; 94 | while (!$WeekDay[$d2]) { 95 | $c++; 96 | $d2 = ($d1 + $c * $direction) % 7; 97 | } 98 | push @T, $c; 99 | } 100 | } 101 | 102 | (@T == 7*$WeekDayCount) 103 | or die q/@T != 7*$WeekDayCount/; 104 | 105 | return @T; 106 | } 107 | 108 | sub move_matrix { 109 | @_ == 1 || die q/Usage: move_matrix($direction)/; 110 | my ($direction) = @_; 111 | 112 | ($direction == 1 || $direction == -1) 113 | or die q/Parameter 'direction' must be -1 or +1/; 114 | 115 | my @T; 116 | for my $current (0, 1) { 117 | push @T, 0; 118 | for my $d1 (0..6) { 119 | my ($c, $d2); 120 | $c = 1 * !$current; 121 | $d2 = ($d1 + $c * $direction) % 7; 122 | while (!$WeekDay[$d2]) { 123 | $c++; 124 | $d2 = ($d1 + $c * $direction) % 7; 125 | } 126 | push @T, $c; 127 | } 128 | } 129 | 130 | (@T == 8*2) 131 | or die q/@T != 8*2/; 132 | 133 | return @T; 134 | } 135 | 136 | my @T; 137 | 138 | print "\n# dt_delta_workdays()\n\n"; 139 | @T = delta_matrix(0); 140 | printf "/* %s (exclusive) */\n", join ' ', @DoW; 141 | for (0..6) { 142 | printf "/* %s */ %s,\n", $DoW[$_], join ',', splice(@T, 0, 7); 143 | } 144 | 145 | @T = delta_matrix(1); 146 | printf "/* %s (inclusive) */\n", join ' ', @DoW;; 147 | for (0..6) { 148 | printf "/* %s */ %s,\n", $DoW[$_], join ',', splice(@T, 0, 7); 149 | } 150 | 151 | print "\n# dt_add_workdays()\n\n"; 152 | printf "/* %s (+n days) */\n", join ' ', 0..$WeekDayCount-1; 153 | @T = add_matrix(+1); 154 | for (0..6) { 155 | my @r = splice(@T, 0, $WeekDayCount); 156 | printf "/* %s */ %s,\n", $DoW[$_], join ',', sprintf('%2d', shift @r), @r; 157 | } 158 | 159 | printf "/* %s (-n days) */\n", join ' ', 0..$WeekDayCount-1; 160 | @T = add_matrix(-1); 161 | for (0..6) { 162 | my @r = splice(@T, 0, $WeekDayCount); 163 | printf "/* %s */ %s,\n", $DoW[$_], join ',', sprintf('%2d', shift @r), @r; 164 | } 165 | 166 | print "\n# dt_next_weekday()\n\n"; 167 | printf "/* %s */\n", join ' ', @DoW; 168 | @T = move_matrix(+1); 169 | while (@T) { 170 | printf "{ %s },\n", join ',', splice(@T, 0, 8); 171 | } 172 | 173 | print "\n# dt_prev_weekday()\n\n"; 174 | printf "/* %s */\n", join ' ', @DoW; 175 | @T = move_matrix(-1); 176 | while (@T) { 177 | printf "{ %s },\n", join ',', splice(@T, 0, 8); 178 | } 179 | 180 | -------------------------------------------------------------------------------- /dt_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include 27 | #include "dt_core.h" 28 | 29 | #define LEAP_YEAR(y) \ 30 | (((y) & 3) == 0 && ((y) % 100 != 0 || (y) % 400 == 0)) 31 | 32 | #define DAYS_IN_YEAR(y) \ 33 | (LEAP_YEAR(y) ? 366 : 365) 34 | 35 | static const int days_preceding_month[2][13] = { 36 | { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }, 37 | { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 } 38 | }; 39 | 40 | static const int days_preceding_quarter[2][5] = { 41 | { 0, 0, 90, 181, 273 }, 42 | { 0, 0, 91, 182, 274 } 43 | }; 44 | 45 | dt_t 46 | dt_from_rdn(int n) { 47 | return n + DT_EPOCH_OFFSET; 48 | } 49 | 50 | dt_t 51 | dt_from_yd(int y, int d) { 52 | y--; 53 | if (y < 0) { 54 | const int n400 = 1 - y/400; 55 | y += n400 * 400; 56 | d -= n400 * 146097; 57 | } 58 | return 365 * y + y/4 - y/100 + y/400 + d + DT_EPOCH_OFFSET; 59 | } 60 | 61 | dt_t 62 | dt_from_ymd(int y, int m, int d) { 63 | if (m < 1 || m > 12) { 64 | y += m / 12; 65 | m %= 12; 66 | if (m < 1) 67 | y--, m += 12; 68 | } 69 | assert(m >= 1); 70 | assert(m <= 12); 71 | return dt_from_yd(y, days_preceding_month[LEAP_YEAR(y)][m] + d); 72 | } 73 | 74 | dt_t 75 | dt_from_yqd(int y, int q, int d) { 76 | if (q < 1 || q > 4) { 77 | y += q / 4; 78 | q %= 4; 79 | if (q < 1) 80 | y--, q += 4; 81 | } 82 | assert(q >= 1); 83 | assert(q <= 4); 84 | return dt_from_yd(y, days_preceding_quarter[LEAP_YEAR(y)][q] + d); 85 | } 86 | 87 | dt_t 88 | dt_from_ywd(int y, int w, int d) { 89 | dt_t dt; 90 | 91 | dt = dt_from_yd(y, 4); 92 | dt -= dt_dow(dt); 93 | dt += w * 7 + d - 7; 94 | return dt; 95 | } 96 | 97 | 98 | #ifndef DT_NO_SHORTCUTS 99 | static const dt_t DT1901 = 693961 + DT_EPOCH_OFFSET; /* 1901-01-01 */ 100 | static const dt_t DT2099 = 766644 + DT_EPOCH_OFFSET; /* 2099-12-31 */ 101 | #endif 102 | 103 | void 104 | dt_to_yd(dt_t d, int *yp, int *dp) { 105 | int y, n100, n1; 106 | 107 | y = 0; 108 | #ifndef DT_NO_SHORTCUTS 109 | /* Shortcut dates between the years 1901-2099 inclusive */ 110 | if (d >= DT1901 && d <= DT2099) { 111 | d -= DT1901 - 1; 112 | y += (4 * d - 1) / 1461; 113 | d -= (1461 * y) / 4; 114 | y += 1901; 115 | } 116 | else 117 | #endif 118 | { 119 | d -= DT_EPOCH_OFFSET; 120 | if (d < 1) { 121 | const int n400 = 1 - d/146097; 122 | y -= n400 * 400; 123 | d += n400 * 146097; 124 | } 125 | d--; 126 | y += 400 * (d / 146097); 127 | d %= 146097; 128 | 129 | n100 = d / 36524; 130 | y += 100 * n100; 131 | d %= 36524; 132 | 133 | y += 4 * (d / 1461); 134 | d %= 1461; 135 | 136 | n1 = d / 365; 137 | y += n1; 138 | d %= 365; 139 | 140 | if (n100 == 4 || n1 == 4) 141 | d = 366; 142 | else 143 | y++, d++; 144 | } 145 | if (yp) *yp = y; 146 | if (dp) *dp = (int)d; 147 | } 148 | 149 | void 150 | dt_to_ymd(dt_t dt, int *yp, int *mp, int *dp) { 151 | int y, doy, m, l; 152 | 153 | dt_to_yd(dt, &y, &doy); 154 | l = LEAP_YEAR(y); 155 | m = doy < 32 ? 1 : 1 + (5 * (doy - 59 - l) + 303) / 153; 156 | 157 | assert(m >= 1); 158 | assert(m <= 12); 159 | 160 | if (yp) *yp = y; 161 | if (mp) *mp = m; 162 | if (dp) *dp = doy - days_preceding_month[l][m]; 163 | } 164 | 165 | void 166 | dt_to_yqd(dt_t dt, int *yp, int *qp, int *dp) { 167 | int y, doy, q, l; 168 | 169 | dt_to_yd(dt, &y, &doy); 170 | l = LEAP_YEAR(y); 171 | q = doy < 91 ? 1 : 1 + (5 * (doy - 59 - l) + 303) / 459; 172 | 173 | assert(q >= 1); 174 | assert(q <= 4); 175 | 176 | if (yp) *yp = y; 177 | if (qp) *qp = q; 178 | if (dp) *dp = doy - days_preceding_quarter[l][q]; 179 | } 180 | 181 | void 182 | dt_to_ywd(dt_t dt, int *yp, int *wp, int *dp) { 183 | int y, doy, dow; 184 | 185 | dt_to_yd(dt, &y, &doy); 186 | dow = dt_dow(dt); 187 | doy = doy + 4 - dow; 188 | if (doy < 1) { 189 | y--; 190 | doy += DAYS_IN_YEAR(y); 191 | } 192 | else if (doy > 365) { 193 | const int diy = DAYS_IN_YEAR(y); 194 | if (doy > diy) { 195 | doy -= diy; 196 | y++; 197 | } 198 | } 199 | if (yp) *yp = y; 200 | if (wp) *wp = (doy + 6) / 7; 201 | if (dp) *dp = dow; 202 | } 203 | 204 | int 205 | dt_rdn(dt_t dt) { 206 | return dt - DT_EPOCH_OFFSET; 207 | } 208 | 209 | dt_dow_t 210 | dt_dow(dt_t dt) { 211 | int dow = (dt - DT_EPOCH_OFFSET) % 7; 212 | if (dow < 1) 213 | dow += 7; 214 | assert(dow >= 1); 215 | assert(dow <= 7); 216 | return dow; 217 | } 218 | 219 | -------------------------------------------------------------------------------- /dt_arithmetic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012-2015 Christian Hansen 3 | * 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #include "dt_core.h" 27 | #include "dt_util.h" 28 | #include "dt_arithmetic.h" 29 | 30 | dt_t 31 | dt_add_years(dt_t dt, int delta, dt_adjust_t adjust) { 32 | int y, d; 33 | 34 | dt_to_yd(dt, &y, &d); 35 | if (adjust == DT_EXCESS || d < 365) 36 | return dt_from_yd(y + delta, d); 37 | else { 38 | int ry = y + delta; 39 | int diy; 40 | 41 | diy = dt_days_in_year(ry); 42 | if (d > diy || (adjust == DT_SNAP && d == dt_days_in_year(y))) 43 | d = diy; 44 | return dt_from_yd(ry, d); 45 | } 46 | } 47 | 48 | dt_t 49 | dt_add_quarters(dt_t dt, int delta, dt_adjust_t adjust) { 50 | int y, q, d; 51 | 52 | dt_to_yqd(dt, &y, &q, &d); 53 | if (adjust == DT_EXCESS || d < 90) 54 | return dt_from_yqd(y, q + delta, d); 55 | else { 56 | int ry = y; 57 | int rq = q + delta; 58 | int diq; 59 | 60 | if (rq < 1 || rq > 4) { 61 | ry += rq / 4; 62 | rq %= 4; 63 | if (rq < 1) 64 | ry--, rq += 4; 65 | } 66 | 67 | diq = dt_days_in_quarter(ry, rq); 68 | if (d > diq || (adjust == DT_SNAP && d == dt_days_in_quarter(y, q))) 69 | d = diq; 70 | return dt_from_yqd(ry, rq, d); 71 | } 72 | } 73 | 74 | dt_t 75 | dt_add_months(dt_t dt, int delta, dt_adjust_t adjust) { 76 | int y, m, d; 77 | 78 | dt_to_ymd(dt, &y, &m, &d); 79 | if (adjust == DT_EXCESS || d < 28) 80 | return dt_from_ymd(y, m + delta, d); 81 | else { 82 | int ry = y; 83 | int rm = m + delta; 84 | int dim; 85 | 86 | if (rm < 1 || rm > 12) { 87 | ry += rm / 12; 88 | rm %= 12; 89 | if (rm < 1) 90 | ry--, rm += 12; 91 | } 92 | 93 | dim = dt_days_in_month(ry, rm); 94 | if (d > dim || (adjust == DT_SNAP && d == dt_days_in_month(y, m))) 95 | d = dim; 96 | return dt_from_ymd(ry, rm, d); 97 | } 98 | } 99 | 100 | void 101 | dt_delta_yd(dt_t dt1, dt_t dt2, int *yp, int *dp) { 102 | int y1, y2, d1, d2, years, days; 103 | 104 | dt_to_yd(dt1, &y1, &d1); 105 | dt_to_yd(dt2, &y2, &d2); 106 | 107 | years = y2 - y1; 108 | days = d2 - d1; 109 | 110 | if (years > 0 && days < 0) { 111 | years--; 112 | days = dt2 - dt_add_years(dt1, years, DT_LIMIT); 113 | } 114 | else if (years < 0 && days > 0) { 115 | years++; 116 | days -= dt_days_in_year(y2); 117 | } 118 | 119 | if (yp) *yp = years; 120 | if (dp) *dp = days; 121 | } 122 | 123 | void 124 | dt_delta_yqd(dt_t dt1, dt_t dt2, int *yp, int *qp, int *dp) { 125 | int y1, y2, q1, q2, d1, d2, years, quarters, days; 126 | 127 | dt_to_yqd(dt1, &y1, &q1, &d1); 128 | dt_to_yqd(dt2, &y2, &q2, &d2); 129 | 130 | quarters = 4 * (y2 - y1) + q2 - q1; 131 | days = d2 - d1; 132 | 133 | if (quarters > 0 && days < 0) { 134 | quarters--; 135 | days = dt2 - dt_add_quarters(dt1, quarters, DT_LIMIT); 136 | } 137 | else if (quarters < 0 && days > 0) { 138 | quarters++; 139 | days -= dt_days_in_quarter(y2, q2); 140 | } 141 | 142 | years = quarters / 4; 143 | quarters = quarters - years * 4; 144 | 145 | if (qp) *yp = years; 146 | if (qp) *qp = quarters; 147 | if (dp) *dp = days; 148 | } 149 | 150 | void 151 | dt_delta_ymd(dt_t dt1, dt_t dt2, int *yp, int *mp, int *dp) { 152 | int y1, y2, m1, m2, d1, d2, years, months, days; 153 | 154 | dt_to_ymd(dt1, &y1, &m1, &d1); 155 | dt_to_ymd(dt2, &y2, &m2, &d2); 156 | 157 | months = 12 * (y2 - y1) + m2 - m1; 158 | days = d2 - d1; 159 | 160 | if (months > 0 && days < 0) { 161 | months--; 162 | days = dt2 - dt_add_months(dt1, months, DT_LIMIT); 163 | } 164 | else if (months < 0 && days > 0) { 165 | months++; 166 | days -= dt_days_in_month(y2, m2); 167 | } 168 | 169 | years = months / 12; 170 | months = months - years * 12; 171 | 172 | if (yp) *yp = years; 173 | if (mp) *mp = months; 174 | if (dp) *dp = days; 175 | } 176 | 177 | int 178 | dt_delta_years(dt_t dt1, dt_t dt2, bool complete) { 179 | int y1, y2, d1, d2, years; 180 | 181 | dt_to_yd(dt1, &y1, &d1); 182 | dt_to_yd(dt2, &y2, &d2); 183 | 184 | years = y2 - y1; 185 | if (complete) { 186 | if (dt1 > dt2) 187 | years += (d2 > d1); 188 | else 189 | years -= (d1 > d2); 190 | } 191 | return years; 192 | } 193 | 194 | int 195 | dt_delta_quarters(dt_t dt1, dt_t dt2, bool complete) { 196 | int y1, y2, q1, q2, d1, d2, quarters; 197 | 198 | dt_to_yqd(dt1, &y1, &q1, &d1); 199 | dt_to_yqd(dt2, &y2, &q2, &d2); 200 | 201 | quarters = 4 * (y2 - y1) + q2 - q1; 202 | if (complete) { 203 | if (dt1 > dt2) 204 | quarters += (d2 > d1); 205 | else 206 | quarters -= (d1 > d2); 207 | } 208 | return quarters; 209 | } 210 | 211 | int 212 | dt_delta_months(dt_t dt1, dt_t dt2, bool complete) { 213 | int y1, y2, m1, m2, d1, d2, months; 214 | 215 | dt_to_ymd(dt1, &y1, &m1, &d1); 216 | dt_to_ymd(dt2, &y2, &m2, &d2); 217 | 218 | months = 12 * (y2 - y1) + m2 - m1; 219 | if (complete) { 220 | if (dt1 > dt2) 221 | months += (d2 > d1); 222 | else 223 | months -= (d1 > d2); 224 | } 225 | return months; 226 | } 227 | 228 | int 229 | dt_delta_weeks(dt_t dt1, dt_t dt2) { 230 | return (dt2 - dt1) / 7; 231 | } 232 | 233 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = cc 2 | GCOV = gcov 3 | CFLAGS = $(DCFLAGS) -Wall -I. -I.. 4 | LDFLAGS += -lc $(DLDFLAGS) 5 | 6 | SOURCES = \ 7 | dt_accessor.c \ 8 | dt_arithmetic.c \ 9 | dt_char.c \ 10 | dt_core.c \ 11 | dt_dow.c \ 12 | dt_easter.c \ 13 | dt_length.c \ 14 | dt_navigate.c \ 15 | dt_parse_iso.c \ 16 | dt_search.c \ 17 | dt_tm.c \ 18 | dt_util.c \ 19 | dt_valid.c \ 20 | dt_weekday.c \ 21 | dt_workday.c \ 22 | dt_zone.c 23 | 24 | OBJECTS = \ 25 | dt_accessor.o \ 26 | dt_arithmetic.o \ 27 | dt_char.o \ 28 | dt_core.o \ 29 | dt_dow.o \ 30 | dt_easter.o \ 31 | dt_length.o \ 32 | dt_navigate.o \ 33 | dt_parse_iso.o \ 34 | dt_search.o \ 35 | dt_tm.o \ 36 | dt_util.o \ 37 | dt_valid.o \ 38 | dt_weekday.o \ 39 | dt_workday.o \ 40 | dt_zone.o 41 | 42 | HARNESS_OBJS = \ 43 | t/add_months.o \ 44 | t/add_quarters.o \ 45 | t/add_weekdays.o \ 46 | t/add_workdays.o \ 47 | t/add_years.o \ 48 | t/char.o \ 49 | t/days_in_month.o \ 50 | t/days_in_quarter.o \ 51 | t/days_in_year.o \ 52 | t/delta_weekdays.o \ 53 | t/delta_workdays.o \ 54 | t/delta_yd.o \ 55 | t/delta_ymd.o \ 56 | t/delta_yqd.o \ 57 | t/easter_orthodox.o \ 58 | t/easter_western.o \ 59 | t/end_of_month.o \ 60 | t/end_of_quarter.o \ 61 | t/end_of_week.o \ 62 | t/end_of_year.o \ 63 | t/is_holiday.o \ 64 | t/is_workday.o \ 65 | t/next_dow.o \ 66 | t/next_weekday.o \ 67 | t/nth_dow.o \ 68 | t/nth_weekday_in_month.o \ 69 | t/nth_weekday_in_quarter.o \ 70 | t/nth_weekday_in_year.o \ 71 | t/parse_iso_date.o \ 72 | t/parse_iso_time.o \ 73 | t/parse_iso_zone.o \ 74 | t/parse_iso_zone_lenient.o \ 75 | t/prev_dow.o \ 76 | t/prev_weekday.o \ 77 | t/roll_workday.o \ 78 | t/start_of_month.o \ 79 | t/start_of_quarter.o \ 80 | t/start_of_week.o \ 81 | t/start_of_year.o \ 82 | t/tm.o \ 83 | t/yd.o \ 84 | t/ymd.o \ 85 | t/ymd_epochs.o \ 86 | t/yqd.o \ 87 | t/ywd.o \ 88 | t/zone.o 89 | 90 | HARNESS_EXES = \ 91 | t/yd.t \ 92 | t/ymd.t \ 93 | t/ymd_epochs.t \ 94 | t/yqd.t \ 95 | t/ywd.t \ 96 | t/tm.t \ 97 | t/easter_western.t \ 98 | t/easter_orthodox.t \ 99 | t/days_in_year.t \ 100 | t/days_in_month.t \ 101 | t/days_in_quarter.t \ 102 | t/start_of_year.t \ 103 | t/start_of_quarter.t \ 104 | t/start_of_month.t \ 105 | t/start_of_week.t \ 106 | t/end_of_year.t \ 107 | t/end_of_quarter.t \ 108 | t/end_of_month.t \ 109 | t/end_of_week.t \ 110 | t/next_dow.t \ 111 | t/prev_dow.t \ 112 | t/next_weekday.t \ 113 | t/prev_weekday.t \ 114 | t/nth_weekday_in_year.t \ 115 | t/nth_weekday_in_quarter.t \ 116 | t/nth_weekday_in_month.t \ 117 | t/nth_dow.t \ 118 | t/add_years.t \ 119 | t/add_quarters.t \ 120 | t/add_months.t \ 121 | t/add_weekdays.t \ 122 | t/delta_yd.t \ 123 | t/delta_ymd.t \ 124 | t/delta_yqd.t \ 125 | t/delta_weekdays.t \ 126 | t/parse_iso_date.t \ 127 | t/parse_iso_time.t \ 128 | t/parse_iso_zone.t \ 129 | t/parse_iso_zone_lenient.t \ 130 | t/add_workdays.t \ 131 | t/delta_workdays.t \ 132 | t/is_holiday.t \ 133 | t/is_workday.t \ 134 | t/roll_workday.t \ 135 | t/char.t \ 136 | t/zone.t 137 | 138 | HARNESS_DEPS = \ 139 | $(OBJECTS) \ 140 | t/tap.o 141 | 142 | .SUFFIXES: 143 | .SUFFIXES: .o .c .t 144 | 145 | .PHONY: check-asan test gcov cover clean 146 | 147 | .o.t: 148 | $(CC) $(LDFLAGS) $< $(HARNESS_DEPS) -o $@ 149 | 150 | dt_accessor.o: \ 151 | dt_accessor.h dt_accessor.c 152 | 153 | dt_arithmetic.o: \ 154 | dt_arithmetic.h dt_arithmetic.c 155 | 156 | dt_char.o: \ 157 | dt_char.h dt_char.c 158 | 159 | dt_core.o: \ 160 | dt_config.h dt_core.h dt_core.c 161 | 162 | dt_dow.o: \ 163 | dt_dow.h dt_dow.c 164 | 165 | dt_easter.o: \ 166 | dt_easter.h dt_easter.c 167 | 168 | dt_length.o: \ 169 | dt_length.h dt_length.c 170 | 171 | dt_navigate.o: \ 172 | dt_navigate.h dt_navigate.c 173 | 174 | dt_parse_iso.o: \ 175 | dt_parse_iso.h dt_parse_iso.c 176 | 177 | dt_search.o: \ 178 | dt_search.h dt_search.c 179 | 180 | dt_tm.o: \ 181 | dt_tm.h dt_tm.c 182 | 183 | dt_weekday.o: \ 184 | dt_weekday.h dt_weekday.c 185 | 186 | dt_workday.o: \ 187 | dt_workday.h dt_workday.c 188 | 189 | dt_zone.o: \ 190 | dt_zone.h dt_zone.c dt_zone_entries.h 191 | 192 | t/tap.o: \ 193 | t/tap.h t/tap.c 194 | 195 | t/add_years.o: \ 196 | $(HARNESS_DEPS) t/add_years.c 197 | t/add_quarters.o: \ 198 | $(HARNESS_DEPS) t/add_quarters.c 199 | t/add_months.o: \ 200 | $(HARNESS_DEPS) t/add_months.c t/add_months.h 201 | t/add_weekdays.o: \ 202 | $(HARNESS_DEPS) t/add_weekdays.c 203 | t/add_workdays.o: \ 204 | $(HARNESS_DEPS) t/add_workdays.c 205 | t/char.o: \ 206 | $(HARNESS_DEPS) t/char.c 207 | t/days_in_month.o: \ 208 | $(HARNESS_DEPS) t/days_in_month.c 209 | t/days_in_quarter.o: \ 210 | $(HARNESS_DEPS) t/days_in_quarter.c 211 | t/days_in_year.o: \ 212 | $(HARNESS_DEPS) t/days_in_year.c 213 | t/delta_weekdays.o: \ 214 | $(HARNESS_DEPS) t/delta_weekdays.c 215 | t/delta_workdays.o: \ 216 | $(HARNESS_DEPS) t/delta_workdays.c 217 | t/delta_yd.o: \ 218 | $(HARNESS_DEPS) t/delta_yd.c 219 | t/delta_ymd.o: \ 220 | $(HARNESS_DEPS) t/delta_ymd.c 221 | t/delta_yqd.o: \ 222 | $(HARNESS_DEPS) t/delta_yqd.c 223 | t/easter_orthodox.o: \ 224 | $(HARNESS_DEPS) t/easter_orthodox.c t/easter_orthodox.h 225 | t/easter_western.o: \ 226 | $(HARNESS_DEPS) t/easter_western.c t/easter_western.h 227 | t/end_of_month.o: \ 228 | $(HARNESS_DEPS) t/end_of_month.c t/end_of_month.h 229 | t/end_of_quarter.o: \ 230 | $(HARNESS_DEPS) t/end_of_quarter.c t/end_of_quarter.h 231 | t/end_of_week.o: \ 232 | $(HARNESS_DEPS) t/end_of_week.c 233 | t/end_of_year.o: \ 234 | $(HARNESS_DEPS) t/end_of_year.c 235 | t/is_holiday.o: \ 236 | $(HARNESS_DEPS) t/is_holiday.c 237 | t/is_workday.o: \ 238 | $(HARNESS_DEPS) t/is_workday.c 239 | t/next_dow.o: \ 240 | $(HARNESS_DEPS) t/next_dow.c 241 | t/next_weekday.o: \ 242 | $(HARNESS_DEPS) t/next_weekday.c 243 | t/nth_dow.o: \ 244 | $(HARNESS_DEPS) t/nth_dow.c t/nth_dow.h 245 | t/nth_weekday_in_quarter.o: \ 246 | $(HARNESS_DEPS) t/nth_weekday_in_quarter.c 247 | t/nth_weekday_in_month.o: \ 248 | $(HARNESS_DEPS) t/nth_weekday_in_month.c 249 | t/roll_workday.o: \ 250 | $(HARNESS_DEPS) t/roll_workday.c 251 | t/parse_iso_date.o: \ 252 | $(HARNESS_DEPS) t/parse_iso_date.c 253 | t/parse_iso_time.o: \ 254 | $(HARNESS_DEPS) t/parse_iso_time.c 255 | t/parse_iso_zone.o: \ 256 | $(HARNESS_DEPS) t/parse_iso_zone.c 257 | t/parse_iso_zone_lenient.o: \ 258 | $(HARNESS_DEPS) t/parse_iso_zone_lenient.c 259 | t/prev_dow.o: \ 260 | $(HARNESS_DEPS) t/prev_dow.c 261 | t/prev_weekday.o: \ 262 | $(HARNESS_DEPS) t/prev_weekday.c 263 | t/start_of_month.o: \ 264 | $(HARNESS_DEPS) t/start_of_month.c t/start_of_month.h 265 | t/start_of_quarter.o: \ 266 | $(HARNESS_DEPS) t/start_of_quarter.c t/start_of_quarter.h 267 | t/start_of_week.o: \ 268 | $(HARNESS_DEPS) t/start_of_week.c 269 | t/start_of_year.o: \ 270 | $(HARNESS_DEPS) t/start_of_year.c 271 | t/tm.o: \ 272 | $(HARNESS_DEPS) t/tm.c 273 | t/yd.o: \ 274 | $(HARNESS_DEPS) t/yd.c 275 | t/ymd.o: \ 276 | $(HARNESS_DEPS) t/ymd.c 277 | t/ymd_epochs.o: \ 278 | $(HARNESS_DEPS) t/ymd_epochs.c 279 | t/yqd.o: \ 280 | $(HARNESS_DEPS) t/yqd.c 281 | t/ywd.o: \ 282 | $(HARNESS_DEPS) t/ywd.c t/ywd.h 283 | t/zone.o: \ 284 | $(HARNESS_DEPS) t/zone.c 285 | 286 | test: $(HARNESS_EXES) 287 | @prove $(HARNESS_EXES) 288 | 289 | check-asan: 290 | @$(MAKE) DCFLAGS="-O1 -g -faddress-sanitizer -fno-omit-frame-pointer" \ 291 | DLDFLAGS="-g -faddress-sanitizer" test 292 | 293 | gcov: 294 | @$(MAKE) DCFLAGS="-O0 -g -coverage" DLDFLAGS="-coverage" test 295 | @$(GCOV) $(SOURCES) 296 | 297 | cover: 298 | @$(MAKE) DCFLAGS="-O0 -g --coverage" DLDFLAGS="-coverage" test 299 | @$(GCOV) -abc $(SOURCES) 300 | @gcov2perl *.gcov 301 | @cover --no-gcov 302 | 303 | clean: 304 | rm -f $(HARNESS_DEPS) $(HARNESS_OBJS) $(HARNESS_EXES) *.gc{ov,da,no} t/*.gc{ov,da,no} 305 | 306 | --------------------------------------------------------------------------------