1// Copyright 2016 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#if defined(_WIN32) || defined(_WIN64)
16#define _CRT_SECURE_NO_WARNINGS 1
17#endif
18
19#include "time_zone_libc.h"
20
21#include <chrono>
22#include <ctime>
23#include <tuple>
24#include <utility>
25
26#include "cctz/civil_time.h"
27#include "cctz/time_zone.h"
28
29namespace cctz {
30
31namespace {
32
33// .first is seconds east of UTC; .second is the time-zone abbreviation.
34using OffsetAbbr = std::pair<int, const char*>;
35
36// Defines a function that can be called as follows:
37//
38// std::tm tm = ...;
39// OffsetAbbr off_abbr = get_offset_abbr(tm);
40//
41#if defined(_WIN32) || defined(_WIN64)
42// Uses the globals: '_timezone', '_dstbias' and '_tzname'.
43OffsetAbbr get_offset_abbr(const std::tm& tm) {
44 const bool is_dst = tm.tm_isdst > 0;
45 const int off = _timezone + (is_dst ? _dstbias : 0);
46 const char* abbr = _tzname[is_dst];
47 return {off, abbr};
48}
49#elif defined(__sun)
50// Uses the globals: 'timezone', 'altzone' and 'tzname'.
51OffsetAbbr get_offset_abbr(const std::tm& tm) {
52 const bool is_dst = tm.tm_isdst > 0;
53 const int off = is_dst ? altzone : timezone;
54 const char* abbr = tzname[is_dst];
55 return {off, abbr};
56}
57#elif defined(__native_client__) || defined(__myriad2__) || defined(__asmjs__)
58// Uses the globals: 'timezone' and 'tzname'.
59OffsetAbbr get_offset_abbr(const std::tm& tm) {
60 const bool is_dst = tm.tm_isdst > 0;
61 const int off = _timezone + (is_dst ? 60 * 60 : 0);
62 const char* abbr = tzname[is_dst];
63 return {off, abbr};
64}
65#else
66//
67// Returns an OffsetAbbr using std::tm fields with various spellings.
68//
69#if !defined(tm_gmtoff) && !defined(tm_zone)
70template <typename T>
71OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::tm_gmtoff) = nullptr,
72 decltype(&T::tm_zone) = nullptr) {
73 return {tm.tm_gmtoff, tm.tm_zone};
74}
75#endif // !defined(tm_gmtoff) && !defined(tm_zone)
76#if !defined(__tm_gmtoff) && !defined(__tm_zone)
77template <typename T>
78OffsetAbbr get_offset_abbr(const T& tm, decltype(&T::__tm_gmtoff) = nullptr,
79 decltype(&T::__tm_zone) = nullptr) {
80 return {tm.__tm_gmtoff, tm.__tm_zone};
81}
82#endif // !defined(__tm_gmtoff) && !defined(__tm_zone)
83#endif
84
85} // namespace
86
87TimeZoneLibC::TimeZoneLibC(const std::string& name)
88 : local_(name == "localtime") {}
89
90time_zone::absolute_lookup TimeZoneLibC::BreakTime(
91 const time_point<sys_seconds>& tp) const {
92 time_zone::absolute_lookup al;
93 std::time_t t = ToUnixSeconds(tp);
94 std::tm tm;
95 if (local_) {
96#if defined(_WIN32) || defined(_WIN64)
97 localtime_s(&tm, &t);
98#else
99 localtime_r(&t, &tm);
100#endif
101 std::tie(al.offset, al.abbr) = get_offset_abbr(tm);
102 } else {
103#if defined(_WIN32) || defined(_WIN64)
104 gmtime_s(&tm, &t);
105#else
106 gmtime_r(&t, &tm);
107#endif
108 al.offset = 0;
109 al.abbr = "UTC";
110 }
111 al.cs = civil_second(tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
112 tm.tm_hour, tm.tm_min, tm.tm_sec);
113 al.is_dst = tm.tm_isdst > 0;
114 return al;
115}
116
117time_zone::civil_lookup TimeZoneLibC::MakeTime(const civil_second& cs) const {
118 time_zone::civil_lookup cl;
119 std::time_t t;
120 if (local_) {
121 // Does not handle SKIPPED/AMBIGUOUS or huge years.
122 std::tm tm;
123 tm.tm_year = static_cast<int>(cs.year() - 1900);
124 tm.tm_mon = cs.month() - 1;
125 tm.tm_mday = cs.day();
126 tm.tm_hour = cs.hour();
127 tm.tm_min = cs.minute();
128 tm.tm_sec = cs.second();
129 tm.tm_isdst = -1;
130 t = std::mktime(&tm);
131 } else {
132 t = cs - civil_second();
133 }
134 cl.kind = time_zone::civil_lookup::UNIQUE;
135 cl.pre = cl.trans = cl.post = FromUnixSeconds(t);
136 return cl;
137}
138
139std::string TimeZoneLibC::Description() const {
140 return local_ ? "localtime" : "UTC";
141}
142
143bool TimeZoneLibC::NextTransition(time_point<sys_seconds>* tp) const {
144 return false;
145}
146
147bool TimeZoneLibC::PrevTransition(time_point<sys_seconds>* tp) const {
148 return false;
149}
150
151} // namespace cctz
152