Mercurial > projects > ldc
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 } |