Выбрать главу

 ptime utctime = EasternTZ::local_to_utc(nytime);

 return ArizonaTZ::utc_to_local(utctime);

}

int main() {

 // May 1st 2004.

 boost::gregorian::date thedate(2004, 6, 1);

 ptime nytime(thedate, hours(19)); // 7 pm

 ptime aztime = NYtoAZ(nytime);

 cout << "1 мая 2004 г. когда было " << nytime.time_of_day().hours();

 cout << ":00 часов в Нью-Йорке, было " << aztime.time_of_day().hours();

 cout << ":00 часов в Аризоне" << endl;

}

Программа из примера 5.8 выводит следующее.

1 мая 2004 г., когда было 19:00 часов в Нью-Йорке, было 16:00 часов в Аризоне

Обсуждение

Преобразование часовых поясов в примере 5.8 выполняется в два шага. Вначале время преобразуется в UTC, а затем время в UTC преобразуется во второй часовой пояс. Заметьте, что часовые пояса в библиотеке Boost date_time представлены как типы, использующие шаблон класса local_adjustor. Каждый тип содержит функции преобразования, которые преобразуют из данного часового пояса в UTC (функция local_tc_utс) и из UTC в данный часовой пояс (функция utc_to_local).

5.5. Определение номера дня в году

Проблема

Требуется определить номер дня в году. Например, 1 января — это первый день в году, 5 февраля это 36-й день в году, и так далее. Но так как некоторые годы — високосные, то после 28 февраля указанный день может иметь не такой же номер, как и в другие годы.

Решение

Решение этой проблемы требует одновременного решения сразу нескольких проблем. Во-первых, требуется знать, сколько дней в каждом месяце, что в свою очередь требует определить, является ли год високосным. Пример 5.9 содержит процедуры, выполняющие эти вычисления.

Пример 5.9. Процедуры, определяющие номер дня в году

#include <iostream>

using namespace std;

enum MonthEnum {

 jan = 0, feb = 1, mar = 2, apr = 3, may = 4, jun = 5,

 jul = 6, aug = 7, sep = 8, oct = 9, nov = 10, dec = 11

};

bool isLeapYear(int y) {

 return (y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0));

}

const int arrayDaysInMonth[] = {

 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31

};

int n;

int arrayFirstOfMonth[] = {

 n = 0,

 n += arrayDaysInMonth[jan],

 n += arrayDaysInMonth[feb],

 n += arrayDaysInMonth[mar],

 n += arrayDaysInMonth[apr],

 n += arrayDaysInMonth[may],

 n += arrayDaysInMonth[jun],

 n += arrayDaysInMonth[jul],

 n += arrayDaysInMonth[aug],

 n += arrayDaysInMonth[sep],

 n += arrayDaysInMonth[::oct],

 n += arrayDaysInMonth[nov]

};

int daysInMonth(MonthEnum month, int year) {

 if (month == feb) {

  return isLeapYear(year) ? 29 : 28;

 } else {

  return arrayDaysInMonth[month];

 }

}

int firstOfMonth(MonthEnum month, int year) {

 return arrayFirstOfMonth[month] + isLeapYear(year);

}

int dayOfYear(MonthEnum month, int monthDay, int year) {

 return firstOfMonth(month, year) + monthDay - 1;

}

int main() {

 cout << "1 июля 1971 г. был " << dayOfYear(jul, 1, 1971);

 cout << днем года" << endl;

}

Программа из примера 5.9 выводит следующее.

1 июля 1971 г. был 181 днем года

Обсуждение

Код примера 5.9 довольно прост, но содержит набор полезных функций для работы с датами в високосных годах. Обратите внимание, что я отбросил подход, который я называю «задокументируй и молись», использованный в предыдущих рецептах. Под этим я подразумеваю, что месяцы больше не представляются индексами, вместо которых используются перечисления. Это значительно снижает вероятность программистской ошибки при передаче месяца в функцию в качестве ее аргумента.

Вычисление високосного года, показанное в примере 5.9, выполняется в соответствии с современным григорианским календарем. Каждый четвертый год — високосный, за исключением каждого сотого, если он не делится на 400 (т.е. 1896 год был високосным, 1900 не был, 2000 был, 2004 был, 2100 год не будет).

5.6. Определение ограниченных типов значений

Проблема

Требуются самопроверяющиеся типы числовых данных, представляющие числа в ограниченном диапазоне допустимых значений, гаком как часы в сутках или минуты в часе.

Решение

При работе с датами и временем часто возникает необходимость ограничить целые значения диапазоном допустимых значений (т.е для секунд в минуте — от 0 до 59, для часов в сутках от 0 до 23, для дней в году — от 0 до 365). Вместо того чтобы каждый раз проверять эти значения при их передаче в функцию, предпочтительной является их автоматическая проверка с помощью перегруженного оператора присвоения. Так как имеется очень большое количество таких типов, следует реализовать один тип, который сможет работать с подобной проверкой для различных числовых диапазонов. Пример 5.10 представляет реализацию шаблона класса ConstrаinedValue, который облегчает задание диапазона целых чисел и определение других ограниченных типов.

Пример 5.10. constrained_value.hpp

#ifndef CONSTRAINED_VALUE_HPP

#define CONSTRAINED_VALUE_HPP

#include <cstdlib>

#include <iostream>

using namespace std;

template<class Policy_T>

struct ConstrainedValue {

public:

 // открытые typedef

 typedef typename Policy_T policy_type;

 typedef typename Policy_T::value_type value_type;

 typedef ConstrainedValue self;

 // конструктор по умолчанию

 ConstrainedValue() : m(Policy_T::default_value) {}

 ConstrainedValue(const self& x) : m(x.m) {}

 ConstrainedValue(const value_type& x) { Policy_T::assign(m, x); }

 operator value_type() const { return m; }

 // использует функцию присвоения, определенную политикой

 void assign(const value_type& x) {

  Policy_T::assign(m, x);

 }

 // операции присвоения

 self& operator=(const value_type& x) { assign(x); return *this; }