Boost is easy. Part 2. Boost.Date_time

    As you already understood, this article will focus on the Boost.Date_Time library. Library for working with time.
     
     
     

    In the article we will consider the following parts of the library:

    • Gregorian
    • Posix time
    • Local time



    Introduction


    Now it is probably difficult to find a person who would not be faced with the need to use time for their own purposes, or rather, his digital presentation. The goals can be completely different, from simple measurements of time intervals during which a certain piece of code is executed (a completely incorrect method of profiling, by the way, but not about that now), to support full-fledged calendars in their applications, which should be relevant for any person on the globe. All these manipulations can be performed using a rather austere and easy to use, but at the same time powerful Boost.Date_Time library. The library takes into account: a leap year, a leap second , daylight saving time, etc.
    We will deal with some terminology used in the library:
    There are three global types of time sharing in the library:
    1. Time Point - a certain point in the time continuum, for example, the date of your birth
    2. Time Duration - a time interval that is not tied to any point in the time continuum
    3. Time Interval - time interval tied to a certain point in the time continuum

    It is also necessary to remember the resolution of each method of obtaining time, the resolution ( Resolution ) shows which minimum unit of time can be used in the analysis of your time object. That is, it is nothing but the degree of accuracy of the obtained time point. Finally, let's move on to a direct acquaintance with the capabilities of the library.

    boost :: gregorian


    All the examples used in this article implies using using namespace boost :: <corresponding_namespace> at the beginning of each compiled unit.
    This component presents us with the Gregorian calendar and all imaginable possibilities of working with it. It, in turn, is divided into the following components:

    Let us analyze the purpose of each of them:

    boost :: gregorian :: date - designed to simply store the date - a point in the time continuum. It supports the creation of dates from a string of a certain format, as well as through the boost :: date_time :: day_clock class , based on the standard C \ C ++ mechanism associated with time_t
    In addition to storing the date (which cannot be changed on an object, except by means of operator =) the class has methods * for receiving specific parts of the date (year (), month (), day () etc.), converting the internal representation of the date into a string, of a specific format (to_simple_string (), to_iso_string (), to_iso_extended_string ()) and converting to (and vice versa) the standard C \ C ++ structure tm (to_tm (), date_from_tm ())

    * -I don’t give a description of each function, moreover, I won’t, gives a complete list of available functions, you can see their list in the links corresponding to a particular class. There are quite a lot of functions and they are quite easy to use, so I omit the presence of the function parameters if I consider it insignificant at the moment.
    Example:
    date xGPWStart(1941, Jun, 22);
    date xNowdays = day_clock::local_day();
    std::cout << "The Great Patriotic War was started in " << xGPWStart << std::endl;
    std::cout << "And the current data is " << xNowdays;

    * This source code was highlighted with Source Code Highlighter.


    Conclusion:
    The Great Patriotic War was started in 1941-Jun-22
    And the current data is 2009-Jul-26

    boost :: gregorian :: date_duration - used to count days, using boost :: gregorian :: date for calculations.
    For the convenience of working with date_duration, there are three classes: months_duration , years_duration and weeks_duration (there are also typedefs for these types, presented for convenience: months , years and weeks, respectively), which can be added or subtracted from date_duration to get the desired result. There is a pitfall associated with these three classes. If you use them in your calculations, then you can get a result that you do not expect. I will give an example:
    date xSomeDay(1999, Jan, 28);
    date xDayInNextMonth;
    std::cout << "That's right: " << ( xDayInNextMonth = xSomeDay + months(1) ) << std::endl;
    std::cout << "And that's not: " << xDayInNextMonth + months(1);

    * This source code was highlighted with Source Code Highlighter.

    That's right: 1999-Feb-28
    And that's not: 1999-Mar-31

    This behavior is due to the peculiarity of the months_duration class , which will always use the end of the month in arithmetic operations if the initial object pointed to one of the possible numbers that end the months (28, 29, 30, 31). Be careful when using this type, by the way month_iterator is devoid of this drawback (advantages?), But we'll talk about it later.

    boost :: gregorian :: date_period- the class is presented for convenient presentation of the interval between two dates, can be used to determine the occurrence of a certain date in the time interval (contains ()), intersect intervals (intersects (), intersection ()), adjacency dates (is_adjacent ()) and determine the relationship the location of one date relative to another (is_after (), is_before ()). In addition, there are methods for combining intervals (merge (), span ()) and changing them (shift (), expand ()). It is important to remember that the last money in the period is not included in the entire period, that is, in the period 1-Jan-1999 \ 10-Jan-1999, the last day will be January 9, and not 10.
    Example:
    date_period xGPWDuration( date(1941, Jun, 22), date(1945, May, 9) );
    date_period xStalinLifeYears( date(1878, Dec, 18), date(1953, Mar, 6) ); date_period xJukovsIncorrectLifeYears( date(1896, Dec, 6), date(1974, Jun, 14) );
    std::cout << "The Great Patriotic War duration is " << xGPWDuration << std::endl;
    std::cout << "Was the GPW inside the Stalin's life years? " << std::boolalpha << xStalinLifeYears.contains(xGPWDuration) << std::endl;
    std::cout << "Jukov's incorrect life years is " << xJukovsIncorrectLifeYears << std::endl;
    xJukovsIncorrectLifeYears.expand( days(5) );
    std::cout << "Jukov's correct life years is " << xJukovsIncorrectLifeYears << std::endl;
    //Last day isn't included in the interval
    date_period xFirstPeriod( date(1999, Jan, 1), date(1999, Jan, 10) );
    date_period xSecondPeriod( date(1999, Jan, 10), date(1999, Jan, 12) );
    std::cout << "Does these periods intersect? " << std::boolalpha << xFirstPeriod.intersects(xSecondPeriod) << std::endl;

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    The Great Patriotic War duration is [1941-Jun-22/1945-May-08]
    Was the GPW inside the Stalin's life years? true
    Jukov's correct life years is [1896-Dec-06/1974-Jun-13]
    Jukov's correct life years is [1896-Dec-01/1974-Jun-18]
    Does these periods intersect? false

    boost :: gregorian :: date_iterator - as the name implies - this is a typical iterator designed to “move” on dates. date_iterator itself is not interesting, because it is an abstract class, its descendant classes are more interesting: day_iterator , week_iterator , month_iterator , year_iterator .
    As an example, we use the example from date_duration , in which we received an incorrect date (due to pitfalls with months). As I mentioned before, there are no such problemsin date_iterator :
    month_iterator xSomeDay(date(1999, Jan, 28));
    std::cout << "That's right: " << *++xSomeDay << std::endl;
    std::cout << "And that's too!: " << *++xSomeDay;

    * This source code was highlighted with Source Code Highlighter.

    That's right: 1999-Feb-28
    And that's too !: 1999-Mar-28


    Algorithms for working with dates - a set of diverse classes and functions for various manipulations on dates. Each class has a get_data () method that allows you to get the date generated by this class. Classes provide us with the following functionality:
    • Get the first, last or arbitrary day for a given month and week (first_day_of_the_week_in_the_month (), last_day_of_the_week_in_the_month (), nth_day_of_the_week_in_the_month). The day of the week to search is set.
    • Set partial date (without year) (partial_date ()). And then get the full date using get_data ()
    • Calculate the first day of the week before or after the specified date (first_day_of_the_week_before (), first_day_of_the_week_after ()). The day of the week is set for calculation

    Functions provide the following functionality:
    • Calculate the number of days from the current date to the next or previous specified day of the week (days_until_weekday (), days_before_week_day ()).
    • Generate a date that will be the date of the next or previous specified day of the week. A new date will be generated, relative to the predefined date.

    Examples:
    last_day_of_the_week_in_month xLastFriday(Friday, Jul);
      partial_date xJunTen(10, Jun);
      std::cout << "What is the date of the last friday in the July 2009? " << xLastFriday.get_date(2009) << std::endl;
      std::cout << "Just dusplay 10 Jun of 2009 " << xJunTen.get_date(2009) << std::endl;
      std::cout << "How much days from now till next friday? " << days_until_weekday( day_clock::local_day(), greg_weekday(Friday) )<< std::endl;

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    What is the date of the last friday in the July 2009? 2009-Jul-31
    Just dusplay 10 Jun of 2009 2009-Jun-10
    How much days from now till next friday? 5

    boost :: gregorian :: gregorian_calendar - provides a useful set of static functions for working with dates.
    Instead of describing the functions, I will give an example of their use (the functions are simple and their name speaks for itself):
      std::cout << "What the day of the GPW begining? " << DayToString( gregorian_calendar::day_of_week( gregorian_calendar::ymd_type(1941, Jun, 22) ) ) << std::endl;
      std::cout << "And what is the number of this day frome the epoch start? " << gregorian_calendar::day_number( gregorian_calendar::ymd_type(1941, Jun, 22) ) << std::endl;
      std::cout << "And what is the number of this day frome the epoch start? " << gregorian_calendar::day_number( gregorian_calendar::ymd_type(1400, Jan, 1) ) << std::endl;
      std::cout << "What is the last day in the February 1941? " << gregorian_calendar::end_of_month_day(1941, Feb) << std::endl;
      std::cout << "What is the date of the 3333333 day from the epoch start? " << date( gregorian_calendar::from_day_number(3333333) ) << std::endl;
      std::cout << "Is the 2004 year a leap year? " << std::boolalpha << gregorian_calendar::is_leap_year(2004) << std::endl;

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    What the day of the GPW begining? Sunday
    And what is the number of this day frome the epoch start? 2430168
    And what is the number of this day frome the epoch start? 2232400
    What is the last day in the February 1941? 28
    What is the date of the 3333333 day from the epoch start? 4414-Apr-03
    Is the 2004 year a leap year? true

    Empirically, it was found that for the functions day_number () and from_day_number (), the minimum values ​​are 1400-Jan-1 and 2232400, respectively. If we try to use a date earlier than 1400-Jan-1, we get an exception. The same is true for the number of days.

    boost :: posix_time


    This component provides us with a convenient method of working with points in time, but unlike boost :: gregorian boost :: posix_time provides the ability to work with lower-resolution time points (up to nanoseconds), a high part of the resolution (date) is implemented using boost: : gregorian . The component is especially convenient for tasks in which high accuracy of obtaining time is required (for example, a record line in a log file). It is divided into the following parts:

    Let's analyze the purpose of each part: boost :: posix_time :: ptime - represents a point in the time continuum. Very similar to boost :: gregorian: date but with a resolution of up to microseconds. When creating an instance of a class using gregorian: date only , the “low-resolution” part is set at midnight (all zeros). Usage example:



    ptime xTime(date(1961, Apr, 12), hours(9) + minutes(7));
    std::cout << "Did you know that Gagrin said \"Poehali\" at " << xTime << "\n";
    ptime xTimeStr( time_from_string("1961-04-12 09.07.00.0000") );
    std::cout << "And the same time point constructed from a string: " << xTimeStr << "\n";
    std::cout << "Current time with second resolution: " << second_clock::local_time() << "\nAnd with microsecond:" << microsec_clock::local_time();

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    Did you know that Gagrin said "Poehali" at 1961-Apr-12 09:07:00
    And the same time point constructed from a string: 1961-Apr-12 09:07:00
    Current time with second resolution: 2009-Jul- 29 16:41:51
    And with microsecond: 2009-Jul-29 16: 41: 51.087000


    boost :: posix_time :: time_duration - represents the duration in time, which is not tied to a specific date. The maximum resolution of duration is limited to nanoseconds if the library is compiled with the macro BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG , and microseconds, by default. Information can be obtained from the object on the number of seconds \ microseconds \ milliseconds \ nanoseconds (with the appropriate assembly) that are contained in the current time duration.
    Example:
    time_duration xTime(1,2,3);
    std::cout << "Print time: " << xTime << "\n";
    std::cout << "Print increased time: " << xTime + hours(3) + seconds(2) + minutes(6) + milliseconds(15) + microseconds(25) << "\n";
    std::cout << "Print total seconds: " << xTime.total_seconds() << " milliseconds: " <<
              xTime.total_milliseconds() << " microseconds: " << xTime.total_microseconds() << "\n";

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    Print time: 01:02:03
    Print increased time: 04: 08: 05.015025
    Print total seconds: 3723 milliseconds: 3723000 microseconds: 3723000000

    boost :: posix_time :: time_period - represents a time span, the class is similar to gregorian :: date_period , but has a lower resolution. The class’s functionality allows you to define the occurrence (contains ()), intersection (intersects ()), and the length (length ()) of the intervals. There is also the possibility of expanding (expand ()), shifting (shift ()) and combining (merge ()) intervals.
    Example:
      ptime xDoomsday( date(2012, Jan, 1) );
      time_period xArmageddonLast(xDoomsday, hours(1));
      time_period xChakNorrisSmoke(xDoomsday, minutes(1));
      std::cout << "Doomsday was during: " << xArmageddonLast<< "\n";
      std::cout << "Chak Norris was smoking at " << xChakNorrisSmoke << "\n";
      std::cout << "Did Chak Norris smoke during Doomsday breathtaking?" << std::boolalpha <
    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    Doomsday was during: [2012-Jan-01 00: 00: 00/2012-Jan-01 00: 59: 59.999999]
    Chak Norris was smoking at [2012-Jan-01 00: 00: 00/2012-Jan-01 00 : 00: 59.999999]
    Did Chak Norris smoke during Doomsday breathtaking? True

    boost :: posix_time :: time_iterator - designed (as everyone probably guessed :)) to iterate over time. A nice feature of this iterator is the ability to set the time interval that will be used in the iteration, i.e. how much and in what units the current point in the time continuum will change at each iteration. As temporary units, all units from an hour to nanoseconds can be used (if collected with the corresponding flag)
    I will give a small example:
    ptime xTime(date(2012, Jan, 1));
    time_iterator xIt(xTime, hours(6));
    std::cout << "6 hours after Domsday has come!!!" << *++xIt;

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    6 hours after Domsday has come !!! 2012-Jan-01 06:00:00

    Local time system


    This component gives us the opportunity to work with time, in different time zones and with different rules for switching to daylight saving time. This is a rather powerful and necessary component, especially necessary for those applications where it is not just the current (local) time that is important, but it is important to take into account various time offsets, according to regional agreements regarding time (daylight saving time, time zone, etc.). So, I will give a list of parts that are included in this component:


    boost :: local_time :: posix_time_zone is a set of data and rules for representing time zones (offset relative to GMT, daylight saving time rules, the name of the time zone and its abbreviation). An object of this type is created based on a string; the string format is a standardized POSIX format (IEEE Std 1003.1) for time zones.
    In general, this line looks like this:
    std offset dst [offset], start [/ time], end [/ time]
    std - Abbreviation for the time zone.
    offset - Offset relative to GMT.
    dst - Abbreviation for time zone, during the summer time.
    [offset]- Shows how much time (in hours) changes when switching to summer time. Optional parameter.
    start and end - Set the daylight saving time interval.
    [/ time] - Sets the exact time within the day at which daylight saving time begins or daylight saving time ends.
    offset and time have the following format: [+ | -] hh [: mm [: ss]] {h = 0-23, m / s = 0-59}
    start and end can be represented in one of the following formats:
    • Mm.wd {month = 1-12, week = 1-5 (5 is always the last), day = 0-6}
    • Jn {n = 1-365 February 29 - not taken into account}
    • n {n = 0-365 Includes February 29th in leap years}

    You can get each part of this line separately, using the methods of this class. I see no reason to give their names here, they are quite transparent and reflect the essence of their purpose, so refer to the documentation for a list of them.
    As an example, I used the GMT + 3 time zone (Moscow time):
    posix_time_zone xZone("MSK+3MSD+01,M3.5.0/02:00,M10.5.0/02:00");
    std::cout << "Dailight period in 2009 started at " << xZone.dst_local_start_time(2009) << "\nAnd it will finish at " << xZone.dst_local_end_time(2009);

    * This source code was highlighted with Source Code Highlighter.


    Conclusion:
    Dailight period in 2009 started at 2009-Mar-29 02:00:00
    And it will finish at 2009-Oct-25 02:00:00


    boost :: local_time :: tz_database is a convenient class for storing many different time zones. When creating an object, an empty database is created (loudly said of course :)), after which it can be manually filled using the add_record () method or read from a csv (comma separated values) file, an example of such a file (with a large number of records ) is contained in % boost% \ libs \ date_time \ data \ date_time_zonespec.csv
    The format of the record inside this file must comply with the following standard:
    “ID”, “STD ABBR”, “STD NAME”, “DST ABBR”, “DST NAME”, “GMT offset”, “DST adjustment”, “DST Start Date rule”, “Start time”, “DST End date rule "," End time "

    Where:
    ID - Contains a string that uniquely identifies the given time zone.
    STD ABBR, STD NAME, DST ABBR, DST NAME - These fields are filled with lines with names and abbreviations of standard and summer time, often the names and abbreviations are identical.
    GMT offset - The time offset, relative to Greenwich. Its format is: {+ | -} hh: mm [: ss]
    DST adjustment - Offset relative to GMT offset during daylight saving time. The format is the same as GMT offset .
    DST Start Date rule - A string describing the day of the year that heralds the start of the daylight saving time period. It also has its own format (how many different formats can there be?):
    weekday; day-of-week; month
    Where:
    weekday - Ordinary numeral, indicating what day the month is in the account, we are interested in.
    day-of-week - day of the week.
    month - month.
    Example:
    -1; 0; 3 - Beginning of “summer time” in Russia (last Sunday of March)

    Start time - The time after midnight at which “summer time” takes effect. The format is the same as GMT offset .
    DST End date rule - A string describing the day of the year that announces the end of the daylight saving time period. The format is identical to the DST Start Date rule .
    End time - An analogue of Start time , only for the end of “summer time”.
    Example:
    tz_database xDb;
    xDb.load_from_file("G:\\Program files\\boost\\boost_1_39_0\\\libs\\date_time\\data\\date_time_zonespec.csv");
    const std::vector& xAllRegions = xDb.region_list();
    std::cout << "Print first 10 zone IDs from the boost time zone file:" << std::endl;
    for(std::vector::const_iterator it = xAllRegions.begin(); it != xAllRegions.begin() + 10; ++it)
        std::cout << *it << std::endl;
    std::cout << "And time when daylight saving was started at 2009: " << xDb.time_zone_from_region("Europe/Moscow")->dst_local_start_time(2009) << std::endl;

    * This source code was highlighted with Source Code Highlighter.


    Conclusion:
    Print first 10 zone IDs from the boost time zone file:
    Africa/Abidjan
    Africa/Accra
    Africa/Addis_Ababa
    Africa/Algiers
    Africa/Asmera
    Africa/Bamako
    Africa/Bangui
    Africa/Banjul
    Africa/Bissau
    Africa/Blantyre
    And time when daylight saving was started at 2009: 2009-Mar-29 02:00:00


    boost :: local_time :: custom_time_zone is a class for creating a time zone description, but unlike the previously described boost :: local_time :: posix_time_zone , this class uses four other classes when constructing a time zone. These classes are: time_duration , time_zone_names , dst_adjustment_offsets and dst_calc_rule . Since the class is not distinguished by anything outstanding, I will simply give an example of its use:
    time_zone_names xNames("Moscow Standart Time", "MST", "Moscow Daylight Time", "MDT");
    time_duration xGMTOffset(3, 0, 0);
    dst_adjustment_offsets xRulesOffsets( time_duration(1,0,0), time_duration(2,0,0), time_duration(3,0,0) );
    //Mak daylight's rule
    last_day_of_the_week_in_month xStartRule(Sunday, Mar);
    last_day_of_the_week_in_month xEndRule(Sunday, Oct);

    boost::shared_ptr xRules( new last_last_dst_rule(xStartRule, xEndRule) );
    custom_time_zone xCustomTimeZone(xNames, xGMTOffset, xRulesOffsets, xRules);
      
    std::cout << "The our time zone name is: " << xCustomTimeZone.std_zone_name() << "\n"
              << "It has an "<< xCustomTimeZone.base_utc_offset() << " offset from GMT.\n"
              << "And daylight period will end at "<< xCustomTimeZone.dst_local_end_time(2009) < std::cout << "Posix string which represents our custom_time_zone object is:\n" << xCustomTimeZone.to_posix_string();

    * This source code was highlighted with Source Code Highlighter.

    Conclusion:
    The our time zone name is: Moscow Standart Time
    It has an 03:00:00 offset from GMT.
    And daylight period will end at 2009-Oct-25 03:00:00
    Posix string which represents our custom_time_zone object is:
    MST + 03MDT + 01, M3.5.0 / 02: 00, M10.5.0 / 03: 00

    The boost :: local_time :: local_date_time and boost :: local_time :: local_time_period classes repeat similar classes from boost :: posix_time , with a binding to the time zone, so I will not consider them.

    Useful Features and Conclusion


    In Boost.Data_time, in addition to the classes for creating dates, there are useful utilities, such as: formatted input / output processing and serialization . Both mechanisms are quite simple to use and I consider their description redundant because the article, so it turned out to be quite voluminous.
    In conclusion, I would like to thank everyone who has mastered reading to this place, and also wish good luck in using Boost.Date_time in their projects, I hope those who have not used it before, this article will encourage them to use (I have already been encouraged :)).
    PS It took me almost a month to write this article, I hope that I will continue to write more often. If you find a mistake in the article, then let me know in PM, do not clog this comment.


    Also popular now: