blob: 446f0010f390ccd3b145d0842603301d62581df0 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/datetime_util.h"
#include <ctime>
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace cobalt {
namespace util {
// This is an alternate definition of CalendarDateToDayIndex from the one
// in datetime_util.cc. It appears to be simpler than the one we chose. The
// reasons we did not choose this implementation are:
// (a) It uses the function timegm which is nonstandard and may not be supported
// on all platforms we want to support. See:
// http://man7.org/linux/man-pages/man3/timegm.3.html#CONFORMING_TO
// (b) In order to imlement timegm ourselves using standard library functions
// it is necessary to invoke functions that make use of the local timezone
// and that are not thread-safe because they use global state. Both of these
// things are unnecessary. It seemed like a better idea to use a pure
// algorithm that would work anywhere.
uint32_t CalendarDateToDayIndexAltImpl(const CalendarDate& calendar_date) {
struct tm time_info;
time_info.tm_sec = 0;
time_info.tm_min = 0;
time_info.tm_hour = 0;
time_info.tm_mday = calendar_date.day_of_month;
time_info.tm_mon = calendar_date.month - 1;
time_info.tm_year = calendar_date.year - 1900;
time_t unix_time = timegm(&time_info);
return unix_time/kNumUnixSecondsPerDay;
}
TEST(DatetimeUtilTest, CalendarDateToDayIndex) {
CalendarDate calendar_date;
// January 1, 1970
calendar_date.day_of_month = 1;
calendar_date.month = 1;
calendar_date.year = 1970;
EXPECT_EQ(0u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(0u, CalendarDateToDayIndexAltImpl(calendar_date));
// January 2, 1970
calendar_date.day_of_month = 2;
EXPECT_EQ(1u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(1u, CalendarDateToDayIndexAltImpl(calendar_date));
// January 31, 1970
calendar_date.day_of_month = 31;
EXPECT_EQ(30u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(30u, CalendarDateToDayIndexAltImpl(calendar_date));
// Febrary 1, 1970
calendar_date.month = 2;
calendar_date.day_of_month = 1;
EXPECT_EQ(31u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(31u, CalendarDateToDayIndexAltImpl(calendar_date));
// 1972 Was a leap year...
// January 1, 1972
calendar_date.day_of_month = 1;
calendar_date.month = 1;
calendar_date.year = 1972;
EXPECT_EQ(365*2u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(365*2u, CalendarDateToDayIndexAltImpl(calendar_date));
// February 1, 1972
calendar_date.day_of_month = 1;
calendar_date.month = 2;
calendar_date.year = 1972;
EXPECT_EQ(365*2u + 31, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(365*2u + 31, CalendarDateToDayIndexAltImpl(calendar_date));
// February 28, 1972
calendar_date.day_of_month = 28;
calendar_date.month = 2;
calendar_date.year = 1972;
EXPECT_EQ(365*2u + 31 + 27, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(365*2u + 31 + 27, CalendarDateToDayIndexAltImpl(calendar_date));
// February 29, 1972
calendar_date.day_of_month = 29;
calendar_date.month = 2;
calendar_date.year = 1972;
EXPECT_EQ(365*2u + 31 + 28, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(365*2u + 31 + 28, CalendarDateToDayIndexAltImpl(calendar_date));
// March 1, 1972
calendar_date.day_of_month = 1;
calendar_date.month = 3;
calendar_date.year = 1972;
EXPECT_EQ(365*2u + 31 + 29, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(365*2u + 31 + 29, CalendarDateToDayIndexAltImpl(calendar_date));
// January 1, 1973
calendar_date.day_of_month = 1;
calendar_date.month = 1;
calendar_date.year = 1973;
EXPECT_EQ(365*2u + 366, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(365*2u + 366, CalendarDateToDayIndexAltImpl(calendar_date));
// March 1, 2000 (2000 years after March 1, year 0)
calendar_date.day_of_month = 1;
calendar_date.month = 3;
calendar_date.year = 2000;
// See comments at KEpochOffset.
EXPECT_EQ(11017u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(11017u, CalendarDateToDayIndexAltImpl(calendar_date));
// October 18, 2016
calendar_date.day_of_month = 18;
calendar_date.month = 10;
calendar_date.year = 2016;
EXPECT_EQ(17092u, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(17092u, CalendarDateToDayIndexAltImpl(calendar_date));
}
void doDayIndexToCalendarDateTest(uint32_t day_index, uint32_t expected_month,
uint32_t expected_day_of_month, uint32_t expected_year) {
auto calendar_date = DayIndexToCalendarDate(day_index);
EXPECT_EQ(expected_day_of_month, calendar_date.day_of_month)
<< "day_index=" << day_index;
EXPECT_EQ(expected_month, calendar_date.month)
<< "day_index=" << day_index;
EXPECT_EQ(expected_year, calendar_date.year)
<< "day_index=" << day_index;
}
TEST(DatetimeUtilTest, DayIndexToCalendarDate) {
// January 1, 1970
doDayIndexToCalendarDateTest(0, 1, 1, 1970);
// January 2, 1970
doDayIndexToCalendarDateTest(1, 1, 2, 1970);
// January 31, 1970
doDayIndexToCalendarDateTest(30, 1, 31, 1970);
// Febrary 1, 1970
doDayIndexToCalendarDateTest(31, 2, 1, 1970);
// 1972 Was a leap year...
// January 1, 1972
doDayIndexToCalendarDateTest(365*2, 1, 1, 1972);
// February 1, 1972
doDayIndexToCalendarDateTest(365*2 + 31, 2, 1, 1972);
// February 28, 1972
doDayIndexToCalendarDateTest(365*2 + 31 + 27, 2, 28, 1972);
// February 29, 1972
doDayIndexToCalendarDateTest(365*2 + 31 + 28, 2, 29, 1972);
// March 1, 1972
doDayIndexToCalendarDateTest(365*2 + 31 + 29, 3, 1, 1972);
// January 1, 1973
doDayIndexToCalendarDateTest(365*2 + 366, 1, 1, 1973);
// March 1, 2000
doDayIndexToCalendarDateTest(11017, 3, 1, 2000);
// October 18, 2016
doDayIndexToCalendarDateTest(17092, 10, 18, 2016);
}
TEST(DatetimeUtilTest, DayIndexCalendarDateInversesTest) {
for (uint32_t day_index = 16000; day_index < 19000; day_index++) {
CalendarDate calendar_date = DayIndexToCalendarDate(day_index);
EXPECT_EQ(day_index, CalendarDateToDayIndex(calendar_date));
EXPECT_EQ(day_index, CalendarDateToDayIndexAltImpl(calendar_date));
}
}
TEST(DatetimeUtilTest, DayIndexToWeekIndex) {
// This is the day index for Friday Dec 2, 2016
static const uint32_t kSomeDayIndex = 17137;
// The week index for the week containing that day.
static const uint32_t kSomeWeekIndex = 2448;
EXPECT_EQ(kSomeWeekIndex, DayIndexToWeekIndex(kSomeDayIndex));
}
TEST(DatetimeUtilTest, CalendarDateToWeekIndex) {
CalendarDate calendar_date;
// Thurs, January 1, 1970
calendar_date.day_of_month = 1;
calendar_date.month = 1;
calendar_date.year = 1970;
EXPECT_EQ(0u, CalendarDateToWeekIndex(calendar_date));
// Fri, January 2, 1970
calendar_date.day_of_month = 2;
EXPECT_EQ(0u, CalendarDateToWeekIndex(calendar_date));
// Sat, January 3, 1970
calendar_date.day_of_month = 3;
EXPECT_EQ(0u, CalendarDateToWeekIndex(calendar_date));
// Sun, January 4, 1970
calendar_date.day_of_month = 4;
EXPECT_EQ(1u, CalendarDateToWeekIndex(calendar_date));
// Mon January 5, 1970
calendar_date.day_of_month = 5;
EXPECT_EQ(1u, CalendarDateToWeekIndex(calendar_date));
// Sat January 10, 1970
calendar_date.day_of_month = 10;
EXPECT_EQ(1u, CalendarDateToWeekIndex(calendar_date));
// Sun January 11, 1970
calendar_date.day_of_month = 11;
EXPECT_EQ(2u, CalendarDateToWeekIndex(calendar_date));
// Mon January 12, 1970
calendar_date.day_of_month = 12;
EXPECT_EQ(2u, CalendarDateToWeekIndex(calendar_date));
// Wed March 4, 1970
calendar_date.day_of_month = 4;
calendar_date.month = 3;
EXPECT_EQ(9u, CalendarDateToWeekIndex(calendar_date));
// Sat March 7, 1970
calendar_date.day_of_month = 7;
EXPECT_EQ(9u, CalendarDateToWeekIndex(calendar_date));
// Sun March 8, 1970
calendar_date.day_of_month = 8;
EXPECT_EQ(10u, CalendarDateToWeekIndex(calendar_date));
}
void doWeekIndexToCalendarDateTest(uint32_t week_index, uint32_t expected_month,
uint32_t expected_day_of_month, uint32_t expected_year) {
auto calendar_date = WeekIndexToCalendarDate(week_index);
EXPECT_EQ(expected_day_of_month, calendar_date.day_of_month)
<< "week_index=" << week_index;
EXPECT_EQ(expected_month, calendar_date.month)
<< "week_index=" << week_index;
EXPECT_EQ(expected_year, calendar_date.year)
<< "week_index=" << week_index;
}
TEST(DatetimeUtilTest, WeekIndexToCalendarDate) {
// January 1, 1970
doWeekIndexToCalendarDateTest(0, 1, 1, 1970);
// January 4, 1970
doWeekIndexToCalendarDateTest(1, 1, 4, 1970);
// January 11, 1970
doWeekIndexToCalendarDateTest(2, 1, 11, 1970);
// Marcy 8, 1970
doWeekIndexToCalendarDateTest(10, 3, 8, 1970);
// Marcy 15, 1970
doWeekIndexToCalendarDateTest(11, 3, 15, 1970);
}
TEST(DatetimeUtilTest, WeekIndexCalendarDateInversesTest) {
for (uint32_t week_index = 2000; week_index < 3000; week_index++) {
CalendarDate calendar_date = WeekIndexToCalendarDate(week_index);
EXPECT_EQ(week_index, CalendarDateToWeekIndex(calendar_date));
}
}
TEST(DatetimeUtilTest, CalendarDateToMonthIndex) {
CalendarDate calendar_date;
// January 1, 1970
calendar_date.day_of_month = 1;
calendar_date.month = 1;
calendar_date.year = 1970;
EXPECT_EQ(0u, CalendarDateToMonthIndex(calendar_date));
// January 31, 1970
calendar_date.day_of_month = 31;
EXPECT_EQ(0u, CalendarDateToMonthIndex(calendar_date));
// February 1, 1970
calendar_date.month = 2;
calendar_date.day_of_month = 1;
EXPECT_EQ(1u, CalendarDateToMonthIndex(calendar_date));
// December 31, 1970
calendar_date.month = 12;
calendar_date.day_of_month = 31;
EXPECT_EQ(11u, CalendarDateToMonthIndex(calendar_date));
// January 1, 1971
calendar_date.month = 1;
calendar_date.day_of_month = 1;
calendar_date.year = 1971;
EXPECT_EQ(12u, CalendarDateToMonthIndex(calendar_date));
// March 4, 1971
calendar_date.month = 3;
calendar_date.day_of_month = 4;
calendar_date.year = 1971;
EXPECT_EQ(14u, CalendarDateToMonthIndex(calendar_date));
// March 4, 1976
calendar_date.month = 3;
calendar_date.day_of_month = 4;
calendar_date.year = 1976;
EXPECT_EQ(74u, CalendarDateToMonthIndex(calendar_date));
}
TEST(DatetimeUtilTest, DayIndexToMonthIndex) {
// This is the day index for Friday Dec 2, 2016
static const uint32_t kSomeDayIndex = 17137;
// The month index for December, 2016.
static const uint32_t kSomeMonthIndex = 563;
EXPECT_EQ(kSomeMonthIndex, DayIndexToMonthIndex(kSomeDayIndex));
}
void doMonthIndexToCalendarDateTest(uint32_t month_index,
uint32_t expected_month, uint32_t expected_year) {
auto calendar_date = MonthIndexToCalendarDate(month_index);
EXPECT_EQ(1u, calendar_date.day_of_month)
<< "month_index=" << month_index;
EXPECT_EQ(expected_month, calendar_date.month)
<< "month_index=" << month_index;
EXPECT_EQ(expected_year, calendar_date.year)
<< "month_index=" << month_index;
}
TEST(DatetimeUtilTest, MonthIndexToCalendarDate) {
// January, 1970
doMonthIndexToCalendarDateTest(0, 1, 1970);
// February, 1970
doMonthIndexToCalendarDateTest(1, 2, 1970);
// March, 1970
doMonthIndexToCalendarDateTest(2, 3, 1970);
// April, 1978
doMonthIndexToCalendarDateTest(123, 4, 1980);
}
TEST(DatetimeUtilTest, MonthIndexCalendarDateInversesTest) {
for (uint32_t month_index = 500; month_index < 1000; month_index++) {
CalendarDate calendar_date = MonthIndexToCalendarDate(month_index);
EXPECT_EQ(month_index, CalendarDateToMonthIndex(calendar_date));
}
}
TEST(DatetimeUtilTest, TimeToDayIndexTest) {
// This unix timestamp corresponds to Friday Dec 2, 2016 in UTC
// and Thursday Dec 1, 2016 in Pacific time.
static const time_t kSomeTimestamp = 1480647356;
// This is the day index for Friday Dec 2, 2016
static const uint32_t kUtcDayIndex = 17137;
// This is the day index for Thurs Dec 1, 2016
static const uint32_t kPacificDayIndex = 17136;
EXPECT_EQ(kUtcDayIndex, TimeToDayIndex(kSomeTimestamp, Metric::UTC));
// Only perform the following check when running this test in the Pacific
// timezone. Note that |timezone| is a global variable defined in <ctime>
// that stores difference between UTC and the latest local standard time, in
// seconds west of UTC. This value is not adjusted for daylight saving.
// See https://www.gnu.org/software/libc/manual/html_node/ \
// Time-Zone-Functions.html#Time-Zone-Functions
if (timezone/3600 == 8) {
EXPECT_EQ(kPacificDayIndex, TimeToDayIndex(kSomeTimestamp, Metric::LOCAL));
}
}
} // namespace util
} // namespace cobalt