comparison tango/tango/time/chrono/Gregorian.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children
comparison
equal deleted inserted replaced
131:5825d48b27d1 132:1700239cab2e
1 /*******************************************************************************
2
3 copyright: Copyright (c) 2005 John Chapman. All rights reserved
4
5 license: BSD style: $(LICENSE)
6
7 version: Mid 2005: Initial release
8 Apr 2007: reshaped
9
10 author: John Chapman, Kris, schveiguy
11
12 ******************************************************************************/
13
14 module tango.time.chrono.Gregorian;
15
16 private import tango.time.chrono.Calendar;
17
18 /**
19 * $(ANCHOR _Gregorian)
20 * Represents the Gregorian calendar.
21 *
22 * Note that this is the Proleptic Gregorian calendar. Most calendars assume
23 * that dates before 9/14/1752 were Julian Dates. Julian differs from
24 * Gregorian in that leap years occur every 4 years, even on 100 year
25 * increments. The Proleptic Gregorian calendar applies the Gregorian leap
26 * year rules to dates before 9/14/1752, making the calculation of dates much
27 * easier.
28 */
29 class Gregorian : Calendar
30 {
31 // import baseclass toTime()
32 alias Calendar.toTime toTime;
33
34 /// static shared instance
35 public static Gregorian generic;
36
37 enum Type
38 {
39 Localized = 1, /// Refers to the localized version of the Gregorian calendar.
40 USEnglish = 2, /// Refers to the US English version of the Gregorian calendar.
41 MiddleEastFrench = 9, /// Refers to the Middle East French version of the Gregorian calendar.
42 Arabic = 10, /// Refers to the _Arabic version of the Gregorian calendar.
43 TransliteratedEnglish = 11, /// Refers to the transliterated English version of the Gregorian calendar.
44 TransliteratedFrench = 12 /// Refers to the transliterated French version of the Gregorian calendar.
45 }
46
47 private Type type_;
48
49 /**
50 * Represents the current era.
51 */
52 enum {AD_ERA = 1, BC_ERA = 2, MAX_YEAR = 9999};
53
54 private static final uint[] DaysToMonthCommon = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
55
56 private static final uint[] DaysToMonthLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366];
57
58 /**
59 * create a generic instance of this calendar
60 */
61 static this()
62 {
63 generic = new Gregorian;
64 }
65
66 /**
67 * Initializes an instance of the Gregorian class using the specified GregorianTypes value. If no value is
68 * specified, the default is Gregorian.Types.Localized.
69 */
70 this (Type type = Type.Localized)
71 {
72 type_ = type;
73 }
74
75 /**
76 * Overridden. Returns a Time value set to the specified date and time in the specified _era.
77 * Params:
78 * year = An integer representing the _year.
79 * month = An integer representing the _month.
80 * day = An integer representing the _day.
81 * hour = An integer representing the _hour.
82 * minute = An integer representing the _minute.
83 * second = An integer representing the _second.
84 * millisecond = An integer representing the _millisecond.
85 * era = An integer representing the _era.
86 * Returns: A Time set to the specified date and time.
87 */
88 override Time toTime (uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond, uint era)
89 {
90 return Time (getDateTicks(year, month, day, era) + getTimeTicks(hour, minute, second)) + TimeSpan.millis(millisecond);
91 }
92
93 /**
94 * Overridden. Returns the day of the week in the specified Time.
95 * Params: time = A Time value.
96 * Returns: A DayOfWeek value representing the day of the week of time.
97 */
98 override DayOfWeek getDayOfWeek(Time time)
99 {
100 int dow;
101 if(time.ticks < 0)
102 {
103 dow = ((time.ticks + 1) / TimeSpan.TicksPerDay) % 7;
104 if(dow < 0)
105 dow += 7;
106 }
107 else
108 dow = (time.ticks / TimeSpan.TicksPerDay + 1) % 7;
109 return cast(DayOfWeek)dow;
110 }
111
112 /**
113 * Overridden. Returns the day of the month in the specified Time.
114 * Params: time = A Time value.
115 * Returns: An integer representing the day of the month of time.
116 */
117 override uint getDayOfMonth(Time time)
118 {
119 return extractPart(time.ticks, DatePart.Day);
120 }
121
122 /**
123 * Overridden. Returns the day of the year in the specified Time.
124 * Params: time = A Time value.
125 * Returns: An integer representing the day of the year of time.
126 */
127 override uint getDayOfYear(Time time)
128 {
129 return extractPart(time.ticks, DatePart.DayOfYear);
130 }
131
132 /**
133 * Overridden. Returns the month in the specified Time.
134 * Params: time = A Time value.
135 * Returns: An integer representing the month in time.
136 */
137 override uint getMonth(Time time)
138 {
139 return extractPart(time.ticks, DatePart.Month);
140 }
141
142 /**
143 * Overridden. Returns the year in the specified Time.
144 * Params: time = A Time value.
145 * Returns: An integer representing the year in time.
146 */
147 override uint getYear(Time time)
148 {
149 return extractPart(time.ticks, DatePart.Year);
150 }
151
152 /**
153 * Overridden. Returns the era in the specified Time.
154 * Params: time = A Time value.
155 * Returns: An integer representing the era in time.
156 */
157 override uint getEra(Time time)
158 {
159 if(time < time.epoch)
160 return BC_ERA;
161 else
162 return AD_ERA;
163 }
164
165 /**
166 * Overridden. Returns the number of days in the specified _year and _month of the specified _era.
167 * Params:
168 * year = An integer representing the _year.
169 * month = An integer representing the _month.
170 * era = An integer representing the _era.
171 * Returns: The number of days in the specified _year and _month of the specified _era.
172 */
173 override uint getDaysInMonth(uint year, uint month, uint era)
174 {
175 auto monthDays = isLeapYear(year, era) ? DaysToMonthLeap : DaysToMonthCommon;
176 return monthDays[month] - monthDays[month - 1];
177 }
178
179 /**
180 * Overridden. Returns the number of days in the specified _year of the specified _era.
181 * Params:
182 * year = An integer representing the _year.
183 * era = An integer representing the _era.
184 * Returns: The number of days in the specified _year in the specified _era.
185 */
186 override uint getDaysInYear(uint year, uint era)
187 {
188 return isLeapYear(year, era) ? 366 : 365;
189 }
190
191 /**
192 * Overridden. Returns the number of months in the specified _year of the specified _era.
193 * Params:
194 * year = An integer representing the _year.
195 * era = An integer representing the _era.
196 * Returns: The number of months in the specified _year in the specified _era.
197 */
198 override uint getMonthsInYear(uint year, uint era)
199 {
200 return 12;
201 }
202
203 /**
204 * Overridden. Indicates whether the specified _year in the specified _era is a leap _year.
205 * Params: year = An integer representing the _year.
206 * Params: era = An integer representing the _era.
207 * Returns: true is the specified _year is a leap _year; otherwise, false.
208 */
209 override bool isLeapYear(uint year, uint era)
210 {
211 return staticIsLeapYear(year, era);
212 }
213
214 /**
215 * $(I Property.) Retrieves the GregorianTypes value indicating the language version of the Gregorian.
216 * Returns: The Gregorian.Type value indicating the language version of the Gregorian.
217 */
218 Type calendarType()
219 {
220 return type_;
221 }
222
223 /**
224 * $(I Property.) Overridden. Retrieves the list of eras in the current calendar.
225 * Returns: An integer array representing the eras in the current calendar.
226 */
227 override uint[] eras()
228 {
229 uint[] tmp = [AD_ERA, BC_ERA];
230 return tmp.dup;
231 }
232
233 /**
234 * $(I Property.) Overridden. Retrieves the identifier associated with the current calendar.
235 * Returns: An integer representing the identifier of the current calendar.
236 */
237 override uint id()
238 {
239 return cast(int) type_;
240 }
241
242 override void split(Time time, ref uint year, ref uint month, ref uint day, ref uint doy, ref uint dow, ref uint era)
243 {
244 splitDate(time.ticks, year, month, day, doy, era);
245 dow = getDayOfWeek(time);
246 }
247
248 package static void splitDate (long ticks, ref uint year, ref uint month, ref uint day, ref uint dayOfYear, ref uint era)
249 {
250 int numDays;
251
252 void calculateYear()
253 {
254 auto whole400Years = numDays / cast(int) TimeSpan.DaysPer400Years;
255 numDays -= whole400Years * cast(int) TimeSpan.DaysPer400Years;
256 auto whole100Years = numDays / cast(int) TimeSpan.DaysPer100Years;
257 if (whole100Years == 4)
258 whole100Years = 3;
259
260 numDays -= whole100Years * cast(int) TimeSpan.DaysPer100Years;
261 auto whole4Years = numDays / cast(int) TimeSpan.DaysPer4Years;
262 numDays -= whole4Years * cast(int) TimeSpan.DaysPer4Years;
263 auto wholeYears = numDays / cast(int) TimeSpan.DaysPerYear;
264 if (wholeYears == 4)
265 wholeYears = 3;
266
267 year = whole400Years * 400 + whole100Years * 100 + whole4Years * 4 + wholeYears + era;
268 numDays -= wholeYears * TimeSpan.DaysPerYear;
269 }
270
271 if(ticks < 0)
272 {
273 // in the BC era
274 era = BC_ERA;
275 //
276 // set up numDays to be like AD. AD days start at
277 // year 1. However, in BC, year 1 is like AD year 0,
278 // so we must subtract one year.
279 //
280 numDays = cast(int)((-ticks - 1) / TimeSpan.TicksPerDay);
281 if(numDays < 366)
282 {
283 // in the year 1 B.C. This is a special case
284 // leap year
285 year = 1;
286 }
287 else
288 {
289 numDays -= 366;
290 calculateYear;
291 }
292 //
293 // numDays is the number of days back from the end of
294 // the year, because the original ticks were negative
295 //
296 numDays = (staticIsLeapYear(year, era) ? 366 : 365) - numDays - 1;
297 }
298 else
299 {
300 era = AD_ERA;
301 numDays = cast(int)(ticks / TimeSpan.TicksPerDay);
302 calculateYear;
303 }
304 dayOfYear = numDays + 1;
305
306 auto monthDays = staticIsLeapYear(year, era) ? DaysToMonthLeap : DaysToMonthCommon;
307 month = numDays >> 5 + 1;
308 while (numDays >= monthDays[month])
309 month++;
310
311 day = numDays - monthDays[month - 1] + 1;
312 }
313
314 package static uint extractPart (long ticks, DatePart part)
315 {
316 uint year, month, day, dayOfYear, era;
317
318 splitDate(ticks, year, month, day, dayOfYear, era);
319
320 if (part is DatePart.Year)
321 return year;
322
323 if (part is DatePart.Month)
324 return month;
325
326 if (part is DatePart.DayOfYear)
327 return dayOfYear;
328
329 return day;
330 }
331
332 package static long getDateTicks (uint year, uint month, uint day, uint era)
333 {
334 auto monthDays = staticIsLeapYear(year, era) ? DaysToMonthLeap : DaysToMonthCommon;
335 if(era == BC_ERA)
336 {
337 year += 2;
338 return -cast(long)( (year - 3) * 365 + year / 4 - year / 100 + year / 400 + monthDays[12] - (monthDays[month - 1] + day - 1)) * TimeSpan.TicksPerDay;
339 }
340 else
341 {
342 year--;
343 return (year * 365 + year / 4 - year / 100 + year / 400 + monthDays[month - 1] + day - 1) * TimeSpan.TicksPerDay;
344 }
345 }
346
347 package static bool staticIsLeapYear(uint year, uint era)
348 {
349 if(era == BC_ERA)
350 return staticIsLeapYear(year - 1, AD_ERA);
351 if(era == AD_ERA)
352 return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
353 return false;
354 }
355 }
356
357 debug(Gregorian)
358 {
359 import tango.io.Stdout;
360
361 void output(Time t)
362 {
363 Date d = Gregorian.generic.toDate(t);
364 TimeOfDay tod = t.time;
365 Stdout.format("{}/{}/{:d4} {} {}:{:d2}:{:d2}.{:d3} dow:{}",
366 d.month, d.day, d.year, d.era == Gregorian.AD_ERA ? "AD" : "BC",
367 tod.hours, tod.minutes, tod.seconds, tod.millis, d.dow).newline;
368 }
369
370 void main()
371 {
372 Time t = Time(365 * TimeSpan.TicksPerDay);
373 output(t);
374 for(int i = 0; i < 366 + 365; i++)
375 {
376 t -= TimeSpan.days(1);
377 output(t);
378 }
379 }
380 }
381
382 debug(UnitTest)
383 {
384 unittest
385 {
386 //
387 // check Gregorian date handles positive time.
388 //
389 Time t = Time.epoch + TimeSpan.days(365);
390 Date d = Gregorian.generic.toDate(t);
391 assert(d.year == 2);
392 assert(d.month == 1);
393 assert(d.day == 1);
394 assert(d.era == Gregorian.AD_ERA);
395 assert(d.doy == 1);
396 //
397 // note that this is in disagreement with the Julian Calendar
398 //
399 assert(d.dow == Gregorian.DayOfWeek.Tuesday);
400
401 //
402 // check that it handles negative time
403 //
404 t = Time.epoch - TimeSpan.days(366);
405 d = Gregorian.generic.toDate(t);
406 assert(d.year == 1);
407 assert(d.month == 1);
408 assert(d.day == 1);
409 assert(d.era == Gregorian.BC_ERA);
410 assert(d.doy == 1);
411 assert(d.dow == Gregorian.DayOfWeek.Saturday);
412
413 }
414 }