ruby-sdn 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,245 @@
1
+ /* $selId: julian.c,v 2.0 1995/10/24 01:13:06 lees Exp $
2
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
3
+ * Permission granted to use, copy, modify, distribute and sell so long as
4
+ * the above copyright and this permission statement are retained in all
5
+ * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
6
+ */
7
+
8
+ /**************************************************************************
9
+ *
10
+ * These are the externally visible components of this file:
11
+ *
12
+ * void
13
+ * SdnToJulian(
14
+ * long int sdn,
15
+ * int *pYear,
16
+ * int *pMonth,
17
+ * int *pDay);
18
+ *
19
+ * Convert a SDN to a Julian calendar date. If the input SDN is less than
20
+ * 1, the three output values will all be set to zero, otherwise *pYear
21
+ * will be >= -4713 and != 0; *pMonth will be in the range 1 to 12
22
+ * inclusive; *pDay will be in the range 1 to 31 inclusive.
23
+ *
24
+ * long int
25
+ * JulianToSdn(
26
+ * int inputYear,
27
+ * int inputMonth,
28
+ * int inputDay);
29
+ *
30
+ * Convert a Julian calendar date to a SDN. Zero is returned when the
31
+ * input date is detected as invalid or out of the supported range. The
32
+ * return value will be > 0 for all valid, supported dates, but there are
33
+ * some invalid dates that will return a positive value. To verify that a
34
+ * date is valid, convert it to SDN and then back and compare with the
35
+ * original.
36
+ *
37
+ * VALID RANGE
38
+ *
39
+ * 4713 B.C. to at least 10000 A.D.
40
+ *
41
+ * Although this software can handle dates all the way back to 4713
42
+ * B.C., such use may not be meaningful. The calendar was created in
43
+ * 46 B.C., but the details did not stabilize until at least 8 A.D.,
44
+ * and perhaps as late at the 4th century. Also, the beginning of a
45
+ * year varied from one culture to another - not all accepted January
46
+ * as the first month.
47
+ *
48
+ * CALENDAR OVERVIEW
49
+ *
50
+ * Julias Ceasar created the calendar in 46 B.C. as a modified form of
51
+ * the old Roman republican calendar which was based on lunar cycles.
52
+ * The new Julian calendar set fixed lengths for the months, abandoning
53
+ * the lunar cycle. It also specified that there would be exactly 12
54
+ * months per year and 365.25 days per year with every 4th year being a
55
+ * leap year.
56
+ *
57
+ * Note that the current accepted value for the tropical year is
58
+ * 365.242199 days, not 365.25. This lead to an 11 day shift in the
59
+ * calendar with respect to the seasons by the 16th century when the
60
+ * Gregorian calendar was created to replace the Julian calendar.
61
+ *
62
+ * The difference between the Julian and today's Gregorian calendar is
63
+ * that the Gregorian does not make centennial years leap years unless
64
+ * they are a multiple of 400, which leads to a year of 365.2425 days.
65
+ * In other words, in the Gregorian calendar, 1700, 1800 and 1900 are
66
+ * not leap years, but 2000 is. All centennial years are leap years in
67
+ * the Julian calendar.
68
+ *
69
+ * The details are unknown, but the lengths of the months were adjusted
70
+ * until they finally stablized in 8 A.D. with their current lengths:
71
+ *
72
+ * January 31
73
+ * February 28/29
74
+ * March 31
75
+ * April 30
76
+ * May 31
77
+ * June 30
78
+ * Quintilis/July 31
79
+ * Sextilis/August 31
80
+ * September 30
81
+ * October 31
82
+ * November 30
83
+ * December 31
84
+ *
85
+ * In the early days of the calendar, the days of the month were not
86
+ * numbered as we do today. The numbers ran backwards (decreasing) and
87
+ * were counted from the Ides (15th of the month - which in the old
88
+ * Roman republican lunar calendar would have been the full moon) or
89
+ * from the Nonae (9th day before the Ides) or from the beginning of
90
+ * the next month.
91
+ *
92
+ * In the early years, the beginning of the year varied, sometimes
93
+ * based on the ascension of rulers. It was not always the first of
94
+ * January.
95
+ *
96
+ * Also, today's epoch, 1 A.D. or the birth of Jesus Christ, did not
97
+ * come into use until several centuries later when Christianity became
98
+ * a dominant religion.
99
+ *
100
+ * ALGORITHMS
101
+ *
102
+ * The calculations are based on two different cycles: a 4 year cycle
103
+ * of leap years and a 5 month cycle of month lengths.
104
+ *
105
+ * The 5 month cycle is used to account for the varying lengths of
106
+ * months. You will notice that the lengths alternate between 30 and
107
+ * 31 days, except for three anomalies: both July and August have 31
108
+ * days, both December and January have 31, and February is less than
109
+ * 30. Starting with March, the lengths are in a cycle of 5 months
110
+ * (31, 30, 31, 30, 31):
111
+ *
112
+ * Mar 31 days \
113
+ * Apr 30 days |
114
+ * May 31 days > First cycle
115
+ * Jun 30 days |
116
+ * Jul 31 days /
117
+ *
118
+ * Aug 31 days \
119
+ * Sep 30 days |
120
+ * Oct 31 days > Second cycle
121
+ * Nov 30 days |
122
+ * Dec 31 days /
123
+ *
124
+ * Jan 31 days \
125
+ * Feb 28/9 days |
126
+ * > Third cycle (incomplete)
127
+ *
128
+ * For this reason the calculations (internally) assume that the year
129
+ * starts with March 1.
130
+ *
131
+ * TESTING
132
+ *
133
+ * This algorithm has been tested from the year 4713 B.C. to 10000 A.D.
134
+ * The source code of the verification program is included in this
135
+ * package.
136
+ *
137
+ * REFERENCES
138
+ *
139
+ * Conversions Between Calendar Date and Julian Day Number by Robert J.
140
+ * Tantzen, Communications of the Association for Computing Machinery
141
+ * August 1963. (Also published in Collected Algorithms from CACM,
142
+ * algorithm number 199). [Note: the published algorithm is for the
143
+ * Gregorian calendar, but was adjusted to use the Julian calendar's
144
+ * simpler leap year rule.]
145
+ *
146
+ **************************************************************************/
147
+
148
+ #include "sdncal.h"
149
+
150
+ #define SDN_OFFSET 32083
151
+ #define DAYS_PER_5_MONTHS 153
152
+ #define DAYS_PER_4_YEARS 1461
153
+
154
+ void
155
+ SdnToJulian(
156
+ long int sdn,
157
+ int *pYear,
158
+ int *pMonth,
159
+ int *pDay)
160
+ {
161
+ int year;
162
+ int month;
163
+ int day;
164
+ long int temp;
165
+ int dayOfYear;
166
+
167
+ if (sdn <= 0) {
168
+ *pYear = 0;
169
+ *pMonth = 0;
170
+ *pDay = 0;
171
+ return;
172
+ }
173
+
174
+ temp = (sdn + SDN_OFFSET) * 4 - 1;
175
+
176
+ /* Calculate the year and day of year (1 <= dayOfYear <= 366). */
177
+ year = temp / DAYS_PER_4_YEARS;
178
+ dayOfYear = (temp % DAYS_PER_4_YEARS) / 4 + 1;
179
+
180
+ /* Calculate the month and day of month. */
181
+ temp = dayOfYear * 5 - 3;
182
+ month = temp / DAYS_PER_5_MONTHS;
183
+ day = (temp % DAYS_PER_5_MONTHS) / 5 + 1;
184
+
185
+ /* Convert to the normal beginning of the year. */
186
+ if (month < 10) {
187
+ month += 3;
188
+ } else {
189
+ year += 1;
190
+ month -= 9;
191
+ }
192
+
193
+ /* Adjust to the B.C./A.D. type numbering. */
194
+ year -= 4800;
195
+ if (year <= 0) year--;
196
+
197
+ *pYear = year;
198
+ *pMonth = month;
199
+ *pDay = day;
200
+ }
201
+
202
+ long int
203
+ JulianToSdn(
204
+ int inputYear,
205
+ int inputMonth,
206
+ int inputDay)
207
+ {
208
+ int year;
209
+ int month;
210
+
211
+ /* check for invalid dates */
212
+ if (inputYear == 0 || inputYear < -4713 ||
213
+ inputMonth <= 0 || inputMonth > 12 ||
214
+ inputDay <= 0 || inputDay > 31)
215
+ {
216
+ return(0);
217
+ }
218
+
219
+ /* check for dates before SDN 1 (Jan 2, 4713 B.C.) */
220
+ if (inputYear == -4713) {
221
+ if (inputMonth == 1 && inputDay == 1) {
222
+ return(0);
223
+ }
224
+ }
225
+
226
+ /* Make year always a positive number. */
227
+ if (inputYear < 0) {
228
+ year = inputYear + 4801;
229
+ } else {
230
+ year = inputYear + 4800;
231
+ }
232
+
233
+ /* Adjust the start of the year. */
234
+ if (inputMonth > 2) {
235
+ month = inputMonth - 3;
236
+ } else {
237
+ month = inputMonth + 9;
238
+ year--;
239
+ }
240
+
241
+ return( (year * DAYS_PER_4_YEARS) / 4
242
+ + (month * DAYS_PER_5_MONTHS + 2) / 5
243
+ + inputDay
244
+ - SDN_OFFSET );
245
+ }
@@ -0,0 +1,90 @@
1
+ #ifndef _INCLUDED_SDNCAL_H
2
+ #define _INCLUDED_SDNCAL_H
3
+
4
+ /* $selId: sdncal.h,v 2.0 1995/10/24 01:13:06 lees Exp $
5
+ * Copyright 1993-1995, Scott E. Lee, all rights reserved.
6
+ * Permission granted to use, copy, modify, distribute and sell so long as
7
+ * the above copyright and this permission statement are retained in all
8
+ * copies. THERE IS NO WARRANTY - USE AT YOUR OWN RISK.
9
+ */
10
+
11
+ /**************************************************************************
12
+ *
13
+ * This package defines a set of routines that convert calendar dates to
14
+ * and from a serial day number (SDN). The SDN is a serial numbering of
15
+ * days where SDN 1 is November 25, 4714 BC in the Gregorian calendar and
16
+ * SDN 2447893 is January 1, 1990. This system of day numbering is
17
+ * sometimes referred to as Julian days, but to avoid confusion with the
18
+ * Julian calendar, it is referred to as serial day numbers here. The term
19
+ * Julian days is also used to mean the number of days since the beginning
20
+ * of the current year.
21
+ *
22
+ * The SDN can be used as an intermediate step in converting from one
23
+ * calendar system to another (such as Gregorian to Jewish). It can also
24
+ * be used for date computations such as easily comparing two dates,
25
+ * determining the day of the week, finding the date of yesterday or
26
+ * calculating the number of days between two dates.
27
+ *
28
+ * When using this software on 16 bit systems, be careful to store SDNs in
29
+ * a long int, because it will not fit in the 16 bits that some systems
30
+ * allocate to an int.
31
+ *
32
+ * For each calendar, there are two routines provided. One converts dates
33
+ * in that calendar to SDN and the other converts SDN to calendar dates.
34
+ * The routines are named SdnTo<CALENDAR>() and <CALENDAR>ToSdn(), where
35
+ * <CALENDAR> is the name of the calendar system.
36
+ *
37
+ * SDN values less than one are not supported. If a conversion routine
38
+ * returns an SDN of zero, this means that the date given is either invalid
39
+ * or is outside the supported range for that calendar.
40
+ *
41
+ * At least some validity checks are performed on input dates. For
42
+ * example, a negative month number will result in the return of zero for
43
+ * the SDN. A returned SDN greater than one does not necessarily mean that
44
+ * the input date was valid. To determine if the date is valid, convert it
45
+ * to SDN, and if the SDN is greater than zero, convert it back to a date
46
+ * and compare to the original. For example:
47
+ *
48
+ * int y1, m1, d1;
49
+ * int y2, m2, d2;
50
+ * long int sdn;
51
+ * ...
52
+ * sdn = GregorianToSdn(y1, m1, d1);
53
+ * if (sdn > 0) {
54
+ * SdnToGregorian(sdn, &y2, &m2, &d2);
55
+ * if (y1 == y2 && m1 == m2 && d1 == d2) {
56
+ * ... date is valid ...
57
+ * }
58
+ * }
59
+ *
60
+ **************************************************************************/
61
+
62
+ /* Gregorian calendar conversions. */
63
+ void SdnToGregorian(long int sdn, int *pYear, int *pMonth, int *pDay);
64
+ long int GregorianToSdn(int year, int month, int day);
65
+ extern const char *MonthNameShort[13];
66
+ extern const char *MonthNameLong[13];
67
+
68
+ /* Julian calendar conversions. */
69
+ void SdnToJulian(long int sdn, int *pYear, int *pMonth, int *pDay);
70
+ long int JulianToSdn(int year, int month, int day);
71
+
72
+ /* Jewish calendar conversions. */
73
+ void SdnToJewish(long int sdn, int *pYear, int *pMonth, int *pDay);
74
+ long int JewishToSdn(int year, int month, int day);
75
+ extern const char *JewishMonthName[14];
76
+
77
+ /* French republic calendar conversions. */
78
+ void SdnToFrench(long int sdn, int *pYear, int *pMonth, int *pDay);
79
+ long int FrenchToSdn(int inputYear, int inputMonth, int inputDay);
80
+ extern const char *FrenchMonthName[14];
81
+
82
+ /* Islamic calendar conversions. */
83
+ /* Not implemented yet. */
84
+
85
+ /* Day of week conversion. 0=Sunday, 6=Saturday */
86
+ int DayOfWeek(long int sdn);
87
+ extern const char *DayNameShort[7];
88
+ extern const char *DayNameLong[7];
89
+
90
+ #endif /* _INCLUDED_SDNCAL_H */
@@ -0,0 +1,212 @@
1
+ /*
2
+ sdnmodule.c: Copyright (C) 1997,1999,2014 Tadayoshi Funaba All rights reserved
3
+ $Id: sdnmodule.c,v 1.7 2014-04-05 16:47:22+09 tadf Exp $
4
+ */
5
+
6
+ #include "ruby.h"
7
+ #ifdef HAVE_RUBY_ENCODING_H
8
+ #include "ruby/encoding.h"
9
+ #endif
10
+ #include "sdncal.h"
11
+
12
+ #define CHECK_RANGE(v, m) \
13
+ { \
14
+ if ((v) < (m)) \
15
+ rb_raise(rb_eArgError, "out of range"); \
16
+ }
17
+
18
+ #define CHECK_DOMAIN(v, m) \
19
+ { \
20
+ if ((v) < (m)) \
21
+ rb_raise(rb_eArgError, "out of domain");\
22
+ }
23
+
24
+ #define CHECK_DOMAIN2(v, m, n) \
25
+ { \
26
+ if ((v) < (m) || (v) > (n)) \
27
+ rb_raise(rb_eArgError, "out of domain");\
28
+ }
29
+
30
+ static VALUE
31
+ sdn_SdnToGregorian(VALUE obj, VALUE sdn)
32
+ {
33
+ int y, m, md;
34
+ long isdn;
35
+
36
+ isdn = NUM2LONG(sdn); CHECK_DOMAIN(isdn, 1);
37
+ SdnToGregorian(isdn, &y, &m, &md);
38
+ return rb_ary_new3(3, INT2NUM(y), INT2NUM(m), INT2NUM(md));
39
+ }
40
+
41
+ static VALUE
42
+ sdn_GregorianToSdn(VALUE obj, VALUE y, VALUE m, VALUE md)
43
+ {
44
+ int iy, im, imd, iy2, im2, imd2;
45
+ long sdn;
46
+
47
+ iy = NUM2INT(y); CHECK_DOMAIN(iy, -4714);
48
+ im = NUM2INT(m); CHECK_DOMAIN2(im, 1, 12);
49
+ imd = NUM2INT(md); CHECK_DOMAIN2(imd, 1, 31);
50
+ sdn = GregorianToSdn(iy, im, imd);
51
+ CHECK_RANGE(sdn, 1);
52
+ SdnToGregorian(sdn, &iy2, &im2, &imd2);
53
+ if (iy != iy2 || im != im2 || imd != imd2)
54
+ rb_raise(rb_eArgError, "invalid date");
55
+ return INT2NUM(sdn);
56
+ }
57
+
58
+ static VALUE
59
+ sdn_SdnToJulian(VALUE obj, VALUE sdn)
60
+ {
61
+ int y, m, md;
62
+ long isdn;
63
+
64
+ isdn = NUM2LONG(sdn); CHECK_DOMAIN(isdn, 1);
65
+ SdnToJulian(isdn, &y, &m, &md);
66
+ return rb_ary_new3(3, INT2NUM(y), INT2NUM(m), INT2NUM(md));
67
+ }
68
+
69
+ static VALUE
70
+ sdn_JulianToSdn(VALUE obj, VALUE y, VALUE m, VALUE md)
71
+ {
72
+ int iy, im, imd, iy2, im2, imd2;
73
+ long sdn;
74
+
75
+ iy = NUM2INT(y); CHECK_DOMAIN(iy, -4713);
76
+ im = NUM2INT(m); CHECK_DOMAIN2(im, 1, 12);
77
+ imd = NUM2INT(md); CHECK_DOMAIN2(imd, 1, 31);
78
+ sdn = JulianToSdn(iy, im, imd);
79
+ CHECK_RANGE(sdn, 1);
80
+ SdnToJulian(sdn, &iy2, &im2, &imd2);
81
+ if (iy != iy2 || im != im2 || imd != imd2)
82
+ rb_raise(rb_eArgError, "invalid date");
83
+ return INT2NUM(sdn);
84
+ }
85
+
86
+ static VALUE
87
+ sdn_SdnToJewish(VALUE obj, VALUE sdn)
88
+ {
89
+ int y, m, md;
90
+ long isdn;
91
+
92
+ isdn = NUM2LONG(sdn); CHECK_DOMAIN(isdn, 347998);
93
+ SdnToJewish(NUM2INT(sdn), &y, &m, &md);
94
+ return rb_ary_new3(3, INT2NUM(y), INT2NUM(m), INT2NUM(md));
95
+ }
96
+
97
+ static VALUE
98
+ sdn_JewishToSdn(VALUE obj, VALUE y, VALUE m, VALUE md)
99
+ {
100
+ int iy, im, imd, iy2, im2, imd2;
101
+ long sdn;
102
+
103
+ iy = NUM2INT(y); CHECK_DOMAIN(iy, 1);
104
+ im = NUM2INT(m); CHECK_DOMAIN2(im, 1, 13);
105
+ imd = NUM2INT(md); CHECK_DOMAIN2(imd, 1, 30);
106
+ sdn = JewishToSdn(iy, im, imd);
107
+ CHECK_RANGE(sdn, 1); /* 347998 */
108
+ SdnToJewish(sdn, &iy2, &im2, &imd2);
109
+ if (iy != iy2 || im != im2 || imd != imd2)
110
+ rb_raise(rb_eArgError, "invalid date");
111
+ return INT2NUM(sdn);
112
+ }
113
+
114
+ static VALUE
115
+ sdn_SdnToFrench(VALUE obj, VALUE sdn)
116
+ {
117
+ int y, m, md;
118
+ long isdn;
119
+
120
+ isdn = NUM2LONG(sdn); CHECK_DOMAIN2(isdn, 2375840, 2380952);
121
+ SdnToFrench(isdn, &y, &m, &md);
122
+ return rb_ary_new3(3, INT2NUM(y), INT2NUM(m), INT2NUM(md));
123
+ }
124
+
125
+ static VALUE
126
+ sdn_FrenchToSdn(VALUE obj, VALUE y, VALUE m, VALUE md)
127
+ {
128
+ int iy, im, imd, iy2, im2, imd2;
129
+ long sdn;
130
+
131
+ iy = NUM2INT(y); CHECK_DOMAIN(iy, 1);
132
+ im = NUM2INT(m); CHECK_DOMAIN2(im, 1, 13);
133
+ imd = NUM2INT(md); CHECK_DOMAIN2(imd, 1, 30);
134
+ sdn = FrenchToSdn(iy, im, imd);
135
+ CHECK_RANGE(sdn, 1);
136
+ SdnToFrench(sdn, &iy2, &im2, &imd2);
137
+ if (iy != iy2 || im != im2 || imd != imd2)
138
+ rb_raise(rb_eArgError, "invalid date");
139
+ return INT2NUM(sdn);
140
+ }
141
+
142
+ static VALUE
143
+ sdn_DayOfWeek(VALUE obj, VALUE sdn)
144
+ {
145
+ long isdn;
146
+ int wd;
147
+
148
+ isdn = NUM2LONG(sdn);
149
+ wd = DayOfWeek(isdn);
150
+ return INT2NUM(wd);
151
+ }
152
+
153
+ static VALUE
154
+ mk_ary_of_str(long len, const char *a[])
155
+ {
156
+ VALUE o;
157
+ long i;
158
+
159
+ o = rb_ary_new2(len);
160
+ for (i = 0; i < len; i++) {
161
+ VALUE e;
162
+
163
+ if (!a[i])
164
+ e = Qnil;
165
+ else {
166
+ e = rb_str_new2(a[i]);
167
+ #ifdef HAVE_RUBY_ENCODING_H
168
+ rb_enc_associate(e, rb_utf8_encoding());
169
+ ENC_CODERANGE_CLEAR(e);
170
+ #endif
171
+ rb_obj_freeze(e);
172
+ }
173
+ rb_ary_push(o, e);
174
+ }
175
+ rb_obj_freeze(o);
176
+ return o;
177
+ }
178
+
179
+ void
180
+ Init_sdn(void)
181
+ {
182
+ VALUE mod;
183
+
184
+ mod = rb_define_module("Sdn");
185
+
186
+ rb_define_module_function(mod, "DayOfWeek", sdn_DayOfWeek, 1);
187
+ rb_define_module_function(mod, "SdnToGregorian", sdn_SdnToGregorian, 1);
188
+ rb_define_module_function(mod, "GregorianToSdn", sdn_GregorianToSdn, 3);
189
+ rb_define_module_function(mod, "SdnToJulian", sdn_SdnToJulian, 1);
190
+ rb_define_module_function(mod, "JulianToSdn", sdn_JulianToSdn, 3);
191
+ rb_define_module_function(mod, "SdnToJewish", sdn_SdnToJewish, 1);
192
+ rb_define_module_function(mod, "JewishToSdn", sdn_JewishToSdn, 3);
193
+ rb_define_module_function(mod, "SdnToFrench", sdn_SdnToFrench, 1);
194
+ rb_define_module_function(mod, "FrenchToSdn", sdn_FrenchToSdn, 3);
195
+
196
+ rb_define_const
197
+ (mod, "VERSION",
198
+ rb_obj_freeze(rb_str_new2("$Revision: 1.7 $")));
199
+
200
+ #define const_array(a) mk_ary_of_str(sizeof a / sizeof a[0], a)
201
+ #define def_const(n, o) rb_define_const(mod, n, o)
202
+ #define def_const_array(n, a) def_const(n, const_array(a))
203
+
204
+ def_const_array("DayNameShort", DayNameShort);
205
+ def_const_array("DayNameLong", DayNameLong);
206
+
207
+ def_const_array("MonthNameShort", MonthNameShort);
208
+ def_const_array("MonthNameLong", MonthNameLong);
209
+
210
+ def_const_array("JewishMonthName", JewishMonthName);
211
+ def_const_array("FrenchMonthName", FrenchMonthName);
212
+ }