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