| 1 |
// Written in the D programming language. |
|---|
| 2 |
|
|---|
| 3 |
/** |
|---|
| 4 |
Macros: |
|---|
| 5 |
WIKI = Phobos/StdGregorian |
|---|
| 6 |
|
|---|
| 7 |
Copyright: Copyright Andrei Alexandrescu 2008 - 2009. |
|---|
| 8 |
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. |
|---|
| 9 |
Authors: $(WEB erdani.org, Andrei Alexandrescu) |
|---|
| 10 |
|
|---|
| 11 |
Copyright Andrei Alexandrescu 2010-. |
|---|
| 12 |
Distributed under the Boost Software License, Version 1.0. |
|---|
| 13 |
(See accompanying file LICENSE_1_0.txt or copy at |
|---|
| 14 |
http://www.boost.org/LICENSE_1_0.txt) |
|---|
| 15 |
*/ |
|---|
| 16 |
module std.gregorian; |
|---|
| 17 |
|
|---|
| 18 |
import std.typecons; |
|---|
| 19 |
import core.sys.posix.time; |
|---|
| 20 |
import std.conv; |
|---|
| 21 |
version(unittest) import std.stdio; |
|---|
| 22 |
|
|---|
| 23 |
unittest |
|---|
| 24 |
{ |
|---|
| 25 |
auto d = Date(2010, May, 1); |
|---|
| 26 |
auto d1 = d; |
|---|
| 27 |
assert(d.year == 2010); |
|---|
| 28 |
assert(d.month == May); |
|---|
| 29 |
assert(d.day == 1); |
|---|
| 30 |
assert(d.dayOfWeek == 6); |
|---|
| 31 |
assert(d.dayOfYear == 121); |
|---|
| 32 |
assert(Date(2010, Jan, 5).dayOfYear == 5); |
|---|
| 33 |
} |
|---|
| 34 |
|
|---|
| 35 |
unittest |
|---|
| 36 |
{ |
|---|
| 37 |
auto d1 = Date(negInfin); |
|---|
| 38 |
auto d2 = Date(posInfin); |
|---|
| 39 |
auto d3 = Date(notADateTime); |
|---|
| 40 |
auto d4 = Date(maxDateTime); |
|---|
| 41 |
auto d5 = Date(minDateTime); |
|---|
| 42 |
} |
|---|
| 43 |
|
|---|
| 44 |
unittest |
|---|
| 45 |
{ |
|---|
| 46 |
auto d1 = fromString("2002-1-25"); |
|---|
| 47 |
auto d2 = fromUndelimitedString("20020125"); |
|---|
| 48 |
} |
|---|
| 49 |
|
|---|
| 50 |
unittest |
|---|
| 51 |
{ |
|---|
| 52 |
auto d1 = dayClockLocalDay(); |
|---|
| 53 |
auto d2 = dayClockUniversalDay(); |
|---|
| 54 |
} |
|---|
| 55 |
|
|---|
| 56 |
alias ushort GregYear; |
|---|
| 57 |
alias ushort GregMonth; |
|---|
| 58 |
alias ushort GregDay; |
|---|
| 59 |
alias uint GregDayOfWeek; |
|---|
| 60 |
alias uint GregDayOfYear; |
|---|
| 61 |
|
|---|
| 62 |
enum { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec } |
|---|
| 63 |
|
|---|
| 64 |
// Special constants |
|---|
| 65 |
struct Special { ulong value; } |
|---|
| 66 |
static immutable |
|---|
| 67 |
notADateTime = Special(0), |
|---|
| 68 |
negInfin = Special(1), |
|---|
| 69 |
posInfin = Special(2), |
|---|
| 70 |
minDateTime = Special(3), |
|---|
| 71 |
maxDateTime = Special(4), |
|---|
| 72 |
notSpecial = Special(5); |
|---|
| 73 |
|
|---|
| 74 |
struct Date |
|---|
| 75 |
{ |
|---|
| 76 |
this(uint year, uint month, uint day) |
|---|
| 77 |
{ |
|---|
| 78 |
immutable |
|---|
| 79 |
a = cast(ushort)((14-month)/12), |
|---|
| 80 |
y = cast(ushort)(year + 4800 - a), |
|---|
| 81 |
m = cast(ushort)(month + 12*a - 3); |
|---|
| 82 |
days_ = day + ((153*m + 2)/5) + 365*y + (y/4) |
|---|
| 83 |
- (y/100) + (y/400) - 32045; |
|---|
| 84 |
} |
|---|
| 85 |
|
|---|
| 86 |
this(Special s) |
|---|
| 87 |
{ |
|---|
| 88 |
} |
|---|
| 89 |
|
|---|
| 90 |
// Accessors |
|---|
| 91 |
@property GregYear year() const |
|---|
| 92 |
{ |
|---|
| 93 |
immutable |
|---|
| 94 |
a = days_ + 32044, |
|---|
| 95 |
b = (4*a + 3)/146097, |
|---|
| 96 |
c = a-((146097*b)/4), |
|---|
| 97 |
d = (4*c + 3)/1461, |
|---|
| 98 |
e = c - (1461*d)/4, |
|---|
| 99 |
m = (5*e + 2)/153; |
|---|
| 100 |
//auto day = cast(ushort) (e - ((153*m + 2)/5) + 1); |
|---|
| 101 |
//auto month = cast(ushort) (m + 3 - 12 * (m/10)); |
|---|
| 102 |
immutable year = cast(ushort) (100*b + d - 4800 + (m/10)); |
|---|
| 103 |
|
|---|
| 104 |
return year; |
|---|
| 105 |
} |
|---|
| 106 |
|
|---|
| 107 |
@property GregMonth month() const |
|---|
| 108 |
{ |
|---|
| 109 |
immutable |
|---|
| 110 |
a = days_ + 32044, |
|---|
| 111 |
b = (4*a + 3)/146097, |
|---|
| 112 |
c = a-((146097*b)/4), |
|---|
| 113 |
d = (4*c + 3)/1461, |
|---|
| 114 |
e = c - (1461*d)/4, |
|---|
| 115 |
m = (5*e + 2)/153; |
|---|
| 116 |
//auto day = cast(ushort) (e - ((153*m + 2)/5) + 1); |
|---|
| 117 |
immutable month = cast(ushort) (m + 3 - 12 * (m/10)); |
|---|
| 118 |
//auto year = cast(ushort) (100*b + d - 4800 + (m/10)); |
|---|
| 119 |
|
|---|
| 120 |
return month; |
|---|
| 121 |
} |
|---|
| 122 |
|
|---|
| 123 |
@property GregDay day() const |
|---|
| 124 |
{ |
|---|
| 125 |
immutable |
|---|
| 126 |
a = days_ + 32044, |
|---|
| 127 |
b = (4*a + 3)/146097, |
|---|
| 128 |
c = a-((146097*b)/4), |
|---|
| 129 |
d = (4*c + 3)/1461, |
|---|
| 130 |
e = c - (1461*d)/4, |
|---|
| 131 |
m = (5*e + 2)/153, |
|---|
| 132 |
day = cast(ushort) (e - ((153*m + 2)/5) + 1); |
|---|
| 133 |
//auto month = cast(ushort) (m + 3 - 12 * (m/10)); |
|---|
| 134 |
//auto year = cast(ushort) (100*b + d - 4800 + (m/10)); |
|---|
| 135 |
|
|---|
| 136 |
return day; |
|---|
| 137 |
} |
|---|
| 138 |
|
|---|
| 139 |
@property Tuple!(GregYear, GregMonth, GregDay) |
|---|
| 140 |
yearMonthDay() const |
|---|
| 141 |
{ |
|---|
| 142 |
immutable |
|---|
| 143 |
a = days_ + 32044, |
|---|
| 144 |
b = (4*a + 3)/146097, |
|---|
| 145 |
c = a-((146097*b)/4), |
|---|
| 146 |
d = (4*c + 3)/1461, |
|---|
| 147 |
e = c - (1461*d)/4, |
|---|
| 148 |
m = (5*e + 2)/153; |
|---|
| 149 |
auto day = cast(ushort) (e - ((153*m + 2)/5) + 1); |
|---|
| 150 |
auto month = cast(ushort) (m + 3 - 12 * (m/10)); |
|---|
| 151 |
auto year = cast(ushort) (100*b + d - 4800 + (m/10)); |
|---|
| 152 |
|
|---|
| 153 |
return tuple(year, month, day); |
|---|
| 154 |
} |
|---|
| 155 |
|
|---|
| 156 |
@property GregDayOfWeek dayOfWeek() const |
|---|
| 157 |
{ |
|---|
| 158 |
immutable |
|---|
| 159 |
ymd = yearMonthDay, |
|---|
| 160 |
a = cast(ushort) ((14-ymd._1)/12), |
|---|
| 161 |
y = cast(ushort) (ymd._0 - a), |
|---|
| 162 |
m = cast(ushort) (ymd._1 + 12*a - 2), |
|---|
| 163 |
d = cast(ushort) ((ymd._2 + y + (y/4) - (y/100) + |
|---|
| 164 |
(y/400) + (31*m)/12) % 7); |
|---|
| 165 |
return d; |
|---|
| 166 |
} |
|---|
| 167 |
|
|---|
| 168 |
@property GregDayOfYear dayOfYear() const |
|---|
| 169 |
{ |
|---|
| 170 |
const start_of_year = Date(year(), 1, 1); |
|---|
| 171 |
auto doy = cast(ushort) ((this - start_of_year).days() + 1); |
|---|
| 172 |
return cast(GregDayOfYear) doy; |
|---|
| 173 |
} |
|---|
| 174 |
|
|---|
| 175 |
@property Date endOfMonth() const |
|---|
| 176 |
{ |
|---|
| 177 |
assert(0); |
|---|
| 178 |
} |
|---|
| 179 |
|
|---|
| 180 |
@property bool isInfinity() const |
|---|
| 181 |
{ |
|---|
| 182 |
return isNegInfinity || isPosInfinity; |
|---|
| 183 |
} |
|---|
| 184 |
|
|---|
| 185 |
@property bool isNegInfinity() const |
|---|
| 186 |
{ |
|---|
| 187 |
return days_ == negInfin.value; |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
@property bool isPosInfinity() const |
|---|
| 191 |
{ |
|---|
| 192 |
return days_ == posInfin.value; |
|---|
| 193 |
} |
|---|
| 194 |
|
|---|
| 195 |
@property bool isNotADate() const |
|---|
| 196 |
{ |
|---|
| 197 |
return days_ == notADateTime.value; |
|---|
| 198 |
} |
|---|
| 199 |
|
|---|
| 200 |
@property bool isSpecial() const |
|---|
| 201 |
{ |
|---|
| 202 |
return isNotADate || isInfinity; |
|---|
| 203 |
} |
|---|
| 204 |
|
|---|
| 205 |
@property Special asSpecial() const |
|---|
| 206 |
{ |
|---|
| 207 |
return days_ < notSpecial.value |
|---|
| 208 |
? Special(days_) |
|---|
| 209 |
: notSpecial; |
|---|
| 210 |
} |
|---|
| 211 |
|
|---|
| 212 |
@property long modJulianDay() const |
|---|
| 213 |
{ |
|---|
| 214 |
auto ymd = yearMonthDay(); |
|---|
| 215 |
return julianDay(ymd) - 2400001; //prerounded |
|---|
| 216 |
} |
|---|
| 217 |
|
|---|
| 218 |
static @property long julianDay(Tuple!(ushort, ushort, ushort) ymd) |
|---|
| 219 |
{ |
|---|
| 220 |
immutable |
|---|
| 221 |
a = cast(ushort) ((14-ymd._1)/12), |
|---|
| 222 |
y = cast(ushort) (ymd._0 + 4800 - a), |
|---|
| 223 |
m = cast(ushort) (ymd._1 + 12*a - 3), |
|---|
| 224 |
d = ymd._2 + ((153*m + 2)/5) + 365*y + (y/4) - (y/100) |
|---|
| 225 |
+ (y/400) - 32045; |
|---|
| 226 |
return d; |
|---|
| 227 |
} |
|---|
| 228 |
|
|---|
| 229 |
static bool isLeapYear(uint year) |
|---|
| 230 |
{ |
|---|
| 231 |
//divisible by 4, not if divisible by 100, but true if |
|---|
| 232 |
//divisible by 400 |
|---|
| 233 |
return (!(year % 4)) && ((year % 100) || (!(year % 400))); |
|---|
| 234 |
} |
|---|
| 235 |
|
|---|
| 236 |
@property int weekNumber() const |
|---|
| 237 |
{ |
|---|
| 238 |
auto |
|---|
| 239 |
ymd = yearMonthDay, |
|---|
| 240 |
julianbegin = julianDay(tuple(cast(ushort)ymd._0, |
|---|
| 241 |
cast(ushort)1, cast(ushort)1)), |
|---|
| 242 |
juliantoday = julianDay(ymd); |
|---|
| 243 |
long day = (julianbegin + 3) % 7; |
|---|
| 244 |
ulong week = (juliantoday + day - julianbegin + 4)/7; |
|---|
| 245 |
|
|---|
| 246 |
if ((week >= 1) && (week <= 52)) |
|---|
| 247 |
{ |
|---|
| 248 |
return cast(int) week; |
|---|
| 249 |
} |
|---|
| 250 |
|
|---|
| 251 |
if ((week == 53)) |
|---|
| 252 |
{ |
|---|
| 253 |
if((day==6) ||(day == 5 && isLeapYear(ymd._0))) |
|---|
| 254 |
{ |
|---|
| 255 |
return cast(int) week; //under these circumstances week == 53. |
|---|
| 256 |
} |
|---|
| 257 |
else |
|---|
| 258 |
{ |
|---|
| 259 |
return 1; //monday - wednesday is in week 1 of next year |
|---|
| 260 |
} |
|---|
| 261 |
} |
|---|
| 262 |
|
|---|
| 263 |
//if the week is not in current year recalculate using the |
|---|
| 264 |
//previous year as the beginning year |
|---|
| 265 |
else |
|---|
| 266 |
if (week == 0) |
|---|
| 267 |
{ |
|---|
| 268 |
julianbegin = julianDay( |
|---|
| 269 |
tuple(cast(ushort) (ymd._0 - 1), cast(ushort) 1, |
|---|
| 270 |
cast(ushort) 1)); |
|---|
| 271 |
juliantoday = julianDay(ymd); |
|---|
| 272 |
day = (julianbegin + 3) % 7; |
|---|
| 273 |
week = (juliantoday + day - julianbegin + 4)/7; |
|---|
| 274 |
return cast(int) week; |
|---|
| 275 |
} |
|---|
| 276 |
return cast(int) week; //not reachable -- well except if day == 5 and |
|---|
| 277 |
//is_leap_year != true |
|---|
| 278 |
} |
|---|
| 279 |
|
|---|
| 280 |
@property uint endOfMonthDay() const |
|---|
| 281 |
{ |
|---|
| 282 |
switch (month) { |
|---|
| 283 |
case 2: |
|---|
| 284 |
if (isLeapYear(year)) { |
|---|
| 285 |
return 29; |
|---|
| 286 |
} else { |
|---|
| 287 |
return 28; |
|---|
| 288 |
}; |
|---|
| 289 |
case 4: |
|---|
| 290 |
case 6: |
|---|
| 291 |
case 9: |
|---|
| 292 |
case 11: |
|---|
| 293 |
return 30; |
|---|
| 294 |
default: |
|---|
| 295 |
return 31; |
|---|
| 296 |
} |
|---|
| 297 |
} |
|---|
| 298 |
|
|---|
| 299 |
@property string toSimpleString() const |
|---|
| 300 |
{ |
|---|
| 301 |
auto ymd = yearMonthDay; |
|---|
| 302 |
return text(ymd._0, '-', ymd._1, '-', ymd._2); |
|---|
| 303 |
} |
|---|
| 304 |
|
|---|
| 305 |
@property string toIsoString() const |
|---|
| 306 |
{ |
|---|
| 307 |
assert(0); |
|---|
| 308 |
} |
|---|
| 309 |
|
|---|
| 310 |
@property string toIsoExtendedString() const |
|---|
| 311 |
{ |
|---|
| 312 |
assert(0); |
|---|
| 313 |
} |
|---|
| 314 |
|
|---|
| 315 |
bool opEquals(ref const Date rhs) const |
|---|
| 316 |
{ |
|---|
| 317 |
return days_ == rhs.days_; |
|---|
| 318 |
} |
|---|
| 319 |
|
|---|
| 320 |
int opCmp(in Date rhs) const |
|---|
| 321 |
{ |
|---|
| 322 |
return days_ < rhs.days_ ? -1 : days_ > rhs.days_ ? 1 : 0; |
|---|
| 323 |
} |
|---|
| 324 |
|
|---|
| 325 |
// Date opBinary(string op)(const Date d) const |
|---|
| 326 |
// if (op == "-") |
|---|
| 327 |
// { |
|---|
| 328 |
// } |
|---|
| 329 |
|
|---|
| 330 |
Days opBinary(string op)(const Date d) const |
|---|
| 331 |
if (op == "-") |
|---|
| 332 |
{ |
|---|
| 333 |
if (!isSpecial && !d.isSpecial) |
|---|
| 334 |
{ |
|---|
| 335 |
return Days(days_ - d.days_); |
|---|
| 336 |
} |
|---|
| 337 |
else |
|---|
| 338 |
{ |
|---|
| 339 |
assert(0); |
|---|
| 340 |
} |
|---|
| 341 |
} |
|---|
| 342 |
|
|---|
| 343 |
tm toTm() |
|---|
| 344 |
{ |
|---|
| 345 |
assert(0); |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
private ulong days_; |
|---|
| 349 |
} |
|---|
| 350 |
|
|---|
| 351 |
struct Days |
|---|
| 352 |
{ |
|---|
| 353 |
private long days_; |
|---|
| 354 |
this(long d) { days_ = d; } |
|---|
| 355 |
this(Special s) { } |
|---|
| 356 |
@property long days() const { return days_; } |
|---|
| 357 |
@property bool isNegative() const { return days_ < 0; } |
|---|
| 358 |
@property static Days unit() { return Days(1); } |
|---|
| 359 |
@property bool isSpecial() |
|---|
| 360 |
{ |
|---|
| 361 |
assert(0); |
|---|
| 362 |
} |
|---|
| 363 |
bool opEquals(ref const Days rhs) const |
|---|
| 364 |
{ |
|---|
| 365 |
return days_ == rhs.days_; |
|---|
| 366 |
} |
|---|
| 367 |
|
|---|
| 368 |
int opCmp(in Days rhs) const |
|---|
| 369 |
{ |
|---|
| 370 |
return days_ < rhs.days_ ? -1 : days_ > rhs.days_ ? 1 : 0; |
|---|
| 371 |
} |
|---|
| 372 |
|
|---|
| 373 |
Days opBinary(string op)(Days d) const |
|---|
| 374 |
if (op == "+" || op == "-") |
|---|
| 375 |
{ |
|---|
| 376 |
} |
|---|
| 377 |
} |
|---|
| 378 |
|
|---|
| 379 |
Date fromString(in char[] s) |
|---|
| 380 |
{ |
|---|
| 381 |
Date result; |
|---|
| 382 |
return result; |
|---|
| 383 |
} |
|---|
| 384 |
|
|---|
| 385 |
Date fromUndelimitedString(in char[] s) |
|---|
| 386 |
{ |
|---|
| 387 |
Date result; |
|---|
| 388 |
return result; |
|---|
| 389 |
} |
|---|
| 390 |
|
|---|
| 391 |
Date dayClockLocalDay() |
|---|
| 392 |
{ |
|---|
| 393 |
Date result; |
|---|
| 394 |
return result; |
|---|
| 395 |
} |
|---|
| 396 |
|
|---|
| 397 |
Date dayClockUniversalDay() |
|---|
| 398 |
{ |
|---|
| 399 |
Date result; |
|---|
| 400 |
return result; |
|---|
| 401 |
} |
|---|
| 402 |
|
|---|
| 403 |
Date dateFromTm(tm) |
|---|
| 404 |
{ |
|---|
| 405 |
assert(0); |
|---|
| 406 |
} |
|---|