Module: FatCore::Date::ClassMethods
- Included in:
- Date
- Defined in:
- lib/fat_core/date.rb
Utilities collapse
- COMMON_YEAR_DAYS_IN_MONTH =
An Array of the number of days in each month indexed by month number, starting with January = 1, etc.
[31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31].freeze
Parsing collapse
-
#parse_american(str) ⇒ ::Date
Convert a string +str+ with an American style date into a ::Date object.
-
#parse_spec(spec, spec_type = :from) ⇒ ::Date
Convert a 'period spec'
specto a ::Date.
Utilities collapse
- #days_in_month(y, m) ⇒ Object
-
#easter(year) ⇒ ::Date
Return the date of Easter for the Western Church in the given year.
-
#nth_wday_in_year_month(n, wday, year, month) ⇒ Object
Return the nth weekday in the given month.
Instance Method Details
#days_in_month(y, m) ⇒ Object
1577 1578 1579 1580 1581 1582 1583 1584 1585 |
# File 'lib/fat_core/date.rb', line 1577 def days_in_month(y, m) raise ArgumentError, 'illegal month number' if m < 1 || m > 12 days = COMMON_YEAR_DAYS_IN_MONTH[m] if m == 2 ::Date.new(y, m, 1).leap? ? 29 : 28 else days end end |
#easter(year) ⇒ ::Date
Return the date of Easter for the Western Church in the given year.
1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 |
# File 'lib/fat_core/date.rb', line 1632 def easter(year) y = year a = y % 19 b, c = y.divmod(100) d, e = b.divmod(4) f = (b + 8) / 25 g = (b - f + 1) / 3 h = (19 * a + b - d - g + 15) % 30 i, k = c.divmod(4) l = (32 + 2 * e + 2 * i - h - k) % 7 m = (a + 11 * h + 22 * l) / 451 n, p = (h + l - 7 * m + 114).divmod(31) ::Date.new(y, n, p + 1) end |
#nth_wday_in_year_month(n, wday, year, month) ⇒ Object
Return the nth weekday in the given month. If n is negative, count from last day of month.
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 |
# File 'lib/fat_core/date.rb', line 1594 def nth_wday_in_year_month(n, wday, year, month) wday = wday.to_i raise ArgumentError, 'illegal weekday number' if wday < 0 || wday > 6 month = month.to_i raise ArgumentError, 'illegal month number' if month < 1 || month > 12 n = n.to_i if n.positive? # Set d to the 1st wday in month d = ::Date.new(year, month, 1) d += 1 while d.wday != wday # Set d to the nth wday in month nd = 1 while nd != n d += 7 nd += 1 end d elsif n.negative? n = -n # Set d to the last wday in month d = ::Date.new(year, month, 1).end_of_month d -= 1 while d.wday != wday # Set d to the nth wday in month nd = 1 while nd != n d -= 7 nd += 1 end d else raise ArgumentError, 'Argument n cannot be zero' end end |
#parse_american(str) ⇒ ::Date
Convert a string +str+ with an American style date into a ::Date object
An American style date is of the form MM/DD/YYYY, that is it places the
month first, then the day of the month, and finally the year. The European
convention is typically to place the day of the month first, DD/MM/YYYY.
A date found in the wild can be ambiguous, e.g. 3/5/2014, but a date
string known to be using the American convention can be parsed using this
method. Both the month and the day can be a single digit. The year can be
either 2 or 4 digits, and if given as 2 digits, it adds 2000 to it to give
the year.
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 |
# File 'lib/fat_core/date.rb', line 1277 def parse_american(str) unless str.to_s =~ %r{\A\s*(\d\d?)\s*/\s*(\d\d?)\s*/\s*((\d\d)?\d\d)\s*\z} raise ArgumentError, "date string must be of form 'MM?/DD?/YY(YY)?'" end year = $3.to_i month = $1.to_i day = $2.to_i year += 2000 if year < 100 ::Date.new(year, month, day) end |
#parse_spec(spec, spec_type = :from) ⇒ ::Date
Convert a 'period spec' spec to a ::Date. A date spec is a short-hand way of
specifying a calendar period either absolutely or relative to the computer
clock. This method returns the first date of that period, when spec_type
is set to :from, the default, and returns the last date of the period
when spec_type is :to.
There are a number of forms the spec can take. In each case,
::Date.parse_spec returns the first date in the period if spec_type is
:from and the last date in the period if spec_type is :to:
YYYYis the whole yearYYYY,YYYY-1HorYYYY-H1is the first calendar half in yearYYYY,H2or2His the second calendar half of the current year,YYYY-3QorYYYY-Q3is the third calendar quarter of year YYYY,Q3or3Qis the third calendar quarter in the current year,YYYY-04orYYYY-4is April, the fourth month of yearYYYY,4-12or04-12is the 12th of April in the current year,4or04is April in the current year,YYYY-W32orYYYY-32Wis the 32nd week in year YYYY,W32or32Wis the 32nd week in the current year,YYYY-MM-DDa particular date, so:fromand:toreturn the same date,this_<chunk>where<chunk>is one ofyear,half,quarter,bimonth,month,semimonth,biweek,week, orday, the corresponding calendar period in which the current date falls,last_<chunk>where<chunk>is one ofyear,half,quarter,bimonth,month,semimonth,biweek,week, orday, the corresponding calendar period immediately before the one in which the current date falls,todayis the same asthis_day,yesterdayis the same aslast_day,foreveris the period from ::Date::BOT to ::Date::EOT, essentially all dates of commercial interest, andnevercauses the method to return nil.
In all of the above example specs, letter used for calendar chunks, W,
Q, and H can be written in lower case as well. Also, you can use /
to separate date components instead of -.
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 |
# File 'lib/fat_core/date.rb', line 1340 def parse_spec(spec, spec_type = :from) spec = spec.to_s.strip unless [:from, :to].include?(spec_type) raise ArgumentError, "invalid date spec type: '#{spec_type}'" end today = ::Date.current case spec.clean when /\A(\d\d\d\d)[-\/](\d\d?)[-\/](\d\d?)\z/ # A specified date ::Date.new($1.to_i, $2.to_i, $3.to_i) when /\AW(\d\d?)\z/, /\A(\d\d?)W\z/ week_num = $1.to_i if week_num < 1 || week_num > 53 raise ArgumentError, "invalid week number (1-53): '#{spec}'" end if spec_type == :from ::Date.commercial(today.year, week_num).beginning_of_week else ::Date.commercial(today.year, week_num).end_of_week end when /\A(\d\d\d\d)[-\/]W(\d\d?)\z/, /\A(\d\d\d\d)[-\/](\d\d?)W\z/ year = $1.to_i week_num = $2.to_i if week_num < 1 || week_num > 53 raise ArgumentError, "invalid week number (1-53): '#{spec}'" end if spec_type == :from ::Date.commercial(year, week_num).beginning_of_week else ::Date.commercial(year, week_num).end_of_week end when /^(\d\d\d\d)[-\/](\d)[Qq]$/, /^(\d\d\d\d)[-\/][Qq](\d)$/ # Year-Quarter year = $1.to_i quarter = $2.to_i unless [1, 2, 3, 4].include?(quarter) raise ArgumentError, "invalid quarter number (1-4): '#{spec}'" end month = quarter * 3 if spec_type == :from ::Date.new(year, month, 1).beginning_of_quarter else ::Date.new(year, month, 1).end_of_quarter end when /^([1234])[qQ]$/, /^[qQ]([1234])$/ # Quarter only this_year = today.year quarter = $1.to_i unless [1, 2, 3, 4].include?(quarter) raise ArgumentError, "invalid quarter number (1-4): '#{spec}'" end date = ::Date.new(this_year, quarter * 3, 15) if spec_type == :from date.beginning_of_quarter else date.end_of_quarter end when /^(\d\d\d\d)[-\/](\d)[Hh]$/, /^(\d\d\d\d)[-\/][Hh](\d)$/ # Year-Half year = $1.to_i half = $2.to_i unless [1, 2].include?(half) raise ArgumentError, "invalid half number: '#{spec}'" end month = half * 6 if spec_type == :from ::Date.new(year, month, 15).beginning_of_half else ::Date.new(year, month, 1).end_of_half end when /^([12])[hH]$/, /^[hH]([12])$/ # Half only this_year = today.year half = $1.to_i unless [1, 2].include?(half) raise ArgumentError, "invalid half number: '#{spec}'" end date = ::Date.new(this_year, half * 6, 15) if spec_type == :from date.beginning_of_half else date.end_of_half end when /^(\d\d\d\d)[-\/](\d\d?)*$/ # Year-Month only year = $1.to_i month = $2.to_i unless [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].include?(month) raise ArgumentError, "invalid month number (1-12): '#{spec}'" end if spec_type == :from ::Date.new(year, month, 1) else ::Date.new(year, month, 1).end_of_month end when /^(\d\d?)[-\/](\d\d?)*$/ # Month-Day only month = $1.to_i day = $2.to_i unless [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].include?(month) raise ArgumentError, "invalid month number (1-12): '#{spec}'" end if spec_type == :from ::Date.new(today.year, month, day) else ::Date.new(today.year, month, day).end_of_month end when /\A(\d\d?)\z/ # Month only month = $1.to_i unless [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].include?(month) raise ArgumentError, "invalid month number (1-12): '#{spec}'" end if spec_type == :from ::Date.new(today.year, month, 1) else ::Date.new(today.year, month, 1).end_of_month end when /^(\d\d\d\d)$/ # Year only if spec_type == :from ::Date.new($1.to_i, 1, 1) else ::Date.new($1.to_i, 12, 31) end when /^(to|this_?)?day/ today when /^(yester|last_?)?day/ today - 1.day when /^(this_?)?week/ spec_type == :from ? today.beginning_of_week : today.end_of_week when /last_?week/ if spec_type == :from (today - 1.week).beginning_of_week else (today - 1.week).end_of_week end when /^(this_?)?biweek/ if spec_type == :from today.beginning_of_biweek else today.end_of_biweek end when /last_?biweek/ if spec_type == :from (today - 2.week).beginning_of_biweek else (today - 2.week).end_of_biweek end when /^(this_?)?semimonth/ spec_type == :from ? today.beginning_of_semimonth : today.end_of_semimonth when /^last_?semimonth/ if spec_type == :from (today - 15.days).beginning_of_semimonth else (today - 15.days).end_of_semimonth end when /^(this_?)?month/ if spec_type == :from today.beginning_of_month else today.end_of_month end when /^last_?month/ if spec_type == :from (today - 1.month).beginning_of_month else (today - 1.month).end_of_month end when /^(this_?)?bimonth/ if spec_type == :from today.beginning_of_bimonth else today.end_of_bimonth end when /^last_?bimonth/ if spec_type == :from (today - 2.month).beginning_of_bimonth else (today - 2.month).end_of_bimonth end when /^(this_?)?quarter/ if spec_type == :from today.beginning_of_quarter else today.end_of_quarter end when /^last_?quarter/ if spec_type == :from (today - 3.months).beginning_of_quarter else (today - 3.months).end_of_quarter end when /^(this_?)?half/ if spec_type == :from today.beginning_of_half else today.end_of_half end when /^last_?half/ if spec_type == :from (today - 6.months).beginning_of_half else (today - 6.months).end_of_half end when /^(this_?)?year/ if spec_type == :from today.beginning_of_year else today.end_of_year end when /^last_?year/ if spec_type == :from (today - 1.year).beginning_of_year else (today - 1.year).end_of_year end when /^forever/ if spec_type == :from ::Date::BOT else ::Date::EOT end when /^never/ nil else raise ArgumentError, "bad date spec: '#{spec}''" end end |