 |
Changeset 3059
- Timestamp:
- 12/20/07 11:10:58
(1 year ago)
- Author:
- schveiguy
- Message:
Added BC date capability to Gregorian Calendar.
Added some unit tests to Gregorian Calendar.
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r3040 |
r3059 |
|
| 135 | 135 | Time toTime (Date d) |
|---|
| 136 | 136 | { |
|---|
| 137 | | return toTime (d.year, d.month, d.day, 0, 0, 0, 0, CURRENT_ERA); |
|---|
| | 137 | return toTime (d.year, d.month, d.day, 0, 0, 0, 0, d.era); |
|---|
| 138 | 138 | } |
|---|
| 139 | 139 | |
|---|
| … | … | |
| 145 | 145 | Time toTime (Date d, TimeOfDay t) |
|---|
| 146 | 146 | { |
|---|
| 147 | | return toTime (d.year, d.month, d.day, t.hours, t.minutes, t.seconds, t.millis, CURRENT_ERA); |
|---|
| | 147 | return toTime (d.year, d.month, d.day, t.hours, t.minutes, t.seconds, t.millis, d.era); |
|---|
| 148 | 148 | } |
|---|
| 149 | 149 | |
|---|
| r3040 |
r3059 |
|
| 8 | 8 | Apr 2007: reshaped |
|---|
| 9 | 9 | |
|---|
| 10 | | author: John Chapman, Kris |
|---|
| | 10 | author: John Chapman, Kris, schveiguy |
|---|
| 11 | 11 | |
|---|
| 12 | 12 | ******************************************************************************/ |
|---|
| … | … | |
| 15 | 15 | |
|---|
| 16 | 16 | private import tango.time.chrono.Calendar; |
|---|
| 17 | | |
|---|
| 18 | 17 | |
|---|
| 19 | 18 | /** |
|---|
| 20 | 19 | * $(ANCHOR _Gregorian) |
|---|
| 21 | 20 | * Represents the Gregorian calendar. |
|---|
| 22 | | */ |
|---|
| | 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 | */ |
|---|
| 23 | 29 | class Gregorian : Calendar |
|---|
| 24 | 30 | { |
|---|
| … | … | |
| 44 | 50 | * Represents the current era. |
|---|
| 45 | 51 | */ |
|---|
| 46 | | enum {AD_ERA = 1, MAX_YEAR = 9999}; |
|---|
| | 52 | enum {AD_ERA = 1, BC_ERA = 2, MAX_YEAR = 9999}; |
|---|
| 47 | 53 | |
|---|
| 48 | 54 | private static final uint[] DaysToMonthCommon = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; |
|---|
| … | … | |
| 82 | 88 | override Time toTime (uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond, uint era) |
|---|
| 83 | 89 | { |
|---|
| 84 | | return Time (getDateTicks(year, month, day) + getTimeTicks(hour, minute, second)) + TimeSpan.millis(millisecond); |
|---|
| | 90 | return Time (getDateTicks(year, month, day, era) + getTimeTicks(hour, minute, second)) + TimeSpan.millis(millisecond); |
|---|
| 85 | 91 | } |
|---|
| 86 | 92 | |
|---|
| … | … | |
| 92 | 98 | override DayOfWeek getDayOfWeek(Time time) |
|---|
| 93 | 99 | { |
|---|
| 94 | | return cast(DayOfWeek)((time.ticks / TimeSpan.TicksPerDay + 1) % 7); |
|---|
| | 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; |
|---|
| 95 | 110 | } |
|---|
| 96 | 111 | |
|---|
| … | … | |
| 138 | 153 | * Overridden. Returns the era in the specified Time. |
|---|
| 139 | 154 | * Params: time = A Time value. |
|---|
| 140 | | * Returns: An integer representing the ear in time. |
|---|
| | 155 | * Returns: An integer representing the era in time. |
|---|
| 141 | 156 | */ |
|---|
| 142 | 157 | override uint getEra(Time time) |
|---|
| 143 | 158 | { |
|---|
| 144 | | return AD_ERA; |
|---|
| | 159 | if(time < time.epoch) |
|---|
| | 160 | return BC_ERA; |
|---|
| | 161 | else |
|---|
| | 162 | return AD_ERA; |
|---|
| 145 | 163 | } |
|---|
| 146 | 164 | |
|---|
| … | … | |
| 155 | 173 | override uint getDaysInMonth(uint year, uint month, uint era) |
|---|
| 156 | 174 | { |
|---|
| 157 | | auto monthDays = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? DaysToMonthLeap : DaysToMonthCommon; |
|---|
| | 175 | auto monthDays = isLeapYear(year, era) ? DaysToMonthLeap : DaysToMonthCommon; |
|---|
| 158 | 176 | return monthDays[month] - monthDays[month - 1]; |
|---|
| 159 | 177 | } |
|---|
| … | … | |
| 191 | 209 | override bool isLeapYear(uint year, uint era) |
|---|
| 192 | 210 | { |
|---|
| 193 | | return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); |
|---|
| | 211 | return staticIsLeapYear(year, era); |
|---|
| 194 | 212 | } |
|---|
| 195 | 213 | |
|---|
| … | … | |
| 209 | 227 | override uint[] eras() |
|---|
| 210 | 228 | { |
|---|
| 211 | | uint[] tmp = [AD_ERA]; |
|---|
| | 229 | uint[] tmp = [AD_ERA, BC_ERA]; |
|---|
| 212 | 230 | return tmp.dup; |
|---|
| 213 | 231 | } |
|---|
| … | … | |
| 224 | 242 | override void split(Time time, ref uint year, ref uint month, ref uint day, ref uint doy, ref uint dow, ref uint era) |
|---|
| 225 | 243 | { |
|---|
| 226 | | splitDate(time.ticks, year, month, day, doy); |
|---|
| 227 | | era = AD_ERA; |
|---|
| | 244 | splitDate(time.ticks, year, month, day, doy, era); |
|---|
| 228 | 245 | dow = getDayOfWeek(time); |
|---|
| 229 | 246 | } |
|---|
| 230 | 247 | |
|---|
| 231 | | package static void splitDate (long ticks, ref uint year, ref uint month, ref uint day, ref uint dayOfYear) |
|---|
| 232 | | { |
|---|
| 233 | | auto numDays = cast(int)(ticks / TimeSpan.TicksPerDay); |
|---|
| 234 | | auto whole400Years = numDays / cast(int) TimeSpan.DaysPer400Years; |
|---|
| 235 | | numDays -= whole400Years * cast(int) TimeSpan.DaysPer400Years; |
|---|
| 236 | | auto whole100Years = numDays / cast(int) TimeSpan.DaysPer100Years; |
|---|
| 237 | | if (whole100Years == 4) |
|---|
| 238 | | whole100Years = 3; |
|---|
| 239 | | |
|---|
| 240 | | numDays -= whole100Years * cast(int) TimeSpan.DaysPer100Years; |
|---|
| 241 | | auto whole4Years = numDays / cast(int) TimeSpan.DaysPer4Years; |
|---|
| 242 | | numDays -= whole4Years * cast(int) TimeSpan.DaysPer4Years; |
|---|
| 243 | | auto wholeYears = numDays / cast(int) TimeSpan.DaysPerYear; |
|---|
| 244 | | if (wholeYears == 4) |
|---|
| 245 | | wholeYears = 3; |
|---|
| 246 | | |
|---|
| 247 | | year = whole400Years * 400 + whole100Years * 100 + whole4Years * 4 + wholeYears + 1; |
|---|
| 248 | | numDays -= wholeYears * TimeSpan.DaysPerYear; |
|---|
| | 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 | } |
|---|
| 249 | 304 | dayOfYear = numDays + 1; |
|---|
| 250 | 305 | |
|---|
| 251 | | auto monthDays = (wholeYears == 3 && (whole4Years != 24 || whole100Years == 3)) ? DaysToMonthLeap : DaysToMonthCommon; |
|---|
| | 306 | auto monthDays = staticIsLeapYear(year, era) ? DaysToMonthLeap : DaysToMonthCommon; |
|---|
| 252 | 307 | month = numDays >> 5 + 1; |
|---|
| 253 | 308 | while (numDays >= monthDays[month]) |
|---|
| … | … | |
| 259 | 314 | package static uint extractPart (long ticks, DatePart part) |
|---|
| 260 | 315 | { |
|---|
| 261 | | uint year, month, day, dayOfYear; |
|---|
| 262 | | |
|---|
| 263 | | splitDate(ticks, year, month, day, dayOfYear); |
|---|
| | 316 | uint year, month, day, dayOfYear, era; |
|---|
| | 317 | |
|---|
| | 318 | splitDate(ticks, year, month, day, dayOfYear, era); |
|---|
| 264 | 319 | |
|---|
| 265 | 320 | if (part is DatePart.Year) |
|---|
| … | … | |
| 275 | 330 | } |
|---|
| 276 | 331 | |
|---|
| 277 | | package long getDateTicks (uint year, uint month, uint day) |
|---|
| 278 | | { |
|---|
| 279 | | auto monthDays = isLeapYear(year, AD_ERA) ? DaysToMonthLeap : DaysToMonthCommon; |
|---|
| 280 | | year--; |
|---|
| 281 | | return (year * 365 + year / 4 - year / 100 + year / 400 + monthDays[month - 1] + day - 1) * TimeSpan.TicksPerDay; |
|---|
| | 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; |
|---|
| 282 | 354 | } |
|---|
| 283 | 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 | debug(Gregorian) |
|---|
| | 385 | { |
|---|
| | 386 | } |
|---|
| | 387 | else |
|---|
| | 388 | { |
|---|
| | 389 | void main() {} |
|---|
| | 390 | } |
|---|
| | 391 | |
|---|
| | 392 | unittest |
|---|
| | 393 | { |
|---|
| | 394 | // |
|---|
| | 395 | // check Gregorian date handles positive time. |
|---|
| | 396 | // |
|---|
| | 397 | Time t = Time.epoch + TimeSpan.days(365); |
|---|
| | 398 | Date d = Gregorian.generic.toDate(t); |
|---|
| | 399 | assert(d.year == 2); |
|---|
| | 400 | assert(d.month == 1); |
|---|
| | 401 | assert(d.day == 1); |
|---|
| | 402 | assert(d.era == Gregorian.AD_ERA); |
|---|
| | 403 | assert(d.doy == 1); |
|---|
| | 404 | // |
|---|
| | 405 | // note that this is in disagreement with the Julian Calendar |
|---|
| | 406 | // |
|---|
| | 407 | assert(d.dow == Gregorian.DayOfWeek.Tuesday); |
|---|
| | 408 | |
|---|
| | 409 | // |
|---|
| | 410 | // check that it handles negative time |
|---|
| | 411 | // |
|---|
| | 412 | t = Time.epoch - TimeSpan.days(366); |
|---|
| | 413 | d = Gregorian.generic.toDate(t); |
|---|
| | 414 | assert(d.year == 1); |
|---|
| | 415 | assert(d.month == 1); |
|---|
| | 416 | assert(d.day == 1); |
|---|
| | 417 | assert(d.era == Gregorian.BC_ERA); |
|---|
| | 418 | assert(d.doy == 1); |
|---|
| | 419 | assert(d.dow == Gregorian.DayOfWeek.Saturday); |
|---|
| | 420 | |
|---|
| | 421 | } |
|---|
| | 422 | } |
|---|
| r3040 |
r3059 |
|
| 103 | 103 | long getTicks(uint year, uint month, uint day) |
|---|
| 104 | 104 | { |
|---|
| 105 | | return Gregorian.generic.getDateTicks(year, month, day); |
|---|
| | 105 | return Gregorian.generic.getDateTicks(year, month, day, Gregorian.AD_ERA); |
|---|
| 106 | 106 | } |
|---|
| 107 | 107 | eraRanges[Gregorian.JAPAN] ~= EraRange(4, getTicks(1989, 1, 8), 1988, 1, Gregorian.MAX_YEAR); |
|---|
Download in other formats:
|
 |