1 // This is a part of Chrono.
2 // See README.md and LICENSE.txt for details.
4 //! The local (system) time zone.
6 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
7 use sys
::{self, Timespec}
;
9 use super::fixed
::FixedOffset
;
10 use super::{LocalResult, TimeZone}
;
11 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
13 use naive
::{NaiveDate, NaiveDateTime}
;
15 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
16 use {Datelike, Timelike}
;
18 /// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
19 /// This assumes that `time` is working correctly, i.e. any error is fatal.
20 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
21 fn tm_to_datetime(mut tm
: sys
::Tm
) -> DateTime
<Local
> {
23 tm
.tm_nsec
+= (tm
.tm_sec
- 59) * 1_000_000_000;
28 fn tm_to_naive_date(tm
: &sys
::Tm
) -> NaiveDate
{
29 // from_yo is more efficient than from_ymd (since it's the internal representation).
30 NaiveDate
::from_yo(tm
.tm_year
+ 1900, tm
.tm_yday
as u32 + 1)
34 fn tm_to_naive_date(tm
: &sys
::Tm
) -> NaiveDate
{
35 // ...but tm_yday is broken in Windows (issue #85)
36 NaiveDate
::from_ymd(tm
.tm_year
+ 1900, tm
.tm_mon
as u32 + 1, tm
.tm_mday
as u32)
39 let date
= tm_to_naive_date(&tm
);
40 let time
= NaiveTime
::from_hms_nano(
46 let offset
= FixedOffset
::east(tm
.tm_utcoff
);
47 DateTime
::from_utc(date
.and_time(time
) - offset
, offset
)
50 /// Converts a local `NaiveDateTime` to the `time::Timespec`.
51 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
52 fn datetime_to_timespec(d
: &NaiveDateTime
, local
: bool
) -> sys
::Timespec
{
53 // well, this exploits an undocumented `Tm::to_timespec` behavior
54 // to get the exact function we want (either `timegm` or `mktime`).
55 // the number 1 is arbitrary but should be non-zero to trigger `mktime`.
56 let tm_utcoff
= if local { 1 }
else { 0 }
;
59 tm_sec
: d
.second() as i32,
60 tm_min
: d
.minute() as i32,
61 tm_hour
: d
.hour() as i32,
62 tm_mday
: d
.day() as i32,
63 tm_mon
: d
.month0() as i32, // yes, C is that strange...
64 tm_year
: d
.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`.
65 tm_wday
: 0, // to_local ignores this
66 tm_yday
: 0, // and this
69 // do not set this, OS APIs are heavily inconsistent in terms of leap second handling
76 /// The local timescale. This is implemented via the standard `time` crate.
78 /// Using the [`TimeZone`](./trait.TimeZone.html) methods
79 /// on the Local struct is the preferred way to construct `DateTime<Local>`
85 /// use chrono::{Local, DateTime, TimeZone};
87 /// let dt: DateTime<Local> = Local::now();
88 /// let dt: DateTime<Local> = Local.timestamp(0, 0);
90 #[derive(Copy, Clone, Debug)]
94 /// Returns a `Date` which corresponds to the current date.
95 pub fn today() -> Date
<Local
> {
99 /// Returns a `DateTime` which corresponds to the current date.
100 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
101 pub fn now() -> DateTime
<Local
> {
102 tm_to_datetime(Timespec
::now().local())
105 /// Returns a `DateTime` which corresponds to the current date.
106 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
107 pub fn now() -> DateTime
<Local
> {
109 let now
: DateTime
<Utc
> = super::Utc
::now();
111 // Workaround missing timezone logic in `time` crate
112 let offset
= FixedOffset
::west((js_sys
::Date
::new_0().get_timezone_offset() as i32) * 60);
113 DateTime
::from_utc(now
.naive_utc(), offset
)
117 impl TimeZone
for Local
{
118 type Offset
= FixedOffset
;
120 fn from_offset(_offset
: &FixedOffset
) -> Local
{
124 // they are easier to define in terms of the finished date and time unlike other offsets
125 fn offset_from_local_date(&self, local
: &NaiveDate
) -> LocalResult
<FixedOffset
> {
126 self.from_local_date(local
).map(|date
| *date
.offset())
129 fn offset_from_local_datetime(&self, local
: &NaiveDateTime
) -> LocalResult
<FixedOffset
> {
130 self.from_local_datetime(local
).map(|datetime
| *datetime
.offset())
133 fn offset_from_utc_date(&self, utc
: &NaiveDate
) -> FixedOffset
{
134 *self.from_utc_date(utc
).offset()
137 fn offset_from_utc_datetime(&self, utc
: &NaiveDateTime
) -> FixedOffset
{
138 *self.from_utc_datetime(utc
).offset()
141 // override them for avoiding redundant works
142 fn from_local_date(&self, local
: &NaiveDate
) -> LocalResult
<Date
<Local
>> {
143 // this sounds very strange, but required for keeping `TimeZone::ymd` sane.
144 // in the other words, we use the offset at the local midnight
145 // but keep the actual date unaltered (much like `FixedOffset`).
146 let midnight
= self.from_local_datetime(&local
.and_hms(0, 0, 0));
147 midnight
.map(|datetime
| Date
::from_utc(*local
, *datetime
.offset()))
150 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
151 fn from_local_datetime(&self, local
: &NaiveDateTime
) -> LocalResult
<DateTime
<Local
>> {
152 let mut local
= local
.clone();
153 // Get the offset from the js runtime
154 let offset
= FixedOffset
::west((js_sys
::Date
::new_0().get_timezone_offset() as i32) * 60);
155 local
-= ::Duration
::seconds(offset
.local_minus_utc() as i64);
156 LocalResult
::Single(DateTime
::from_utc(local
, offset
))
159 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
160 fn from_local_datetime(&self, local
: &NaiveDateTime
) -> LocalResult
<DateTime
<Local
>> {
161 let timespec
= datetime_to_timespec(local
, true);
163 // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
164 let mut tm
= timespec
.local();
165 assert_eq
!(tm
.tm_nsec
, 0);
166 tm
.tm_nsec
= local
.nanosecond() as i32;
168 LocalResult
::Single(tm_to_datetime(tm
))
171 fn from_utc_date(&self, utc
: &NaiveDate
) -> Date
<Local
> {
172 let midnight
= self.from_utc_datetime(&utc
.and_hms(0, 0, 0));
173 Date
::from_utc(*utc
, *midnight
.offset())
176 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind"))]
177 fn from_utc_datetime(&self, utc
: &NaiveDateTime
) -> DateTime
<Local
> {
178 // Get the offset from the js runtime
179 let offset
= FixedOffset
::west((js_sys
::Date
::new_0().get_timezone_offset() as i32) * 60);
180 DateTime
::from_utc(*utc
, offset
)
183 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"), feature = "wasmbind")))]
184 fn from_utc_datetime(&self, utc
: &NaiveDateTime
) -> DateTime
<Local
> {
185 let timespec
= datetime_to_timespec(utc
, false);
187 // datetime_to_timespec completely ignores leap seconds, so we need to adjust for them
188 let mut tm
= timespec
.local();
189 assert_eq
!(tm
.tm_nsec
, 0);
190 tm
.tm_nsec
= utc
.nanosecond() as i32;
199 use offset
::TimeZone
;
203 fn test_local_date_sanity_check() {
205 assert_eq
!(Local
.ymd(2999, 12, 28).day(), 28);
209 fn test_leap_second() {
211 let today
= Local
::today();
213 let dt
= today
.and_hms_milli(1, 2, 59, 1000);
214 let timestr
= dt
.time().to_string();
215 // the OS API may or may not support the leap second,
216 // but there are only two sensible options.
217 assert
!(timestr
== "01:02:60" || timestr
== "01:03:00", "unexpected timestr {:?}", timestr
);
219 let dt
= today
.and_hms_milli(1, 2, 3, 1234);
220 let timestr
= dt
.time().to_string();
222 timestr
== "01:02:03.234" || timestr
== "01:02:04.234",
223 "unexpected timestr {:?}",