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