locale.C 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Copyright (C) 2013 Emweb bvba, Kessel-Lo, Belgium.
  3. *
  4. * See the LICENSE file for terms of use.
  5. */
  6. // see http://stackoverflow.com/questions/15234527/boost-1-53-local-date-time-compiler-error-with-std-c0x
  7. #if __cplusplus >= 201103L
  8. #define BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
  9. #endif
  10. #include <Wt/WApplication>
  11. #include <Wt/WAbstractTableModel>
  12. #include <Wt/WEnvironment>
  13. #include <Wt/WComboBox>
  14. #include <Wt/WLabel>
  15. #include <Wt/WTemplate>
  16. #include <Wt/WTime>
  17. #include <Wt/WReadOnlyProxyModel>
  18. #include <Wt/WLocalDateTime>
  19. #include <boost/algorithm/string.hpp>
  20. /*
  21. * This is a model that reads the "date_time_zonespec.csv" time zone
  22. * database that comes with Boost.
  23. *
  24. * As additional functionality it offers the capability to propose a
  25. * timezone based on a known current time zone offset (obtained from
  26. * Wt::WEnvironment::timeZoneOffset()).
  27. */
  28. class TimeZoneModel : public Wt::WAbstractTableModel
  29. {
  30. public:
  31. static const int BoostTimeZoneRole = Wt::UserRole;
  32. static const int PosixTimeZoneRole = Wt::UserRole + 1;
  33. TimeZoneModel()
  34. : showOffset_(true)
  35. { }
  36. void load(const std::string& fileName)
  37. {
  38. tz_db_.load_from_file(fileName);
  39. ids_ = tz_db_.region_list();
  40. }
  41. void setShowOffset(bool enabled)
  42. {
  43. showOffset_ = enabled;
  44. }
  45. int suggestedTimeZone(int currentOffset)
  46. {
  47. boost::posix_time::ptime nowUtc
  48. = boost::posix_time::microsec_clock::universal_time();
  49. boost::posix_time::time_duration nowOffset
  50. = boost::posix_time::minutes(currentOffset);
  51. int bestPreference = 0;
  52. int bestRow = -1;
  53. for (unsigned i = 0; i < ids_.size(); ++i) {
  54. std::string id = ids_[i];
  55. boost::local_time::time_zone_ptr tz = tz_db_.time_zone_from_region(id);
  56. boost::posix_time::ptime nowLocal
  57. = boost::local_time::local_date_time(nowUtc, tz).local_time();
  58. if (nowLocal - nowUtc == nowOffset) {
  59. int pref = computePreference(id, tz);
  60. if (pref > bestPreference) {
  61. bestRow = i;
  62. bestPreference = pref;
  63. }
  64. }
  65. }
  66. return bestRow;
  67. }
  68. virtual int rowCount(const Wt::WModelIndex& parent = Wt::WModelIndex())
  69. const
  70. {
  71. if (!parent.isValid())
  72. return ids_.size();
  73. else
  74. return 0;
  75. }
  76. virtual int columnCount(const Wt::WModelIndex& parent = Wt::WModelIndex())
  77. const
  78. {
  79. if (!parent.isValid())
  80. return 1;
  81. else
  82. return 0;
  83. }
  84. static std::string locality(const std::string& id) {
  85. std::string result = id.substr(id.find('/') + 1);
  86. boost::replace_all(result, "_", " ");
  87. return result;
  88. }
  89. virtual boost::any data(const Wt::WModelIndex& index,
  90. int role = Wt::DisplayRole) const
  91. {
  92. std::string id = ids_[index.row()];
  93. switch (role) {
  94. case Wt::DisplayRole: {
  95. if (showOffset_) {
  96. boost::local_time::time_zone_ptr tz = tz_db_.time_zone_from_region(id);
  97. Wt::WTime t = Wt::WTime(0, 0, 0)
  98. .addSecs(tz->base_utc_offset().total_seconds());
  99. std::string result = locality(id) + " (GMT" +
  100. t.toString("+hh:mm").toUTF8() + ")";
  101. return result;
  102. } else
  103. return locality(id);
  104. }
  105. case Wt::LevelRole:
  106. return id.substr(0, id.find('/'));
  107. case BoostTimeZoneRole:
  108. return tz_db_.time_zone_from_region(id);
  109. case PosixTimeZoneRole:
  110. return tz_db_.time_zone_from_region(id)->to_posix_string();
  111. default:
  112. return boost::any();
  113. }
  114. }
  115. virtual boost::any headerData(int section,
  116. Wt::Orientation orientation = Wt::Horizontal,
  117. int role = Wt::DisplayRole) const
  118. {
  119. if (orientation == Wt::Horizontal) {
  120. switch (role) {
  121. case Wt::DisplayRole:
  122. return std::string("locality");
  123. default:
  124. return boost::any();
  125. }
  126. } else
  127. return boost::any();
  128. }
  129. protected:
  130. virtual int computePreference(const std::string& id,
  131. boost::local_time::time_zone_ptr tz)
  132. {
  133. /*
  134. * We implement here the following heuristic:
  135. *
  136. * If abbrev != name:
  137. * preference = 6 (this seems to select 'standard' cities in USA)
  138. * Otherwise,
  139. * take first city of the following 'preferred' list:
  140. * - Europe (5)
  141. * - Asia (4),
  142. * - Australia (3),
  143. * - America (2)
  144. * Otherwise, 1
  145. */
  146. if (tz->std_zone_abbrev() != tz->std_zone_name())
  147. return 6;
  148. else {
  149. if (boost::starts_with(id, "Europe/"))
  150. return 5;
  151. else if (boost::starts_with(id, "Australia/"))
  152. return 4;
  153. else if (boost::starts_with(id, "Asia/"))
  154. return 3;
  155. else if (boost::starts_with(id, "America/"))
  156. return 2;
  157. else
  158. return 1;
  159. }
  160. }
  161. private:
  162. boost::local_time::tz_database tz_db_;
  163. std::vector<std::string> ids_;
  164. bool showOffset_;
  165. };
  166. TimeZoneModel timeZones;
  167. class LocaleApplication : public Wt::WApplication
  168. {
  169. public:
  170. LocaleApplication(const Wt::WEnvironment& env)
  171. : WApplication(env)
  172. {
  173. messageResourceBundle().use("templates");
  174. new Wt::WLabel("Select your time zone: ", root());
  175. localeCombo_ = new Wt::WComboBox(root());
  176. info_ = new Wt::WTemplate(Wt::WString::tr("info"), root());
  177. Wt::WReadOnlyProxyModel *regions = new Wt::WReadOnlyProxyModel(this);
  178. regions->setSourceModel(&timeZones);
  179. localeCombo_->setModel(regions);
  180. localeCombo_->setCurrentIndex
  181. (timeZones.suggestedTimeZone(env.timeZoneOffset()));
  182. localeCombo_->changed().connect(this, &LocaleApplication::updateLocale);
  183. updateLocale();
  184. }
  185. void updateLocale()
  186. {
  187. std::string tz = boost::any_cast<std::string>
  188. (timeZones.index(localeCombo_->currentIndex(), 0)
  189. .data(TimeZoneModel::PosixTimeZoneRole));
  190. Wt::WLocale l = locale();
  191. l.setTimeZone(tz);
  192. setLocale(l);
  193. Wt::WString format = "yyyy-MM-dd HH:mm:ss Z";
  194. info_->bindString("time-zone", locale().timeZone());
  195. info_->bindString("utc-time",
  196. Wt::WDateTime::currentDateTime().toString(format));
  197. info_->bindString("local-time",
  198. Wt::WLocalDateTime::currentDateTime().toString(format));
  199. }
  200. private:
  201. Wt::WComboBox *localeCombo_;
  202. Wt::WTemplate *info_;
  203. };
  204. Wt::WApplication *createApplication(const Wt::WEnvironment& env)
  205. {
  206. return new LocaleApplication(env);
  207. }
  208. int main(int argc, char **argv)
  209. {
  210. timeZones.load("date_time_zonespec.csv");
  211. return Wt::WRun(argc, argv, &createApplication);
  212. }