2534595af1a2532185d22368f3c2af0224bf115b
[platform/upstream/libzypp.git] / zypp / Date.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/Date.cc
10  *
11 */
12 #include <iostream>
13 //#include <zypp/base/Logger.h>
14
15 #include <zypp/base/String.h>
16 #include <zypp/base/Xml.h>
17
18 #include <zypp/Date.h>
19
20 using std::endl;
21
22 ///////////////////////////////////////////////////////////////////
23 namespace zypp
24 {
25   ///////////////////////////////////////////////////////////////////
26   namespace
27   {
28     ///////////////////////////////////////////////////////////////////
29     /// \class LocaleGuard
30     /// \brief Temporarily adjust Locale
31     /// \ingroup RAII
32     struct LocaleGuard
33     {
34       LocaleGuard()
35       {
36         const char * tmp = ::setlocale( LC_TIME, NULL );
37         _mylocale = tmp ? tmp : "";
38
39         if ( _mylocale.find( "UTF-8" ) == std::string::npos
40           && _mylocale.find( "utf-8" ) == std::string::npos
41           && _mylocale != "POSIX"
42           && _mylocale != "C"
43           && _mylocale != "" )
44         {
45           // language[_territory][.codeset][@modifier]
46           // add/exchange codeset with UTF-8
47           std::string needLocale = ".UTF-8";
48           std::string::size_type loc = _mylocale.find_first_of( ".@" );
49           if ( loc != std::string::npos )
50           {
51             // prepend language[_territory]
52             needLocale = _mylocale.substr( 0, loc ) + needLocale;
53             loc = _mylocale.find_last_of( "@" );
54             if ( loc != std::string::npos )
55             {
56               // append [@modifier]
57               needLocale += _mylocale.substr( loc );
58             }
59           }
60           else
61           {
62             // append ".UTF-8"
63             needLocale = _mylocale + needLocale;
64           }
65           ::setlocale( LC_TIME, needLocale.c_str() );
66         }
67         else
68         {
69           // no need to change the locale
70           _mylocale.clear();
71         }
72       }
73
74       ~LocaleGuard()
75       {
76         if ( ! _mylocale.empty() )
77           ::setlocale( LC_TIME, _mylocale.c_str() );
78       }
79     private:
80       std::string _mylocale;
81     };
82     ///////////////////////////////////////////////////////////////////
83
84     inline bool isDST( struct tm & tm )
85     {
86       time_t t = ::mktime( &tm );
87       struct tm *tm2 = ::localtime( &t );
88       return ( tm2 && tm2->tm_isdst > 0 );
89     }
90
91     inline const char * _dateFormat( Date::DateFormat dateFormat_r )
92     {
93       static const char * fmt[] = {
94         "",             ///< ""
95         "%Y-%m-%d",     ///< 2014-02-07
96         "%Y-%m",        ///< 2014-02
97         "%Y",           ///< 2014
98         "%G-W%V",       ///< 2014-W06
99         "%G-W%V-%u",    ///< 2014-W06-5 (1 is Monday)
100         "%Y-%j",        ///< 2014-038
101       };
102       return fmt[dateFormat_r.asIntegral()];
103     }
104
105     inline const char * _timeFormat( Date::TimeFormat timeFormat_r )
106     {
107       static const char * fmt[] = {
108         "",             ///< ""
109         "%H:%M:%S",     ///< 07:06:41
110         "%H:%M",        ///< 07:06
111         "%H",           ///< 07
112       };
113       return fmt[timeFormat_r.asIntegral()];
114     }
115
116     inline const char * _timeZoneFormat( Date::TimeZoneFormat timeZoneFormat_r )
117     {
118       static const char * fmt[] = {
119         "",             ///< ""
120         " %Z",          ///< UTC, CET, ...
121         "%z",           ///< +0000
122       };
123       return fmt[timeZoneFormat_r.asIntegral()];
124     }
125
126     inline std::string doForm( const std::string & format_r, Date::TimeBase base_r, const Date::ValueType & date_r )
127     {
128       if ( ! date_r )
129         return "0";
130
131       LocaleGuard guard;
132       static char buf[512];
133       if ( ! strftime( buf, 512, format_r.c_str(), (base_r == Date::TB_UTC ? gmtime : localtime)( &date_r ) ) )
134         *buf = '\0';
135       else
136       {
137         // strip a trailing '00' in a timeZoneFormat
138         unsigned l = ::strlen( buf );
139         if ( l >= 5
140           && ( buf[l-1] == '0' )
141           && ( buf[l-2] == '0' )
142           && ( buf[l-5] == '+' || buf[l-5] == '-') )
143           buf[l-2] = '\0';
144       }
145       return buf;
146     }
147   } // namespace
148   ///////////////////////////////////////////////////////////////////
149
150   const Date::ValueType Date::second;
151   const Date::ValueType Date::minute;
152   const Date::ValueType Date::hour;
153   const Date::ValueType Date::day;
154   const Date::ValueType Date::month28;
155   const Date::ValueType Date::month29;
156   const Date::ValueType Date::month30;
157   const Date::ValueType Date::month31;
158   const Date::ValueType Date::month;
159   const Date::ValueType Date::year365;
160   const Date::ValueType Date::year366;
161   const Date::ValueType Date::year;
162
163   Date::Date( const std::string & seconds_r )
164   { str::strtonum( seconds_r, _date ); }
165
166   Date::Date( const std::string & date_str, const std::string & format )
167     : _date( Date( date_str, format, TB_LOCALTIME ) )
168   {}
169
170   Date::Date( const std::string & date_str, const std::string & format, Date::TimeBase base_r )
171     : _date(0)
172   {
173     LocaleGuard guard;
174
175     struct tm tm = {0,0,0,0,0,0,0,0,0,0,0};
176     char * res = ::strptime( date_str.c_str(), format.c_str(), &tm );
177     if (res == NULL)
178       throw DateFormatException( str::form( "Invalid date format: '%s'", date_str.c_str() ) );
179
180     if ( isDST(tm) )
181       tm.tm_isdst = 1;
182     _date = (base_r == TB_UTC ? ::timegm : ::timelocal)( &tm );
183   }
184
185   std::string Date::form( const std::string & format_r, Date::TimeBase base_r ) const
186   { return doForm( format_r, base_r, _date ); }
187
188   std::string Date::print( DateFormat dateFormat_r, TimeFormat timeFormat_r, TimeZoneFormat timeZoneFormat_r, TimeBase base_r ) const
189   {
190     str::Str str;
191     if ( dateFormat_r != DateFormat::none )
192       str << _dateFormat( dateFormat_r );
193     if ( timeFormat_r != TimeFormat::none )
194     {
195       if ( dateFormat_r != DateFormat::none )
196         str << ' ';
197       str << _timeFormat( timeFormat_r );
198       if ( timeZoneFormat_r != TimeZoneFormat::none )
199         str << _timeZoneFormat( timeZoneFormat_r );
200     }
201     return doForm( str, base_r, _date );
202   }
203
204   std::string Date::printISO( DateFormat dateFormat_r, TimeFormat timeFormat_r, TimeZoneFormat timeZoneFormat_r, TimeBase base_r ) const
205   {
206     str::Str str;
207     if ( dateFormat_r != DateFormat::none )
208       str << _dateFormat( dateFormat_r );
209     if ( timeFormat_r != TimeFormat::none )
210     {
211       if ( dateFormat_r != DateFormat::none )
212         str << 'T';
213       str << _timeFormat( timeFormat_r );
214       switch ( timeZoneFormat_r.asEnum() )
215       {
216         case TimeZoneFormat::none:
217           break;
218         case TimeZoneFormat::name:
219           if ( base_r == TB_UTC )
220           {
221             str << 'Z';
222             break;
223           }
224           // else: FALLTHROUGH and print offset!
225         case TimeZoneFormat::offset:
226           str << _timeZoneFormat( TimeZoneFormat::offset );
227           break;
228       }
229     }
230     return doForm( str, base_r, _date );
231   }
232
233   std::ostream & dumpAsXmlOn( std::ostream & str, const Date & obj, const std::string & name_r )
234   {
235     return xmlout::node( str, name_r, {
236       { "time_t",       Date::ValueType(obj) },
237       { "text",         obj.printISO( Date::TB_UTC ) },
238     } );
239   }
240
241 } // namespace zypp
242 ///////////////////////////////////////////////////////////////////