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 11 12 ******************************************************************************/ 13 14 module tango.time.chrono.Calendar; 15 16 public import tango.time.Time; 17 18 private import tango.core.Exception; 19 20 21 22 /** 23 * $(ANCHOR _Calendar) 24 * Represents time in week, month and year divisions. 25 * Remarks: Calendar is the abstract base class for the following Calendar implementations: 26 * $(LINK2 #Gregorian, Gregorian), $(LINK2 #Hebrew, Hebrew), $(LINK2 #Hijri, Hijri), 27 * $(LINK2 #Japanese, Japanese), $(LINK2 #Korean, Korean), $(LINK2 #Taiwan, Taiwan) and 28 * $(LINK2 #ThaiBuddhist, ThaiBuddhist). 29 */ 30 public abstract class Calendar 31 { 32 //nasty mixin hacks. Clear them and stick with package(tango) when 2.066.1 support ends 33 private template PackageTest() 34 { 35 mixin(`package(tango) void foo123(){}`); 36 } 37 static if (__traits(compiles, PackageTest!())) 38 enum packageDecl = q{package(tango)}; 39 else 40 enum packageDecl = q{package}; 41 mixin(packageDecl ~ q{{ 42 /** 43 * Indicates the current era of the calendar. 44 */ 45 enum {CURRENT_ERA = 0}; 46 47 // Corresponds to Win32 calendar IDs 48 enum 49 { 50 GREGORIAN = 1, 51 GREGORIAN_US = 2, 52 JAPAN = 3, 53 TAIWAN = 4, 54 KOREA = 5, 55 HIJRI = 6, 56 THAI = 7, 57 HEBREW = 8, 58 GREGORIAN_ME_FRENCH = 9, 59 GREGORIAN_ARABIC = 10, 60 GREGORIAN_XLIT_ENGLISH = 11, 61 GREGORIAN_XLIT_FRENCH = 12 62 } 63 64 enum WeekRule 65 { 66 FirstDay, /// Indicates that the first week of the year is the first week containing the first day of the year. 67 FirstFullWeek, /// Indicates that the first week of the year is the first full week following the first day of the year. 68 FirstFourDayWeek /// Indicates that the first week of the year is the first week containing at least four days. 69 } 70 71 enum DatePart 72 { 73 Year, 74 Month, 75 Day, 76 DayOfYear 77 } 78 }}); 79 80 public enum DayOfWeek 81 { 82 Sunday, /// Indicates _Sunday. 83 Monday, /// Indicates _Monday. 84 Tuesday, /// Indicates _Tuesday. 85 Wednesday, /// Indicates _Wednesday. 86 Thursday, /// Indicates _Thursday. 87 Friday, /// Indicates _Friday. 88 Saturday /// Indicates _Saturday. 89 } 90 91 92 /** 93 * Get the components of a Time structure using the rules of the 94 * calendar. This is useful if you want more than one of the given 95 * components. Note that this doesn't handle the time of day, as that 96 * is calculated directly from the Time struct. 97 * 98 * The default implemenation is to call all the other accessors 99 * directly, a derived class may override if it has a more efficient 100 * method. 101 */ 102 const Date toDate (const(Time) time) 103 { 104 Date d; 105 split (time, d.year, d.month, d.day, d.doy, d.dow, d.era); 106 return d; 107 } 108 109 /** 110 * Get the components of a Time structure using the rules of the 111 * calendar. This is useful if you want more than one of the given 112 * components. Note that this doesn't handle the time of day, as that 113 * is calculated directly from the Time struct. 114 * 115 * The default implemenation is to call all the other accessors 116 * directly, a derived class may override if it has a more efficient 117 * method. 118 */ 119 const void split (const(Time) time, ref uint year, ref uint month, ref uint day, ref uint doy, ref uint dow, ref uint era) 120 { 121 year = getYear(time); 122 month = getMonth(time); 123 day = getDayOfMonth(time); 124 doy = getDayOfYear(time); 125 dow = getDayOfWeek(time); 126 era = getEra(time); 127 } 128 129 /** 130 * Returns a Time value set to the specified date and time in the current era. 131 * Params: 132 * year = An integer representing the _year. 133 * month = An integer representing the _month. 134 * day = An integer representing the _day. 135 * hour = An integer representing the _hour. 136 * minute = An integer representing the _minute. 137 * second = An integer representing the _second. 138 * millisecond = An integer representing the _millisecond. 139 * Returns: The Time set to the specified date and time. 140 */ 141 const Time toTime (uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond=0) 142 { 143 return toTime (year, month, day, hour, minute, second, millisecond, CURRENT_ERA); 144 } 145 146 /** 147 * Returns a Time value for the given Date, in the current era 148 * Params: 149 * date = a representation of the Date 150 * Returns: The Time set to the specified date. 151 */ 152 const Time toTime (const(Date) d) 153 { 154 return toTime (d.year, d.month, d.day, 0, 0, 0, 0, d.era); 155 } 156 157 /** 158 * Returns a Time value for the given DateTime, in the current era 159 * Params: 160 * dt = a representation of the date and time 161 * Returns: The Time set to the specified date and time. 162 */ 163 const Time toTime (const(DateTime) dt) 164 { 165 return toTime (dt.date, dt.time); 166 } 167 168 /** 169 * Returns a Time value for the given Date and TimeOfDay, in the current era 170 * Params: 171 * d = a representation of the date 172 * t = a representation of the day time 173 * Returns: The Time set to the specified date and time. 174 */ 175 const Time toTime (const(Date) d, const(TimeOfDay) t) 176 { 177 return toTime (d.year, d.month, d.day, t.hours, t.minutes, t.seconds, t.millis, d.era); 178 } 179 180 /** 181 * When overridden, returns a Time value set to the specified date and time in the specified _era. 182 * Params: 183 * year = An integer representing the _year. 184 * month = An integer representing the _month. 185 * day = An integer representing the _day. 186 * hour = An integer representing the _hour. 187 * minute = An integer representing the _minute. 188 * second = An integer representing the _second. 189 * millisecond = An integer representing the _millisecond. 190 * era = An integer representing the _era. 191 * Returns: A Time set to the specified date and time. 192 */ 193 abstract const Time toTime (uint year, uint month, uint day, uint hour, uint minute, uint second, uint millisecond, uint era); 194 195 /** 196 * When overridden, returns the day of the week in the specified Time. 197 * Params: time = A Time value. 198 * Returns: A DayOfWeek value representing the day of the week of time. 199 */ 200 abstract const DayOfWeek getDayOfWeek (const(Time) time); 201 202 /** 203 * When overridden, returns the day of the month in the specified Time. 204 * Params: time = A Time value. 205 * Returns: An integer representing the day of the month of time. 206 */ 207 abstract const uint getDayOfMonth (const(Time) time); 208 209 /** 210 * When overridden, returns the day of the year in the specified Time. 211 * Params: time = A Time value. 212 * Returns: An integer representing the day of the year of time. 213 */ 214 abstract const uint getDayOfYear (const(Time) time); 215 216 /** 217 * When overridden, returns the month in the specified Time. 218 * Params: time = A Time value. 219 * Returns: An integer representing the month in time. 220 */ 221 abstract const uint getMonth (const(Time) time); 222 223 /** 224 * When overridden, returns the year in the specified Time. 225 * Params: time = A Time value. 226 * Returns: An integer representing the year in time. 227 */ 228 abstract const uint getYear (const(Time) time); 229 230 /** 231 * When overridden, returns the era in the specified Time. 232 * Params: time = A Time value. 233 * Returns: An integer representing the ear in time. 234 */ 235 abstract const uint getEra (const(Time) time); 236 237 /** 238 * Returns the number of days in the specified _year and _month of the current era. 239 * Params: 240 * year = An integer representing the _year. 241 * month = An integer representing the _month. 242 * Returns: The number of days in the specified _year and _month of the current era. 243 */ 244 const uint getDaysInMonth (uint year, uint month) 245 { 246 return getDaysInMonth (year, month, CURRENT_ERA); 247 } 248 249 /** 250 * When overridden, returns the number of days in the specified _year and _month of the specified _era. 251 * Params: 252 * year = An integer representing the _year. 253 * month = An integer representing the _month. 254 * era = An integer representing the _era. 255 * Returns: The number of days in the specified _year and _month of the specified _era. 256 */ 257 abstract const uint getDaysInMonth (uint year, uint month, uint era); 258 259 /** 260 * Returns the number of days in the specified _year of the current era. 261 * Params: year = An integer representing the _year. 262 * Returns: The number of days in the specified _year in the current era. 263 */ 264 const uint getDaysInYear (uint year) 265 { 266 return getDaysInYear (year, CURRENT_ERA); 267 } 268 269 /** 270 * When overridden, returns the number of days in the specified _year of the specified _era. 271 * Params: 272 * year = An integer representing the _year. 273 * era = An integer representing the _era. 274 * Returns: The number of days in the specified _year in the specified _era. 275 */ 276 abstract const uint getDaysInYear (uint year, uint era); 277 278 /** 279 * Returns the number of months in the specified _year of the current era. 280 * Params: year = An integer representing the _year. 281 * Returns: The number of months in the specified _year in the current era. 282 */ 283 const uint getMonthsInYear (uint year) 284 { 285 return getMonthsInYear (year, CURRENT_ERA); 286 } 287 288 /** 289 * When overridden, returns the number of months in the specified _year of the specified _era. 290 * Params: 291 * year = An integer representing the _year. 292 * era = An integer representing the _era. 293 * Returns: The number of months in the specified _year in the specified _era. 294 */ 295 abstract const uint getMonthsInYear (uint year, uint era); 296 297 /** 298 * Returns the week of the year that includes the specified Time. 299 * Params: 300 * time = A Time value. 301 * rule = A WeekRule value defining a calendar week. 302 * firstDayOfWeek = A DayOfWeek value representing the first day of the week. 303 * Returns: An integer representing the week of the year that includes the date in time. 304 */ 305 const uint getWeekOfYear (const(Time) time, WeekRule rule, DayOfWeek firstDayOfWeek) 306 { 307 auto year = getYear (time); 308 auto jan1 = cast(int) getDayOfWeek (toTime (year, 1, 1, 0, 0, 0, 0)); 309 310 switch (rule) 311 { 312 case WeekRule.FirstDay: 313 int n = jan1 - cast(int) firstDayOfWeek; 314 if (n < 0) 315 n += 7; 316 return (getDayOfYear (time) + n - 1) / 7 + 1; 317 318 case WeekRule.FirstFullWeek: 319 case WeekRule.FirstFourDayWeek: 320 int fullDays = (rule is WeekRule.FirstFullWeek) ? 7 : 4; 321 int n = cast(int) firstDayOfWeek - jan1; 322 if (n != 0) 323 { 324 if (n < 0) 325 n += 7; 326 else 327 if (n >= fullDays) 328 n -= 7; 329 } 330 331 int day = getDayOfYear (time) - n; 332 if (day > 0) 333 return (day - 1) / 7 + 1; 334 year = getYear(time) - 1; 335 int month = getMonthsInYear (year); 336 day = getDaysInMonth (year, month); 337 return getWeekOfYear(toTime(year, month, day, 0, 0, 0, 0), rule, firstDayOfWeek); 338 339 default: 340 break; 341 } 342 throw new IllegalArgumentException("Value was out of range."); 343 } 344 345 /** 346 * Indicates whether the specified _year in the current era is a leap _year. 347 * Params: year = An integer representing the _year. 348 * Returns: true is the specified _year is a leap _year; otherwise, false. 349 */ 350 const bool isLeapYear(uint year) 351 { 352 return isLeapYear(year, CURRENT_ERA); 353 } 354 355 /** 356 * When overridden, indicates whether the specified _year in the specified _era is a leap _year. 357 * Params: year = An integer representing the _year. 358 * Params: era = An integer representing the _era. 359 * Returns: true is the specified _year is a leap _year; otherwise, false. 360 */ 361 abstract const bool isLeapYear(uint year, uint era); 362 363 /** 364 * $(I Property.) When overridden, retrieves the list of eras in the current calendar. 365 * Returns: An integer array representing the eras in the current calendar. 366 */ 367 @property abstract const uint[] eras(); 368 369 /** 370 * $(I Property.) Retrieves the identifier associated with the current calendar. 371 * Returns: An integer representing the identifier of the current calendar. 372 */ 373 @property const uint id() 374 { 375 return -1; 376 } 377 378 /** 379 * Returns a new Time with the specified number of months added. If 380 * the months are negative, the months are subtracted. 381 * 382 * If the target month does not support the day component of the input 383 * time, then an error will be thrown, unless truncateDay is set to 384 * true. If truncateDay is set to true, then the day is reduced to 385 * the maximum day of that month. 386 * 387 * For example, adding one month to 1/31/2000 with truncateDay set to 388 * true results in 2/28/2000. 389 * 390 * The default implementation uses information provided by the 391 * calendar to calculate the correct time to add. Derived classes may 392 * override if there is a more optimized method. 393 * 394 * Note that the generic method does not take into account crossing 395 * era boundaries. Derived classes may support this. 396 * 397 * Params: t = A time to add the months to 398 * Params: nMonths = The number of months to add. This can be 399 * negative. 400 * Params: truncateDay = Round the day down to the maximum day of the 401 * target month if necessary. 402 * 403 * Returns: A Time that represents the provided time with the number 404 * of months added. 405 */ 406 const Time addMonths(const(Time) t, int nMonths, bool truncateDay = false) 407 { 408 uint era = getEra(t); 409 uint year = getYear(t); 410 uint month = getMonth(t); 411 412 // 413 // Assume we go back to day 1 of the current year, taking 414 // into account that offset using the nMonths and nDays 415 // offsets. 416 // 417 nMonths += month - 1; 418 int origDom = cast(int)getDayOfMonth(t); 419 long nDays = origDom - cast(int)getDayOfYear(t); 420 if(nMonths > 0) 421 { 422 // 423 // Adding, add all the years until the year we want to 424 // be in. 425 // 426 auto miy = getMonthsInYear(year, era); 427 while(nMonths >= miy) 428 { 429 // 430 // skip a whole year 431 // 432 nDays += getDaysInYear(year, era); 433 nMonths -= miy; 434 year++; 435 436 // 437 // update miy 438 // 439 miy = getMonthsInYear(year, era); 440 } 441 } 442 else if(nMonths < 0) 443 { 444 // 445 // subtracting months 446 // 447 while(nMonths < 0) 448 { 449 auto miy = getMonthsInYear(--year, era); 450 nDays -= getDaysInYear(year, era); 451 nMonths += miy; 452 } 453 } 454 455 // 456 // we now are offset to the resulting year. 457 // Add the rest of the months to get to the day we want. 458 // 459 int newDom = cast(int)getDaysInMonth(year, nMonths + 1, era); 460 if(origDom > newDom) 461 { 462 // 463 // error, the resulting day of month is out of range. See 464 // if we should truncate 465 // 466 if(truncateDay) 467 nDays -= newDom - origDom; 468 else 469 throw new IllegalArgumentException("days out of range"); 470 471 } 472 for(int m = 0; m < nMonths; m++) 473 nDays += getDaysInMonth(year, m + 1, era); 474 return t + TimeSpan.fromDays(nDays); 475 } 476 477 /** 478 * Add the specified number of years to the given Time. 479 * 480 * The generic algorithm uses information provided by the abstract 481 * methods. Derived classes may re-implement this in order to 482 * optimize the algorithm 483 * 484 * Note that the generic algorithm does not take into account crossing 485 * era boundaries. Derived classes may support this. 486 * 487 * Params: t = A time to add the years to 488 * Params: nYears = The number of years to add. This can be negative. 489 * 490 * Returns: A Time that represents the provided time with the number 491 * of years added. 492 */ 493 const Time addYears(const(Time) t, int nYears) 494 { 495 auto date = toDate(t); 496 auto tod = t.ticks % TimeSpan.TicksPerDay; 497 if(tod < 0) 498 tod += TimeSpan.TicksPerDay; 499 date.year += nYears; 500 return toTime(date) + TimeSpan(tod); 501 } 502 503 package static long getTimeTicks (uint hour, uint minute, uint second) 504 { 505 return (TimeSpan.fromHours(hour) + TimeSpan.fromMinutes(minute) + TimeSpan.fromSeconds(second)).ticks; 506 } 507 }