some review fixes and add new function toStartOfWeek

This commit is contained in:
Andy Yang 2019-06-18 17:48:07 +08:00
parent ec9a97d499
commit 6085fce8d7
6 changed files with 180 additions and 141 deletions

View File

@ -35,35 +35,53 @@ struct ZeroTransform
static inline UInt16 execute(UInt16, UInt8, const DateLUTImpl &) { return 0; }
};
struct WeekImpl
struct ToWeekImpl
{
static constexpr auto name = "week";
static constexpr auto name = "toWeek";
static inline UInt8 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{
UInt32 year = 0;
return time_zone.calc_week(time_zone.toDayNum(t), week_mode, &year);
YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode);
return yw.second;
}
static inline UInt8 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
{
UInt32 year = 0;
return time_zone.calc_week(DayNum(d), week_mode, &year);
YearWeek yw = time_zone.toYearWeek(DayNum(d), week_mode);
return yw.second;
}
using FactorTransform = ZeroTransform;
};
struct YearWeekImpl
struct ToYearWeekImpl
{
static constexpr auto name = "yearWeek";
static constexpr auto name = "toYearWeek";
static inline UInt32 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{
return time_zone.calc_yearWeek(time_zone.toDayNum(t), week_mode);
YearWeek yw = time_zone.toYearWeek(time_zone.toDayNum(t), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR));
return yw.first * 100 + yw.second;
}
static inline UInt32 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
{
return time_zone.calc_yearWeek(DayNum(d), week_mode);
YearWeek yw = time_zone.toYearWeek(DayNum(d), week_mode | static_cast<UInt32>(WeekModeFlag::YEAR));
return yw.first * 100 + yw.second;
}
using FactorTransform = ZeroTransform;
};
struct ToStartOfWeekImpl
{
static constexpr auto name = "toStartOfWeek";
static inline UInt16 execute(UInt32 t, UInt8 week_mode, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfWeek(time_zone.toDayNum(t), week_mode);
}
static inline UInt16 execute(UInt16 d, UInt8 week_mode, const DateLUTImpl & time_zone)
{
return time_zone.toFirstDayNumOfWeek(DayNum(d), week_mode);
}
using FactorTransform = ZeroTransform;

View File

@ -7,13 +7,19 @@
namespace DB
{
using FunctionWeek = FunctionCustomWeekToSomething<DataTypeUInt8, WeekImpl>;
using FunctionYearWeek = FunctionCustomWeekToSomething<DataTypeUInt32, YearWeekImpl>;
using FunctionToWeek = FunctionCustomWeekToSomething<DataTypeUInt8, ToWeekImpl>;
using FunctionToYearWeek = FunctionCustomWeekToSomething<DataTypeUInt32, ToYearWeekImpl>;
using FunctionToStartOfWeek = FunctionCustomWeekToSomething<DataTypeDate, ToStartOfWeekImpl>;
void registerFunctionToCustomWeek(FunctionFactory & factory)
{
factory.registerFunction<FunctionWeek>();
factory.registerFunction<FunctionYearWeek>();
factory.registerFunction<FunctionToWeek>();
factory.registerFunction<FunctionToYearWeek>();
factory.registerFunction<FunctionToStartOfWeek>();
/// Compatibility aliases for mysql.
factory.registerAlias("week", "toWeek", FunctionFactory::CaseInsensitive);
factory.registerAlias("yearweek", "toYearWeek", FunctionFactory::CaseInsensitive);
}
}

View File

@ -61,3 +61,13 @@
2017-01-09 00:00:00 2 3 201702 201703
2017-01-10 00:00:00 2 3 201702 201703
2017-01-11 00:00:00 2 3 201702 201703
2018-12-25 2018-12-25 00:00:00 2018-12-23 2018-12-23 2018-12-24 2018-12-24
2018-12-26 2018-12-26 00:00:00 2018-12-23 2018-12-23 2018-12-24 2018-12-24
2018-12-27 2018-12-27 00:00:00 2018-12-23 2018-12-23 2018-12-24 2018-12-24
2018-12-28 2018-12-28 00:00:00 2018-12-23 2018-12-23 2018-12-24 2018-12-24
2018-12-29 2018-12-29 00:00:00 2018-12-23 2018-12-23 2018-12-24 2018-12-24
2018-12-30 2018-12-30 00:00:00 2018-12-30 2018-12-30 2018-12-24 2018-12-24
2018-12-31 2018-12-31 00:00:00 2018-12-30 2018-12-30 2018-12-31 2018-12-31
2019-01-01 2019-01-01 00:00:00 2018-12-30 2018-12-30 2018-12-31 2018-12-31
2019-01-02 2019-01-02 00:00:00 2018-12-30 2018-12-30 2018-12-31 2018-12-31
2019-01-03 2019-01-03 00:00:00 2018-12-30 2018-12-30 2018-12-31 2018-12-31

View File

@ -1,42 +1,52 @@
-- week mode [0,7], week test case. refer to the mysql test case
SELECT week(toDate('1998-01-01')), week(toDate('1997-01-01')), week(toDate('1998-01-01'), 1), week(toDate('1997-01-01'), 1);
SELECT week(toDate('1998-12-31')), week(toDate('1997-12-31')), week(toDate('1998-12-31'), 1), week(toDate('1997-12-31'), 1);
SELECT week(toDate('1995-01-01')), week(toDate('1995-01-01'), 1);
SELECT yearWeek(toDate('1981-12-31'), 1), yearWeek(toDate('1982-01-01'), 1), yearWeek(toDate('1982-12-31'), 1), yearWeek(toDate('1983-01-01'), 1);
SELECT yearWeek(toDate('1987-01-01'), 1), yearWeek(toDate('1987-01-01'));
SELECT toWeek(toDate('1998-01-01')), toWeek(toDate('1997-01-01')), toWeek(toDate('1998-01-01'), 1), toWeek(toDate('1997-01-01'), 1);
SELECT toWeek(toDate('1998-12-31')), toWeek(toDate('1997-12-31')), toWeek(toDate('1998-12-31'), 1), toWeek(toDate('1997-12-31'), 1);
SELECT toWeek(toDate('1995-01-01')), toWeek(toDate('1995-01-01'), 1);
SELECT toYearWeek(toDate('1981-12-31'), 1), toYearWeek(toDate('1982-01-01'), 1), toYearWeek(toDate('1982-12-31'), 1), toYearWeek(toDate('1983-01-01'), 1);
SELECT toYearWeek(toDate('1987-01-01'), 1), toYearWeek(toDate('1987-01-01'));
SELECT week(toDate('2000-01-01'),0) AS w2000, week(toDate('2001-01-01'),0) AS w2001, week(toDate('2002-01-01'),0) AS w2002,week(toDate('2003-01-01'),0) AS w2003, week(toDate('2004-01-01'),0) AS w2004, week(toDate('2005-01-01'),0) AS w2005, week(toDate('2006-01-01'),0) AS w2006;
SELECT week(toDate('2000-01-06'),0) AS w2000, week(toDate('2001-01-06'),0) AS w2001, week(toDate('2002-01-06'),0) AS w2002,week(toDate('2003-01-06'),0) AS w2003, week(toDate('2004-01-06'),0) AS w2004, week(toDate('2005-01-06'),0) AS w2005, week(toDate('2006-01-06'),0) AS w2006;
SELECT week(toDate('2000-01-01'),1) AS w2000, week(toDate('2001-01-01'),1) AS w2001, week(toDate('2002-01-01'),1) AS w2002,week(toDate('2003-01-01'),1) AS w2003, week(toDate('2004-01-01'),1) AS w2004, week(toDate('2005-01-01'),1) AS w2005, week(toDate('2006-01-01'),1) AS w2006;
SELECT week(toDate('2000-01-06'),1) AS w2000, week(toDate('2001-01-06'),1) AS w2001, week(toDate('2002-01-06'),1) AS w2002,week(toDate('2003-01-06'),1) AS w2003, week(toDate('2004-01-06'),1) AS w2004, week(toDate('2005-01-06'),1) AS w2005, week(toDate('2006-01-06'),1) AS w2006;
SELECT yearWeek(toDate('2000-01-01'),0) AS w2000, yearWeek(toDate('2001-01-01'),0) AS w2001, yearWeek(toDate('2002-01-01'),0) AS w2002,yearWeek(toDate('2003-01-01'),0) AS w2003, yearWeek(toDate('2004-01-01'),0) AS w2004, yearWeek(toDate('2005-01-01'),0) AS w2005, yearWeek(toDate('2006-01-01'),0) AS w2006;
SELECT yearWeek(toDate('2000-01-06'),0) AS w2000, yearWeek(toDate('2001-01-06'),0) AS w2001, yearWeek(toDate('2002-01-06'),0) AS w2002,yearWeek(toDate('2003-01-06'),0) AS w2003, yearWeek(toDate('2004-01-06'),0) AS w2004, yearWeek(toDate('2005-01-06'),0) AS w2005, yearWeek(toDate('2006-01-06'),0) AS w2006;
SELECT yearWeek(toDate('2000-01-01'),1) AS w2000, yearWeek(toDate('2001-01-01'),1) AS w2001, yearWeek(toDate('2002-01-01'),1) AS w2002,yearWeek(toDate('2003-01-01'),1) AS w2003, yearWeek(toDate('2004-01-01'),1) AS w2004, yearWeek(toDate('2005-01-01'),1) AS w2005, yearWeek(toDate('2006-01-01'),1) AS w2006;
SELECT yearWeek(toDate('2000-01-06'),1) AS w2000, yearWeek(toDate('2001-01-06'),1) AS w2001, yearWeek(toDate('2002-01-06'),1) AS w2002,yearWeek(toDate('2003-01-06'),1) AS w2003, yearWeek(toDate('2004-01-06'),1) AS w2004, yearWeek(toDate('2005-01-06'),1) AS w2005, yearWeek(toDate('2006-01-06'),1) AS w2006;
SELECT week(toDate('1998-12-31'),2),week(toDate('1998-12-31'),3), week(toDate('2000-01-01'),2), week(toDate('2000-01-01'),3);
SELECT week(toDate('2000-12-31'),2),week(toDate('2000-12-31'),3);
SELECT toWeek(toDate('2000-01-01'),0) AS w2000, toWeek(toDate('2001-01-01'),0) AS w2001, toWeek(toDate('2002-01-01'),0) AS w2002,toWeek(toDate('2003-01-01'),0) AS w2003, toWeek(toDate('2004-01-01'),0) AS w2004, toWeek(toDate('2005-01-01'),0) AS w2005, toWeek(toDate('2006-01-01'),0) AS w2006;
SELECT toWeek(toDate('2000-01-06'),0) AS w2000, toWeek(toDate('2001-01-06'),0) AS w2001, toWeek(toDate('2002-01-06'),0) AS w2002,toWeek(toDate('2003-01-06'),0) AS w2003, toWeek(toDate('2004-01-06'),0) AS w2004, toWeek(toDate('2005-01-06'),0) AS w2005, toWeek(toDate('2006-01-06'),0) AS w2006;
SELECT toWeek(toDate('2000-01-01'),1) AS w2000, toWeek(toDate('2001-01-01'),1) AS w2001, toWeek(toDate('2002-01-01'),1) AS w2002,toWeek(toDate('2003-01-01'),1) AS w2003, toWeek(toDate('2004-01-01'),1) AS w2004, toWeek(toDate('2005-01-01'),1) AS w2005, toWeek(toDate('2006-01-01'),1) AS w2006;
SELECT toWeek(toDate('2000-01-06'),1) AS w2000, toWeek(toDate('2001-01-06'),1) AS w2001, toWeek(toDate('2002-01-06'),1) AS w2002,toWeek(toDate('2003-01-06'),1) AS w2003, toWeek(toDate('2004-01-06'),1) AS w2004, toWeek(toDate('2005-01-06'),1) AS w2005, toWeek(toDate('2006-01-06'),1) AS w2006;
SELECT toYearWeek(toDate('2000-01-01'),0) AS w2000, toYearWeek(toDate('2001-01-01'),0) AS w2001, toYearWeek(toDate('2002-01-01'),0) AS w2002,toYearWeek(toDate('2003-01-01'),0) AS w2003, toYearWeek(toDate('2004-01-01'),0) AS w2004, toYearWeek(toDate('2005-01-01'),0) AS w2005, toYearWeek(toDate('2006-01-01'),0) AS w2006;
SELECT toYearWeek(toDate('2000-01-06'),0) AS w2000, toYearWeek(toDate('2001-01-06'),0) AS w2001, toYearWeek(toDate('2002-01-06'),0) AS w2002,toYearWeek(toDate('2003-01-06'),0) AS w2003, toYearWeek(toDate('2004-01-06'),0) AS w2004, toYearWeek(toDate('2005-01-06'),0) AS w2005, toYearWeek(toDate('2006-01-06'),0) AS w2006;
SELECT toYearWeek(toDate('2000-01-01'),1) AS w2000, toYearWeek(toDate('2001-01-01'),1) AS w2001, toYearWeek(toDate('2002-01-01'),1) AS w2002,toYearWeek(toDate('2003-01-01'),1) AS w2003, toYearWeek(toDate('2004-01-01'),1) AS w2004, toYearWeek(toDate('2005-01-01'),1) AS w2005, toYearWeek(toDate('2006-01-01'),1) AS w2006;
SELECT toYearWeek(toDate('2000-01-06'),1) AS w2000, toYearWeek(toDate('2001-01-06'),1) AS w2001, toYearWeek(toDate('2002-01-06'),1) AS w2002,toYearWeek(toDate('2003-01-06'),1) AS w2003, toYearWeek(toDate('2004-01-06'),1) AS w2004, toYearWeek(toDate('2005-01-06'),1) AS w2005, toYearWeek(toDate('2006-01-06'),1) AS w2006;
SELECT toWeek(toDate('1998-12-31'),2),toWeek(toDate('1998-12-31'),3), toWeek(toDate('2000-01-01'),2), toWeek(toDate('2000-01-01'),3);
SELECT toWeek(toDate('2000-12-31'),2),toWeek(toDate('2000-12-31'),3);
SELECT week(toDate('1998-12-31'),0) AS w0, week(toDate('1998-12-31'),1) AS w1, week(toDate('1998-12-31'),2) AS w2, week(toDate('1998-12-31'),3) AS w3, week(toDate('1998-12-31'),4) AS w4, week(toDate('1998-12-31'),5) AS w5, week(toDate('1998-12-31'),6) AS w6, week(toDate('1998-12-31'),7) AS w7;
SELECT week(toDate('2000-01-01'),0) AS w0, week(toDate('2000-01-01'),1) AS w1, week(toDate('2000-01-01'),2) AS w2, week(toDate('2000-01-01'),3) AS w3, week(toDate('2000-01-01'),4) AS w4, week(toDate('2000-01-01'),5) AS w5, week(toDate('2000-01-01'),6) AS w6, week(toDate('2000-01-01'),7) AS w7;
SELECT week(toDate('2000-01-06'),0) AS w0, week(toDate('2000-01-06'),1) AS w1, week(toDate('2000-01-06'),2) AS w2, week(toDate('2000-01-06'),3) AS w3, week(toDate('2000-01-06'),4) AS w4, week(toDate('2000-01-06'),5) AS w5, week(toDate('2000-01-06'),6) AS w6, week(toDate('2000-01-06'),7) AS w7;
SELECT week(toDate('2000-12-31'),0) AS w0, week(toDate('2000-12-31'),1) AS w1, week(toDate('2000-12-31'),2) AS w2, week(toDate('2000-12-31'),3) AS w3, week(toDate('2000-12-31'),4) AS w4, week(toDate('2000-12-31'),5) AS w5, week(toDate('2000-12-31'),6) AS w6, week(toDate('2000-12-31'),7) AS w7;
SELECT week(toDate('2001-01-01'),0) AS w0, week(toDate('2001-01-01'),1) AS w1, week(toDate('2001-01-01'),2) AS w2, week(toDate('2001-01-01'),3) AS w3, week(toDate('2001-01-01'),4) AS w4, week(toDate('2001-01-01'),5) AS w5, week(toDate('2001-01-01'),6) AS w6, week(toDate('2001-01-01'),7) AS w7;
SELECT toWeek(toDate('1998-12-31'),0) AS w0, toWeek(toDate('1998-12-31'),1) AS w1, toWeek(toDate('1998-12-31'),2) AS w2, toWeek(toDate('1998-12-31'),3) AS w3, toWeek(toDate('1998-12-31'),4) AS w4, toWeek(toDate('1998-12-31'),5) AS w5, toWeek(toDate('1998-12-31'),6) AS w6, toWeek(toDate('1998-12-31'),7) AS w7;
SELECT toWeek(toDate('2000-01-01'),0) AS w0, toWeek(toDate('2000-01-01'),1) AS w1, toWeek(toDate('2000-01-01'),2) AS w2, toWeek(toDate('2000-01-01'),3) AS w3, toWeek(toDate('2000-01-01'),4) AS w4, toWeek(toDate('2000-01-01'),5) AS w5, toWeek(toDate('2000-01-01'),6) AS w6, toWeek(toDate('2000-01-01'),7) AS w7;
SELECT toWeek(toDate('2000-01-06'),0) AS w0, toWeek(toDate('2000-01-06'),1) AS w1, toWeek(toDate('2000-01-06'),2) AS w2, toWeek(toDate('2000-01-06'),3) AS w3, toWeek(toDate('2000-01-06'),4) AS w4, toWeek(toDate('2000-01-06'),5) AS w5, toWeek(toDate('2000-01-06'),6) AS w6, toWeek(toDate('2000-01-06'),7) AS w7;
SELECT toWeek(toDate('2000-12-31'),0) AS w0, toWeek(toDate('2000-12-31'),1) AS w1, toWeek(toDate('2000-12-31'),2) AS w2, toWeek(toDate('2000-12-31'),3) AS w3, toWeek(toDate('2000-12-31'),4) AS w4, toWeek(toDate('2000-12-31'),5) AS w5, toWeek(toDate('2000-12-31'),6) AS w6, toWeek(toDate('2000-12-31'),7) AS w7;
SELECT toWeek(toDate('2001-01-01'),0) AS w0, toWeek(toDate('2001-01-01'),1) AS w1, toWeek(toDate('2001-01-01'),2) AS w2, toWeek(toDate('2001-01-01'),3) AS w3, toWeek(toDate('2001-01-01'),4) AS w4, toWeek(toDate('2001-01-01'),5) AS w5, toWeek(toDate('2001-01-01'),6) AS w6, toWeek(toDate('2001-01-01'),7) AS w7;
SELECT yearWeek(toDate('2000-12-31'),0), yearWeek(toDate('2000-12-31'),1), yearWeek(toDate('2000-12-31'),2), yearWeek(toDate('2000-12-31'),3), yearWeek(toDate('2000-12-31'),4), yearWeek(toDate('2000-12-31'),5), yearWeek(toDate('2000-12-31'),6), yearWeek(toDate('2000-12-31'),7);
SELECT toYearWeek(toDate('2000-12-31'),0), toYearWeek(toDate('2000-12-31'),1), toYearWeek(toDate('2000-12-31'),2), toYearWeek(toDate('2000-12-31'),3), toYearWeek(toDate('2000-12-31'),4), toYearWeek(toDate('2000-12-31'),5), toYearWeek(toDate('2000-12-31'),6), toYearWeek(toDate('2000-12-31'),7);
-- week mode 8,9
SELECT
toDate('2016-12-21') + number AS d,
week(d, 8) AS week8,
week(d, 9) AS week9,
yearWeek(d, 8) AS yearWeek8,
yearWeek(d, 9) AS yearWeek9
toWeek(d, 8) AS week8,
toWeek(d, 9) AS week9,
toYearWeek(d, 8) AS yearWeek8,
toYearWeek(d, 9) AS yearWeek9
FROM numbers(21);
SELECT toDateTime(toDate('2016-12-22') + number, 'Europe/Moscow' ) AS d,
week(d, 8, 'Europe/Moscow') AS week8,
week(d, 9, 'Europe/Moscow') AS week9,
yearWeek(d, 8, 'Europe/Moscow') AS yearWeek8,
yearWeek(d, 9, 'Europe/Moscow') AS yearWeek9
toWeek(d, 8, 'Europe/Moscow') AS week8,
toWeek(d, 9, 'Europe/Moscow') AS week9,
toYearWeek(d, 8, 'Europe/Moscow') AS yearWeek8,
toYearWeek(d, 9, 'Europe/Moscow') AS yearWeek9
FROM numbers(21);
-- toStartOfWeek
SELECT
toDate('2018-12-25') + number AS x,
toDateTime(x) AS x_t,
toStartOfWeek(x) AS w0,
toStartOfWeek(x_t) AS wt0,
toStartOfWeek(x, 3) AS w3,
toStartOfWeek(x_t, 3) AS wt3
FROM numbers(10);

View File

@ -95,6 +95,12 @@ Returns the date.
Rounds down a date or date with time to the nearest Monday.
Returns the date.
## toStartOfWeek(t[,mode])
Rounds down a date or date with time to the nearest Sunday or Monday by mode.
Returns the date.
The mode argument works exactly like the mode argument to toWeek(). For the single-argument syntax, a mode value of 0 is used.
## toStartOfDay
Rounds down a date with time to the start of the day.
@ -169,9 +175,9 @@ Converts a date or date with time to a UInt16 number containing the ISO Year num
Converts a date or date with time to a UInt8 number containing the ISO Week number.
## week(date[,mode])
This function returns the week number for date or datetime. The two-argument form of week() enables you to specify whether the week starts on Sunday or Monday and whether the return value should be in the range from 0 to 53 or from 1 to 53. If the mode argument is omitted, the default mode is 0.
`toISOWeek() `is a compatibility function that is equivalent to `week(date,3)`.
## toWeek(date[,mode])
This function returns the week number for date or datetime. The two-argument form of toWeek() enables you to specify whether the week starts on Sunday or Monday and whether the return value should be in the range from 0 to 53 or from 1 to 53. If the mode argument is omitted, the default mode is 0.
`toISOWeek() `is a compatibility function that is equivalent to `toWeek(date,3)`.
The following table describes how the mode argument works.
| Mode | First day of week | Range | Week 1 is the first week … |
@ -196,7 +202,7 @@ For mode values with a meaning of “with 4 or more days this year,” weeks are
For mode values with a meaning of “contains January 1”, the week contains January 1 is week 1. It doesn't matter how many days in the new year the week contained, even if it contained only one day.
```
week(date, [, mode][, Timezone])
toWeek(date, [, mode][, Timezone])
```
**Parameters**
@ -207,7 +213,7 @@ week(date, [, mode][, Timezone])
**Example**
``` sql
SELECT toDate('2016-12-27') AS date, week(date) AS week0, week(date,1) AS week1, week(date,9) AS week9;
SELECT toDate('2016-12-27') AS date, toWeek(date) AS week0, toWeek(date,1) AS week1, toWeek(date,9) AS week9;
```
```
@ -216,17 +222,17 @@ SELECT toDate('2016-12-27') AS date, week(date) AS week0, week(date,1) AS week1,
└────────────┴───────┴───────┴───────┘
```
## yearWeek(date[,mode])
## toYearWeek(date[,mode])
Returns year and week for a date. The year in the result may be different from the year in the date argument for the first and the last week of the year.
The mode argument works exactly like the mode argument to week(). For the single-argument syntax, a mode value of 0 is used.
The mode argument works exactly like the mode argument to toWeek(). For the single-argument syntax, a mode value of 0 is used.
`toISOYear() `is a compatibility function that is equivalent to `intDiv(yearWeek(date,3),100)`.
`toISOYear() `is a compatibility function that is equivalent to `intDiv(toYearWeek(date,3),100)`.
**Example**
``` sql
SELECT toDate('2016-12-27') AS date, yearWeek(date) AS yearWeek0, yearWeek(date,1) AS yearWeek1, yearWeek(date,9) AS yearWeek9;
SELECT toDate('2016-12-27') AS date, toYearWeek(date) AS yearWeek0, toYearWeek(date,1) AS yearWeek1, toYearWeek(date,9) AS yearWeek9;
```
```

View File

@ -14,18 +14,21 @@
#define DATE_LUT_MAX_YEAR 2105 /// Last supported year
#define DATE_LUT_YEARS (1 + DATE_LUT_MAX_YEAR - DATE_LUT_MIN_YEAR) /// Number of years in lookup table
/// Flags for calc_week() function.
#define WEEK_MONDAY_FIRST 1
#define WEEK_YEAR 2
#define WEEK_FIRST_WEEKDAY 4
#define WEEK_NEWYEAR_DAY 8
#if defined(__PPC__)
# if !__clang__
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
# endif
#if !__clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#endif
/// Flags for toYearWeek() function.
enum class WeekModeFlag : UInt8
{
MONDAY_FIRST = 1,
YEAR = 2,
FIRST_WEEKDAY = 4,
NEWYEAR_DAY = 8
};
typedef std::pair<UInt16, UInt8> YearWeek;
/** Lookup table to conversion of time to date, and to month / year / day of week / day of month and so on.
* First time was implemented for OLAPServer, that needed to do billions of such transformations.
@ -330,15 +333,15 @@ public:
/*
The bits in week_mode has the following meaning:
WEEK_MONDAY_FIRST (0) If not set Sunday is first day of week
WeekModeFlag::MONDAY_FIRST (0) If not set Sunday is first day of week
If set Monday is first day of week
WEEK_YEAR (1) If not set Week is in range 0-53
WeekModeFlag::YEAR (1) If not set Week is in range 0-53
Week 0 is returned for the the last week of the previous year (for
a date at start of january) In this case one can get 53 for the
first week of next year. This flag ensures that the week is
relevant for the given year. Note that this flag is only
releveant if WEEK_JANUARY is not set.
releveant if WeekModeFlag::JANUARY is not set.
If set Week is in range 1-53.
@ -346,52 +349,51 @@ public:
the week is that last week of previous year) and week 1 for a
date in December.
WEEK_FIRST_WEEKDAY (2) If not set Weeks are numbered according
WeekModeFlag::FIRST_WEEKDAY (2) If not set Weeks are numbered according
to ISO 8601:1988
If set The week that contains the first
'first-day-of-week' is week 1.
WEEK_NEWYEAR_DAY (3)
WeekModeFlag::NEWYEAR_DAY (3)
If set The week that contains the January 1 is week 1.
Week is in range 1-53.
And ignore WEEK_YEAR, WEEK_FIRST_WEEKDAY
And ignore WeekModeFlag::YEAR, WeekModeFlag::FIRST_WEEKDAY
ISO 8601:1988 means that if the week containing January 1 has
four or more days in the new year, then it is week 1;
Otherwise it is the last week of the previous year, and the
next week is week 1.
*/
inline unsigned calc_week(DayNum d, UInt32 week_mode, UInt32 * year) const
inline YearWeek toYearWeek(DayNum d, UInt8 week_mode) const
{
bool newyear_day_mode = week_mode & WEEK_NEWYEAR_DAY;
bool newyear_day_mode = week_mode & static_cast<UInt8>(WeekModeFlag::NEWYEAR_DAY);
week_mode = check_week_mode(week_mode);
bool monday_first_mode = week_mode & WEEK_MONDAY_FIRST;
bool week_year_mode = week_mode & WEEK_YEAR;
bool first_weekday_mode = week_mode & WEEK_FIRST_WEEKDAY;
bool monday_first_mode = week_mode & static_cast<UInt8>(WeekModeFlag::MONDAY_FIRST);
bool week_year_mode = week_mode & static_cast<UInt8>(WeekModeFlag::YEAR);
bool first_weekday_mode = week_mode & static_cast<UInt8>(WeekModeFlag::FIRST_WEEKDAY);
// Calculate week number of WEEK_NEWYEAR_DAY mode
// Calculate week number of WeekModeFlag::NEWYEAR_DAY mode
if (newyear_day_mode)
{
return calc_newyear_week(d, monday_first_mode, year);
return toYearWeekOfNewyearMode(d, monday_first_mode);
}
UInt32 days = 0;
UInt64 daynr = calc_daynr(toYear(d), toMonth(d), toDayOfMonth(d));
UInt64 first_daynr = calc_daynr(toYear(d), 1, 1);
YearWeek yw(toYear(d), 0);
UInt16 days = 0;
UInt16 daynr = makeDayNum(yw.first, toMonth(d), toDayOfMonth(d));
UInt16 first_daynr = makeDayNum(yw.first, 1, 1);
// 0 for monday, 1 for tuesday ...
// get weekday from first day in year.
UInt32 weekday = calc_weekday(first_daynr, !monday_first_mode);
*year = toYear(d);
UInt16 weekday = calc_weekday(DayNum(first_daynr), !monday_first_mode);
if (toMonth(d) == 1 && toDayOfMonth(d) <= 7 - weekday)
{
if (!week_year_mode && ((first_weekday_mode && weekday != 0) || (!first_weekday_mode && weekday >= 4)))
return 0;
return yw;
week_year_mode = 1;
(*year)--;
first_daynr -= (days = calc_days_in_year(*year));
(yw.first)--;
first_daynr -= (days = calc_days_in_year(yw.first));
weekday = (weekday + 53 * 7 - days) % 7;
}
@ -402,33 +404,29 @@ public:
if (week_year_mode && days >= 52 * 7)
{
weekday = (weekday + calc_days_in_year(*year)) % 7;
weekday = (weekday + calc_days_in_year(yw.first)) % 7;
if ((!first_weekday_mode && weekday < 4) || (first_weekday_mode && weekday == 0))
{
(*year)++;
return 1;
(yw.first)++;
yw.second = 1;
return yw;
}
}
return days / 7 + 1;
yw.second = days / 7 + 1;
return yw;
}
inline unsigned calc_yearWeek(DayNum d, UInt32 week_mode) const
{
UInt32 year = 0;
UInt8 week = calc_week(d, week_mode | WEEK_YEAR, &year);
return week + year * 100;
}
/// Calculate week number of WEEK_NEWYEAR_DAY mode
/// Calculate week number of WeekModeFlag::NEWYEAR_DAY mode
/// The week number 1 is the first week in year that contains January 1,
inline unsigned calc_newyear_week(DayNum d, bool monday_first_mode, UInt32 * year) const
inline YearWeek toYearWeekOfNewyearMode(DayNum d, bool monday_first_mode) const
{
YearWeek yw(0, 0);
UInt16 offset_day = monday_first_mode ? 0U : 1U;
// Checking the week across the year
*year = toYear(DayNum(d + 7 - toDayOfWeek(DayNum(d + offset_day))));
yw.first = toYear(DayNum(d + 7 - toDayOfWeek(DayNum(d + offset_day))));
DayNum first_day = makeDayNum(*year, 1, 1);
DayNum first_day = makeDayNum(yw.first, 1, 1);
DayNum this_day = d;
if (monday_first_mode)
@ -445,60 +443,51 @@ public:
if (toDayOfWeek(d) != 7)
this_day = DayNum(d - toDayOfWeek(d));
}
return (this_day - first_day) / 7 + 1;
yw.second = (this_day - first_day) / 7 + 1;
return yw;
}
inline unsigned check_week_mode(UInt32 mode) const
/**
* get first day of week with week_mode, return Sunday or Monday
*/
inline DayNum toFirstDayNumOfWeek(DayNum d, UInt8 week_mode) const
{
UInt32 week_format = (mode & 7);
if (!(week_format & WEEK_MONDAY_FIRST))
week_format ^= WEEK_FIRST_WEEKDAY;
bool monday_first_mode = week_mode & static_cast<UInt8>(WeekModeFlag::MONDAY_FIRST);
if (monday_first_mode)
{
return toFirstDayNumOfWeek(d);
}
else
{
return (toDayOfWeek(d) != 7) ? DayNum(d - toDayOfWeek(d)) : d;
}
}
/*
* check and change mode to effective
*/
inline UInt8 check_week_mode(UInt8 mode) const
{
UInt8 week_format = (mode & 7);
if (!(week_format & static_cast<UInt8>(WeekModeFlag::MONDAY_FIRST)))
week_format ^= static_cast<UInt8>(WeekModeFlag::FIRST_WEEKDAY);
return week_format;
}
/*
Calculate nr of day since year 0 in new date-system (from 1615)
SYNOPSIS
calc_daynr()
year Year (exact 4 digit year, no year conversions)
month Month
day Day
NOTES: 0000-00-00 is a valid date, and will return 0
RETURN
Days since 0000-00-00
*/
inline UInt64 calc_daynr(UInt32 year, UInt32 month, UInt32 day) const
* Calc weekday from d
* Returns 0 for monday, 1 for tuesday ...
*/
inline unsigned calc_weekday(DayNum d, bool sunday_first_day_of_week) const
{
UInt64 delsum;
int temp;
int y = year; /* may be < 0 temporarily */
if (y == 0 && month == 0)
return 0; /* Skip errors */
/* Cast to int to be able to handle month == 0 */
delsum = static_cast<UInt64>(365 * y + 31 * (month - 1) + day);
delsum = static_cast<UInt64>(365 * y + 31 * (static_cast<int>(month) - 1) + static_cast<int>(day));
if (month <= 2)
y--;
if (!sunday_first_day_of_week)
return toDayOfWeek(d) - 1;
else
delsum -= static_cast<UInt64>(static_cast<int>(month) * 4 + 23) / 10;
temp = ((y / 100 + 1) * 3) / 4;
return delsum + y / 4 - temp;
} /* calc_daynr */
/*
Calc weekday from daynr
Returns 0 for monday, 1 for tuesday ...
*/
inline unsigned calc_weekday(UInt64 daynr, bool sunday_first_day_of_week) const
{
return (static_cast<UInt32>((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
return toDayOfWeek(DayNum(d + 1)) - 1;
}
/* Calc days in one year. */
inline unsigned calc_days_in_year(UInt32 year) const
inline unsigned calc_days_in_year(UInt16 year) const
{
return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)) ? 366 : 365);
}