repository state before leap second removal

This commit is contained in:
Andrew Gallant 2024-02-19 09:51:27 -05:00
parent e4dfabe415
commit ee0cced9c0
No known key found for this signature in database
GPG key ID: B2E3A4923F8B0D44
83 changed files with 40572 additions and 0 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/Cargo.lock
/target
/tmp
/ecma-test262

View file

@ -6,3 +6,45 @@ description = "WIP"
license = "Unlicense OR MIT"
edition = "2021"
exclude = ["/.github", "/tmp"]
autotests = false
[features]
default = ["std"]
std = ["alloc"]
alloc = []
logging = ["dep:log"]
[dependencies]
log = { version = "0.4.21", optional = true }
[dev-dependencies]
anyhow = "1.0.81"
env_logger = "0.11.3"
insta = "1.38.0"
quickcheck = { version = "1.0.3", default-features = false }
tabwriter = "1.4.0"
walkdir = "2.5.0"
[[test]]
path = "tests/lib.rs"
name = "integration"
[profile.test]
opt-level = 3
[profile.testrelease]
inherits = "test"
debug-assertions = false
[package.metadata.docs.rs]
# We want to document all features.
all-features = true
# Since this crate's feature setup is pretty complicated, it is worth opting
# into a nightly unstable option to show the features that need to be enabled
# for public API items. To do that, we set 'docsrs', and when that's enabled,
# we enable the 'doc_auto_cfg' feature.
#
# To test this locally, run:
#
# RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features
rustdoc-args = ["--cfg", "docsrs"]

663
NOTES.md Normal file
View file

@ -0,0 +1,663 @@
Links:
* [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html)
* [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339)
* [Syntax extensions to RFC 3339](https://ryzokuken.dev/draft-ryzokuken-datetime-extended/documents/rfc-3339.html)
* [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536)
* [New RFC 8536](https://datatracker.ietf.org/doc/draft-murchison-rfc8536bis/)
* [RFC 9557](https://www.rfc-editor.org/rfc/rfc9557.html)
* [RFC 2822 datetimes](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3)
* [FreeDesktop.org on localtime](https://www.freedesktop.org/software/systemd/man/latest/localtime.html)
* [About the IANA Time Zone Database](https://data.iana.org/time-zones/tz-link.html)
* [Lisp code for *Calendrical calculations*](https://github.com/EdReingold/calendar-code2)
* [SOFA](https://www.iausofa.org/)
* [MUSL `tzset`](https://git.musl-libc.org/cgit/musl/tree/src/time/__tz.c#n127)
* [Talk about leap seconds](https://www.youtube.com/watch?v=meK8dY9l_gE&t=9s)
* [Toward a Standard C++ Date Class](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3344.pdf)
* [2023-02 - Calendrical C++: std::chrono, History, Mathematics and the Computus - Ben Deane](https://www.youtube.com/watch?v=fIWDQclgUEE)
* [Local Time Disambiguation (PEP 495)](https://peps.python.org/pep-0495/)
* [Temporal tests](https://github.com/tc39/test262/tree/main/test/built-ins/Temporal)
* [Modeling Time - Eric Evans (Joda Time?)](https://www.youtube.com/watch?v=T29WzvaPNc8&t=657s)
* [Fixing JavaScript Date Getting Started](https://maggiepint.com/2017/04/09/fixing-javascript-date-getting-started/)
* [PEP-4095](https://peps.python.org/pep-0495/)
* [Temporal API blog](https://2ality.com/2021/06/temporal-api.html)
Other Rust crates:
* [chrono](https://github.com/chronotope/chrono)
* [chrono-tz](https://github.com/chronotope/chrono-tz)
* [time](https://github.com/time-rs/time)
* [icu_calendar](https://docs.rs/icu_calendar/latest/icu_calendar/)
* [hifitime](https://docs.rs/hifitime/latest/hifitime/index.html)
* [hifitime model checking](https://model-checking.github.io/kani-verifier-blog/2023/03/31/how-kani-helped-find-bugs-in-hifitime.html)
* [kine](https://crates.io/crates/kine)
* [Flexible datetime parsing](https://crates.io/crates/parse_datetime)
* [eos](https://github.com/Rapptz/eos) (unfinished/unpublished)
* [temporal_rs](https://github.com/boa-dev/temporal)
* [tz-rs](https://github.com/x-hgg-x/tz-rs)
* [iana-time-zone](https://github.com/strawlab/iana-time-zone)
* [parse-zoneinfo](https://github.com/chronotope/parse-zoneinfo)
* [tzfile](https://github.com/kennytm/tzfile)
Other datetime libraries:
* [ThreeTen](https://www.threeten.org/threeten-extra/). Seems to have good
support for leap seconds. See https://github.com/robinst/java-threeten-test.
Read/perused:
* [Time Standards Reference](https://geometrian.com/programming/reference/timestds/index.php)
* [Actual leap seconds](https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list)
* [Even more leap seconds](https://maia.usno.navy.mil/ser7/tai-utc.dat)
* [ISO 8601 summary](https://www.cl.cam.ac.uk/~mgk25/iso-time.html)
* [POSIX `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html)
* [Joda Time User Guide](https://www.joda.org/joda-time/userguide.html)
* [JSR 310](https://jcp.org/aboutJava/communityprocess/pfd/jsr310/JSR-310-guide.html)
* [`java.time` API](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html)
* [Leap seconds](https://en.wikipedia.org/wiki/Leap_second)
* [Koka's standard `time` library](https://koka-lang.github.io/koka/doc/std_time.html)
* ["Seconds Since the Epoch"](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16)
* [Storing UTC is not a silver bullet](https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/)
* [Unix time and leap seconds](https://en.wikipedia.org/wiki/Unix_time#Leap_seconds)
* [UTC-SLS](https://www.cl.cam.ac.uk/~mgk25/time/utc-sls/)
* [`time` crate's issue on `localtime_r` unsoundness](https://github.com/time-rs/time/issues/293)
* [Pure Rust implementation of `localtime_r`](https://github.com/x-hgg-x/tz-rs)
* [Interesting issue about TAI support in Chrono](https://github.com/chronotope/chrono/issues/21)
* [NIST leap seconds via FTP](ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list)
* [Interesting use case from dtolnay for `Date` type](https://github.com/chronotope/chrono/issues/820)
* [Desirable characteristics of time systems](https://www.ucolick.org/~sla/leapsecs/picktwo.html)
* [TC39 rejecting leap second handling](https://github.com/tc39/proposal-temporal/issues/54)
* [cctz philosophy](https://github.com/google/cctz#fundamental-concepts)
* [Russ Cox on "absolute" vs "civil" time](https://github.com/golang/go/issues/19700#issuecomment-559250634)
* [Javascript code for converting between TAI and Unix](https://github.com/qntm/t-a-i)
* [TC39 "Temporal"](https://tc39.es/proposal-temporal/docs/)
* [TC39 Polyfill](https://github.com/js-temporal/temporal-polyfill)
* [TC39 on non-standard IANA time zone suffix](https://tc39.es/proposal-temporal/docs/strings.html#iana-time-zone-names)
* [Java's description of the same thing](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#ISO_ZONED_DATE_TIME)
* [Proposed RFC3339 Update](https://datatracker.ietf.org/wg/sedate/about/)
* [RFC 5545 (iCalendar) on date arithmetic](https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.6)
* [Previous section on datetime](https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.5)
* [C++ Chrono Extension](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0355r7.html)
* [How gcc's libstdc++'s chrono library deals with leap seconds](https://github.com/gcc-mirror/gcc/blob/c1d1571329b4e0923a104b6139cd7db2f0aa1c1d/libstdc%2B%2B-v3/include/std/chrono#L3207)
* [Some nice insight into C++ chrono's handling of leap seconds](https://stackoverflow.com/a/56620657)
* [Proleptic Gregorian Calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar)
* ["A Brief History of Leap Seconds"](https://www.youtube.com/watch?v=meK8dY9l_gE&t=9s)
* ["The Long, Painful History of Time"](https://naggum.no/lugm-time.html)
* This has an interesting idea to use 2000-03-01 as the epoch.
* [TC39 "Temporal"](https://tc39.es/proposal-temporal/docs/)
* [Time zones](https://en.wikipedia.org/wiki/Time_zone)
* [TAI timezone with TC39 for leap seconds](https://github.com/ryzokuken/temporal-tai)
* [On missing leap second support in Luxon](https://github.com/moment/luxon/issues/1176)
* [JSR-310 and leap seconds](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html)
* [`Clock` explicitly says leap seconds are usually ignored](https://docs.oracle.com/javase/8/docs/api/java/time/Clock.html)
* [More on JDK not supporting leap seconds](https://stackoverflow.com/questions/30984599/how-does-the-oracle-java-jvm-know-a-leap-second-is-occurring)
* [Time4J apparently handles leap seconds](https://github.com/MenoData/Time4J)
* [`time` Rust crate and leap seconds](https://github.com/time-rs/time/discussions/413)
* [ICU timezone detection](https://github.com/nodejs/node/blob/5d0a770c129c00e3942263b429f8efa4c42efba9/deps/icu-small/source/common/putil.cpp#L1065)
* [Low level date algorithms](http://howardhinnant.github.io/date_algorithms.html)
* [Howard Hinnat's C++ `date` library](https://github.com/howardhinnant/date)
* [CppCon 2015: Howard Hinnant “A C++14 approach to dates and times"](https://www.youtube.com/watch?v=tzyGjOm8AKo)
* ["Date Algorithms" by Peter Baum](https://www.researchgate.net/publication/316558298_Date_Algorithms)
* [A discussion about ranged integers in Rust](https://github.com/rust-lang/rfcs/issues/671)
* [A possibly simpler implementation of Temporal's rounding](https://github.com/fullcalendar/temporal-polyfill/blob/main/packages/temporal-polyfill/src/internal/round.ts)
* [On the naming of types in Temporal](https://github.com/unicode-org/icu4x/issues/2583#issuecomment-1251846194)
* [On time zone abbreviations](https://github.com/tc39/proposal-temporal/issues/1694)
* [Commit adding `chrono` to `libstdc++ in `gcc`](https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=9fc61d45fa15fdd3b084d30998ecda164af33e95)
* This also explains why only `tzdata.zi` is used.
* [Interesting case of (I think?) UB for a simple use of C++/Chrono](https://old.reddit.com/r/cpp/comments/l755me/stdchrono_question_of_the_day_whats_the_result_of/)
* [See also Howard Hinnant's response about Chrono's ability to use different representations for different units.](https://old.reddit.com/r/cpp/comments/l755me/stdchrono_question_of_the_day_whats_the_result_of/hul90k4/)
* Interestingly Hinnant does not seem to comment on the UB...
* Oh, he does comment [here](https://old.reddit.com/r/cpp/comments/l755me/stdchrono_question_of_the_day_whats_the_result_of/gl6y9cg/).
* And [here](https://old.reddit.com/r/cpp/comments/l755me/stdchrono_question_of_the_day_whats_the_result_of/gl8hmph/).
* Yes yes, "henny penny" and chicken little, har har har. But I thought
modern C++ was getting safer? LOL. Like I get what Howard is saying. It's
hard to do any better given what C++ is today. But man, the self reflection
(for C++ as a whole) is just missing from a lot (not all) discourse.
* [A Foundation to Sleep On](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2661.htm)
* [StackOverflow info on `timezone` tag](https://stackoverflow.com/tags/timezone/info)
* This is (to me) surprisingly practical and useful.
* [Nice description of `TZ` env var](https://www.gnu.org/software/libc/manual/2.25/html_node/TZ-Variable.html)
* [Tricky handling of `GetDynamicTimeZoneInformation` on Windows](https://github.com/unicode-org/icu/blob/943b0ca31b38f5c7ba8d58c5f3d88d34c4ebff8d/icu4c/source/common/wintz.cpp#L63-L88)
* [SPICE on UTC/TAI/etc](https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/req/time.html#SPICE%20Time%20Representations)
* [Getting TAI time on Linux requires configuration](https://www.bortzmeyer.org/tai-on-debian.html)
Links with specific targeted info that might be useful:
* [`TZ` can be ambiguous](https://github.com/chronotope/chrono/issues/1153)
* [OpenWRT support in Chrono](https://github.com/chronotope/chrono/issues/1129)
* [Support for Android in Hinnant's C++ `date` library](https://github.com/HowardHinnant/date/pull/628)
* [`TZ` can be ambiguous](https://github.com/chronotope/chrono/issues/1153)
* [`tzdb` git repo](https://github.com/eggert/tz)
* [Interesting case for datetime difference with timezone](https://github.com/tc39/proposal-temporal/pull/2760#issuecomment-1965355111)
* [Finding 25-hour leap days. Do this with `jiff`!](https://typesandtimes.net/2024/02/kazakhstan-25-hour-leap-years)
* [Curious rounding bug.](https://github.com/tc39/proposal-temporal/issues/2790#issuecomment-1971250440)
* [Nice date arithmetic/rounding test cases](https://github.com/tc39/proposal-temporal/issues/998#issuecomment-716764012)
* [Specific use cases for duration rounding](https://github.com/tc39/proposal-temporal/issues/856)
* [Good test case for subtracting zoned times](https://github.com/tc39/proposal-temporal/pull/2760#issue-2103137194)
* Rounding:
* [Proposal: Rounding method (and rounding for difference and toString) for non-Duration types](https://github.com/tc39/proposal-temporal/issues/827)
* [Proposal: rounding and balancing for Duration type (replaces #789)](https://github.com/tc39/proposal-temporal/issues/856)
* [Interesting case of rounding to months](https://github.com/tc39/proposal-temporal/issues/2535)
* [difference should always return a "top-heavy balanced" duration with largest-first order of operations](https://github.com/tc39/proposal-temporal/issues/993#issuecomment-709711892)
* [Inconsistent results in DifferenceISODate?](https://github.com/tc39/proposal-temporal/issues/2535)
* [Discussion thread for resolution of weeks rounding bug](https://github.com/tc39/proposal-temporal/issues/2728)
* [Android's support for time zones appears weak](https://github.com/chronotope/chrono/pull/1018#issuecomment-1536311725)
* [Detecting system time zone (Rust/Chrono)](https://github.com/chronotope/chrono-tz/issues/13)
* [timestamp() doesn't match Display (Rust/Chrono)](https://github.com/chronotope/chrono/issues/318)
* [add/sub days (Rust/Chrono)](https://github.com/chronotope/chrono/pull/784)
* [Provide docs that do an audit of calendrical fallacies](https://yourcalendricalfallacyis.com/)
* [Relative duration rounding](https://github.com/tc39/proposal-temporal/issues/2792#issuecomment-2058186806)
Commentary on Rust datetime libraries
-------------------------------------
* [A comment thread on Chrono's API being complex.](https://old.reddit.com/r/rust/comments/18i8y39/learning_rust_my_experience_so_far_has_been_mixed/)
* [Chrono and locales](https://old.reddit.com/r/rust/comments/19cup9v/how_to_parse_datetime_in_locale_other_than_english/)
How to deal with the environment
--------------------------------
It seems there are at least 3 relevant environment variables? `TZ`, defined by
POSIX. Then there's [`TZDIR`](https://github.com/chronotope/chrono/issues/1265)
which is used by glibc. And then I think `ZONEINFO` is used by Go.
`TZ` is fine. I know how to deal with that. But what happens if both `TZDIR`
and `ZONEINFO` are defined?
It seems like [`TZDIR` is more broadly supported](https://man7.org/linux/man-pages/man8/tzselect.8.html).
I'm unsure of the origins of the `ZONEINFO` environment variable. Is this just
Go being a special snowflake?
It seems like we should ignore `ZONEINFO` but respect `TZDIR`. If users demand
it, we could fallback to `ZONEINFO`, but only if `TZDIR` is unset/empty.
How weeks are handled in rounding
---------------------------------
From: https://github.com/tc39/proposal-temporal/issues/827
Specifically: if largestUnit is 'month' or 'year' and smallestUnit is 'day' or
smaller, then weeks will always be zero and only days and/or months will be
included in the result. Edge cases that are NOT ambiguous are noted below.
7.7.1 If smallestUnit is 'week', then there is no ambiguity and any remainder
will go into week.
7.7.2 If largestUnit is 'day', then there is no ambiguity because weeks are
excluded and week will be zero.
7.7.3 In the degenerate case of { largestUnit: 'week', smallestUnit: 'day' }
then there is also no ambiguity so both fields will be populated.
Names
-----
* `wheel`
* `gigawatt`
* `pend`
* `planck`
* `jiff`, `jiffy`
2024-02-28
----------
* I really don't like the name `jiff::Time`. I don't mind the name
`jiff::ZonedTime` (not yet defined), but `jiff::Time` and `jiff::civil::Time`
having the same type name is just super annoying. At first I thought it would
be fine since a `civil::Time` is basically never used, so who cares what it's
called since it's stuffed away in its own module. But a `civil::DateTime` will
very likely be used occasionally, and if we renamed `civil::Time` to, say,
`civil::Clock`, then the `civil::DateTime` name would no longer make much
sense. `civil::DateTime` is pretty much the canonical name for what it is,
so... we should leave it. And that means `civil::Time` stays. Which in turn
means we probably need a different name for `jiff::Time`. The most obvious
alternative is `jiff::Instant`, but this would be too confusing of a conflict
with `std::time::Instant`. *sigh* Other ideas: `TimePoint`, `AbsoluteTime`,
`TimeInstant`, `Point`, `Moment`. Bah. I want one word. And that word should
combine nicely with a `Zoned` prefix. UPDATE(2024-03-16): I ended up going with
`jiff::Instant` and `jiff::Zoned`. I'm still not 100% solid on those names,
but I haven't found an alternative that I like better.
* We should probably balance `Span` values before returning them. See what the
`civil::Time` type currently returns for example. Just sticking to nanoseconds
and requiring users to explicitly round/balance is very likely to confuse them.
UPDATE(2024-03-16): Unfortunately, balancing by default means that sometimes
the ops won't be commutative, particularly for dates. We can balance times, but
nothing above the unit of "day."
* Add a cutesy API for easily building `Span` values. For example, perhaps
via a `ToSpan` trait, `2.hours() + 10.minutes()`. UPDATE(2024-03-16): Done, but
I'm still not 100% happy with it.
* We still need to figure out rounding in `Span` and how to deal with the
`relative-to` nonsense. I want to store it on the `Span`, but this could be
confusing at points. The alternative is to do what Temporal does and ask for
`relative-to` whenever rounding occurs. But this just... seems like overkill?
Temporal also allows one to pass the rounding options to most/all of the
date arithmetic methods, which is also something I really don't want to do.
UPDATE(2024-03-16): `relative-to` is only needed when rounding durations, so
we'll just allow users to provide it at that point. We also will not do any
rounding as part of computing spans in the first place, so we side-step needing
to deal with duration rounding complexity everywhere. This does mean users will
need to do duration rounding explicitly if they want it though, so this case
will be a bit more verbose/annoying than Temporal proper.
* We are otherwise getting pretty close to a point where we have most of the
fundamental pieces built. And now we just need to kind of put everything
together and start layering the timezone, parsing and formatting bullshit on
top of it. I had expected the base layers to require more code. Am I missing
anything? The rounding is potentially one big one, but it doesn't seem awful.
UPDATE(2024-03-16): No, rounding is awful. Saving that for when timezone stuff
is implemented.
* What about things like, "find the 4th Thursday in March 2024?" I think this
is just a matter of knowing what day a year began with. UPDATE(2024-03-16):
This is done. Along with "find the next Nth weekday."
* Do we need explicit millisecond and microsecond fields on `Span`? The
icky part is that they will be two additional `i64` values, which seems
very non-ideal. But if we don't have them, then do we still support
rounding/balancing them? I don't think we can, because then, e.g, milliseconds
and microseconds can't be "more." We have no real way to distinguish them
from one another. Sigh... UPDATE(2024-03-16): Yeah I just bit the bullet and
added them. If size of `Span` truly ends up being an issue, we could consider
limiting some fields to `i32` instead of `i64`, but ultimately it will be
pretty marginal. I generally don't think we can do much about `Span` being a
big type. Probably the work-around for this if it is a problem is to provide
alternative lower level APIs that operate on smaller types but aren't as
ergonomic.
* Add panicking variants of arithmetic via Add/Sub. UPDATE(2024-03-16): Done.
* Flush out the error types a bit more? UPDATE(2024-03-16): I've made
incremental progress here, but still not totally happy. The main issue at
present is that probably a lot of error messages aren't directly connected
to values given by callers. I'm not sure if we can do much better here
unfortunately.
* Settle on a TimeZone design. I've been thinking of having a TimeZoneDatabase
trait that `ZonedTime` is generic over (with a default type). The trait would
let you add timezones to it, and query it, and you would get `Copy` handles
back. The main complication is avoiding the need for time values to hold the
database itself. I want to keep times `Copy`. But this really constrains
the design of the time zone database and likely means every database has to
be a global that can never be freed. e.g., What happens when a `ZonedTime`
looks up a timezone handle that no longer exists? So we might just need to
accept that a `ZonedTime` is not `Copy`. We could make the timezone itself
a type parameter, and some timezones will be `Copy`, but this is a big
complication IMO and makes the API harder to understand. But note also that if
a `ZonedTime` is not `Copy`, and a `Span` will potentially hold a `ZonedTime`
via `relative_to`, then `Span` won't implement `Copy` either. No, `Span` will
not hold a `ZonedTime`, so the non-`Copy`ness of `Zoned` won't infect `Span`.
2024-03-16
----------
I'm thinking mostly about the timezone database design. At present, I think
making all timezones `Copy` is not in the cards. Specifically, a real timezone
has potentially lots of data associated with corresponding to DST transitions
(among potentially other kinds of transitions, but DST transitions are the
common case). That data is variable sized and cannot be `Copy`. Therefore, the
only real way to make it `Copy` is to stuff the data into a global database (or
one that is passed around) and put a handle in `Zoned` where the handle is just
an index into the database. But there are significant problems to this
approach:
* There's no real way to invalidate handles if the user wants to drop the
time zone database. So this design effectively requires that any time zone
database is global and can never be dropped.
* While we can likely button things up for the default case, this design also
means that if users use custom time zone databases, then they'll likely be
required to make sure they're passing in the right database when the handle
needs to be resolved. We could potentially work around this by pushing the
database into a type parameter on `Zoned`.
* In order to get information about a timezone for a handle, one has to go
to the database. We could potentially cache things like offsets in the high
bits of a handle, but whenever one needs to consult the timezone transitions
(which I think is needed for pretty much everything), we'd have to go to the
database. This is likely to be a problem because the database probably wants to
be mutable, so that we can update timezone data. This in turn means there could
be some gnarly synchronization involved here.
I overall felt like this was too much complication, particularly without
more user feedback. In theory, we could still make *some* values of `Zoned`
implement `Copy` by making it generic over a `TimeZone` trait. Then *some*
implementations of that trait (say, timezones that are just offsets) could
be `Copy`. The problem with this approach IMO is that it pushes folks toward
doing the wrong thing because using just the offset is likely to lead to
incorrect results because it doesn't deal with DST transitions. So then you
wind up in a situation where you have a "fast"-probably-wrong approach and a
"slow"-but-correct approach. This is especially quarrelsome because it can
impact code architecture where the former is more flexible given its `Copy`
impl while the latter is not.
So I think ultimately what we should do is define a concrete `TimeZone` type,
and a `Zoned` has an `Instant` and a `TimeZone`. And that's it. A `TimeZone`
type will be reference counted internally so that at least cloning is cheap.
The other possible advantage compared to the handle approach is that once a
`TimeZone` is constructed, it will never be mutated. Where as with the handle
approach, subsequent reads of the database could return different results if
the tzdata has changed on disk. The bummer here is using an `Arc` in the first
place, since cloning it could become a bottleneck when the `TimeZone` is shared
across multiple threads. But I'm not sure I really see a better approach here
that doesn't leak memory.
So if `TimeZone` is just an immutable reference counted type, that means more
of the interesting bits are off-loaded to the `TimeZoneDatabase`. Here's what
I'm thinking, with the docs here being more of an implementation sketch than
user-facing public docs.
```rust
/// Upon construction, the database may do some up-front work.
///
/// In the case of the binary IANA database commonly found in Unix
/// environments, this will traverse the directory structure to load
/// all available names of timezones. This is necessary in order to support
/// case insensitive timezone lookups. For example, in Go, time.LoadLocation
/// for `america/new_york` fails while `America/New_York` succeeds. Internally,
/// this will have a `RwLock<Map<Name, Result<TimeZone, Error>>>`.
///
/// In the case of a plain text IANA database, then the entire set of data
/// (hopefully one file via `tzdata.zi`) are loaded into memory since it
/// doesn't appear straight-forward to load timezones in an incremental
/// fashion. So in this case, we'd use a `Map<Name, TimeZone>`.
///
/// I think Android also uses a slightly different format here, so we might
/// need a third type of IANA, although I think it still ultimately uses the
/// same binary format but just compacted into one file.
///
/// We might implement a third option which uses Windows time zone data, but
/// from what I can tell, it's bunk. So I think we'll skip this one initially.
///
/// And I think this should also be "unavailable" with perhaps a message
/// indicating why.
pub struct TimeZoneDatabse;
impl TimeZoneDatabase {
/// This tries to load a time zone database from the environmnet in a way
/// that is likely to be correct. Here is what I'm thinking:
///
/// 1. Respect `ZONEINFO` environment variable.
/// 2. Use platform to look for binary iana database.
/// ~~3. Use platform to look for plain text database.~~
/// 4. Fall back to bundled tzdb if available.
fn from_env() -> TimeZoneDatabase {
todo!()
}
/// Creates a time zone database that reads a directory of binary IANA
/// time zone files.
fn from_iana_binary_path(
dir: impl AsRef<Path>,
) -> Result<TimeZoneDatabase, Error> {
todo!()
}
/// Creates a time zone database that reads from a single file containing
/// the plain text IANA database. (e.g., `tzdata.zi`.)
///
/// NOTE: I've scratched the text database support from initial release.
fn from_iana_text_path(
file: impl AsRef<Path>,
) -> Result<TimeZoneDatabase, Error> {
todo!()
}
/// Creates a time zone database that reads from zero or more files
/// containing the plain text IANA database. (e.g., `[northamerica,
/// southamerica]`.)
///
/// NOTE: I've scratched the text database support from initial release.
fn from_iana_text_paths(
files: impl IntoIterator<Item = impl AsRef<Path>>,
) -> Result<TimeZoneDatabase, Error> {
todo!()
}
/// Creates a time zone database that reads from a single stream containing
/// the plain text IANA database. (e.g., `tzdata.zi`.)
///
/// NOTE: I've scratched the text database support from initial release.
fn from_iana_text_reader(
rdr: impl std::io::Read,
) -> Result<TimeZoneDatabase, Error> {
todo!()
}
/// Returns a timezone corresponding to the given name. If one doesn't
/// exist or if there was a problem loading it, then an error is returned.
///
/// Depending on cache settings, this may reuse a previously loaded
/// timezone for the given name. If caching is disabled or the previously
/// loaded timezone has expired, then the timezone will be re-loaded.
fn timezone(&self, name: &str) -> Result<TimeZone, Error> {
todo!()
}
/// Sets the cache behavior of this timezone database.
///
/// When the duration is zero, then no caching will be performed. Timezone
/// lookups will always reload data from the underlying source.
///
/// For the IANA binary database, "no caching" is straight-forward: we just
/// always reload from disk.
///
/// But for the plain text database, does this mean we read and parse the
/// entire textual data every time? And when the cache is busted, is that
/// what we do? There are pretty extreme differences here, which is why we
/// have options to ensure one kind of database or the other.
///
/// I think we should set a long default (1 day?) here since timezone data
/// rarely changes. I'm thinking like one day? I suppose the problem is
/// that when you update the timezone data, you'd ideally like for it to
/// refreshed somewhat immediately. I suppose we can support that with
/// `clear_cache`, although I could imagine that being annyoing. Maybe a
/// shorter default TTL like 1 hour would be better?
fn time_to_live(&self, duration: Span) {
todo!()
}
/// This clears any cached timezones in this database.
///
/// This guarantees that any subsequent lookups for a timezone will reload
/// the data.
fn clear_cache(&self) {
todo!()
}
}
```
tzdb bundling
-------------
Bundling tzdb within Jiff isn't a good idea because the database changes. And
it would be best if such changes didn't require re-compiling the library.
But, Windows doesn't have a system copy of tzdb. So it's worth bundling in
that case.
We'd therefore like to express a default feature of "bundle tzdb, but only on
platforms that probably need it." And also, "you shouldn't need to pay for
downloading or compiling this bundled copy if you don't need it." I believe
this requires the following setup:
```toml
[features]
default = ["tzdb-bundle-platform"]
tzdb-bundle-platform = ["dep:jiff-tzdb-platform"]
tzdb-bundle-always = ["dep:jiff-tzdb"]
[dependencies]
jiff-tzdb = { version = "0.1", path = "jiff-tzdb", optional = true }
[target.'cfg(windows)'.dependencies]
jiff-tzdb-platform = { version = "0.1", path = "jiff-tzdb-windows", optional = true }
```
And then make `jiff-tzdb-windows` a crate that literally just re-exports
`jiff-tzdb`. This is a bit odd, but I don't think I can accomplish the above
setup with just `jiff-tzdb`. The issue is that we need a platform-only
dependency to be enabled when `tzdb-bundle-platform` is enabled. That way, for
platforms that don't need it, enabling `tzdb-bundle-platform` is a no-op.
TAI support
-----------
I think I should add a `Tai` implementation of the `TimeScale` trait. I think
that it shouldn't be a simple "marker" type like `Unix` and should instead
carry the leap-second table with it. We can provide different ways of
constructing the leap-second table, e.g., from a `TimeZoneDatabase` or perhaps
a built-in copy or perhaps even from the text-file source itself. Once we have
the table and the `Tai` scale, then conversions can be done between `Unix` and
`Tai`. Once we have that, differences can be computed between `Instant`s that
are accurate.
Note though that the conversion is necessarily non-lossy. That is, some Unix
times can map to two distinct TAI times, and some Unix times might not even
exist for a given TAI time. (Although that latter case can only happen when a
negative leap second occurs, which has never happened yet.) So we'll need to
figure out how to handle those cases. I especially want to make them infallible
because these conversions happen in APIs that I otherwise want to be infallible
(like `Instant::to_datetime`). We could expose special fallible conversion
routines and otherwise do infallible conversions everywhere else. It's probably
fine?
In the case of a fold (positive leap second), we should pick the first, since
that's what things like RFC 5545 require and what Temporal does by clamping
`23:59:60` to `23:59:59`.
In the case of a gap (negative leap second), I think there is an unambiguous
mapping. That is, the straight-forward thing should just work? Either way, we
should be able to pretty easily write a test for it.
One other complication here is that we probably need to provide a way to build
an `Instant` straight from a `TAI` timestamp value. For example, from a
`clock_gettime(CLOCK_TAI, ...)` call.
Feedback from tweets
--------------------
See: https://twitter.com/burntsushi5/status/1763284928970572112
See: https://bsky.app/profile/burntsushi.net/post/3kmld45nj722o
> 1. want to be able to get a list of timestamps while specifying 3 out of 4 of
> these:
> - initial timestamp
> - final timestamp
> - dt
> - number of timestamps
>
> 2. want different options for "rounding" timestamps to different levels of
> precision (e.g. ceil/floor/round to month/year/etc)
>
> I am not a js user but I do a lot of time data processing in python. wrote
> this tooltime for the functionality I mention
> https://github.com/sslivkoff/tooltime?tab=readme-ov-file#create-standardized-timeperiods
>
> the get_standard_timeperiod() and get_standard_intervals() are the two
> functions that I havent been able to easily find in other libs
> (impl here
> https://github.com/sslivkoff/tooltime/blob/main/tooltime/timeperiod_utils/timeperiod_standard.py)
>
> basically making it easy to 1) put messy realworld timestamps into regular
> intervals, and 2) being able to easily generate regular intervals under
> different parameterizations
>
> oh thats awesome!!!
>
> love the modes. probably more than I would ever need with all those half-X
> roudingMode's. and not sure the use of largestUnit
>
> roundingIncrement would be very nice
> Mostly just easy integration. My biggest pain point was trying to get them to
> work with Serde. It's been a while, but it ended up being easier to just make
> my own Date type rather than dealing with the errors. Having one DateTime
> across crates (Serde, Polars etc.) would be nice.
> Don't panic. Use rust's error handling. We've fought chrono panics for a
> while on @nu_shell, mostly because of cross-platform oddball file system or
> file corruption but also things like Windows named pipe dates of 1/1/1601,
> IIRC.
>
> Here's another one while I'm dreaming. :) Precise datetime math that includes
> leap calculations but take it a step further and allow the result to be
> expressed in a human readable duration that includes decades, years, months,
> weeks, days, hours, minutes, seconds, etc.
> Here's a real answer that I had a professional need for a few years ago: a
> natural language-agnostic syntax tree for complicated date/time expressions,
> e.g. "the day after next Friday" => DayDelta(NextWeekday(Now, Friday), 1)
> Clear documentation which representation is used for what. datetime libraries
> naturally have to deal with different representations of similar things, that
> is just essential complexity. 1/2
>
> If you add a ton of similar things that are are either just marginally useful
> or legacy you end up in chaos. I'm looking at you Java.
>
> Yes, java.time is good and I realize that what I want is kind of a xkcd/927
> problem that cannot be solved by a library alone. What I wish for a library
> is that it does not pretend to be the only datetime library in existence, but
> acknowledges the fact that there are others.
>
> To stay with the Java example: Clearly say in the documentation, why
> http://java.date.Instance is always better than http://java.util.Date and
> what to do if you have to deal with http://java.util.Date for legacy reasons.
>
> (In my opinion http://java.util.Date should be deprecated completely, but it
> isn't. That would send a clear message and prevent a lot of confusion. With
> java.time being part of the standard we could have done it. Not in the power
> of a new library, though.)
>
> Also, java.time can keep its API surface minimal by strictly committing to
> immutable and thread-safe while completely disregarding performance. Not sure
> if we could get away with this in Rust.
> Consistent use of enums for weekdays / months without needing to use u8
> Date, DateTime, DateTimeMs to represent different resolutions. I wrote my
> own code for this (internal to a project) to keep the complexity down and
> make the API coder friendly. I only needed UNIX GMT time. A date-time crate
> doesn't have to be hard, but they seem to make it hard
>
> Yes, UNIX time in seconds, plus milliseconds. Really I'm just improvising
> these types as I need them. I needed "round up" and "round down" calls
> for N years, quarters, months, weeks, days, hours etc. I found chrono
> overcomplicated, overengineered and awkward. Life is short
> Doing the simple things seems unreasonably hard with Chrono. I shouldn't have
> to constantly look up the syntax for getting the current timestamp
> Strict definition of the output of the Display trait and it being trivial
> to parse.
> Julian/ordinal dates and easy conversion from SAS date, time, and datetime
> types, please
> Configurable precision to reduce on memory consumption in no_std/embedded
> devices. Chrono's Duration is i64 + u32. Cc @thibautvandv
> I would say TC39 Temporal but in Rust. But it seems you are already aware of
> it.
> Currently, calculating the difference in days between two
> dates (e.g., implementing the std::ops::Sub trait for
> chrono::NaiveDate) requires borrowing the special date calculation
> algorithm from the C++20 extension to the std::chrono library
> (https://howardhinnant.github.io/date_algorithms.html#days_from_civil).
> Easier formatting and parsing. It always takes google to solve.
> I think something such as thread time recorder, meaning that can record the
> execution time of a thread and maybe only of that thread, so that we may not
> need to use threadlocal types directly.
>
> I believe that would be a great addition, but just said that felt maybe
> relevant here.
>
> The reason I said that here is because recording time atomically from a time
> object itself can be highly convenient wrapper if given.
> I don't have a use case, but I watched a talk related to Joda time that made
> me excited about the challenge of domain modelling durations and periods with
> variable resolution as to work well for fuzzy human time like "in about 5
> minutes" is encoded as specific date with error
> Not be terribly over-engineered and make common use-cases available in
> <1 minute of reading doc. Reuse std::time::Duration instead of rolling a
> separate type. Make working with unix-timestamps comfy: they are everywhere
> and expecting to change that is unrealistic.

55
scripts/jiff-debug Executable file
View file

@ -0,0 +1,55 @@
#!/bin/bash
# This is a script for printing the debug representation of some Jiff types.
# Principally, this is for internal types. Ideally we might have a proper
# jiff-cli for this, but doing so would require exporting internal types, which
# I really don't want to do. Instead, we use unit tests as a sort of CLI target
# and pass arguments via environment variables.
if [ -z "$1" ]; then
echo "Usage: $(basename "$0") (posix-tz | iana-tz | tzif) ..." >&2
exit 1
fi
case "$1" in
posix-tz)
if [ -z "$2" ]; then
echo "Usage: $(basename "$0") posix-tz <time-zone-string>" >&2
exit 1
fi
JIFF_DEBUG_POSIX_TZ="$2" cargo test --quiet --lib --features logging \
tz::posix::tests::debug_posix_tz -- --nocapture \
> /dev/null
;;
iana-tz)
if [ -z "$2" ]; then
echo "Usage: $(basename "$0") iana-tz <time-zone-string>" >&2
exit 1
fi
JIFF_DEBUG_IANA_TZ="$2" cargo test --quiet --lib --features logging \
tz::posix::tests::debug_iana_tz -- --nocapture \
2>&1 > /dev/null
;;
tzif)
if [ -z "$2" ]; then
echo "Usage: $(basename "$0") tzif <path/to/tzif/file>" >&2
exit 1
fi
JIFF_DEBUG_TZIF_PATH="$2" cargo test --quiet --lib --features logging \
tz::tzif::tests::debug_tzif -- --nocapture \
2>&1 > /dev/null
;;
zoneinfo-walk)
if [ -z "$2" ]; then
echo "Usage: $(basename "$0") zoneinfo-walk <path/to/zoneinfo/dir>" >&2
exit 1
fi
JIFF_DEBUG_ZONEINFO_DIR="$2" cargo test --quiet --lib --features logging \
tz::db::zoneinfo::tests::debug_zoneinfo_walk -- --nocapture \
2>&1 > /dev/null
;;
*)
echo "unrecognized sub-command '$1'" >&2
exit 1
;;
esac

2743
src/civil/date.rs Normal file

File diff suppressed because it is too large Load diff

1360
src/civil/datetime.rs Normal file

File diff suppressed because it is too large Load diff

518
src/civil/iso_week_date.rs Normal file
View file

@ -0,0 +1,518 @@
use crate::{
civil::{Date, Weekday},
error::Error,
util::{
common::is_leap_year_unranged,
rangeint::{RFrom, RInto},
t::{self, ISOWeek, ISOYear, C},
},
};
/// A type representing an [ISO 8601 week date].
///
/// The ISO 8601 week date scheme devises a calendar where days are identified
/// by their year, week number and weekday. All years have either precisely
/// 52 or 53 weeks.
///
/// The first week of an ISO 8601 year corresponds to the week containing the
/// first Thursday of the year. For this reason, an ISO 8601 week year can be
/// mismatched with the day's corresponding Gregorian year. For example, the
/// ISO 8601 week date for `1995-01-01` is `1994-W52-7` (with `7` corresponding
/// to Sunday).
///
/// ISO 8601 also considers Monday to be the start of the week, and uses
/// a 1-based numbering system. That is, Monday corresponds to `1` while
/// Sunday corresponds to `7` and is the last day of the week. Weekdays are
/// encapsulated by the [`Weekday`] type, which provides routines for easily
/// converting between different schemes (such as weeks where Sunday is the
/// beginning).
///
/// [ISO 8601 week date]: https://en.wikipedia.org/wiki/ISO_week_date
///
/// # Use case
///
/// Some domains use this method of timekeeping. Otherwise, unless you
/// specifically want a week oriented calendar, it's likely that you'll never
/// need to care about this type.
///
/// # Default value
///
/// For convenience, this type implements the `Default` trait. Its default
/// value is the first day of the zeroth year. i.e., `0000-W1-1`.
///
/// # Example: sample dates
///
/// This example shows a couple ISO 8601 week dates and their corresponding
/// Gregorian equivalents:
///
/// ```
/// use jiff::civil::{Date, ISOWeekDate, Weekday};
///
/// let date = Date::constant(2019, 12, 30);
/// let weekdate = ISOWeekDate::new(2020, 1, Weekday::Monday).unwrap();
/// assert_eq!(date.to_iso_week_date(), weekdate);
///
/// let date = Date::constant(2024, 3, 9);
/// let weekdate = ISOWeekDate::new(2024, 10, Weekday::Saturday).unwrap();
/// assert_eq!(date.to_iso_week_date(), weekdate);
/// ```
///
/// # Example: overlapping leap and long years
///
/// A "long" ISO 8601 week year is a year with 53 weeks. That is, it is a year
/// that includes a leap week. This example shows all years in the 20th
/// century that are both Gregorian leap years and long years.
///
/// ```
/// use jiff::civil::Date;
///
/// let mut overlapping = vec![];
/// for year in 1900..=1999 {
/// let date = Date::constant(year, 1, 1);
/// if date.in_leap_year() && date.to_iso_week_date().in_long_year() {
/// overlapping.push(year);
/// }
/// }
/// assert_eq!(overlapping, vec![
/// 1904, 1908, 1920, 1932, 1936, 1948, 1960, 1964, 1976, 1988, 1992,
/// ]);
/// ```
#[derive(Clone, Copy, Hash)]
pub struct ISOWeekDate {
year: ISOYear,
week: ISOWeek,
weekday: Weekday,
}
impl ISOWeekDate {
/// The maximum representable ISO week date.
///
/// The maximum corresponds to the ISO week date of the maximum [`Date`]
/// value. That is, `-9999-01-01`.
pub const MIN: ISOWeekDate = ISOWeekDate {
year: ISOYear::new_unchecked(-9999),
week: ISOWeek::new_unchecked(1),
weekday: Weekday::Monday,
};
/// The minimum representable ISO week date.
///
/// The minimum corresponds to the ISO week date of the minimum [`Date`]
/// value. That is, `9999-12-31`.
pub const MAX: ISOWeekDate = ISOWeekDate {
year: ISOYear::new_unchecked(9999),
week: ISOWeek::new_unchecked(52),
weekday: Weekday::Friday,
};
/// The first day of the zeroth year.
///
/// This is guaranteed to be equivalent to `ISOWeekDate::default()`. Note
/// that this is not equivalent to `Date::default()`.
///
/// # Example
///
/// ```
/// use jiff::civil::{Date, ISOWeekDate};
///
/// assert_eq!(ISOWeekDate::ZERO, ISOWeekDate::default());
/// // The first day of the 0th year in the ISO week calendar is actually
/// // the third day of the 0th year in the proleptic Gregorian calendar!
/// assert_eq!(ISOWeekDate::default().to_date(), Date::constant(0, 1, 3));
/// ```
pub const ZERO: ISOWeekDate = ISOWeekDate {
year: ISOYear::new_unchecked(0),
week: ISOWeek::new_unchecked(1),
weekday: Weekday::Monday,
};
/// Create a new ISO week date from it constituent parts.
///
/// If the given values are out of range (based on what is representable
/// as a [`Date`]), then this returns an error. This will also return an
/// error if a leap week is given (week number `53`) for a year that does
/// not contain a leap week.
///
/// # Example
///
/// This example shows some the boundary conditions involving minimum
/// and maximum dates:
///
/// ```
/// use jiff::civil::{Date, ISOWeekDate, Weekday};
///
/// // The year 1949 does not contain a leap week.
/// assert!(ISOWeekDate::new(1949, 53, Weekday::Monday).is_err());
///
/// // Examples of dates at or exceeding the maximum.
/// let max = ISOWeekDate::new(9999, 52, Weekday::Friday).unwrap();
/// assert_eq!(max, ISOWeekDate::MAX);
/// assert_eq!(max.to_date(), Date::constant(9999, 12, 31));
/// assert!(ISOWeekDate::new(9999, 52, Weekday::Saturday).is_err());
/// assert!(ISOWeekDate::new(9999, 53, Weekday::Monday).is_err());
///
/// // Examples of dates at or exceeding the minimum.
/// let min = ISOWeekDate::new(-9999, 1, Weekday::Monday).unwrap();
/// assert_eq!(min, ISOWeekDate::MIN);
/// assert_eq!(min.to_date(), Date::constant(-9999, 1, 1));
/// assert!(ISOWeekDate::new(-10000, 52, Weekday::Sunday).is_err());
/// ```
#[inline]
pub fn new(
year: i16,
week: i8,
weekday: Weekday,
) -> Result<ISOWeekDate, Error> {
let year = ISOYear::try_new("year", year)?;
let week = ISOWeek::try_new("week", week)?;
ISOWeekDate::new_ranged(year, week, weekday)
}
/// Converts a Gregorian date to an ISO week date.
///
/// The minimum and maximum allowed values of an ISO week date are
/// set based on the minimum and maximum values of a `Date`. Therefore,
/// converting to and from `Date` values is non-lossy and infallible.
///
/// This routine is equivalent to [`Date::to_iso_week_date`]. This
/// routine is also available via a `From<Date>` trait implementation for
/// `ISOWeekDate`.
///
/// # Example
///
/// ```
/// use jiff::civil::{Date, ISOWeekDate, Weekday};
///
/// let date = Date::constant(1948, 2, 10);
/// let weekdate = ISOWeekDate::from_date(date);
/// assert_eq!(
/// weekdate,
/// ISOWeekDate::new(1948, 7, Weekday::Tuesday).unwrap(),
/// );
/// ```
#[inline]
pub fn from_date(date: Date) -> ISOWeekDate {
date.to_iso_week_date()
}
// N.B. I tried defining a `ISOWeekDate::constant` for defining ISO week
// dates as constants, but it was too annoying to do. We could do it if
// there was a compelling reason for it though.
/// Returns the year component of this ISO 8601 week date.
///
/// The value returned is guaranteed to be in the range `-9999..=9999`.
///
/// # Example
///
/// ```
/// use jiff::civil::Date;
///
/// let weekdate = Date::constant(2019, 12, 30).to_iso_week_date();
/// assert_eq!(weekdate.year(), 2020);
/// ```
#[inline]
pub fn year(self) -> i16 {
self.year_ranged().get()
}
/// Returns the week component of this ISO 8601 week date.
///
/// The value returned is guaranteed to be in the range `1..=53`. A
/// value of `53` can only occur for "long" years. That is, years
/// with a leap week. This occurs precisely in cases for which
/// [`ISOWeekDate::in_long_year`] returns `true`.
///
/// # Example
///
/// ```
/// use jiff::civil::Date;
///
/// let weekdate = Date::constant(2019, 12, 30).to_iso_week_date();
/// assert_eq!(weekdate.year(), 2020);
/// assert_eq!(weekdate.week(), 1);
///
/// let weekdate = Date::constant(1948, 12, 31).to_iso_week_date();
/// assert_eq!(weekdate.year(), 1948);
/// assert_eq!(weekdate.week(), 53);
/// ```
#[inline]
pub fn week(self) -> i8 {
self.week_ranged().get()
}
/// Returns the day component of this ISO 8601 week date.
///
/// One can use methods on `Weekday` such as
/// [`Weekday::to_sunday_zero_offset`] to convert the weekday to a number.
///
/// # Example
///
/// ```
/// use jiff::civil::{Date, Weekday};
///
/// let weekdate = Date::constant(1948, 12, 31).to_iso_week_date();
/// assert_eq!(weekdate.year(), 1948);
/// assert_eq!(weekdate.week(), 53);
/// assert_eq!(weekdate.weekday(), Weekday::Friday);
/// ```
#[inline]
pub fn weekday(self) -> Weekday {
self.weekday
}
/// Returns true if and only if the year of this week date is a "long"
/// year.
///
/// A long year is one that contains precisely 53 weeks. All other years
/// contain precisely 52 weeks.
///
/// # Example
///
/// ```
/// use jiff::civil::{ISOWeekDate, Weekday};
///
/// let weekdate = ISOWeekDate::new(1948, 7, Weekday::Monday).unwrap();
/// assert!(weekdate.in_long_year());
/// let weekdate = ISOWeekDate::new(1949, 7, Weekday::Monday).unwrap();
/// assert!(!weekdate.in_long_year());
/// ```
#[inline]
pub fn in_long_year(self) -> bool {
is_long_year(self.year_ranged())
}
/// Converts this ISO week date to a Gregorian [`Date`].
///
/// The minimum and maximum allowed values of an ISO week date are
/// set based on the minimum and maximum values of a `Date`. Therefore,
/// converting to and from `Date` values is non-lossy and infallible.
///
/// This routine is equivalent to [`Date::from_iso_week_date`].
///
/// # Example
///
/// ```
/// use jiff::civil::{Date, ISOWeekDate, Weekday};
///
/// let weekdate = ISOWeekDate::new(1948, 7, Weekday::Tuesday).unwrap();
/// assert_eq!(weekdate.to_date(), Date::constant(1948, 2, 10));
/// ```
#[inline]
pub fn to_date(self) -> Date {
Date::from_iso_week_date(self)
}
}
impl ISOWeekDate {
/// Creates a new ISO week date from ranged values.
///
/// While the ranged values given eliminate some error cases, not all
/// combinations of year/week/weekday values are valid ISO week dates
/// supported by this crate. For example, a week of `53` for short years,
/// or more niche, a week date that would be bigger than what is supported
/// by our `Date` type.
#[inline]
pub(crate) fn new_ranged(
year: impl RInto<ISOYear>,
week: impl RInto<ISOWeek>,
weekday: Weekday,
) -> Result<ISOWeekDate, Error> {
let year = year.rinto();
let week = week.rinto();
// All combinations of years, weeks and weekdays allowed by our
// range types are valid ISO week dates with one exception: a week
// number of 53 is only valid for "long" years. Or years with an ISO
// leap week. It turns out this only happens when the last day of the
// year is a Thursday.
//
// Note that if the ranges in this crate are changed, this could be
// a little trickier if the range of ISOYear is different from Year.
debug_assert_eq!(t::Year::MIN, ISOYear::MIN);
debug_assert_eq!(t::Year::MAX, ISOYear::MAX);
if week == 53 && !is_long_year(year) {
return Err(Error::specific("ISO week number", week));
}
// And also, the maximum Date constrains what we can utter with
// ISOWeekDate so that we can preserve infallible conversions between
// them. So since 9999-12-31 maps to 9999 W52 Friday, it follows that
// Saturday and Sunday are not allowed. So reject them.
//
// We don't need to worry about the minimum because the minimum date
// (-9999-01-01) corresponds also to the minimum possible combination
// of an ISO week date's fields: -9999 W01 Monday. Nice.
if year == ISOYear::MAX_SELF
&& week == 52
&& weekday.to_monday_zero_offset()
> Weekday::Friday.to_monday_zero_offset()
{
return Err(Error::signed(
"weekday",
weekday.to_monday_zero_offset(),
Weekday::Monday.to_monday_one_offset(),
Weekday::Friday.to_monday_one_offset(),
));
}
Ok(ISOWeekDate { year, week, weekday })
}
/// Like `ISOWeekDate::new_ranged`, but constrains out-of-bounds values
/// to their closest valid equivalent.
///
/// For example, given 9999 W52 Saturday, this will return 9999 W52 Friday.
#[inline]
pub(crate) fn new_ranged_constrain(
year: impl RInto<ISOYear>,
week: impl RInto<ISOWeek>,
mut weekday: Weekday,
) -> ISOWeekDate {
let year = year.rinto();
let mut week = week.rinto();
debug_assert_eq!(t::Year::MIN, ISOYear::MIN);
debug_assert_eq!(t::Year::MAX, ISOYear::MAX);
if week == 53 && !is_long_year(year) {
week = ISOWeek::new(52).unwrap();
}
if year == ISOYear::MAX_SELF
&& week == 52
&& weekday.to_monday_zero_offset()
> Weekday::Friday.to_monday_zero_offset()
{
weekday = Weekday::Friday;
}
ISOWeekDate { year, week, weekday }
}
#[inline]
pub(crate) fn year_ranged(self) -> ISOYear {
self.year
}
#[inline]
pub(crate) fn week_ranged(self) -> ISOWeek {
self.week
}
}
impl Default for ISOWeekDate {
fn default() -> ISOWeekDate {
ISOWeekDate::ZERO
}
}
impl core::fmt::Debug for ISOWeekDate {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("ISOWeekDate")
.field("year", &self.year_ranged().debug())
.field("week", &self.week_ranged().debug())
.field("weekday", &self.weekday)
.finish()
}
}
impl Eq for ISOWeekDate {}
impl PartialEq for ISOWeekDate {
#[inline]
fn eq(&self, other: &ISOWeekDate) -> bool {
// We roll our own so that we can call 'get' on our ranged integers
// in order to provoke panics for bugs in dealing with boundary
// conditions.
self.weekday == other.weekday
&& self.week.get() == other.week.get()
&& self.year.get() == other.year.get()
}
}
impl Ord for ISOWeekDate {
#[inline]
fn cmp(&self, other: &ISOWeekDate) -> core::cmp::Ordering {
(self.year.get(), self.week.get(), self.weekday.to_monday_one_offset())
.cmp(&(
other.year.get(),
other.week.get(),
other.weekday.to_monday_one_offset(),
))
}
}
impl PartialOrd for ISOWeekDate {
#[inline]
fn partial_cmp(&self, other: &ISOWeekDate) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<Date> for ISOWeekDate {
#[inline]
fn from(date: Date) -> ISOWeekDate {
ISOWeekDate::from_date(date)
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for ISOWeekDate {
fn arbitrary(g: &mut quickcheck::Gen) -> ISOWeekDate {
let year = ISOYear::arbitrary(g);
let week = ISOWeek::arbitrary(g);
let weekday = Weekday::arbitrary(g);
ISOWeekDate::new_ranged_constrain(year, week, weekday)
}
fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = ISOWeekDate>> {
alloc::boxed::Box::new(
(self.year_ranged(), self.week_ranged(), self.weekday())
.shrink()
.map(|(year, week, weekday)| {
ISOWeekDate::new_ranged_constrain(year, week, weekday)
}),
)
}
}
/// Returns true if the given ISO year is a "long" year or not.
///
/// A "long" year is a year with 53 weeks. Otherwise, it's a "short" year
/// with 52 weeks.
fn is_long_year(year: ISOYear) -> bool {
// Inspired by: https://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year
let last = Date::new_ranged(year, C(12), C(31))
.expect("last day of year is always valid");
let weekday = last.weekday();
let expected_weekday =
if last.in_leap_year() { Weekday::Friday } else { Weekday::Thursday };
weekday == Weekday::Thursday
|| (last.in_leap_year() && weekday == Weekday::Friday)
}
#[cfg(test)]
mod tests {
use super::*;
quickcheck::quickcheck! {
fn prop_all_long_years_have_53rd_week(year: ISOYear) -> bool {
!is_long_year(year)
|| ISOWeekDate::new(year.get(), 53, Weekday::Sunday).is_ok()
}
fn prop_prev_day_is_less(wd: ISOWeekDate) -> quickcheck::TestResult {
use crate::ToSpan;
if wd == ISOWeekDate::MIN {
return quickcheck::TestResult::discard();
}
let prev_date = wd.to_date().checked_add(-1.days()).unwrap();
quickcheck::TestResult::from_bool(prev_date.to_iso_week_date() < wd)
}
fn prop_next_day_is_greater(wd: ISOWeekDate) -> quickcheck::TestResult {
use crate::ToSpan;
if wd == ISOWeekDate::MAX {
return quickcheck::TestResult::discard();
}
let next_date = wd.to_date().checked_add(1.days()).unwrap();
quickcheck::TestResult::from_bool(wd < next_date.to_iso_week_date())
}
}
}

43
src/civil/mod.rs Normal file
View file

@ -0,0 +1,43 @@
pub use self::{
date::{Date, DateSeries},
datetime::{DateTime, DateTimeSeries},
iso_week_date::ISOWeekDate,
time::{Time, TimeSeries},
weekday::{Weekday, WeekdaysForward, WeekdaysReverse},
};
mod date;
mod datetime;
mod iso_week_date;
mod time;
mod weekday;
/// The era corresponding to a particular year.
///
/// The BCE era corresponds to years less than or equal to `0`, while the CE
/// era corresponds to years greater than `0`.
///
/// In particular, this crate allows years to be negative and also to be `0`,
/// which is contrary to the common practice of excluding the year `0` when
/// writing dates for the Gregorian calendar. Moreover, common practice eschews
/// negative years in favor of labeling a year with an era notation. That is,
/// the year `1 BCE` is year `0` in this crate. The year `2 BCE` is the year
/// `-1` in this crate.
///
/// To get the year in its era format, use [`Date::era_year`].
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Era {
/// The "before common era" era.
///
/// This corresponds to all years less than or equal to `0`.
///
/// This is precisely equivalent to the "BC" or "before Christ" era.
BCE,
/// The "common era" era.
///
/// This corresponds to all years greater than `0`.
///
/// This is precisely equivalent to the "AD" or "anno Domini" or "in the
/// year of the Lord" era.
CE,
}

2080
src/civil/time.rs Normal file

File diff suppressed because it is too large Load diff

838
src/civil/weekday.rs Normal file
View file

@ -0,0 +1,838 @@
use crate::{
error::Error,
span::Span,
util::{
rangeint::{RFrom, RInto},
t::{self, C},
},
};
/// A representation for the day of the week.
///
/// The default representation follows ISO 8601. That is, the week starts with
/// Monday and numbering starts at `1`. However, the various constructors and
/// accessors support using other schemes in wide use:
///
/// * [`Weekday::from_monday_zero_offset`] builds a weekday from
/// a scheme that starts the week on Monday at offset `0`, while
/// [`Weekday::to_monday_zero_offset`] converts to it.
/// * [`Weekday::from_monday_one_offset`] builds a weekday from a scheme
/// that starts the week on Monday at offset `1` (the default representation),
/// while [`Weekday::to_monday_one_offset`] converts to it.
/// * [`Weekday::from_sunday_zero_offset`] builds a weekday from
/// a scheme that starts the week on Sunday at offset `0`, while
/// [`Weekday::to_sunday_zero_offset`] converts to it.
/// * [`Weekday::from_sunday_one_offset`] builds a weekday from
/// a scheme that starts the week on Sunday at offset `1`, while
/// [`Weekday::to_sunday_one_offset`] converts to it.
///
/// # Arithmetic
///
/// This type provides [`Weekday::wrapping_add`] and [`Weekday::wrapping_sub`]
/// for performing wrapping arithmetic on weekdays. These are also available
/// via operator overloading:
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Monday + 1, Weekday::Tuesday);
/// assert_eq!(Weekday::Sunday - 1, Weekday::Saturday);
/// ```
///
/// # Comparisons
///
/// This type provides `Eq` and `PartialEq` trait implementations for easy
/// comparison:
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Wednesday, Weekday::Wednesday + 7);
/// assert_ne!(Weekday::Thursday, Weekday::Friday);
/// ```
///
/// But this type specifically does not provide `Ord` or `PartialOrd` trait
/// implementations. Such an implementation would require deciding whether
/// Sunday is less than Monday or greater than Monday. The former case
/// corresponds to a week scheme where Sunday is the first day in the week,
/// where as the latter corresponds to a scheme where Monday is the first day.
/// Since both schemes are in widespread use, it would be inappropriate to bake
/// in an assumption of one or the other. Instead, one can convert a weekday
/// into the desired offset scheme, and then compare the offsets:
///
/// ```
/// use jiff::civil::Weekday;
///
/// let (sun, mon) = (Weekday::Sunday, Weekday::Monday);
/// assert!(sun.to_sunday_zero_offset() < mon.to_sunday_zero_offset());
/// assert!(sun.to_monday_zero_offset() > mon.to_monday_zero_offset());
/// ```
///
/// # Example
///
/// This example shows the result of converting to and from different schemes:
///
/// ```
/// use jiff::civil::Weekday;
///
/// // The different representations of Monday.
/// assert_eq!(Weekday::Monday.to_monday_zero_offset(), 0);
/// assert_eq!(Weekday::Monday.to_monday_one_offset(), 1);
/// assert_eq!(Weekday::Monday.to_sunday_zero_offset(), 1);
/// assert_eq!(Weekday::Monday.to_sunday_one_offset(), 2);
///
/// // The different representations of Sunday.
/// assert_eq!(Weekday::Sunday.to_monday_zero_offset(), 6);
/// assert_eq!(Weekday::Sunday.to_monday_one_offset(), 7);
/// assert_eq!(Weekday::Sunday.to_sunday_zero_offset(), 0);
/// assert_eq!(Weekday::Sunday.to_sunday_one_offset(), 1);
/// ```
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum Weekday {
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7,
}
impl Weekday {
/// Convert an offset to a structured `Weekday`.
///
/// The offset should be from a scheme where the first day of the week
/// is Monday and starts numbering at `0`.
///
/// # Errors
///
/// This returns an error when the given offset is not in the range
/// `0..=6`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// let weekday = Weekday::from_monday_zero_offset(3)?;
/// assert_eq!(weekday, Weekday::Thursday);
///
/// assert!(Weekday::from_monday_zero_offset(7).is_err());
/// assert!(Weekday::from_monday_zero_offset(-1).is_err());
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn from_monday_zero_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayZero::try_new("weekday", offset)?;
Ok(Weekday::from_monday_zero_offset_ranged(offset))
}
/// Convert an offset to a structured `Weekday`.
///
/// The offset should be from a scheme where the first day of the week
/// is Monday and starts numbering at `1`.
///
/// # Errors
///
/// This returns an error when the given offset is not in the range
/// `1..=7`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// let weekday = Weekday::from_monday_one_offset(4)?;
/// assert_eq!(weekday, Weekday::Thursday);
///
/// assert!(Weekday::from_monday_one_offset(8).is_err());
/// assert!(Weekday::from_monday_one_offset(0).is_err());
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn from_monday_one_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayOne::try_new("weekday", offset)?;
Ok(Weekday::from_monday_one_offset_ranged(offset))
}
/// Convert an offset to a structured `Weekday`.
///
/// The offset should be from a scheme where the first day of the week
/// is Sunday and starts numbering at `0`.
///
/// # Errors
///
/// This returns an error when the given offset is not in the range
/// `0..=6`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// let weekday = Weekday::from_sunday_zero_offset(4)?;
/// assert_eq!(weekday, Weekday::Thursday);
///
/// assert!(Weekday::from_sunday_zero_offset(7).is_err());
/// assert!(Weekday::from_sunday_zero_offset(-1).is_err());
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn from_sunday_zero_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayZero::try_new("weekday", offset)?;
Ok(Weekday::from_sunday_zero_offset_ranged(offset))
}
/// Convert an offset to a structured `Weekday`.
///
/// The offset should be from a scheme where the first day of the week
/// is Sunday and starts numbering at `1`.
///
/// # Errors
///
/// This returns an error when the given offset is not in the range
/// `1..=7`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// let weekday = Weekday::from_sunday_one_offset(5)?;
/// assert_eq!(weekday, Weekday::Thursday);
///
/// assert!(Weekday::from_sunday_one_offset(8).is_err());
/// assert!(Weekday::from_sunday_one_offset(0).is_err());
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn from_sunday_one_offset(offset: i8) -> Result<Weekday, Error> {
let offset = t::WeekdayOne::try_new("weekday", offset)?;
Ok(Weekday::from_sunday_one_offset_ranged(offset))
}
/// Returns this weekday as an offset.
///
/// The offset returned is computed based on a week that starts with Monday
/// and begins numbering at `0`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Thursday.to_monday_zero_offset(), 3);
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn to_monday_zero_offset(self) -> i8 {
self.to_monday_zero_offset_ranged().get()
}
/// Returns this weekday as an offset.
///
/// The offset returned is computed based on a week that starts with Monday
/// and begins numbering at `1`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Thursday.to_monday_one_offset(), 4);
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn to_monday_one_offset(self) -> i8 {
self.to_monday_one_offset_ranged().get()
}
/// Returns this weekday as an offset.
///
/// The offset returned is computed based on a week that starts with Sunday
/// and begins numbering at `0`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Thursday.to_sunday_zero_offset(), 4);
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn to_sunday_zero_offset(self) -> i8 {
self.to_sunday_zero_offset_ranged().get()
}
/// Returns this weekday as an offset.
///
/// The offset returned is computed based on a week that starts with Sunday
/// and begins numbering at `1`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Thursday.to_sunday_one_offset(), 5);
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn to_sunday_one_offset(self) -> i8 {
self.to_sunday_one_offset_ranged().get()
}
/// Returns the next weekday, wrapping around at the end of week to the
/// beginning of the week.
///
/// This is a convenience routing for calling [`Weekday::wrapping_add`]
/// with a value of `1`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Wednesday.next(), Weekday::Thursday);
/// assert_eq!(Weekday::Sunday.next(), Weekday::Monday);
/// assert_eq!(Weekday::Saturday.next(), Weekday::Sunday);
/// ```
#[inline]
pub fn next(self) -> Weekday {
self.wrapping_add(1)
}
/// Returns the previous weekday, wrapping around at the beginning of week
/// to the end of the week.
///
/// This is a convenience routing for calling [`Weekday::wrapping_sub`]
/// with a value of `1`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Wednesday.previous(), Weekday::Tuesday);
/// assert_eq!(Weekday::Sunday.previous(), Weekday::Saturday);
/// assert_eq!(Weekday::Saturday.previous(), Weekday::Friday);
/// ```
#[inline]
pub fn previous(self) -> Weekday {
self.wrapping_sub(1)
}
/// Returns the number of days from `other` to this weekday.
///
/// Adding the returned number of days to `other` is guaranteed to sum to
/// this weekday. The number of days returned is guaranteed to be in the
/// range `0..=6`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Friday.since(Weekday::Tuesday), 3);
/// assert_eq!(Weekday::Tuesday.since(Weekday::Tuesday), 0);
/// assert_eq!(Weekday::Monday.since(Weekday::Sunday), 1);
/// assert_eq!(Weekday::Sunday.since(Weekday::Monday), 6);
/// ```
#[inline]
pub fn since(self, other: Weekday) -> i8 {
self.since_ranged(other).get()
}
/// Returns the number of days until `other` from this weekday.
///
/// Adding the returned number of days to this weekday is guaranteed to sum
/// to `other` weekday. The number of days returned is guaranteed to be in
/// the range `0..=6`.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Friday.until(Weekday::Tuesday), 4);
/// assert_eq!(Weekday::Tuesday.until(Weekday::Tuesday), 0);
/// assert_eq!(Weekday::Monday.until(Weekday::Sunday), 6);
/// assert_eq!(Weekday::Sunday.until(Weekday::Monday), 1);
/// ```
#[inline]
pub fn until(self, other: Weekday) -> i8 {
self.until_ranged(other).get()
}
/// Add the given number of days to this weekday, using wrapping arithmetic,
/// and return the resulting weekday.
///
/// Adding a multiple of `7` (including `0`) is guaranteed to produce the
/// same weekday as this one.
///
/// Note that this routine is also available via the `+` operator.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Sunday.wrapping_add(1), Weekday::Monday);
/// assert_eq!(Weekday::Sunday.wrapping_add(2), Weekday::Tuesday);
/// assert_eq!(Weekday::Saturday.wrapping_add(1), Weekday::Sunday);
/// assert_eq!(Weekday::Saturday.wrapping_add(7), Weekday::Saturday);
/// assert_eq!(Weekday::Sunday.wrapping_add(-1), Weekday::Saturday);
/// ```
///
/// Wrapping arithmetic is also performed by the `+` operator:
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Sunday + 1, Weekday::Monday);
/// assert_eq!(Weekday::Sunday + 2, Weekday::Tuesday);
/// assert_eq!(Weekday::Saturday + 1, Weekday::Sunday);
/// assert_eq!(Weekday::Saturday + 7, Weekday::Saturday);
/// assert_eq!(Weekday::Sunday + -1, Weekday::Saturday);
/// // The weekday can also be on the right hand side of addition:
/// assert_eq!(1 + Weekday::Sunday, Weekday::Monday);
/// ```
#[inline]
pub fn wrapping_add(self, days: impl Into<i64>) -> Weekday {
let start = t::NoUnits::rfrom(self.to_monday_zero_offset_ranged());
// OK because all i64 values fit in a NoUnits.
let rhs = t::NoUnits::new(days.into()).unwrap();
let end = start.wrapping_add(rhs) % C(7);
Weekday::from_monday_zero_offset_ranged(end)
}
/// Subtract the given number of days from this weekday, using wrapping
/// arithmetic, and return the resulting weekday.
///
/// Subtracting a multiple of `7` (including `0`) is guaranteed to produce
/// the same weekday as this one.
///
/// Note that this routine is also available via the `-` operator.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Sunday.wrapping_sub(1), Weekday::Saturday);
/// assert_eq!(Weekday::Sunday.wrapping_sub(2), Weekday::Friday);
/// assert_eq!(Weekday::Saturday.wrapping_sub(1), Weekday::Friday);
/// assert_eq!(Weekday::Saturday.wrapping_sub(7), Weekday::Saturday);
/// assert_eq!(Weekday::Sunday.wrapping_sub(-1), Weekday::Monday);
/// ```
///
/// Wrapping arithmetic is also performed by the `-` operator:
///
/// ```
/// use jiff::civil::Weekday;
///
/// assert_eq!(Weekday::Sunday - 1, Weekday::Saturday);
/// assert_eq!(Weekday::Sunday - 2, Weekday::Friday);
/// assert_eq!(Weekday::Saturday - 1, Weekday::Friday);
/// assert_eq!(Weekday::Saturday - 7, Weekday::Saturday);
/// assert_eq!(Weekday::Sunday - -1, Weekday::Monday);
/// ```
///
/// Unlike addition, since subtraction is not commutative and negating a
/// weekday has no semantic meaning, the weekday cannot be on the right
/// hand side of the `-` operator.
#[inline]
pub fn wrapping_sub(self, days: impl Into<i64>) -> Weekday {
self.wrapping_add(-days.into())
}
/// Starting with this weekday, this returns an unending iterator that
/// cycles forward through the days of the week.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// let mut it = Weekday::Tuesday.cycle_forward();
/// assert_eq!(it.next(), Some(Weekday::Tuesday));
/// assert_eq!(it.next(), Some(Weekday::Wednesday));
/// assert_eq!(it.next(), Some(Weekday::Thursday));
/// assert_eq!(it.next(), Some(Weekday::Friday));
/// assert_eq!(it.next(), Some(Weekday::Saturday));
/// assert_eq!(it.next(), Some(Weekday::Sunday));
/// assert_eq!(it.next(), Some(Weekday::Monday));
/// assert_eq!(it.next(), Some(Weekday::Tuesday));
/// ```
#[inline]
pub fn cycle_forward(self) -> WeekdaysForward {
let nexts = [
self,
self.wrapping_add(1),
self.wrapping_add(2),
self.wrapping_add(3),
self.wrapping_add(4),
self.wrapping_add(5),
self.wrapping_add(6),
];
WeekdaysForward { it: nexts.into_iter().cycle() }
}
/// Starting with this weekday, this returns an unending iterator that
/// cycles backward through the days of the week.
///
/// # Example
///
/// ```
/// use jiff::civil::Weekday;
///
/// let mut it = Weekday::Tuesday.cycle_reverse();
/// assert_eq!(it.next(), Some(Weekday::Tuesday));
/// assert_eq!(it.next(), Some(Weekday::Monday));
/// assert_eq!(it.next(), Some(Weekday::Sunday));
/// assert_eq!(it.next(), Some(Weekday::Saturday));
/// assert_eq!(it.next(), Some(Weekday::Friday));
/// assert_eq!(it.next(), Some(Weekday::Thursday));
/// assert_eq!(it.next(), Some(Weekday::Wednesday));
/// assert_eq!(it.next(), Some(Weekday::Tuesday));
/// ```
#[inline]
pub fn cycle_reverse(self) -> WeekdaysReverse {
let nexts = [
self,
self.wrapping_sub(1),
self.wrapping_sub(2),
self.wrapping_sub(3),
self.wrapping_sub(4),
self.wrapping_sub(5),
self.wrapping_sub(6),
];
WeekdaysReverse { it: nexts.into_iter().cycle() }
}
}
impl Weekday {
#[inline]
pub(crate) fn from_monday_zero_offset_ranged(
offset: impl RInto<t::WeekdayZero>,
) -> Weekday {
match offset.rinto().get() {
0 => Weekday::Monday,
1 => Weekday::Tuesday,
2 => Weekday::Wednesday,
3 => Weekday::Thursday,
4 => Weekday::Friday,
5 => Weekday::Saturday,
6 => Weekday::Sunday,
_ => unreachable!(),
}
}
#[inline]
pub(crate) fn from_monday_one_offset_ranged(
offset: impl RInto<t::WeekdayOne>,
) -> Weekday {
let offset_zero = offset.rinto() - C(1);
Weekday::from_monday_zero_offset_ranged(offset_zero)
}
#[inline]
pub(crate) fn from_sunday_zero_offset_ranged(
offset: impl RInto<t::WeekdayZero>,
) -> Weekday {
let offset_sunday = (offset.rinto() - C(1)) % C(7);
Weekday::from_monday_zero_offset_ranged(offset_sunday)
}
#[inline]
pub(crate) fn from_sunday_one_offset_ranged(
offset: impl RInto<t::WeekdayOne>,
) -> Weekday {
let offset_zero = offset.rinto() - C(1);
Weekday::from_sunday_zero_offset_ranged(offset_zero)
}
#[inline]
pub(crate) fn to_monday_zero_offset_ranged(self) -> t::WeekdayZero {
(self.to_monday_one_offset_ranged() - C(1)).rinto()
}
#[inline]
pub(crate) fn to_monday_one_offset_ranged(self) -> t::WeekdayOne {
t::WeekdayOne::new_unchecked(self as i8)
}
#[inline]
pub(crate) fn to_sunday_zero_offset_ranged(self) -> t::WeekdayZero {
(self.to_monday_zero_offset_ranged() + C(1)) % C(7)
}
#[inline]
pub(crate) fn to_sunday_one_offset_ranged(self) -> t::WeekdayOne {
(self.to_sunday_zero_offset_ranged() + C(1)).rinto()
}
#[inline]
pub(crate) fn since_ranged(self, other: Weekday) -> t::WeekdayZero {
(self.to_monday_zero_offset_ranged()
- other.to_monday_zero_offset_ranged())
% C(7)
}
#[inline]
pub(crate) fn until_ranged(self, other: Weekday) -> t::WeekdayZero {
other.since_ranged(self)
}
}
impl core::ops::Add<i8> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i8) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<i16> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i16) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<i32> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i32) -> Weekday {
self.wrapping_add(rhs)
}
}
impl core::ops::Add<i64> for Weekday {
type Output = Weekday;
#[inline]
fn add(self, rhs: i64) -> Weekday {
self.wrapping_add(rhs)
}
}
// Since addition is commutative, we don't might if users write `n + weekday`
// or `weekday + n`.
impl core::ops::Add<Weekday> for i8 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::Add<Weekday> for i16 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::Add<Weekday> for i32 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::Add<Weekday> for i64 {
type Output = Weekday;
#[inline]
fn add(self, rhs: Weekday) -> Weekday {
rhs.wrapping_add(self)
}
}
impl core::ops::AddAssign<i8> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i8) {
*self = *self + rhs;
}
}
impl core::ops::AddAssign<i16> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i16) {
*self = *self + rhs;
}
}
impl core::ops::AddAssign<i32> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i32) {
*self = *self + rhs;
}
}
impl core::ops::AddAssign<i64> for Weekday {
#[inline]
fn add_assign(&mut self, rhs: i64) {
*self = *self + rhs;
}
}
// Subtraction isn't commutative, so we only define it when the right hand
// side is an integer. Otherwise we'd need a concept of what it means to
// "negate" a weekday, which doesn't really make sense?
impl core::ops::Sub<i8> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i8) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::Sub<i16> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i16) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::Sub<i32> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i32) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::Sub<i64> for Weekday {
type Output = Weekday;
#[inline]
fn sub(self, rhs: i64) -> Weekday {
self.wrapping_sub(rhs)
}
}
impl core::ops::SubAssign<i8> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i8) {
*self = *self - rhs;
}
}
impl core::ops::SubAssign<i16> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i16) {
*self = *self - rhs;
}
}
impl core::ops::SubAssign<i32> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i32) {
*self = *self - rhs;
}
}
impl core::ops::SubAssign<i64> for Weekday {
#[inline]
fn sub_assign(&mut self, rhs: i64) {
*self = *self - rhs;
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for Weekday {
fn arbitrary(g: &mut quickcheck::Gen) -> Weekday {
let offset = t::WeekdayZero::arbitrary(g);
Weekday::from_monday_zero_offset_ranged(offset)
}
fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Weekday>> {
alloc::boxed::Box::new(
self.to_monday_zero_offset_ranged()
.shrink()
.map(Weekday::from_monday_zero_offset_ranged),
)
}
}
/// An unending iterator of the days of the week.
///
/// This iterator is created by calling [`Weekday::cycle_forward`].
#[derive(Clone, Debug)]
pub struct WeekdaysForward {
it: core::iter::Cycle<core::array::IntoIter<Weekday, 7>>,
}
impl Iterator for WeekdaysForward {
type Item = Weekday;
#[inline]
fn next(&mut self) -> Option<Weekday> {
self.it.next()
}
}
impl core::iter::FusedIterator for WeekdaysForward {}
/// An unending iterator of the days of the week in reverse.
///
/// This iterator is created by calling [`Weekday::cycle_reverse`].
#[derive(Clone, Debug)]
pub struct WeekdaysReverse {
it: core::iter::Cycle<core::array::IntoIter<Weekday, 7>>,
}
impl Iterator for WeekdaysReverse {
type Item = Weekday;
#[inline]
fn next(&mut self) -> Option<Weekday> {
self.it.next()
}
}
impl core::iter::FusedIterator for WeekdaysReverse {}
#[cfg(test)]
mod tests {
use super::*;
quickcheck::quickcheck! {
fn prop_since_add_equals_self(wd1: Weekday, wd2: Weekday) -> bool {
let days = wd1.since(wd2);
wd2.wrapping_add(days) == wd1
}
fn prop_until_add_equals_other(wd1: Weekday, wd2: Weekday) -> bool {
let days = wd1.until(wd2);
wd1.wrapping_add(days) == wd2
}
}
}

255
src/data/leap-seconds.list Normal file
View file

@ -0,0 +1,255 @@
#
# In the following text, the symbol '#' introduces
# a comment, which continues from that symbol until
# the end of the line. A plain comment line has a
# whitespace character following the comment indicator.
# There are also special comment lines defined below.
# A special comment will always have a non-whitespace
# character in column 2.
#
# A blank line should be ignored.
#
# The following table shows the corrections that must
# be applied to compute International Atomic Time (TAI)
# from the Coordinated Universal Time (UTC) values that
# are transmitted by almost all time services.
#
# The first column shows an epoch as a number of seconds
# since 1 January 1900, 00:00:00 (1900.0 is also used to
# indicate the same epoch.) Both of these time stamp formats
# ignore the complexities of the time scales that were
# used before the current definition of UTC at the start
# of 1972. (See note 3 below.)
# The second column shows the number of seconds that
# must be added to UTC to compute TAI for any timestamp
# at or after that epoch. The value on each line is
# valid from the indicated initial instant until the
# epoch given on the next one or indefinitely into the
# future if there is no next line.
# (The comment on each line shows the representation of
# the corresponding initial epoch in the usual
# day-month-year format. The epoch always begins at
# 00:00:00 UTC on the indicated day. See Note 5 below.)
#
# Important notes:
#
# 1. Coordinated Universal Time (UTC) is often referred to
# as Greenwich Mean Time (GMT). The GMT time scale is no
# longer used, and the use of GMT to designate UTC is
# discouraged.
#
# 2. The UTC time scale is realized by many national
# laboratories and timing centers. Each laboratory
# identifies its realization with its name: Thus
# UTC(NIST), UTC(USNO), etc. The differences among
# these different realizations are typically on the
# order of a few nanoseconds (i.e., 0.000 000 00x s)
# and can be ignored for many purposes. These differences
# are tabulated in Circular T, which is published monthly
# by the International Bureau of Weights and Measures
# (BIPM). See www.bipm.org for more information.
#
# 3. The current definition of the relationship between UTC
# and TAI dates from 1 January 1972. A number of different
# time scales were in use before that epoch, and it can be
# quite difficult to compute precise timestamps and time
# intervals in those "prehistoric" days. For more information,
# consult:
#
# The Explanatory Supplement to the Astronomical
# Ephemeris.
# or
# Terry Quinn, "The BIPM and the Accurate Measurement
# of Time," Proc. of the IEEE, Vol. 79, pp. 894-905,
# July, 1991. <http://dx.doi.org/10.1109/5.84965>
# reprinted in:
# Christine Hackman and Donald B Sullivan (eds.)
# Time and Frequency Measurement
# American Association of Physics Teachers (1996)
# <http://tf.nist.gov/general/pdf/1168.pdf>, pp. 75-86
#
# 4. The decision to insert a leap second into UTC is currently
# the responsibility of the International Earth Rotation and
# Reference Systems Service. (The name was changed from the
# International Earth Rotation Service, but the acronym IERS
# is still used.)
#
# Leap seconds are announced by the IERS in its Bulletin C.
#
# See www.iers.org for more details.
#
# Every national laboratory and timing center uses the
# data from the BIPM and the IERS to construct UTC(lab),
# their local realization of UTC.
#
# Although the definition also includes the possibility
# of dropping seconds ("negative" leap seconds), this has
# never been done and is unlikely to be necessary in the
# foreseeable future.
#
# 5. If your system keeps time as the number of seconds since
# some epoch (e.g., NTP timestamps), then the algorithm for
# assigning a UTC time stamp to an event that happens during a positive
# leap second is not well defined. The official name of that leap
# second is 23:59:60, but there is no way of representing that time
# in these systems.
# Many systems of this type effectively stop the system clock for
# one second during the leap second and use a time that is equivalent
# to 23:59:59 UTC twice. For these systems, the corresponding TAI
# timestamp would be obtained by advancing to the next entry in the
# following table when the time equivalent to 23:59:59 UTC
# is used for the second time. Thus the leap second which
# occurred on 30 June 1972 at 23:59:59 UTC would have TAI
# timestamps computed as follows:
#
# ...
# 30 June 1972 23:59:59 (2287785599, first time): TAI= UTC + 10 seconds
# 30 June 1972 23:59:60 (2287785599,second time): TAI= UTC + 11 seconds
# 1 July 1972 00:00:00 (2287785600) TAI= UTC + 11 seconds
# ...
#
# If your system realizes the leap second by repeating 00:00:00 UTC twice
# (this is possible but not usual), then the advance to the next entry
# in the table must occur the second time that a time equivalent to
# 00:00:00 UTC is used. Thus, using the same example as above:
#
# ...
# 30 June 1972 23:59:59 (2287785599): TAI= UTC + 10 seconds
# 30 June 1972 23:59:60 (2287785600, first time): TAI= UTC + 10 seconds
# 1 July 1972 00:00:00 (2287785600,second time): TAI= UTC + 11 seconds
# ...
#
# in both cases the use of timestamps based on TAI produces a smooth
# time scale with no discontinuity in the time interval. However,
# although the long-term behavior of the time scale is correct in both
# methods, the second method is technically not correct because it adds
# the extra second to the wrong day.
#
# This complexity would not be needed for negative leap seconds (if they
# are ever used). The UTC time would skip 23:59:59 and advance from
# 23:59:58 to 00:00:00 in that case. The TAI offset would decrease by
# 1 second at the same instant. This is a much easier situation to deal
# with, since the difficulty of unambiguously representing the epoch
# during the leap second does not arise.
#
# Some systems implement leap seconds by amortizing the leap second
# over the last few minutes of the day. The frequency of the local
# clock is decreased (or increased) to realize the positive (or
# negative) leap second. This method removes the time step described
# above. Although the long-term behavior of the time scale is correct
# in this case, this method introduces an error during the adjustment
# period both in time and in frequency with respect to the official
# definition of UTC.
#
# Questions or comments to:
# Judah Levine
# Time and Frequency Division
# NIST
# Boulder, Colorado
# Judah.Levine@nist.gov
#
# Last Update of leap second values: 8 July 2016
#
# The following line shows this last update date in NTP timestamp
# format. This is the date on which the most recent change to
# the leap second data was added to the file. This line can
# be identified by the unique pair of characters in the first two
# columns as shown below.
#
#$ 3676924800
#
# The NTP timestamps are in units of seconds since the NTP epoch,
# which is 1 January 1900, 00:00:00. The Modified Julian Day number
# corresponding to the NTP time stamp, X, can be computed as
#
# X/86400 + 15020
#
# where the first term converts seconds to days and the second
# term adds the MJD corresponding to the time origin defined above.
# The integer portion of the result is the integer MJD for that
# day, and any remainder is the time of day, expressed as the
# fraction of the day since 0 hours UTC. The conversion from day
# fraction to seconds or to hours, minutes, and seconds may involve
# rounding or truncation, depending on the method used in the
# computation.
#
# The data in this file will be updated periodically as new leap
# seconds are announced. In addition to being entered on the line
# above, the update time (in NTP format) will be added to the basic
# file name leap-seconds to form the name leap-seconds.<NTP TIME>.
# In addition, the generic name leap-seconds.list will always point to
# the most recent version of the file.
#
# This update procedure will be performed only when a new leap second
# is announced.
#
# The following entry specifies the expiration date of the data
# in this file in units of seconds since the origin at the instant
# 1 January 1900, 00:00:00. This expiration date will be changed
# at least twice per year whether or not a new leap second is
# announced. These semi-annual changes will be made no later
# than 1 June and 1 December of each year to indicate what
# action (if any) is to be taken on 30 June and 31 December,
# respectively. (These are the customary effective dates for new
# leap seconds.) This expiration date will be identified by a
# unique pair of characters in columns 1 and 2 as shown below.
# In the unlikely event that a leap second is announced with an
# effective date other than 30 June or 31 December, then this
# file will be edited to include that leap second as soon as it is
# announced or at least one month before the effective date
# (whichever is later).
# If an announcement by the IERS specifies that no leap second is
# scheduled, then only the expiration date of the file will
# be advanced to show that the information in the file is still
# current -- the update time stamp, the data and the name of the file
# will not change.
#
# Updated through IERS Bulletin C66
# File expires on: 28 June 2024
#
#@ 3928521600
#
2272060800 10 # 1 Jan 1972
2287785600 11 # 1 Jul 1972
2303683200 12 # 1 Jan 1973
2335219200 13 # 1 Jan 1974
2366755200 14 # 1 Jan 1975
2398291200 15 # 1 Jan 1976
2429913600 16 # 1 Jan 1977
2461449600 17 # 1 Jan 1978
2492985600 18 # 1 Jan 1979
2524521600 19 # 1 Jan 1980
2571782400 20 # 1 Jul 1981
2603318400 21 # 1 Jul 1982
2634854400 22 # 1 Jul 1983
2698012800 23 # 1 Jul 1985
2776982400 24 # 1 Jan 1988
2840140800 25 # 1 Jan 1990
2871676800 26 # 1 Jan 1991
2918937600 27 # 1 Jul 1992
2950473600 28 # 1 Jul 1993
2982009600 29 # 1 Jul 1994
3029443200 30 # 1 Jan 1996
3076704000 31 # 1 Jul 1997
3124137600 32 # 1 Jan 1999
3345062400 33 # 1 Jan 2006
3439756800 34 # 1 Jan 2009
3550089600 35 # 1 Jul 2012
3644697600 36 # 1 Jul 2015
3692217600 37 # 1 Jan 2017
#
# the following special comment contains the
# hash value of the data in this file computed
# use the secure hash algorithm as specified
# by FIPS 180-1. See the files in ~/pub/sha for
# the details of how this hash value is
# computed. Note that the hash computation
# ignores comments and whitespace characters
# in data lines. It includes the NTP values
# of both the last modification time and the
# expiration time of the file, but not the
# white space on those lines.
# the hash line is also ignored in the
# computation.
#
#h 16edd0f0 3666784f 37db6bdd e74ced87 59af48f1

82
src/data/leapseconds Normal file
View file

@ -0,0 +1,82 @@
# Allowance for leap seconds added to each time zone file.
# This file is in the public domain.
# This file is generated automatically from the data in the public-domain
# NIST format leap-seconds.list file, which can be copied from
# <ftp://ftp.nist.gov/pub/time/leap-seconds.list>
# or <ftp://ftp.boulder.nist.gov/pub/time/leap-seconds.list>.
# The NIST file is used instead of its IERS upstream counterpart
# <https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list>
# because under US law the NIST file is public domain
# whereas the IERS file's copyright and license status is unclear.
# For more about leap-seconds.list, please see
# The NTP Timescale and Leap Seconds
# <https://www.eecis.udel.edu/~mills/leap.html>.
# The rules for leap seconds are specified in Annex 1 (Time scales) of:
# Standard-frequency and time-signal emissions.
# International Telecommunication Union - Radiocommunication Sector
# (ITU-R) Recommendation TF.460-6 (02/2002)
# <https://www.itu.int/rec/R-REC-TF.460-6-200202-I/>.
# The International Earth Rotation and Reference Systems Service (IERS)
# periodically uses leap seconds to keep UTC to within 0.9 s of UT1
# (a proxy for Earth's angle in space as measured by astronomers)
# and publishes leap second data in a copyrighted file
# <https://hpiers.obspm.fr/iers/bul/bulc/Leap_Second.dat>.
# See: Levine J. Coordinated Universal Time and the leap second.
# URSI Radio Sci Bull. 2016;89(4):30-6. doi:10.23919/URSIRSB.2016.7909995
# <https://ieeexplore.ieee.org/document/7909995>.
# There were no leap seconds before 1972, as no official mechanism
# accounted for the discrepancy between atomic time (TAI) and the earth's
# rotation. The first ("1 Jan 1972") data line in leap-seconds.list
# does not denote a leap second; it denotes the start of the current definition
# of UTC.
# All leap-seconds are Stationary (S) at the given UTC time.
# The correction (+ or -) is made at the given time, so in the unlikely
# event of a negative leap second, a line would look like this:
# Leap YEAR MON DAY 23:59:59 - S
# Typical lines look like this:
# Leap YEAR MON DAY 23:59:60 + S
Leap 1972 Jun 30 23:59:60 + S
Leap 1972 Dec 31 23:59:60 + S
Leap 1973 Dec 31 23:59:60 + S
Leap 1974 Dec 31 23:59:60 + S
Leap 1975 Dec 31 23:59:60 + S
Leap 1976 Dec 31 23:59:60 + S
Leap 1977 Dec 31 23:59:60 + S
Leap 1978 Dec 31 23:59:60 + S
Leap 1979 Dec 31 23:59:60 + S
Leap 1981 Jun 30 23:59:60 + S
Leap 1982 Jun 30 23:59:60 + S
Leap 1983 Jun 30 23:59:60 + S
Leap 1985 Jun 30 23:59:60 + S
Leap 1987 Dec 31 23:59:60 + S
Leap 1989 Dec 31 23:59:60 + S
Leap 1990 Dec 31 23:59:60 + S
Leap 1992 Jun 30 23:59:60 + S
Leap 1993 Jun 30 23:59:60 + S
Leap 1994 Jun 30 23:59:60 + S
Leap 1995 Dec 31 23:59:60 + S
Leap 1997 Jun 30 23:59:60 + S
Leap 1998 Dec 31 23:59:60 + S
Leap 2005 Dec 31 23:59:60 + S
Leap 2008 Dec 31 23:59:60 + S
Leap 2012 Jun 30 23:59:60 + S
Leap 2015 Jun 30 23:59:60 + S
Leap 2016 Dec 31 23:59:60 + S
# UTC timestamp when this leap second list expires.
# Any additional leap seconds will come after this.
# This Expires line is commented out for now,
# so that pre-2020a zic implementations do not reject this file.
#Expires 2024 Jun 28 00:00:00
# POSIX timestamps for the data in this file:
#updated 1467936000 (2016-07-08 00:00:00 UTC)
#expires 1719532800 (2024-06-28 00:00:00 UTC)
# Updated through IERS Bulletin C66
# File expires on: 28 June 2024

608
src/error.rs Normal file
View file

@ -0,0 +1,608 @@
use alloc::{boxed::Box, string::String};
use crate::{
civil::DateTime,
tz::{Offset, TimeZone},
};
/// Creates a new ad hoc error with no causal chain.
///
/// This accepts the same arguments as the `format!` macro. The error it
/// creates is just a wrapper around the string created by `format!`.
macro_rules! err {
($($tt:tt)*) => {{
crate::error::Error::adhoc(alloc::format!($($tt)*))
}}
}
pub(crate) use err;
#[derive(Debug)]
pub struct Error {
inner: Box<ErrorInner>,
}
#[derive(Debug)]
struct ErrorInner {
kind: ErrorKind,
cause: Option<Error>,
}
/// The underlying kind of a [`Error`].
#[derive(Debug)]
enum ErrorKind {
/// An ad hoc error that is constructed from anything that implements
/// the `core::fmt::Display` trait.
///
/// In theory we try to avoid these, but they tend to be awfully
/// convenient. In practice, we use them a lot, and only use a structured
/// representation when a lot of different error cases fit neatly into a
/// structure (like range errors).
Adhoc(AdhocError),
/// An error that occurs when a number is not within its allowed range.
///
/// This can occur directly as a result of a number provided by the caller
/// of a public API, or as a result of an operation on a number that
/// results in it being out of range.
Range(RangeError),
/// An error that occurs when a lookup of a time zone, by name, fails
/// because that time zone does not exist.
TimeZoneLookup(TimeZoneLookupError),
/// An error that occurs when convertion from a civil datetime to a
/// precise instant results in ambiguity (for example, because the civil
/// datetime occurs in or around a DST transition).
AmbiguousInstant(AmbiguousInstantError),
/// An error associated with a file path.
///
/// This is generally expected to always have a cause attached to it
/// explaining what went wrong. The error variant is just a path to make
/// it composable with other error types.
///
/// The cause is typically `Adhoc` or `IO`.
///
/// When `std` is not enabled, this variant can never be constructed.
FilePath(FilePathError),
/// An error that occurs when interacting with the file system.
///
/// This is effectively a wrapper around `std::io::Error` coupled with a
/// `std::path::PathBuf`.
///
/// When `std` is not enabled, this variant can never be constructed.
IO(IOError),
}
impl Error {
/// Returns a reference to the underlying error kind.
fn kind(&self) -> &ErrorKind {
&self.inner.kind
}
/// Creates a new "ad hoc" error value.
///
/// An ad hoc error value is just an opaque string. In theory we should
/// avoid creating such error values, but in practice, they are extremely
/// convenient. And the alternative is quite brutal given the varied ways
/// in which things in a datetime library can fail. (Especially parsing
/// errors.)
pub(crate) fn adhoc(
err: impl core::fmt::Display + Send + Sync + 'static,
) -> Error {
Error::from(ErrorKind::Adhoc(AdhocError(Box::new(err))))
}
pub(crate) fn unsigned(
what: &'static str,
given: impl Into<u128>,
min: impl Into<i128>,
max: impl Into<i128>,
) -> Error {
Error::from(ErrorKind::Range(RangeError::unsigned(
what, given, min, max,
)))
}
pub(crate) fn signed(
what: &'static str,
given: impl Into<i128>,
min: impl Into<i128>,
max: impl Into<i128>,
) -> Error {
Error::from(ErrorKind::Range(RangeError::signed(
what, given, min, max,
)))
}
pub(crate) fn specific(
what: &'static str,
given: impl Into<i128>,
) -> Error {
Error::from(ErrorKind::Range(RangeError::specific(what, given)))
}
pub(crate) fn time_zone_lookup(
source: impl Into<String>,
name: impl Into<String>,
) -> Error {
let inner = TimeZoneLookupErrorInner {
source: source.into(),
name: name.into(),
};
Error::from(ErrorKind::TimeZoneLookup(TimeZoneLookupError(Box::new(
inner,
))))
}
pub(crate) fn ambiguous_gap(
tz: TimeZone,
dt: DateTime,
before: Offset,
after: Offset,
) -> Error {
let kind = AmbiguousInstantErrorKind::Gap { before, after };
let err =
AmbiguousInstantError(Box::new(AmbiguousInstantErrorInner {
tz,
dt,
kind,
}));
Error::from(ErrorKind::AmbiguousInstant(err))
}
pub(crate) fn ambiguous_fold(
tz: TimeZone,
dt: DateTime,
before: Offset,
after: Offset,
) -> Error {
let kind = AmbiguousInstantErrorKind::Fold { before, after };
let err =
AmbiguousInstantError(Box::new(AmbiguousInstantErrorInner {
tz,
dt,
kind,
}));
Error::from(ErrorKind::AmbiguousInstant(err))
}
/// A convenience constructor for building an I/O error associated with
/// a file path.
///
/// Generallly speaking, an I/O error should always be associated with some
/// sort of context. In Jiff, it's always a file path (at time of writing).
///
/// This is only available when the `std` feature is enabled.
#[cfg(feature = "std")]
pub(crate) fn fs(
path: impl Into<std::path::PathBuf>,
err: std::io::Error,
) -> Error {
Error::io(err).path(path)
}
/// A convenience constructor for building an I/O error.
///
/// This returns an error that is just a simple wrapper around the
/// `std::io::Error` type. In general, callers should alwasys attach some
/// kind of context to this error (like a file path).
///
/// This is only available when the `std` feature is enabled.
#[cfg(feature = "std")]
pub(crate) fn io(err: std::io::Error) -> Error {
Error::from(ErrorKind::IO(IOError { err }))
}
/// Contextualizes this error by associating the given file path with it.
///
/// This is a convenience routine for calling `Error::context` with a
/// `FilePathError`.
///
/// This is only available when the `std` feature is enabled.
#[cfg(feature = "std")]
pub(crate) fn path(self, path: impl Into<std::path::PathBuf>) -> Error {
let err = Error::from(ErrorKind::FilePath(FilePathError {
path: path.into(),
}));
self.context(err)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let mut err = self;
loop {
write!(f, "{}", err.inner.kind)?;
err = match err.inner.cause.as_ref() {
None => break,
Some(err) => err,
};
write!(f, ": ")?;
}
Ok(())
}
}
impl core::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self {
ErrorKind::Adhoc(ref msg) => msg.fmt(f),
ErrorKind::Range(ref err) => err.fmt(f),
ErrorKind::TimeZoneLookup(ref err) => err.fmt(f),
ErrorKind::AmbiguousInstant(ref err) => err.fmt(f),
ErrorKind::FilePath(ref err) => err.fmt(f),
ErrorKind::IO(ref err) => err.fmt(f),
}
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error { inner: Box::new(ErrorInner { kind, cause: None }) }
}
}
struct AdhocError(Box<dyn core::fmt::Display + Send + Sync + 'static>);
#[cfg(feature = "std")]
impl std::error::Error for AdhocError {}
impl core::fmt::Display for AdhocError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl core::fmt::Debug for AdhocError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
self.0.fmt(f)
}
}
/// An error that occurs when an input value is out of bounds.
///
/// The error message produced by this type will include a name describing
/// which input was out of bounds, the value given and its minimum and maximum
/// allowed values.
#[derive(Debug)]
struct RangeError(Box<RangeErrorKind>);
#[derive(Debug)]
enum RangeErrorKind {
Positive { what: &'static str, given: u128, min: i128, max: i128 },
Negative { what: &'static str, given: i128, min: i128, max: i128 },
Specific { what: &'static str, given: i128 },
}
impl RangeError {
fn unsigned(
what: &'static str,
given: impl Into<u128>,
min: impl Into<i128>,
max: impl Into<i128>,
) -> RangeError {
RangeError(Box::new(RangeErrorKind::Positive {
what,
given: given.into(),
min: min.into(),
max: max.into(),
}))
}
fn signed(
what: &'static str,
given: impl Into<i128>,
min: impl Into<i128>,
max: impl Into<i128>,
) -> RangeError {
RangeError(Box::new(RangeErrorKind::Negative {
what,
given: given.into(),
min: min.into(),
max: max.into(),
}))
}
fn specific(what: &'static str, given: impl Into<i128>) -> RangeError {
RangeError(Box::new(RangeErrorKind::Specific {
what,
given: given.into(),
}))
}
}
#[cfg(feature = "std")]
impl std::error::Error for RangeError {}
impl core::fmt::Display for RangeError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match *self.0 {
RangeErrorKind::Positive { what, given, min, max } => {
write!(
f,
"parameter '{what}' with value {given} \
is not in the required range of {min}..={max}",
)
}
RangeErrorKind::Negative { what, given, min, max } => {
write!(
f,
"parameter '{what}' with value {given} \
is not in the required range of {min}..={max}",
)
}
RangeErrorKind::Specific { what, given } => {
write!(f, "parameter '{what}' with value {given} is illegal",)
}
}
}
}
#[derive(Debug)]
struct TimeZoneLookupError(Box<TimeZoneLookupErrorInner>);
#[derive(Debug)]
struct TimeZoneLookupErrorInner {
source: String,
name: String,
}
#[cfg(feature = "std")]
impl std::error::Error for TimeZoneLookupError {}
impl core::fmt::Display for TimeZoneLookupError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"failed to find timezone {:?} in {} database",
self.0.name, self.0.source
)
}
}
#[derive(Debug)]
struct AmbiguousInstantError(Box<AmbiguousInstantErrorInner>);
#[derive(Debug)]
struct AmbiguousInstantErrorInner {
tz: crate::tz::TimeZone,
dt: crate::civil::DateTime,
kind: AmbiguousInstantErrorKind,
}
#[derive(Debug)]
enum AmbiguousInstantErrorKind {
Gap { before: Offset, after: Offset },
Fold { before: Offset, after: Offset },
}
#[cfg(feature = "std")]
impl std::error::Error for AmbiguousInstantError {}
impl core::fmt::Display for AmbiguousInstantError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self.0.kind {
AmbiguousInstantErrorKind::Gap { before, after } => {
write!(
f,
"the datetime {dt} is ambiguous in time zone {tz}, \
as it falls in a gap between offsets {before} and \
{after}",
dt = self.0.dt,
tz = self.0.tz.name(),
)
}
AmbiguousInstantErrorKind::Fold { before, after } => {
write!(
f,
"the datetime {dt} is ambiguous in time zone {tz}, \
as it falls in a fold between offsets {before} and \
{after}",
dt = self.0.dt,
tz = self.0.tz.name(),
)
}
}
}
}
/// A `std::io::Error`.
///
/// This type is itself always available, even when the `std` feature is not
/// enabled. When `std` is not enabled, a value of this type can never be
/// constructed.
///
/// Otherwise, this type is a simple wrapper around `std::io::Error`. Its
/// purpose is to encapsulate the conditional compilation based on the `std`
/// feature.
struct IOError {
#[cfg(feature = "std")]
err: std::io::Error,
}
#[cfg(feature = "std")]
impl std::error::Error for IOError {}
impl core::fmt::Display for IOError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
#[cfg(feature = "std")]
{
write!(f, "{}", self.err)
}
#[cfg(not(feature = "std"))]
{
write!(f, "<BUG: SHOULD NOT EXIST>")
}
}
}
impl core::fmt::Debug for IOError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
#[cfg(feature = "std")]
{
f.debug_struct("IOError").field("err", &self.err).finish()
}
#[cfg(not(feature = "std"))]
{
write!(f, "<BUG: SHOULD NOT EXIST>")
}
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for IOError {
fn from(err: std::io::Error) -> IOError {
IOError { err }
}
}
struct FilePathError {
#[cfg(feature = "std")]
path: std::path::PathBuf,
}
#[cfg(feature = "std")]
impl std::error::Error for FilePathError {}
impl core::fmt::Display for FilePathError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
#[cfg(feature = "std")]
{
write!(f, "{}", self.path.display())
}
#[cfg(not(feature = "std"))]
{
write!(f, "<BUG: SHOULD NOT EXIST>")
}
}
}
impl core::fmt::Debug for FilePathError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
#[cfg(feature = "std")]
{
f.debug_struct("FilePathError").field("path", &self.path).finish()
}
#[cfg(not(feature = "std"))]
{
write!(f, "<BUG: SHOULD NOT EXIST>")
}
}
}
/// A simple trait to encapsulate automatic conversion to `Error`.
///
/// This trait basically exists to make `Error::context` work without needing
/// to rely on public `From` impls. For example, without this trait, we might
/// otherwise write `impl From<String> for Error`. But this would make it part
/// of the public API. Which... maybe we should do, but at time of writing,
/// I'm starting very conservative so that we can evolve errors in semver
/// compatible ways.
pub(crate) trait IntoError {
fn into_error(self) -> Error;
}
impl IntoError for Error {
fn into_error(self) -> Error {
self
}
}
impl IntoError for &'static str {
fn into_error(self) -> Error {
Error::adhoc(self)
}
}
impl IntoError for String {
fn into_error(self) -> Error {
Error::adhoc(self)
}
}
/// A trait for contextualizing error values.
///
/// This makes it easy to contextualize either `Error` or `Result<T, Error>`.
/// Specifically, in the latter case, it absolves one of the need to call
/// `map_err` everywhere one wants to add context to an error.
///
/// This trick was borrowed from `anyhow`.
pub(crate) trait ErrorContext {
/// Contextualize the given consequent error with this (`self`) error as
/// the cause.
///
/// This is equivalent to saying that "consequent is caused by self."
///
/// Note that if an `Error` is given for `kind`, then this panics if it has
/// a cause. (Because the cause would otherwise be dropped. An error causal
/// chain is just a linked list, not a tree.)
fn context(self, consequent: impl IntoError) -> Self;
/// Like `context`, but hides error construction within a closure.
///
/// This is useful if the creation of the consequent error is not otherwise
/// guarded and when error construction is potentially "costly" (i.e., it
/// allocates). The closure avoids paying the cost of contextual error
/// creation in the happy path.
///
/// Usually this only makes sense to use on a `Result<T, Error>`, otherwise
/// the closure is just executed immediately anyway.
fn with_context<E: IntoError>(
self,
consequent: impl FnOnce() -> E,
) -> Self;
}
impl ErrorContext for Error {
fn context(self, consequent: impl IntoError) -> Error {
let mut err = consequent.into_error();
assert!(
err.inner.cause.is_none(),
"cause of consequence must be `None`"
);
err.inner.cause = Some(self);
err
}
fn with_context<E: IntoError>(
self,
consequent: impl FnOnce() -> E,
) -> Error {
let mut err = consequent().into_error();
assert!(
err.inner.cause.is_none(),
"cause of consequence must be `None`"
);
err.inner.cause = Some(self);
err
}
}
impl<T> ErrorContext for Result<T, Error> {
fn context(self, consequent: impl IntoError) -> Result<T, Error> {
self.map_err(|err| err.context(consequent))
}
fn with_context<E: IntoError>(
self,
consequent: impl FnOnce() -> E,
) -> Result<T, Error> {
self.map_err(|err| err.with_context(consequent))
}
}
#[cfg(test)]
mod tests {
use super::*;
// We test that our 'Error' type is the size we expect. This isn't an API
// guarantee, but if the size increases, we really want to make sure we
// decide to do that intentionally. So this should be a speed bump. And in
// general, we should not increase the size without a very good reason.
#[test]
fn error_size() {
let expected_size = core::mem::size_of::<usize>();
assert_eq!(expected_size, core::mem::size_of::<Error>());
}
}

272
src/format/mod.rs Normal file
View file

@ -0,0 +1,272 @@
use alloc::{string::String, vec::Vec};
use crate::{
civil,
error::{err, Error},
tz::Offset,
util::{escape, t},
Instant, TimeScale, Zoned,
};
use self::util::{Decimal, DecimalFormatter};
mod offset;
pub mod rfc3339;
mod rfc9557;
pub mod temporal;
mod util;
const _: &'static dyn Printer = &self::rfc3339::Printer::new();
pub trait Parser {
fn name(&self) -> &'static str;
fn parse_zoned<'i, S: TimeScale>(
&self,
_input: &'i [u8],
) -> Result<Parsed<'i, Zoned<S>>, Error> {
Err(err!(
"{name} does not support parsing zoned instants",
name = self.name()
))
}
fn parse_instant<'i, S: TimeScale>(
&self,
_input: &'i [u8],
) -> Result<Parsed<'i, Instant<S>>, Error> {
Err(err!(
"{name} does not support parsing instants",
name = self.name()
))
}
fn parse_datetime<'i>(
&self,
_input: &'i [u8],
) -> Result<Parsed<'i, civil::DateTime>, Error> {
Err(err!(
"{name} does not support parsing civil datetimes",
name = self.name()
))
}
fn parse_date<'i>(
&self,
_input: &'i [u8],
) -> Result<Parsed<'i, civil::Date>, Error> {
Err(err!(
"{name} does not support parsing civil date",
name = self.name()
))
}
fn parse_time<'i>(
&self,
_input: &'i [u8],
) -> Result<Parsed<'i, civil::Time>, Error> {
Err(err!(
"{name} does not support parsing civil times",
name = self.name()
))
}
}
/// The result of parsing a value out of a slice of bytes.
///
/// This contains both the parsed value and the offset at which the value
/// ended in the input given. This makes it possible to parse, for example, a
/// datetime value as a prefix of some larger string without knowing ahead of
/// time where it ends.
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Parsed<'i, V> {
/// The value parsed.
pub value: V,
/// The remaining unparsed input.
pub input: &'i [u8],
}
impl<'i, V: core::fmt::Display> Parsed<'i, V> {
/// Ensures that the parsed value represents the entire input. This occurs
/// precisely when the `input` on this parsed value is empty.
///
/// This is useful when one expects a parsed value to consume the entire
/// input, and to consider it an error if it doesn't.
pub fn into_full(self) -> Result<V, Error> {
if self.input.is_empty() {
return Ok(self.value);
}
Err(err!(
"parsed value '{value}', but unparsed input {unparsed:?}
remains (expected no unparsed input)",
value = self.value,
unparsed = escape::Bytes(self.input),
))
}
}
impl<'i, V: core::fmt::Debug> core::fmt::Debug for Parsed<'i, V> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Parsed")
.field("value", &self.value)
.field("input", &escape::Bytes(self.input))
.finish()
}
}
pub trait Printer {
fn name(&self) -> &'static str;
fn print_zoned<S: TimeScale, W: Write>(
&self,
_zoned: &Zoned<S>,
_wtr: W,
) -> Result<(), Error>
where
Self: Sized,
{
Err(err!(
"{name} does not support printing zoned instants",
name = self.name()
))
}
fn print_instant<S: TimeScale, W: Write>(
&self,
_instant: &Instant<S>,
_wtr: W,
) -> Result<(), Error>
where
Self: Sized,
{
Err(err!(
"{name} does not support printing instants",
name = self.name()
))
}
fn print_datetime<W: Write>(
&self,
_datetime: &civil::DateTime,
_wtr: W,
) -> Result<(), Error>
where
Self: Sized,
{
Err(err!(
"{name} does not support printing civil datetimes",
name = self.name()
))
}
fn print_date<W: Write>(
&self,
_date: &civil::Date,
_wtr: W,
) -> Result<(), Error>
where
Self: Sized,
{
Err(err!(
"{name} does not support printing civil dates",
name = self.name()
))
}
fn print_time<W: Write>(
&self,
_time: &civil::Time,
_wtr: W,
) -> Result<(), Error>
where
Self: Sized,
{
Err(err!(
"{name} does not support printing civil times",
name = self.name()
))
}
}
pub trait Write {
fn write_str(&mut self, string: &str) -> Result<(), Error>;
#[inline]
fn write_char(&mut self, char: char) -> Result<(), Error> {
self.write_str(char.encode_utf8(&mut [0; 4]))
}
}
impl Write for String {
#[inline]
fn write_str(&mut self, string: &str) -> Result<(), Error> {
self.push_str(string);
Ok(())
}
}
impl Write for Vec<u8> {
#[inline]
fn write_str(&mut self, string: &str) -> Result<(), Error> {
self.extend_from_slice(string.as_bytes());
Ok(())
}
}
impl<W: Write> Write for &mut W {
fn write_str(&mut self, string: &str) -> Result<(), Error> {
(**self).write_str(string)
}
#[inline]
fn write_char(&mut self, char: char) -> Result<(), Error> {
(**self).write_char(char)
}
}
#[cfg(feature = "std")]
#[derive(Clone, Debug)]
pub struct StdWrite<W>(pub W);
#[cfg(feature = "std")]
impl<W: std::io::Write> Write for StdWrite<W> {
#[inline]
fn write_str(&mut self, string: &str) -> Result<(), Error> {
use alloc::string::ToString;
self.0.write_all(string.as_bytes()).map_err(Error::adhoc)
}
}
#[derive(Clone, Debug)]
pub struct FmtWrite<W>(pub W);
impl<W: core::fmt::Write> Write for FmtWrite<W> {
#[inline]
fn write_str(&mut self, string: &str) -> Result<(), Error> {
use alloc::string::ToString;
self.0.write_str(string).map_err(Error::adhoc)
}
}
trait WriteExt: Write {
/// Write the given number as a decimal using ASCII digits to this buffer.
/// The given formatter controls how the decimal is formatted.
#[inline]
fn write_int(
&mut self,
formatter: &DecimalFormatter,
n: impl Into<i64>,
) -> Result<(), Error> {
self.write_decimal(&Decimal::new(formatter, n.into()))
}
/// Write the given decimal number to this buffer.
#[inline]
fn write_decimal(&mut self, decimal: &Decimal) -> Result<(), Error> {
self.write_str(decimal.as_str())
}
}
impl<W: Write> WriteExt for W {}

1021
src/format/offset.rs Normal file

File diff suppressed because it is too large Load diff

213
src/format/rfc3339.rs Normal file
View file

@ -0,0 +1,213 @@
use alloc::{string::String, vec::Vec};
use crate::{
civil::DateTime,
error::Error,
format::{self, util::DecimalFormatter, Write, WriteExt},
tz::Offset,
Instant, TimeScale, TimeZone, Zoned,
};
#[derive(Clone, Debug)]
pub struct Parser {
flexible_separator: bool,
rfc9557: bool,
}
#[derive(Clone, Debug)]
pub struct Printer {
lowercase: bool,
separator: u8,
rfc9557: bool,
}
impl Printer {
pub const fn new() -> Printer {
Printer { lowercase: false, separator: b'T', rfc9557: true }
}
pub const fn lowercase(self, yes: bool) -> Printer {
Printer { lowercase: yes, ..self }
}
pub const fn separator(self, ascii_char: u8) -> Printer {
assert!(ascii_char.is_ascii(), "RFC3339 separator must be ASCII");
Printer { separator: ascii_char, ..self }
}
pub const fn rfc9557(self, yes: bool) -> Printer {
Printer { rfc9557: yes, ..self }
}
/// Formats the given datetime into the writer given.
fn print_datetime<W: Write>(
&self,
dt: &DateTime,
mut wtr: W,
) -> Result<(), Error> {
static FMT_YEAR: DecimalFormatter =
DecimalFormatter::new().minimum_digits(4);
static FMT_TWO: DecimalFormatter =
DecimalFormatter::new().minimum_digits(2);
static FMT_FRACTION: DecimalFormatter =
DecimalFormatter::new().minimum_digits(9);
wtr.write_int(&FMT_YEAR, dt.date().year())?;
wtr.write_str("-");
wtr.write_int(&FMT_TWO, dt.date().month())?;
wtr.write_str("-")?;
wtr.write_int(&FMT_TWO, dt.date().day())?;
wtr.write_char(char::from(if self.lowercase {
self.separator.to_ascii_lowercase()
} else {
self.separator
}))?;
wtr.write_int(&FMT_TWO, dt.time().hour())?;
wtr.write_str(":")?;
wtr.write_int(&FMT_TWO, dt.time().minute())?;
wtr.write_str(":")?;
wtr.write_int(&FMT_TWO, dt.time().second())?;
let fractional_nanosecond = dt.time().subsec_nanosecond();
if fractional_nanosecond != 0 {
wtr.write_str(".")?;
wtr.write_int(&FMT_FRACTION, fractional_nanosecond)?;
}
Ok(())
}
/// Formats the given offset into the writer given.
fn print_offset<W: Write>(
&self,
offset: &Offset,
mut wtr: W,
) -> Result<(), Error> {
static FMT_TWO: DecimalFormatter =
DecimalFormatter::new().minimum_digits(2);
wtr.write_str(if offset.is_negative() { "-" } else { "+" })?;
let mut hours = offset.part_hours_ranged().abs().get();
let mut minutes = offset.part_minutes_ranged().abs().get();
// RFC 3339 requires that time zone offsets are an integral number
// of minutes. While rounding based on seconds doesn't seem clearly
// indicated, the `1937-01-01T12:00:27.87+00:20` example seems
// to suggest that the number of minutes should be "as close as
// possible" to the actual offset. So we just do basic rounding
// here.
if offset.part_seconds_ranged().abs() >= 30 {
if minutes == 59 {
hours = hours.saturating_add(1);
minutes = 0;
} else {
minutes = minutes.saturating_add(1);
}
}
wtr.write_int(&FMT_TWO, hours)?;
wtr.write_str(":")?;
wtr.write_int(&FMT_TWO, minutes)?;
Ok(())
}
/// Prints the "zulu" indicator.
///
/// This should only be used when the local offset is not known. For
/// example, when printing an `Instant`.
fn print_zulu<W: Write>(&self, mut wtr: W) -> Result<(), Error> {
wtr.write_str(if self.lowercase { "z" } else { "Z" })
}
/// Formats the given time zone name into the writer given.
///
/// This is a no-op when RFC 9557 support isn't enabled. And when the given
/// time zone is not an IANA time zone name, then the offset is printed
/// instead. (This means the offset will be printed twice, which is indeed
/// an intended behavior of RFC 9557 for cases where a time zone name is
/// not used or unavailable.)
fn print_time_zone<W: Write>(
&self,
time_zone: &TimeZone,
offset: &Offset,
mut wtr: W,
) -> Result<(), Error> {
if !self.rfc9557 {
return Ok(());
}
wtr.write_str("[")?;
if let Some(iana_name) = time_zone.iana_name() {
wtr.write_str(iana_name)?;
} else {
self.print_offset(offset, &mut wtr)?;
}
wtr.write_str("]")?;
Ok(())
}
}
impl Default for Printer {
fn default() -> Printer {
Printer::new()
}
}
impl format::Printer for Printer {
fn name(&self) -> &'static str {
"RFC3339"
}
fn print_zoned<S: TimeScale, W: Write>(
&self,
zoned: &Zoned<S>,
mut wtr: W,
) -> Result<(), Error> {
let instant = zoned.to_instant();
let tz = zoned.time_zone();
let (offset, _, _) = tz.to_offset(instant);
let dt = offset.to_datetime(instant);
self.print_datetime(&dt, &mut wtr)?;
self.print_offset(&offset, &mut wtr)?;
self.print_time_zone(&tz, &offset, &mut wtr)?;
Ok(())
}
fn print_instant<S: TimeScale, W: Write>(
&self,
instant: &Instant<S>,
mut wtr: W,
) -> Result<(), Error> {
let dt = TimeZone::UTC.to_datetime(*instant);
self.print_datetime(&dt, &mut wtr)?;
self.print_zulu(&mut wtr)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::format::Printer as _;
use super::*;
#[test]
fn print_zoned() {
let dt = DateTime::constant(2024, 3, 10, 5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned("America/New_York").unwrap();
let mut buf = String::new();
Printer::new().print_zoned(&zoned, &mut buf).unwrap();
assert_eq!(buf, "2024-03-10T05:34:45-04:00[America/New_York]");
let dt = DateTime::constant(2024, 3, 10, 5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned("America/New_York").unwrap();
let zoned = zoned.with_time_zone(TimeZone::UTC);
let mut buf = String::new();
Printer::new().print_zoned(&zoned, &mut buf).unwrap();
assert_eq!(buf, "2024-03-10T09:34:45+00:00[UTC]");
}
#[test]
fn print_instant() {
let dt = DateTime::constant(2024, 3, 10, 5, 34, 45, 0);
let zoned: Zoned = dt.to_zoned("America/New_York").unwrap();
let mut buf = String::new();
Printer::new().print_instant(&zoned.to_instant(), &mut buf).unwrap();
assert_eq!(buf, "2024-03-10T09:34:45Z");
}
}

1020
src/format/rfc9557.rs Normal file

File diff suppressed because it is too large Load diff

2298
src/format/temporal.rs Normal file

File diff suppressed because it is too large Load diff

236
src/format/util.rs Normal file
View file

@ -0,0 +1,236 @@
/// A simple formatter for converting `i64` values to ASCII byte strings.
///
/// This avoids going through the formatting machinery which seems to
/// substantially slow things down.
///
/// The `itoa` crate does the same thing as this formatter, but is a bit
/// faster. We roll our own which is a bit slower, but gets us enough of a win
/// to be satisfied with and with pure safe code.
///
/// By default, this only includes the sign if it's negative. To always include
/// the sign, set `force_sign` to `true`.
#[derive(Clone, Debug)]
pub(crate) struct DecimalFormatter {
force_sign: Option<bool>,
minimum_digits: Option<u8>,
fractional: bool,
}
impl DecimalFormatter {
/// Creates a new decimal formatter using the default configuration.
pub(crate) const fn new() -> DecimalFormatter {
DecimalFormatter {
force_sign: None,
minimum_digits: None,
fractional: false,
}
}
/// Format the given value using this configuration as a decimal ASCII
/// number.
pub(crate) const fn format(&self, value: i64) -> Decimal {
Decimal::new(self, value)
}
/// Forces the sign to be rendered, even if it's positive.
///
/// When `zero_is_positive` is true, then a zero value is formatted with a
/// positive sign. Otherwise, it is formatted with a negative sign.
pub(crate) const fn force_sign(
self,
zero_is_positive: bool,
) -> DecimalFormatter {
DecimalFormatter { force_sign: Some(zero_is_positive), ..self }
}
/// The minimum number of digits that this number should be formatted with.
/// If the number would have fewer digits than this, then it is padded out
/// with zeros until the minimum is reached.
///
/// The minimum number of digits is capped at the maximum number of digits
/// for an i64 value (which is 19).
pub(crate) const fn minimum_digits(
self,
mut digits: u8,
) -> DecimalFormatter {
if digits > Decimal::MAX_I64_DIGITS {
digits = Decimal::MAX_I64_DIGITS;
}
DecimalFormatter { minimum_digits: Some(digits), ..self }
}
/// Sets the maximum precision of this as a fractional number.
///
/// This is useful for formatting the fractional digits to the right-hand
/// side of a decimal point.
///
/// This implies `minimum_digits(max_precision)`, but also strips all
/// trailing zeros.
///
/// The maximum precision is capped at the maximum number of digits for an
/// i64 value (which is 19).
pub(crate) const fn fractional(
self,
max_precision: u8,
) -> DecimalFormatter {
DecimalFormatter {
fractional: true,
..self.minimum_digits(max_precision)
}
}
}
impl Default for DecimalFormatter {
fn default() -> DecimalFormatter {
DecimalFormatter::new()
}
}
/// A formatted decimal number that can be converted to a sequence of bytes.
#[derive(Debug)]
pub(crate) struct Decimal {
buf: [u8; Self::MAX_I64_LEN as usize],
start: u8,
end: u8,
}
impl Decimal {
/// Discovered via `i64::MIN.to_string().len()`.
const MAX_I64_LEN: u8 = 20;
/// Discovered via `i64::MAX.to_string().len()`.
const MAX_I64_DIGITS: u8 = 19;
/// Using the given formatter, turn the value given into a decimal
/// representation using ASCII bytes.
pub(crate) const fn new(
formatter: &DecimalFormatter,
value: i64,
) -> Decimal {
let sign = value.signum();
let Some(mut value) = value.checked_abs() else {
let buf = [
b'-', b'9', b'2', b'2', b'3', b'3', b'7', b'2', b'0', b'3',
b'6', b'8', b'5', b'4', b'7', b'7', b'5', b'8', b'0', b'8',
];
return Decimal { buf, start: 0, end: Self::MAX_I64_LEN };
};
let mut decimal = Decimal {
buf: [0; Self::MAX_I64_LEN as usize],
start: Self::MAX_I64_LEN,
end: Self::MAX_I64_LEN,
};
loop {
decimal.start -= 1;
let digit = (value % 10) as u8;
value /= 10;
decimal.buf[decimal.start as usize] = b'0' + digit;
if value == 0 {
break;
}
}
if let Some(minimum_digits) = formatter.minimum_digits {
while decimal.len() < minimum_digits {
decimal.start -= 1;
decimal.buf[decimal.start as usize] = b'0';
}
}
if sign < 0 {
decimal.start -= 1;
decimal.buf[decimal.start as usize] = b'-';
} else if let Some(zero_is_positive) = formatter.force_sign {
let ascii_sign =
if sign > 0 || zero_is_positive { b'+' } else { b'-' };
decimal.start -= 1;
decimal.buf[decimal.start as usize] = ascii_sign;
}
if formatter.fractional {
while decimal.end > 0
&& decimal.buf[decimal.end as usize - 1] == b'0'
{
decimal.end -= 1;
}
}
decimal
}
/// Returns the total number of ASCII bytes (including the sign) that are
/// used to represent this decimal number.
const fn len(&self) -> u8 {
Self::MAX_I64_LEN - self.start
}
/// Returns the ASCII representation of this decimal as a byte slice.
///
/// The slice returned is guaranteed to be valid ASCII.
pub(crate) fn as_bytes(&self) -> &[u8] {
&self.buf[usize::from(self.start)..usize::from(self.end)]
}
/// Returns the ASCII representation of this decimal as a string slice.
pub(crate) fn as_str(&self) -> &str {
// SAFETY: This is safe because all bytes written to `self.buf` are
// guaranteed to be ASCII (including in its initial state), and thus,
// any subsequence is guaranteed to be valid UTF-8.
unsafe { core::str::from_utf8_unchecked(self.as_bytes()) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decimal() {
let x = DecimalFormatter::new().format(i64::MIN);
assert_eq!(x.as_str(), "-9223372036854775808");
let x = DecimalFormatter::new().format(i64::MIN + 1);
assert_eq!(x.as_str(), "-9223372036854775807");
let x = DecimalFormatter::new().format(i64::MAX);
assert_eq!(x.as_str(), "9223372036854775807");
let x = DecimalFormatter::new().force_sign(true).format(i64::MAX);
assert_eq!(x.as_str(), "+9223372036854775807");
let x = DecimalFormatter::new().format(0);
assert_eq!(x.as_str(), "0");
let x = DecimalFormatter::new().force_sign(true).format(0);
assert_eq!(x.as_str(), "+0");
let x = DecimalFormatter::new().force_sign(false).format(0);
assert_eq!(x.as_str(), "-0");
let x = DecimalFormatter::new().minimum_digits(4).format(0);
assert_eq!(x.as_str(), "0000");
let x = DecimalFormatter::new().minimum_digits(4).format(789);
assert_eq!(x.as_str(), "0789");
let x = DecimalFormatter::new().minimum_digits(4).format(-789);
assert_eq!(x.as_str(), "-0789");
let x = DecimalFormatter::new()
.force_sign(true)
.minimum_digits(4)
.format(789);
assert_eq!(x.as_str(), "+0789");
let x = DecimalFormatter::new().fractional(9).format(123_000_000);
assert_eq!(x.as_str(), "123");
let x = DecimalFormatter::new().fractional(9).format(123_456_000);
assert_eq!(x.as_str(), "123456");
let x = DecimalFormatter::new().fractional(9).format(123_456_789);
assert_eq!(x.as_str(), "123456789");
let x = DecimalFormatter::new().fractional(9).format(456_789);
assert_eq!(x.as_str(), "000456789");
let x = DecimalFormatter::new().fractional(9).format(789);
assert_eq!(x.as_str(), "000000789");
}
}

1202
src/instant.rs Normal file

File diff suppressed because it is too large Load diff

453
src/leapseconds.rs Normal file
View file

@ -0,0 +1,453 @@
use alloc::{vec, vec::Vec};
use crate::{
civil::{Date, DateTime, Time},
error::{err, Error, ErrorContext},
instant::{Instant, Tai, Unix},
tz::TimeZone,
util::{
parse,
rangeint::{RFrom, RInto, TryRFrom},
t::{self, C},
},
};
/// The number of seconds that elapsed between the NTP epoch
/// (`1900-01-01T00:00:00Z`) and the Unix epoch (`1970-01-01T00:00:00Z`).
const NTP_UNIX_EPOCH_DIFFERENCE: i64 = 2208988800;
/// Leap seconds in NIST format, which is mostly a copy of the [IERS upstream
/// format].
///
/// Apparently the IERS data file isn't commonly used because of some bullshit
/// about copyright.
///
/// [IERA upstream format]: https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list
static NIST: &'static [u8] = include_bytes!("data/leap-seconds.list");
/// Leap seconds in IANA format.
///
/// I'm not totally clear on why this format exists, but it expresses the same
/// thing as the NIST formatted data, but... in a different format. It is also
/// derived from the NIST data. Here are some differences I observed:
///
/// * The leap seconds specified in UTC. This is arguably more correct, because
/// this is to me how leap seconds are defined. But, it also makes the format
/// more annoying to parse and use. For example, it implies that you need a
/// way to go from civil datetimes to Unix timestamps. The NIST data instead
/// reports leap seconds in Unix time with a `1900-01-01T00:00:00Z` epoch.
/// * Each leap second is specified as a `+` or a `-`, where as in NIST,
/// each leap second reports the total accumulated offset. I find the NIST
/// representation easier to work with personally.
/// * The NIST data includes the 10 second offset at the start of UTC (Jan 1
/// 1972), where as the IANA data does not. The IANA data seems to omit it
/// because it doesn't technically denote a single leap second. But in my view,
/// it effectively denotes an accumulation of 10 leap seconds.
/// * The NIST data uses comments to denote the date immediately following the
/// leap second, where as the IANA UTC datetimes correspond to leap second
/// itself.
///
/// We just support both because it's not that hard to, and because it'd be
/// frustrating if you only had one format and not the other. For example, on
/// my Archlinux system, I have both `/usr/share/zoneinfo/leapseconds` (that's
/// IANA) and `/usr/share/zoneinfo/leap-seconds.list` (that's NIST), but on my
/// mac, all I have is `/usr/share/zoneinfo/leapseconds`. Fun times.
static IANA: &'static [u8] = include_bytes!("data/leapseconds");
#[derive(Debug, Eq, PartialEq)]
pub struct LeapSeconds {
expires: Instant,
by_unix: Vec<LeapSecondUnix>,
by_tai: Vec<LeapSecondTai>,
}
impl LeapSeconds {
pub fn builtin() -> LeapSeconds {
LeapSeconds::from_nist_bytes(NIST)
.expect("hard-coded leap seconds always parse correctly")
}
pub fn from_nist(data: &str) -> Result<LeapSeconds, Error> {
parse_nist(data)
}
pub fn from_nist_bytes(data: &[u8]) -> Result<LeapSeconds, Error> {
let data = core::str::from_utf8(data)
.map_err(Error::adhoc)
.context("invalid UTF-8")?;
LeapSeconds::from_nist(data)
}
pub fn from_iana(data: &str) -> Result<LeapSeconds, Error> {
parse_iana(data)
}
pub fn from_iana_bytes(data: &[u8]) -> Result<LeapSeconds, Error> {
let data = core::str::from_utf8(data)
.map_err(Error::adhoc)
.context("invalid UTF-8")?;
LeapSeconds::from_iana(data)
}
pub fn unix_to_tai(
&self,
instant: Instant<Unix>,
) -> Result<Instant<Tai>, Error> {
let tai_timestamp =
self.unix_to_tai_timestamp(instant.second_ranged())?;
Ok(Instant::from_timestamp_ranged(
tai_timestamp,
instant.nanosecond_ranged(),
))
}
pub fn tai_to_unix(&self, instant: Instant<Tai>) -> Instant<Unix> {
let (unix_timestamp, _) =
self.tai_to_unix_timestamp(instant.second_ranged());
Instant::from_timestamp_ranged(
unix_timestamp,
instant.nanosecond_ranged(),
)
}
pub fn unix_to_tai_timestamp(
&self,
unix_timestamp: t::UnixSeconds,
) -> Result<t::TaiSeconds, Error> {
let result = self
.by_unix
.binary_search_by(|ls| ls.timestamp.cmp(&unix_timestamp));
let i = match result {
Ok(i) => i,
Err(i) if i == 0 => {
return t::TaiSeconds::try_rfrom(
"unix-to-tai seconds",
unix_timestamp,
);
}
Err(i) => i - 1,
};
let unix_timestamp = t::TaiSeconds::rfrom(unix_timestamp);
Ok(unix_timestamp.try_checked_add(
"unix-to-tai seconds",
self.by_unix[i].leap_seconds,
)?)
}
pub fn tai_to_unix_timestamp(
&self,
tai_timestamp: t::TaiSeconds,
) -> (t::UnixSeconds, bool) {
let result = self
.by_tai
.binary_search_by(|ls| ls.timestamp.cmp(&tai_timestamp));
let i = match result {
Ok(i) => i,
Err(i) if i == 0 => return (tai_timestamp.rinto(), false),
Err(i) => i - 1,
};
// The leap second table is keyed by the first second *after* the leap
// second. So we know we have a leap second when it is precisely one
// less than the timestamp in the *next* entry.
let is_leap = i
.checked_add(1)
.and_then(|i| self.by_tai.get(i))
.and_then(|ls| ls.timestamp.checked_sub(C(1)))
.map_or(false, |ts| ts == tai_timestamp);
((tai_timestamp + self.by_tai[i].leap_seconds).rinto(), is_leap)
}
}
#[derive(Debug, Eq, PartialEq)]
struct LeapSecondTai {
timestamp: t::TaiSeconds,
leap_seconds: t::LeapSecondOffset,
}
#[derive(Debug, Eq, PartialEq)]
struct LeapSecondUnix {
timestamp: t::UnixSeconds,
leap_seconds: t::LeapSecondOffset,
}
impl LeapSecondUnix {
/// Creates a new leap second for the given Unix timestamp and the leap
/// second offset. This returns an error if the given values are not valid
/// Unix timestamps in Jiff.
fn new(
timestamp: i64,
leap_seconds: i64,
) -> Result<LeapSecondUnix, Error> {
let timestamp =
t::UnixSeconds::try_new("", timestamp).with_context(|| {
err!("Unix timestamp {timestamp} is out of Jiff's range")
})?;
let leap_seconds = t::LeapSecondOffset::try_new("", leap_seconds)
.with_context(|| {
err!(
"leap second offset {leap_seconds} is out of Jiff's range"
)
})?;
Ok(LeapSecondUnix { timestamp, leap_seconds })
}
/// Convert this leap second in terms of Unix time to TAI time.
///
/// In effect, the corresponding TAI leap second is just the Unix time
/// plus the number of leap seconds that have been added/subtracted since
/// that point.
fn to_tai(&self) -> Result<LeapSecondTai, Error> {
let LeapSecondUnix { timestamp, leap_seconds } = *self;
let timestamp = t::TaiSeconds::rfrom(timestamp)
.try_checked_add("", leap_seconds)
.with_context(|| {
err!(
"overflow occurred when adding Unix timestamp \
{timestamp} to leap second offset {leap_seconds}",
)
})?;
let leap_seconds = t::LeapSecondOffset::rfrom(-leap_seconds);
Ok(LeapSecondTai { timestamp, leap_seconds })
}
}
fn parse_nist(string: &str) -> Result<LeapSeconds, Error> {
let mut expires: Option<Instant> = None;
let mut by_unix = vec![];
let mut by_tai = vec![];
for line in string.lines() {
if let Some(expires_str) = line.strip_prefix("#@") {
if expires.is_some() {
return Err(err!("found multiple expiration lines"));
}
let n = parse::i64(expires_str.trim().as_bytes()).with_context(
|| err!("failed to parse expiration timestamp from {line:?}"),
)?;
expires = Some(ntp_to_instant(n)?);
} else if line.starts_with("#") || line.trim().is_empty() {
continue;
} else {
let mut fields = line.trim().split_whitespace();
let start_str = fields.next().ok_or_else(|| {
err!(
"could not find first field in line {line:?} \
in NIST formatted leap second data",
)
})?;
let offset_str = fields.next().ok_or_else(|| {
err!(
"could not find second field in line {line:?} \
in NIST formatted leap second data",
)
})?;
let ntp_timestamp = parse::i64(start_str.as_bytes())
.with_context(|| {
err!("failed to parse NTP timestamp from {line:?}")
})?;
let timestamp = ntp_to_unix(ntp_timestamp).with_context(|| {
err!("failed to convert NTP timestamp to Unix in {line:?}")
})?;
let leap_seconds = parse::i64(offset_str.as_bytes())
.with_context(|| {
err!("failed to parse leap second offset in {line:?}")
})?;
let unix = LeapSecondUnix::new(timestamp, leap_seconds)
.with_context(|| err!("out of Jiff's range in {line:?}"))?;
let tai = unix.to_tai()?;
by_unix.push(unix);
by_tai.push(tai);
}
}
let Some(expires) = expires else {
return Err(err!(
"could not find expiration in NIST formatted leap second data"
));
};
Ok(LeapSeconds { expires, by_unix, by_tai })
}
fn parse_iana(string: &str) -> Result<LeapSeconds, Error> {
let mut expires: Option<Instant> = None;
let mut by_unix = vec![];
let mut by_tai = vec![];
let mut leap_seconds: i64 = 9;
// Add a dummy line corresponding to the initial 10 second offset.
let init = "Leap 1971 Dec 31 23:59:60 + S";
for line in [init].into_iter().chain(string.lines()) {
if let Some(expires_str) = line.strip_prefix("#Expires") {
if expires.is_some() {
return Err(err!("found multiple expiration lines"));
}
expires = Some(parse_iana_expires(expires_str)?);
} else if let Some(expires_str) = line.strip_prefix("Expires") {
if expires.is_some() {
return Err(err!("found multiple expiration lines"));
}
expires = Some(parse_iana_expires(expires_str)?);
} else if line.starts_with("#") || line.trim().is_empty() {
continue;
} else {
let leap = line.strip_prefix("Leap").ok_or_else(|| {
err!("expected 'Leap' prefix in line {line:?}")
})?;
let dt = if let Some((datetime_str, _)) = leap.split_once("+") {
let dt = parse_iana_datetime(datetime_str)
.context("failed to parse leap second")?;
leap_seconds = leap_seconds
.checked_add(1)
.ok_or_else(|| err!("leap second offset is too big"))?;
dt
} else if let Some((datetime_str, _)) = leap.split_once("-") {
let dt = parse_iana_datetime(datetime_str)
.context("failed to parse leap second")?;
leap_seconds = leap_seconds
.checked_sub(1)
.ok_or_else(|| err!("leap second offset is too small"))?;
dt
} else {
return Err(err!(
"expected '+' or '-' in leap second line {line:?}"
));
};
let instant = TimeZone::UTC.to_instant(dt).with_context(|| {
err!("leap second from line {line:?} is out of bounds")
})?;
let timestamp = instant.to_unix().0;
let unix = LeapSecondUnix::new(timestamp, leap_seconds)
.with_context(|| err!("out of Jiff's range in {line:?}"))?;
let tai = unix.to_tai()?;
by_unix.push(unix);
by_tai.push(tai);
}
}
let Some(expires) = expires else {
return Err(err!(
"could not find expiration in NIST formatted leap second data"
));
};
Ok(LeapSeconds { expires, by_unix, by_tai })
}
fn parse_iana_expires(expires_str: &str) -> Result<Instant, Error> {
let dt = parse_iana_datetime(expires_str)
.context("failed to parse expiration datetime")?;
let instant = TimeZone::UTC
.to_instant(dt)
.context("expiration datetime out of bounds")?;
Ok(instant)
}
fn parse_iana_datetime(datetime_str: &str) -> Result<DateTime, Error> {
let mut fields = datetime_str.trim().split_whitespace();
let year_str = fields.next().ok_or_else(|| {
err!("could not find year field in {datetime_str:?}")
})?;
let month_str = fields.next().ok_or_else(|| {
err!("could not find month field in {datetime_str:?}")
})?;
let day_str = fields
.next()
.ok_or_else(|| err!("could not find day field in {datetime_str:?}"))?;
let time_str = fields.next().ok_or_else(|| {
err!("could not find time field in {datetime_str:?}")
})?;
let mut time_fields = time_str.trim().split(':');
let hour_str = time_fields
.next()
.ok_or_else(|| err!("could not find hour field in {time_str:?}"))?;
let minute_str = time_fields
.next()
.ok_or_else(|| err!("could not find minute field in {time_str:?}"))?;
let second_str = time_fields
.next()
.ok_or_else(|| err!("could not find second field in {time_str:?}"))?;
let year64 = parse::i64(year_str.as_bytes())
.with_context(|| err!("failed to parse year from {year_str:?}"))?;
let year = t::Year::try_new("year", year64)?;
let month = parse_iana_month(month_str)?;
let day64 = parse::i64(day_str.as_bytes())
.with_context(|| err!("failed to parse day from {day_str:?}"))?;
let day = t::Day::try_new("day", day64)?;
let hour64 = parse::i64(hour_str.as_bytes())
.with_context(|| err!("failed to parse hour from {hour_str:?}"))?;
let hour = t::Hour::try_new("hour", hour64)?;
let minute64 = parse::i64(minute_str.as_bytes())
.with_context(|| err!("failed to parse minute from {minute_str:?}"))?;
let minute = t::Minute::try_new("minute", minute64)?;
let mut second64 = parse::i64(second_str.as_bytes())
.with_context(|| err!("failed to parse second from {second_str:?}"))?;
let second = t::UtcSecond::try_new("second", second64)?;
let date = Date::new_ranged(year, month, day)?;
let time = Time::new_ranged(hour, minute, second);
Ok(DateTime::from_parts(date, time))
}
fn parse_iana_month(month_str: &str) -> Result<t::Month, Error> {
let n = match month_str {
"Jan" => 1,
"Feb" => 2,
"Mar" => 3,
"Apr" => 4,
"May" => 5,
"Jun" => 6,
"Jul" => 7,
"Aug" => 8,
"Sep" => 9,
"Oct" => 10,
"Nov" => 11,
"Dec" => 12,
_ => return Err(err!("unrecognized month name {month_str:?}")),
};
Ok(t::Month::new_unchecked(n))
}
fn ntp_to_instant(ntp_timestamp: i64) -> Result<Instant, Error> {
Instant::from_unix(ntp_to_unix(ntp_timestamp)?, 0).with_context(|| {
err!(
"Unix time (from NTP timestamp {ntp_timestamp}) overflows \
allowed range in Jiff",
)
})
}
fn ntp_to_unix(ntp_timestamp: i64) -> Result<i64, Error> {
ntp_timestamp.checked_sub(NTP_UNIX_EPOCH_DIFFERENCE).ok_or_else(|| {
err!(
"overflow when converting NTP timestamp \
{ntp_timestamp} to Unix time"
)
})
}
#[cfg(test)]
mod tests {
use super::*;
/// The leap seconds parsed from the NIST formatted file should be
/// precisely equivalent to the leap seconds parsed from the IANA
/// formatted file.
#[test]
fn equivalent() {
let nist = LeapSeconds::from_nist_bytes(NIST).unwrap();
let iana = LeapSeconds::from_iana_bytes(IANA).unwrap();
assert_eq!(nist, iana);
}
#[test]
fn nist() {
insta::assert_debug_snapshot!(LeapSeconds::from_nist_bytes(NIST));
}
#[test]
fn iana() {
insta::assert_debug_snapshot!(LeapSeconds::from_iana_bytes(IANA));
}
}

View file

@ -1 +1,261 @@
#![allow(warnings)]
#![no_std]
#![deny(rustdoc::broken_intra_doc_links)]
// No clue why this thing is still unstable because it's pretty amazing. This
// adds Cargo feature annotations to items in the rustdoc output. Which is
// sadly hugely beneficial for this crate due to the number of features.
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// We generally want all types to impl Debug.
#![warn(missing_debug_implementations)]
// It should be possible to support other pointer widths, but this library
// hasn't been tested nor thought about much in contexts with pointers less
// than 32 bits.
//
// If you need support for 8-bit or 16-bit, please submit a bug report at
// https://github.com/BurntSushi/jiff
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
compile_error!("jiff currently not supported on non-{32,64}");
#[cfg(any(test, feature = "std"))]
extern crate std;
// We don't currently support a non-alloc mode for Jiff. I'm not opposed to
// doing it, but dynamic memory allocation is pretty heavily weaved into some
// core parts of Jiff. In particular, its error types.
//
// When I originally started writing Jiff, my goal was to support a core-only
// mode. But it became too painful to make every use of the heap conditional.
//
// With that said, I believe errors are the only thing that is "pervasive"
// that allocates. That, and time zones. So if core-only was deeply desired,
// we could probably pull the "much worse user experience" level and make it
// happen. Alternatively, we could carve out a "jiff core" crate, although I
// don't really know what that would look like.
//
// If you have a use case for a core-only Jiff, please file an issue. And
// please don't just say, "I want Jiff without heap allocation." Please give a
// detailed accounting of 1) why you can't use dynamic memory allocation and 2)
// what specific functionality from Jiff you actually need.
extern crate alloc;
pub use crate::{
error::Error,
instant::{Instant, Tai, TimeScale, Unix},
span::{Span, ToSpan},
tz::TimeZone,
zoned::Zoned,
};
#[macro_use]
mod logging;
pub mod civil;
mod error;
pub mod format;
mod instant;
mod leapseconds;
pub mod round;
mod scale;
pub mod span;
pub mod tz;
mod util;
mod zoned;
#[cfg(test)]
mod tests {
use std::time::SystemTime;
use crate::{civil::Date, round::Unit, util::t};
use super::*;
#[test]
fn topscratch() {
let _ = env_logger::try_init();
// std::dbg!(t::SpanMonths::MIN, t::SpanMonths::MAX);
// std::dbg!(t::SpanWeeks::MIN, t::SpanWeeks::MAX);
// std::dbg!(t::SpanDays::MIN, t::SpanDays::MAX);
// std::dbg!(t::SpanHours::MIN, t::SpanHours::MAX);
// std::dbg!(t::SpanMinutes::MIN, t::SpanMinutes::MAX);
// std::dbg!(t::SpanSeconds::MIN, t::SpanSeconds::MAX);
// std::dbg!(t::SpanNanoseconds::MIN, t::SpanNanoseconds::MAX);
// std::dbg!(t::UnixSeconds::MIN, t::UnixSeconds::MAX);
// let tzname = "America/New_York";
//
// let dt = civil::DateTime::constant(2024, 3, 10, 2, 30, 0, 0);
// let inst: Zoned = dt.to_zoned(tzname).unwrap();
// let start = inst.start_of_day().unwrap();
// let end = start.checked_add(1.day()).unwrap();
// std::dbg!(start.to_instant().until(end.to_instant()));
//
// let dt = civil::DateTime::constant(2024, 11, 3, 2, 30, 0, 0);
// let inst: Zoned = dt.to_zoned(tzname).unwrap();
// let start = inst.start_of_day().unwrap();
// let end = start.checked_add(1.day()).unwrap();
// std::dbg!(start.to_instant().until(end.to_instant()));
// let dt = civil::DateTime::constant(-9999, 1, 2, 1, 59, 59, 0);
// let inst: Zoned =
// dt.to_zoned_with(TimeZone::fixed(tz::Offset::UTC)).unwrap();
// let dt = civil::DateTime::constant(2024, 3, 9, 2, 30, 0, 0);
// let inst: Zoned = dt.to_zoned(tzname).unwrap();
// let later = inst.checked_add(1.day()).unwrap();
// // let later = inst.checked_add(24.hours()).unwrap();
// std::dbg!(inst.to_datetime());
// std::dbg!(later.to_datetime());
// let dt = civil::DateTime::constant(2024, 11, 2, 1, 30, 0, 0);
// let inst: Zoned = dt.to_zoned(tzname).unwrap();
// let later = inst.checked_add(1.day()).unwrap();
// // let later = inst.checked_add(24.hours()).unwrap();
// std::dbg!(inst.to_datetime());
// std::dbg!(later.to_datetime());
// std::dbg!(later.time_zone().to_offset(later.to_instant()));
// let tzname = "America/Los_Angeles";
// let tzdb = tz::TimeZoneDatabase::from_env();
// let tz = tzdb.get(tzname).unwrap();
// let dt = civil::DateTime::constant(2015, 06, 30, 23, 59, 60, 0);
// let unix: Instant = Instant::from_datetime_zulu(dt).unwrap();
// let tai: Instant<Tai> = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(unix.to_datetime());
// std::dbg!(tai.to_datetime());
// let unixz = unix.to_zoned(tzname).unwrap();
// let taiz = tai.to_zoned(tzname).unwrap();
// let unixz = Zoned::new(unix, TimeZone::UTC);
// let taiz = Zoned::new(tai, TimeZone::UTC);
// std::dbg!(unixz.to_datetime());
// std::dbg!(taiz.to_datetime());
// let now = Zoned::new(Instant::now(), tz);
// std::dbg!(now.to_datetime());
// let naive_dt = NaiveDateTime::new(date, time);
// let dt = TIMEZONE.from_local_datetime(&naive_dt).single().unwrap();
// // The timezone is EST and the DST offset is zero
// assert_timezone_and_offset(&dt, "EST", 0);
// let dt = civil::DateTime::constant(2015, 06, 30, 23, 59, 59, 0);
// let unix: Instant = Instant::from_datetime_zulu(dt).unwrap();
// let tai: Instant<Tai> = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, unix.second(), tai.second(), tai.to_datetime(), "-----");
//
// let dt = civil::DateTime::constant(2015, 06, 30, 23, 59, 60, 0);
// let unix: Instant = Instant::from_datetime_zulu(dt).unwrap();
// let tai: Instant<Tai> = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, unix.second(), tai.second(), tai.to_datetime(), "-----");
//
// let dt = civil::DateTime::constant(2015, 7, 1, 0, 0, 0, 0);
// let unix: Instant = Instant::from_datetime_zulu(dt).unwrap();
// let tai: Instant<Tai> = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, unix.second(), tai.second(), tai.to_datetime(), "-----");
//
// let dt = civil::DateTime::constant(2015, 7, 1, 0, 0, 1, 0);
// let unix: Instant = Instant::from_datetime_zulu(dt).unwrap();
// let tai: Instant<Tai> = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, unix.second(), tai.second(), tai.to_datetime(), "-----");
// let dt1 = civil::DateTime::constant(2015, 06, 30, 23, 59, 59, 0);
// let unix1: Instant = Instant::from_datetime_zulu(dt1).unwrap();
// let tai1: Instant<Tai> = Instant::from_datetime_zulu(dt1).unwrap();
//
// let dt2 = civil::DateTime::constant(2015, 7, 1, 0, 0, 0, 0);
// let unix2: Instant = Instant::from_datetime_zulu(dt2).unwrap();
// let tai2: Instant<Tai> = Instant::from_datetime_zulu(dt2).unwrap();
//
// std::dbg!(dt2.since(dt1).get_seconds());
// std::dbg!(unix2.since(unix1).get_seconds());
// std::dbg!(tai2.since(tai1).get_seconds());
// let dt = civil::DateTime::constant(2016, 12, 31, 23, 59, 59, 0);
// let inst: Instant = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, inst);
//
// let dt = civil::DateTime::constant(2016, 12, 31, 23, 59, 60, 0);
// let inst: Instant = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, inst);
//
// let dt = civil::DateTime::constant(2017, 1, 1, 0, 0, 0, 0);
// let inst: Instant = Instant::from_datetime_zulu(dt).unwrap();
// std::dbg!(dt, inst);
// let dt1: Zoned = civil::DateTime::constant(2014, 3, 15, 19, 0, 0, 0)
// .to_zoned("America/New_York")
// .unwrap();
// let dt2: Zoned = civil::DateTime::constant(2016, 3, 15, 19, 0, 0, 0)
// .to_zoned("America/New_York")
// .unwrap();
// let span = dt1.until_with_largest_unit(Unit::Hour, &dt2).unwrap();
// std::eprintln!("{span:?}");
//
// let dt1: Zoned<Tai> =
// civil::DateTime::constant(2014, 3, 15, 19, 0, 0, 0)
// .to_zoned("America/New_York")
// .unwrap();
// let dt2: Zoned<Tai> =
// civil::DateTime::constant(2016, 3, 15, 19, 0, 0, 0)
// .to_zoned("America/New_York")
// .unwrap();
// let span = dt1.until_with_largest_unit(Unit::Hour, &dt2).unwrap();
// std::eprintln!("{span:?}");
{
use crate::format::temporal::{
DateTimeParser, Disambiguation, ResolveOffsetConflict,
};
// let x = "2020-01-01T12:00-02:00[America/Sao_Paulo]";
// let x = "2024-03-10T02:30[America/New_York]";
let x = "2024-11-03T01:30-05[America/New_York]";
let parsed = DateTimeParser::new()
.parse_temporal_datetime(x.as_bytes())
.unwrap()
.into_full()
.unwrap();
let result: Result<Zoned, crate::Error> = parsed.to_zoned(
crate::tz::db(),
ResolveOffsetConflict::Reject,
Disambiguation::Reject,
);
match result {
Ok(zdt) => {
std::eprintln!("{}", zdt);
}
Err(err) => {
std::eprintln!("{}", err);
}
}
}
/*
{
use crate::format::temporal::{
DateTimeParser, Disambiguation, ResolveOffsetConflict,
};
let x = "2024-11-03T01:30-05:00[America/New_York]";
let parsed = DateTimeParser::new()
.parse_temporal_datetime(x.as_bytes())
.unwrap()
.into_full()
.unwrap();
let result: Result<Zoned, crate::Error> = parsed.to_zoned(
crate::tz::db(),
ResolveOffsetConflict::Reject,
Disambiguation::Compatible,
);
match result {
Ok(zdt) => {
std::eprintln!("{}", zdt);
}
Err(err) => {
std::eprintln!("{}", err);
}
}
}
*/
}
}

63
src/logging.rs Normal file
View file

@ -0,0 +1,63 @@
// Some feature combinations result in some of these macros never being used.
// Which is fine. Just squash the warnings.
#![allow(unused_macros)]
macro_rules! log {
($($tt:tt)*) => {
#[cfg(feature = "logging")]
{
$($tt)*
}
}
}
macro_rules! error {
($($tt:tt)*) => { log!(log::error!($($tt)*)) }
}
macro_rules! warn {
($($tt:tt)*) => { log!(log::warn!($($tt)*)) }
}
macro_rules! info {
($($tt:tt)*) => { log!(log::info!($($tt)*)) }
}
macro_rules! debug {
($($tt:tt)*) => { log!(log::debug!($($tt)*)) }
}
macro_rules! trace {
($($tt:tt)*) => { log!(log::trace!($($tt)*)) }
}
/// A copy of std's `dbg!` macro that doesn't do pretty printing.
///
/// This is nice because we usually want more compact output in this crate.
/// Also, because we don't import std's prelude, we have to use `std::dbg!`.
/// This macro definition makes it available as `dbg!`.
#[cfg(feature = "std")]
#[macro_export]
macro_rules! dbg {
() => {
std::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!())
};
($val:expr $(,)?) => {
match $val {
tmp => {
std::eprintln!(
"[{}:{}:{}] {} = {:?}",
$crate::file!(),
$crate::line!(),
$crate::column!(),
$crate::stringify!($val),
&tmp,
);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::dbg!($val)),+,)
};
}

1521
src/round.rs Normal file

File diff suppressed because it is too large Load diff

107
src/scale.rs Normal file
View file

@ -0,0 +1,107 @@
use core::{
fmt::Debug,
ops::{Add, Div, Mul, Rem, Sub},
};
use crate::{
error::Error,
util::{
rangeint::{ri64, RInto, RangedDebug, TryRFrom},
t::{
FractionalNanosecond, SpanSeconds, SubsecNanosecond, TaiSeconds,
UnixSeconds,
},
},
};
pub trait TimeScale: TimeScaleInternal {}
impl<S: TimeScaleInternal> TimeScale for S {}
#[derive(Clone, Copy, Debug, Default)]
pub struct Unix;
#[derive(Clone, Copy, Debug, Default)]
pub struct Tai;
pub(crate) trait TimeScaleInternal: Clone + Copy {
type Seconds: Seconds;
fn name() -> &'static str;
fn from_unix_timestamp(
unix_second: UnixSeconds,
) -> Result<Self::Seconds, Error>;
fn to_unix_timestamp(scale_second: Self::Seconds) -> UnixSeconds;
}
pub(crate) trait Seconds:
Copy
+ Clone
+ Debug
+ Eq
+ PartialEq
+ PartialOrd
+ Ord
+ Add
+ Sub
+ Mul
+ Div
+ Rem
{
fn new(val: i64) -> Option<Self>;
fn from_span_seconds(val: SpanSeconds) -> Self;
fn try_from_span_seconds(val: SpanSeconds) -> Result<Self, Error>;
fn to_span_seconds(self) -> SpanSeconds;
fn get(self) -> i64;
}
/*
impl TimeScaleInternal for super::Unix {
type Seconds = UnixSeconds;
fn name() -> &'static str {
"UNIX"
}
fn from_unix_time(
unix_time_seconds: UnixSeconds,
unix_time_nanos: FractionalNanosecond,
) -> Result<(UnixSeconds, FractionalNanosecond), Error> {
Ok((unix_time_seconds, unix_time_nanos))
}
fn to_unix_time(
scale_time_seconds: UnixSeconds,
scale_time_nanos: FractionalNanosecond,
) -> (UnixSeconds, FractionalNanosecond) {
(scale_time_seconds, scale_time_nanos)
}
}
impl Seconds for UnixSeconds {
fn new(val: i64) -> Option<Self> {
UnixSeconds::new(val)
}
fn from_span_seconds(val: SpanSeconds) -> Self {
val.rinto()
}
fn try_from_span_seconds(val: SpanSeconds) -> Result<Self, Error> {
Self::try_rfrom("instant-seconds", val)
}
fn to_span_seconds(self) -> SpanSeconds {
self.rinto()
}
fn get(self) -> i64 {
UnixSeconds::get(self)
}
}
*/

View file

@ -0,0 +1,241 @@
---
source: src/leapseconds.rs
expression: "LeapSeconds::from_iana_bytes(IANA)"
---
Ok(
LeapSeconds {
expires: Instant {
scale: "UNIX",
second: 1719532800,
nanosecond: 0,
},
by_unix: [
LeapSecondUnix {
timestamp: 63072000,
leap_seconds: 10,
},
LeapSecondUnix {
timestamp: 78796800,
leap_seconds: 11,
},
LeapSecondUnix {
timestamp: 94694400,
leap_seconds: 12,
},
LeapSecondUnix {
timestamp: 126230400,
leap_seconds: 13,
},
LeapSecondUnix {
timestamp: 157766400,
leap_seconds: 14,
},
LeapSecondUnix {
timestamp: 189302400,
leap_seconds: 15,
},
LeapSecondUnix {
timestamp: 220924800,
leap_seconds: 16,
},
LeapSecondUnix {
timestamp: 252460800,
leap_seconds: 17,
},
LeapSecondUnix {
timestamp: 283996800,
leap_seconds: 18,
},
LeapSecondUnix {
timestamp: 315532800,
leap_seconds: 19,
},
LeapSecondUnix {
timestamp: 362793600,
leap_seconds: 20,
},
LeapSecondUnix {
timestamp: 394329600,
leap_seconds: 21,
},
LeapSecondUnix {
timestamp: 425865600,
leap_seconds: 22,
},
LeapSecondUnix {
timestamp: 489024000,
leap_seconds: 23,
},
LeapSecondUnix {
timestamp: 567993600,
leap_seconds: 24,
},
LeapSecondUnix {
timestamp: 631152000,
leap_seconds: 25,
},
LeapSecondUnix {
timestamp: 662688000,
leap_seconds: 26,
},
LeapSecondUnix {
timestamp: 709948800,
leap_seconds: 27,
},
LeapSecondUnix {
timestamp: 741484800,
leap_seconds: 28,
},
LeapSecondUnix {
timestamp: 773020800,
leap_seconds: 29,
},
LeapSecondUnix {
timestamp: 820454400,
leap_seconds: 30,
},
LeapSecondUnix {
timestamp: 867715200,
leap_seconds: 31,
},
LeapSecondUnix {
timestamp: 915148800,
leap_seconds: 32,
},
LeapSecondUnix {
timestamp: 1136073600,
leap_seconds: 33,
},
LeapSecondUnix {
timestamp: 1230768000,
leap_seconds: 34,
},
LeapSecondUnix {
timestamp: 1341100800,
leap_seconds: 35,
},
LeapSecondUnix {
timestamp: 1435708800,
leap_seconds: 36,
},
LeapSecondUnix {
timestamp: 1483228800,
leap_seconds: 37,
},
],
by_tai: [
LeapSecondTai {
timestamp: 63072010,
leap_seconds: -10,
},
LeapSecondTai {
timestamp: 78796811,
leap_seconds: -11,
},
LeapSecondTai {
timestamp: 94694412,
leap_seconds: -12,
},
LeapSecondTai {
timestamp: 126230413,
leap_seconds: -13,
},
LeapSecondTai {
timestamp: 157766414,
leap_seconds: -14,
},
LeapSecondTai {
timestamp: 189302415,
leap_seconds: -15,
},
LeapSecondTai {
timestamp: 220924816,
leap_seconds: -16,
},
LeapSecondTai {
timestamp: 252460817,
leap_seconds: -17,
},
LeapSecondTai {
timestamp: 283996818,
leap_seconds: -18,
},
LeapSecondTai {
timestamp: 315532819,
leap_seconds: -19,
},
LeapSecondTai {
timestamp: 362793620,
leap_seconds: -20,
},
LeapSecondTai {
timestamp: 394329621,
leap_seconds: -21,
},
LeapSecondTai {
timestamp: 425865622,
leap_seconds: -22,
},
LeapSecondTai {
timestamp: 489024023,
leap_seconds: -23,
},
LeapSecondTai {
timestamp: 567993624,
leap_seconds: -24,
},
LeapSecondTai {
timestamp: 631152025,
leap_seconds: -25,
},
LeapSecondTai {
timestamp: 662688026,
leap_seconds: -26,
},
LeapSecondTai {
timestamp: 709948827,
leap_seconds: -27,
},
LeapSecondTai {
timestamp: 741484828,
leap_seconds: -28,
},
LeapSecondTai {
timestamp: 773020829,
leap_seconds: -29,
},
LeapSecondTai {
timestamp: 820454430,
leap_seconds: -30,
},
LeapSecondTai {
timestamp: 867715231,
leap_seconds: -31,
},
LeapSecondTai {
timestamp: 915148832,
leap_seconds: -32,
},
LeapSecondTai {
timestamp: 1136073633,
leap_seconds: -33,
},
LeapSecondTai {
timestamp: 1230768034,
leap_seconds: -34,
},
LeapSecondTai {
timestamp: 1341100835,
leap_seconds: -35,
},
LeapSecondTai {
timestamp: 1435708836,
leap_seconds: -36,
},
LeapSecondTai {
timestamp: 1483228837,
leap_seconds: -37,
},
],
},
)

View file

@ -0,0 +1,241 @@
---
source: src/leapseconds.rs
expression: "LeapSeconds::from_nist_bytes(NIST)"
---
Ok(
LeapSeconds {
expires: Instant {
scale: "UNIX",
second: 1719532800,
nanosecond: 0,
},
by_unix: [
LeapSecondUnix {
timestamp: 63072000,
leap_seconds: 10,
},
LeapSecondUnix {
timestamp: 78796800,
leap_seconds: 11,
},
LeapSecondUnix {
timestamp: 94694400,
leap_seconds: 12,
},
LeapSecondUnix {
timestamp: 126230400,
leap_seconds: 13,
},
LeapSecondUnix {
timestamp: 157766400,
leap_seconds: 14,
},
LeapSecondUnix {
timestamp: 189302400,
leap_seconds: 15,
},
LeapSecondUnix {
timestamp: 220924800,
leap_seconds: 16,
},
LeapSecondUnix {
timestamp: 252460800,
leap_seconds: 17,
},
LeapSecondUnix {
timestamp: 283996800,
leap_seconds: 18,
},
LeapSecondUnix {
timestamp: 315532800,
leap_seconds: 19,
},
LeapSecondUnix {
timestamp: 362793600,
leap_seconds: 20,
},
LeapSecondUnix {
timestamp: 394329600,
leap_seconds: 21,
},
LeapSecondUnix {
timestamp: 425865600,
leap_seconds: 22,
},
LeapSecondUnix {
timestamp: 489024000,
leap_seconds: 23,
},
LeapSecondUnix {
timestamp: 567993600,
leap_seconds: 24,
},
LeapSecondUnix {
timestamp: 631152000,
leap_seconds: 25,
},
LeapSecondUnix {
timestamp: 662688000,
leap_seconds: 26,
},
LeapSecondUnix {
timestamp: 709948800,
leap_seconds: 27,
},
LeapSecondUnix {
timestamp: 741484800,
leap_seconds: 28,
},
LeapSecondUnix {
timestamp: 773020800,
leap_seconds: 29,
},
LeapSecondUnix {
timestamp: 820454400,
leap_seconds: 30,
},
LeapSecondUnix {
timestamp: 867715200,
leap_seconds: 31,
},
LeapSecondUnix {
timestamp: 915148800,
leap_seconds: 32,
},
LeapSecondUnix {
timestamp: 1136073600,
leap_seconds: 33,
},
LeapSecondUnix {
timestamp: 1230768000,
leap_seconds: 34,
},
LeapSecondUnix {
timestamp: 1341100800,
leap_seconds: 35,
},
LeapSecondUnix {
timestamp: 1435708800,
leap_seconds: 36,
},
LeapSecondUnix {
timestamp: 1483228800,
leap_seconds: 37,
},
],
by_tai: [
LeapSecondTai {
timestamp: 63072010,
leap_seconds: -10,
},
LeapSecondTai {
timestamp: 78796811,
leap_seconds: -11,
},
LeapSecondTai {
timestamp: 94694412,
leap_seconds: -12,
},
LeapSecondTai {
timestamp: 126230413,
leap_seconds: -13,
},
LeapSecondTai {
timestamp: 157766414,
leap_seconds: -14,
},
LeapSecondTai {
timestamp: 189302415,
leap_seconds: -15,
},
LeapSecondTai {
timestamp: 220924816,
leap_seconds: -16,
},
LeapSecondTai {
timestamp: 252460817,
leap_seconds: -17,
},
LeapSecondTai {
timestamp: 283996818,
leap_seconds: -18,
},
LeapSecondTai {
timestamp: 315532819,
leap_seconds: -19,
},
LeapSecondTai {
timestamp: 362793620,
leap_seconds: -20,
},
LeapSecondTai {
timestamp: 394329621,
leap_seconds: -21,
},
LeapSecondTai {
timestamp: 425865622,
leap_seconds: -22,
},
LeapSecondTai {
timestamp: 489024023,
leap_seconds: -23,
},
LeapSecondTai {
timestamp: 567993624,
leap_seconds: -24,
},
LeapSecondTai {
timestamp: 631152025,
leap_seconds: -25,
},
LeapSecondTai {
timestamp: 662688026,
leap_seconds: -26,
},
LeapSecondTai {
timestamp: 709948827,
leap_seconds: -27,
},
LeapSecondTai {
timestamp: 741484828,
leap_seconds: -28,
},
LeapSecondTai {
timestamp: 773020829,
leap_seconds: -29,
},
LeapSecondTai {
timestamp: 820454430,
leap_seconds: -30,
},
LeapSecondTai {
timestamp: 867715231,
leap_seconds: -31,
},
LeapSecondTai {
timestamp: 915148832,
leap_seconds: -32,
},
LeapSecondTai {
timestamp: 1136073633,
leap_seconds: -33,
},
LeapSecondTai {
timestamp: 1230768034,
leap_seconds: -34,
},
LeapSecondTai {
timestamp: 1341100835,
leap_seconds: -35,
},
LeapSecondTai {
timestamp: 1435708836,
leap_seconds: -36,
},
LeapSecondTai {
timestamp: 1483228837,
leap_seconds: -37,
},
],
},
)

863
src/span/duration.rs Normal file
View file

@ -0,0 +1,863 @@
use core::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
use crate::{
civil,
error::Error,
span::Span,
util::{
rangeint::{RFrom, RInto, TryRFrom},
t::{self, NoUnits128, C},
},
};
/// Represents a duration in time in units of seconds with a fractional
/// nanosecond part.
///
/// The range of seconds is big enough to represent all possible spans in the
/// range `UnixSeconds::MIN..=UnixSeconds::MAX`. That is, it can represent any
/// span of time with the allowed range of representable times supported by
/// this library. (It can technically represent up to `999_999_999` nanoseconds
/// outside of both the minimum and maximum.)
///
/// # Construction
///
/// Typically construction is done via [`Span::time_parts_to_duration`].
///
/// # Comparison with `std::time::Duration`
///
/// The main operational difference is that this duration can be negative,
/// where as the standard library duration is always positive. Otherwise, this
/// duration is clamped to the specific range of seconds supported by this
/// library, and supports a wider variety of operations tailored to this
/// datetime library.
#[derive(Clone, Copy)]
pub(crate) struct TimeDuration {
seconds: t::SpanSeconds,
nanoseconds: t::FractionalNanosecond,
}
impl TimeDuration {
/// The minimum possible time duration.
pub(crate) const MIN: TimeDuration = TimeDuration {
seconds: t::SpanSeconds::MIN_SELF,
nanoseconds: t::FractionalNanosecond::MIN_SELF,
};
/// The maximum possible time duration.
pub(crate) const MAX: TimeDuration = TimeDuration {
seconds: t::SpanSeconds::MAX_SELF,
nanoseconds: t::FractionalNanosecond::MAX_SELF,
};
/// The zero duration.
pub(crate) const ZERO: TimeDuration = TimeDuration {
seconds: t::SpanSeconds::N::<0>(),
nanoseconds: t::FractionalNanosecond::N::<0>(),
};
/// A duration corresponding to a single civil day. i.e., 86,400 seconds.
pub(crate) const CIVIL_DAY: TimeDuration = TimeDuration {
seconds: t::SpanSeconds::N::<86_400>(),
nanoseconds: t::FractionalNanosecond::N::<0>(),
};
/// Create a new duration from the given number of seconds.
#[inline]
pub(crate) fn new(
seconds: impl RInto<t::SpanSeconds>,
nanoseconds: impl RInto<t::FractionalNanosecond>,
) -> TimeDuration {
let seconds = seconds.rinto();
let nanoseconds = nanoseconds.rinto();
if seconds.signum() == nanoseconds.signum()
|| seconds == 0
|| nanoseconds == 0
{
return TimeDuration { seconds, nanoseconds };
}
let seconds = seconds.without_bounds();
let nanoseconds = nanoseconds.without_bounds();
let [delta_seconds, delta_nanos] = t::NoUnits::vary_many(
[seconds, nanoseconds],
|[seconds, nanoseconds]| {
if seconds < 0 && nanoseconds > 0 {
[C(1), (-t::NANOS_PER_SECOND).rinto()]
} else if seconds > 0 && nanoseconds < 0 {
[C(-1), t::NANOS_PER_SECOND.rinto()]
} else {
[C(0), C(0)]
}
},
);
TimeDuration {
seconds: (seconds + delta_seconds).rinto(),
nanoseconds: (nanoseconds + delta_nanos).rinto(),
}
}
/// Create a new duration from primitive values.
///
/// # Errors
///
/// This returns an error when either `seconds` exceeds the maximum span
/// of seconds allowed, or if `nanoseconds` is not in the range
/// `-999_999_999..=999_999_999`.
#[inline]
pub(crate) fn try_new(
seconds: i64,
nanoseconds: i32,
) -> Result<TimeDuration, Error> {
let seconds = t::SpanSeconds::try_new("seconds", seconds)?;
let nanoseconds =
t::FractionalNanosecond::try_new("nanoseconds", nanoseconds)?;
Ok(TimeDuration::new(seconds, nanoseconds))
}
/// Returns *only* the time parts of the given [`Span`] as a
/// [`TimeDuration`].
///
/// That is, the `years`, `months`, `weeks` and `days` fields on a span are
/// ignored.
///
/// # Errors
///
/// This returns an error if this span exceeds what can be represented
/// in a `SpanSeconds`.
#[inline]
pub(crate) fn from_span(span: &Span) -> Result<TimeDuration, Error> {
let hours = t::SpanSeconds::rfrom(span.get_hours_ranged());
let minutes = span.get_minutes_ranged();
let mut seconds: t::SpanSeconds = (hours * t::SECONDS_PER_HOUR)
.try_checked_add(
"minutes-to-seconds",
minutes * t::SECONDS_PER_MINUTE,
)?
.try_checked_add("seconds", span.get_seconds_ranged())?;
let milliseconds = span.get_milliseconds_ranged();
seconds = seconds.try_checked_add(
"milliseconds-to-seconds",
milliseconds.div_ceil(t::MILLIS_PER_SECOND),
)?;
let microseconds = span.get_microseconds_ranged();
seconds = seconds.try_checked_add(
"microseconds-to-seconds",
microseconds.div_ceil(t::MICROS_PER_SECOND),
)?;
let nanoseconds = span.get_nanoseconds_ranged();
seconds = seconds.try_checked_add(
"nanoseconds-to-seconds",
nanoseconds.div_ceil(t::NANOS_PER_SECOND),
)?;
let mut fractional_nanoseconds = t::FractionalNanosecond::rfrom(
nanoseconds.rem_ceil(t::NANOS_PER_SECOND),
);
fractional_nanoseconds +=
t::NANOS_PER_MICRO * microseconds.rem_ceil(t::MICROS_PER_SECOND);
fractional_nanoseconds +=
t::NANOS_PER_MILLI * milliseconds.rem_ceil(t::MILLIS_PER_SECOND);
seconds += fractional_nanoseconds.div_ceil(t::NANOS_PER_SECOND);
fractional_nanoseconds =
fractional_nanoseconds.rem_ceil(t::NANOS_PER_SECOND);
Ok(TimeDuration::new(seconds, fractional_nanoseconds))
}
#[inline]
pub(crate) fn from_nanoseconds(
nanoseconds: NoUnits128,
) -> Result<TimeDuration, Error> {
let seconds = t::SpanSeconds::try_rfrom(
"duration-as-nanoseconds",
nanoseconds.div_ceil(t::NANOS_PER_SECOND),
)?;
let nanoseconds = nanoseconds.rem_ceil(t::NANOS_PER_SECOND);
Ok(TimeDuration::new(seconds, nanoseconds))
}
/// Return a new duration with the number of seconds set to the given
/// value. The nanoseconds remain the same as in `self`.
#[inline]
pub(crate) fn with_seconds(
self,
seconds: impl RInto<t::SpanSeconds>,
) -> TimeDuration {
TimeDuration::new(seconds, self.nanoseconds)
}
/// Return a new duration with the number of nanoseconds set to the given
/// value. The seconds remain the same as in `self`.
#[inline]
pub(crate) fn with_nanos(
self,
nanoseconds: impl RInto<t::FractionalNanosecond>,
) -> TimeDuration {
TimeDuration::new(self.seconds, nanoseconds)
}
/// Negate this duration.
///
/// If it was negative, then this will make the duration positive. If it
/// was positive, then this wil make the duration negative. If the duration
/// is `0`, then this is a no-op.
#[inline]
pub(crate) fn negate(self) -> TimeDuration {
TimeDuration { seconds: -self.seconds, nanoseconds: -self.nanoseconds }
}
/// Returns the absolute value of this duration.
///
/// If the duration was negative, and then this returns the same duration
/// but with the sign flipped to positive. If the duration was positive,
/// then this returns the duration as is.
#[inline]
pub(crate) fn abs(self) -> TimeDuration {
TimeDuration {
seconds: self.seconds.abs(),
nanoseconds: self.nanoseconds.abs(),
}
}
/// Convert this duration into a `Span`.
#[inline]
pub(crate) fn to_span(self) -> Span {
Span::new()
.seconds_ranged(self.seconds())
.nanoseconds_ranged(self.nanoseconds())
}
#[inline]
pub(crate) fn to_nanoseconds(self) -> NoUnits128 {
let seconds = NoUnits128::rfrom(self.seconds());
let nanoseconds = NoUnits128::rfrom(self.nanoseconds());
seconds * t::NANOS_PER_SECOND + nanoseconds
}
/// Return the primitive second and nanosecond components of this duration.
#[inline]
pub(crate) fn to_parts(self) -> (i64, i32) {
(self.seconds.get(), self.nanoseconds.get())
}
/// Return the ranged second and nanosecond components of this duration.
#[inline]
pub(crate) fn to_parts_ranged(
self,
) -> (t::SpanSeconds, t::FractionalNanosecond) {
(self.seconds, self.nanoseconds)
}
/// Returns the number of seconds in this duration.
#[inline]
pub(crate) fn seconds(self) -> t::SpanSeconds {
self.seconds
}
/// Returns the number of nanoseconds in this duration.
///
/// As the return type indicates, the number of nanoseconds is always less
/// than 1 second.
#[inline]
pub(crate) fn nanoseconds(self) -> t::FractionalNanosecond {
self.nanoseconds
}
/// Return the sign of this duration.
#[inline]
pub(crate) fn signum(self) -> t::Sign {
let seconds = self.seconds().without_bounds();
let nanoseconds = self.nanoseconds().without_bounds();
let [signum] = t::NoUnits::vary_many(
[seconds, nanoseconds],
|[seconds, nanoseconds]| {
if seconds != 0 {
[seconds.signum()]
} else {
[nanoseconds.signum()]
}
},
);
signum.rinto()
}
/// Returns true if this duration is precisely equal to zero.
#[inline]
pub(crate) fn is_zero(self) -> bool {
self.seconds() == 0 && self.nanoseconds() == 0
}
/// Adds the given right-hand-side duration to this one.
///
/// # Errors
///
/// If the addition of two durations would represent a bigger span of time
/// than what is supported by this library, then an error is returned.
#[inline]
pub(crate) fn checked_add(
self,
rhs: TimeDuration,
) -> Result<TimeDuration, Error> {
let seconds =
self.seconds().try_checked_add("seconds", rhs.seconds())?;
let nanoseconds = self.nanoseconds().without_bounds()
+ rhs.nanoseconds().without_bounds();
if let Ok(nanoseconds) =
t::FractionalNanosecond::try_rfrom("nanoseconds", nanoseconds)
{
return Ok(TimeDuration::new(seconds, nanoseconds));
}
let seconds = seconds.without_bounds();
// let nanoseconds = nanoseconds.without_bounds();
// This is a little tricky, but both our seconds and nanosecond values
// can change, and that change is dependent on both or either of
// seconds and nanoseconds. Because of that, we need our range integers
// to capture the full scope of dependent variation. So we need to
// calculate how much we're going to change each by. This is also why
// we convert our nanoseconds to `SpanSeconds`, so that we can vary
// both values at the same time.
//
// It is much simpler to implement this using div/mod (basically,
// "balancing"), but I theorized that it would be better to use
// simpler ops here given that this is probably a hot path (since
// a TimeDuration is the representation of an Instant). I haven't
// actually benchmarked it though.
let [delta_seconds, delta_nanos] = t::NoUnits::vary_many(
[seconds, nanoseconds],
|[seconds, nanoseconds]| {
// We generally have two cases to handle here: when the
// addition of nanoseconds causes it to go out of range (in
// either direction), or when seconds and nanoseconds have a
// different sign.
//
// The first case is somewhat straightforward. If nanoseconds
// overflows, then it can overflow by at most 999_999_999. So
// we add (or subtract) one second, and then add (or subtract)
// 1_000_000_000 to the leftovers.
//
// The second case is handled by the `TimeDuration::new`
// constructor call below.
if nanoseconds >= t::NANOS_PER_SECOND {
[C(1), (-t::NANOS_PER_SECOND).rinto()]
} else if nanoseconds <= -t::NANOS_PER_SECOND {
[C(-1), t::NANOS_PER_SECOND.rinto()]
} else {
[C(0), C(0)]
}
},
);
let mut seconds = t::SpanSeconds::rfrom(seconds);
let mut nanoseconds = t::FractionalNanosecond::rfrom(nanoseconds);
seconds = seconds.try_checked_add("seconds", delta_seconds)?;
nanoseconds += delta_nanos;
Ok(TimeDuration::new(seconds, nanoseconds))
}
/// Subtracts the given right-hand-side duration from this one.
///
/// # Errors
///
/// If the subtraction of two durations would represent a bigger span of
/// time than what is supported by this library, then an error is returned.
#[inline]
pub(crate) fn checked_sub(
self,
rhs: TimeDuration,
) -> Result<TimeDuration, Error> {
self.checked_add(rhs.negate())
}
/// Multiplies this duration by the given factor.
///
/// # Errors
///
/// If the multiplication overflows this duration, then an error is
/// returned.
#[inline]
pub(crate) fn checked_mul(
self,
rhs: impl RInto<t::NoUnits>,
) -> Result<TimeDuration, Error> {
let rhs = rhs.rinto();
let seconds = self.seconds().try_checked_mul("seconds", rhs)?;
let nanoseconds = self
.nanoseconds()
.without_bounds()
.try_checked_mul("nanoseconds", rhs)?;
let add_seconds = nanoseconds.div_ceil(t::NANOS_PER_SECOND);
let nanoseconds = t::FractionalNanosecond::rfrom(
nanoseconds.rem_ceil(t::NANOS_PER_SECOND),
);
let seconds = seconds.try_checked_add("seconds", add_seconds)?;
Ok(TimeDuration { seconds, nanoseconds })
}
}
impl Default for TimeDuration {
#[inline]
fn default() -> TimeDuration {
TimeDuration::ZERO
}
}
impl Eq for TimeDuration {}
impl PartialEq for TimeDuration {
#[inline]
fn eq(&self, rhs: &TimeDuration) -> bool {
self.seconds.get() == rhs.seconds.get()
&& self.nanoseconds.get() == rhs.nanoseconds.get()
}
}
impl Ord for TimeDuration {
#[inline]
fn cmp(&self, rhs: &TimeDuration) -> core::cmp::Ordering {
(self.seconds.get(), self.nanoseconds.get())
.cmp(&(rhs.seconds.get(), rhs.nanoseconds.get()))
}
}
impl PartialOrd for TimeDuration {
#[inline]
fn partial_cmp(&self, rhs: &TimeDuration) -> Option<core::cmp::Ordering> {
Some(self.cmp(rhs))
}
}
impl Neg for TimeDuration {
type Output = TimeDuration;
#[inline]
fn neg(self) -> Self {
self.negate()
}
}
impl Add for TimeDuration {
type Output = TimeDuration;
#[inline]
fn add(self, rhs: TimeDuration) -> Self {
// See comments in `checked_add` for how this works. The only
// difference here is that we don't do checked arithmetic. We rely on
// our range integers to catch boundary bugs.
let seconds = self.seconds() + rhs.seconds();
let nanoseconds = self.nanoseconds().without_bounds()
+ rhs.nanoseconds().without_bounds();
if let Ok(nanoseconds) =
t::FractionalNanosecond::try_rfrom("nanoseconds", nanoseconds)
{
return TimeDuration::new(seconds, nanoseconds);
}
let seconds = seconds.without_bounds();
let [delta_seconds, delta_nanos] = t::NoUnits::vary_many(
[seconds, nanoseconds],
|[seconds, nanoseconds]| {
if nanoseconds >= t::NANOS_PER_SECOND {
[C(1), (-t::NANOS_PER_SECOND).rinto()]
} else if nanoseconds <= -t::NANOS_PER_SECOND {
[C(-1), t::NANOS_PER_SECOND.rinto()]
} else {
[C(0), C(0)]
}
},
);
let mut seconds = t::SpanSeconds::rfrom(seconds);
let mut nanoseconds = t::FractionalNanosecond::rfrom(nanoseconds);
seconds += delta_seconds;
nanoseconds += delta_nanos;
TimeDuration::new(seconds, nanoseconds)
}
}
impl AddAssign for TimeDuration {
#[inline]
fn add_assign(&mut self, rhs: TimeDuration) {
*self = *self + rhs;
}
}
impl Sub for TimeDuration {
type Output = TimeDuration;
#[inline]
fn sub(self, rhs: TimeDuration) -> Self {
self.add(-rhs)
}
}
impl SubAssign for TimeDuration {
#[inline]
fn sub_assign(&mut self, rhs: TimeDuration) {
*self = *self - rhs;
}
}
impl<N: RInto<t::NoUnits>> Mul<N> for TimeDuration {
type Output = TimeDuration;
#[inline]
fn mul(self, rhs: N) -> Self {
let rhs = rhs.rinto();
let mut seconds = self.seconds() * rhs;
let nanoseconds = self.nanoseconds().without_bounds() * rhs;
let add_seconds = nanoseconds.div_ceil(t::NANOS_PER_SECOND);
seconds += add_seconds;
let nanoseconds = t::FractionalNanosecond::rfrom(
nanoseconds.rem_ceil(t::NANOS_PER_SECOND),
);
TimeDuration { seconds, nanoseconds }
}
}
impl core::fmt::Debug for TimeDuration {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let minus = if self.signum() >= 0 { "" } else { "-" };
if self.nanoseconds == 0 {
write!(f, "{minus}{:?}s", self.seconds.abs().debug())
} else {
write!(
f,
"{minus}{:?}s{:?}ns",
self.seconds.abs().debug(),
self.nanoseconds.abs().debug(),
)
}
}
}
#[cfg(test)]
impl quickcheck::Arbitrary for TimeDuration {
fn arbitrary(g: &mut quickcheck::Gen) -> TimeDuration {
let seconds = t::SpanSeconds::arbitrary(g);
let nanoseconds = t::FractionalNanosecond::arbitrary(g);
TimeDuration::new(seconds, nanoseconds)
}
fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
alloc::boxed::Box::new((self.seconds, self.nanoseconds).shrink().map(
|(seconds, nanoseconds)| TimeDuration { seconds, nanoseconds },
))
}
}
#[cfg(test)]
mod tests {
use crate::ToSpan;
use super::*;
fn span_to_time_duration(span: Span) -> Result<(i64, i32), Error> {
TimeDuration::from_span(&span).map(|d| d.to_parts())
}
#[test]
fn from_span() {
let d = |span| span_to_time_duration(span).unwrap();
assert_eq!((1, 0), d(Span::new().seconds(1)));
assert_eq!((1, 1), d(Span::new().seconds(1).nanoseconds(1)));
assert_eq!(
(2, 0),
d(Span::new().seconds(1).nanoseconds(1_000_000_000))
);
assert_eq!(
(t::SpanSeconds::MAX_REPR, 0),
d(Span::new().hours(t::SpanHours::MAX_REPR))
);
assert_eq!(
(t::SpanSeconds::MAX_REPR, 999_999_999),
d(Span::new()
.hours(t::SpanHours::MAX_REPR)
.nanoseconds(999_999_999))
);
assert_eq!((1, 1_000_000), d(Span::new().milliseconds(1_001)));
assert_eq!((1, 1_000), d(Span::new().microseconds(1_000_001)));
assert_eq!(
(2, 1_001_000),
d(Span::new().milliseconds(1_001).microseconds(1_000_001))
);
assert_eq!(
(3, 1_001_001),
d(Span::new()
.milliseconds(1_001)
.microseconds(1_000_001)
.nanoseconds(1_000_000_001))
);
assert_eq!(
(0, 123_456_789),
d(Span::new()
.milliseconds(123)
.microseconds(456)
.nanoseconds(789))
);
assert_eq!(
(1, 0),
d(Span::new()
.milliseconds(900)
.microseconds(50_000)
.nanoseconds(50_000_000)),
);
// Negative spans.
assert_eq!((-3, -400), d(Span::new().nanoseconds(-3_000_000_400i64)));
// Error cases where the `Span` represents a period of time that cannot
// be represented by a `TimeDuration`.
let d = |span| span_to_time_duration(span);
assert!(
d(Span::new().hours(t::SpanHours::MAX_REPR).minutes(1)).is_err()
);
assert!(
d(Span::new().hours(t::SpanHours::MAX_REPR).seconds(1)).is_err()
);
assert!(d(Span::new()
.hours(t::SpanHours::MAX_REPR)
.nanoseconds(1_000_000_000))
.is_err());
}
#[test]
fn checked_add() {
let add = |(s1, n1), (s2, n2)| {
let d1 = TimeDuration::try_new(s1, n1).unwrap();
let d2 = TimeDuration::try_new(s2, n2).unwrap();
d1.checked_add(d2)
.map(|d3| (d3.seconds().get(), d3.nanoseconds().get()))
};
assert_eq!((0, 0), add((0, 0), (0, 0)).unwrap());
assert_eq!((3, 999_999_999), add((1, 999_999_998), (2, 1)).unwrap());
assert_eq!((4, 0), add((1, 999_999_999), (2, 1)).unwrap());
assert_eq!(
(4, 999999998),
add((1, 999_999_999), (2, 999_999_999)).unwrap(),
);
assert_eq!(
(t::SpanSeconds::MAX_REPR, 0),
add((t::SpanSeconds::MAX_REPR, 0), (0, 0)).unwrap(),
);
assert_eq!(
(t::SpanSeconds::MAX_REPR, 999_999_999),
add((t::SpanSeconds::MAX_REPR, 0), (0, 999_999_999)).unwrap(),
);
// Tests with negatives.
assert_eq!((2, 0), add((3, 0), (-1, 0)).unwrap());
// e.g., The nanos are being ADDED here. But if the -1 means the
// duration is overall negative, then they should be subtracted.
assert_eq!((1, 1), add((3, 0), (-1, -999_999_999)).unwrap());
assert_eq!((-7, -5_000), add((3, 0), (-10, -5_000)).unwrap());
assert_eq!(
(-6, -999_999_000),
add((3, 6_000), (-10, -5_000)).unwrap(),
);
// Error cases where addition results in a duration that is too big.
assert!(add((t::SpanSeconds::MAX_REPR, 0), (1, 0)).is_err());
assert!(add((t::SpanSeconds::MAX_REPR, 1), (0, 999_999_999)).is_err());
assert!(add(
(t::SpanSeconds::MAX_REPR, 0),
(t::SpanSeconds::MAX_REPR, 0)
)
.is_err());
}
#[test]
fn add() {
let add = |(s1, n1), (s2, n2)| {
let d1 = TimeDuration::new(C(s1), C(n1));
let d2 = TimeDuration::new(C(s2), C(n2));
(d1 + d2).to_parts()
};
assert_eq!((0, 0), add((0, 0), (0, 0)));
assert_eq!((3, 999_999_999), add((1, 999_999_998), (2, 1)));
assert_eq!((4, 0), add((1, 999_999_999), (2, 1)));
assert_eq!((4, 999999998), add((1, 999_999_999), (2, 999_999_999)));
assert_eq!(
(t::SpanSeconds::MAX_REPR, 0),
add((t::SpanSeconds::MAX_REPR, 0), (0, 0))
);
assert_eq!(
(t::SpanSeconds::MAX_REPR, 999_999_999),
add((t::SpanSeconds::MAX_REPR, 0), (0, 999_999_999))
);
// Tests with negatives.
assert_eq!((2, 0), add((3, 0), (-1, 0)));
// e.g., The nanos are being ADDED here. But if the -1 means the
// duration is overall negative, then they should be subtracted.
assert_eq!((1, 1), add((3, 0), (-1, -999_999_999)));
assert_eq!((-7, -5_000), add((3, 0), (-10, -5_000)));
assert_eq!((-6, -999_999_000), add((3, 6_000), (-10, -5_000)));
}
#[test]
fn checked_sub() {
let sub = |(s1, n1), (s2, n2)| {
let d1 = TimeDuration::try_new(s1, n1).unwrap();
let d2 = TimeDuration::try_new(s2, n2).unwrap();
d1.checked_sub(d2)
.map(|d3| (d3.seconds().get(), d3.nanoseconds.get()))
};
assert_eq!((0, 0), sub((0, 0), (0, 0)).unwrap());
assert_eq!((2, 0), sub((5, 0), (3, 0)).unwrap());
assert_eq!((2, 400), sub((5, 1_000), (3, 600)).unwrap());
assert_eq!((1, 999_999_900), sub((5, 1_000), (3, 1_100)).unwrap());
assert_eq!((0, -3), sub((1, 999_999_998), (2, 1)).unwrap());
}
#[test]
fn sub() {
let sub = |(s1, n1), (s2, n2)| {
let d1 = TimeDuration::new(C(s1), C(n1));
let d2 = TimeDuration::new(C(s2), C(n2));
(d1 - d2).to_parts()
};
assert_eq!((0, 0), sub((0, 0), (0, 0)));
assert_eq!((2, 0), sub((5, 0), (3, 0)));
assert_eq!((2, 400), sub((5, 1_000), (3, 600)));
assert_eq!((1, 999_999_900), sub((5, 1_000), (3, 1_100)));
assert_eq!((0, -3), sub((1, 999_999_998), (2, 1)));
}
#[test]
fn new() {
let t = TimeDuration::try_new(1, -1).unwrap();
assert_eq!(t.to_parts(), (0, 999_999_999));
let t = TimeDuration::try_new(-1, 1).unwrap();
assert_eq!(t.to_parts(), (0, -999_999_999));
let t = TimeDuration::try_new(1, 1).unwrap();
assert_eq!(t.to_parts(), (1, 1));
let t = TimeDuration::try_new(-1, -1).unwrap();
assert_eq!(t.to_parts(), (-1, -1));
let t = TimeDuration::try_new(0, 1).unwrap();
assert_eq!(t.to_parts(), (0, 1));
let t = TimeDuration::try_new(0, -1).unwrap();
assert_eq!(t.to_parts(), (0, -1));
let t = TimeDuration::try_new(1, 0).unwrap();
assert_eq!(t.to_parts(), (1, 0));
let t = TimeDuration::try_new(-1, 0).unwrap();
assert_eq!(t.to_parts(), (-1, 0));
let t = TimeDuration::try_new(1, -999_999_999).unwrap();
assert_eq!(t.to_parts(), (0, 1));
let t = TimeDuration::try_new(-1, 999_999_999).unwrap();
assert_eq!(t.to_parts(), (0, -1));
let t = TimeDuration::try_new(1, 999_999_999).unwrap();
assert_eq!(t.to_parts(), (1, 999_999_999));
let t = TimeDuration::try_new(-1, -999_999_999).unwrap();
assert_eq!(t.to_parts(), (-1, -999_999_999));
let t = TimeDuration::new(
t::SpanSeconds::MAX_SELF,
t::FractionalNanosecond::MIN_SELF,
);
assert_eq!(t.to_parts(), (t::SpanSeconds::MAX_REPR - 1, 1));
let t = TimeDuration::new(
t::SpanSeconds::MAX_SELF,
t::FractionalNanosecond::MAX_SELF,
);
assert_eq!(t.to_parts(), (t::SpanSeconds::MAX_REPR, 999_999_999));
let t = TimeDuration::new(
t::SpanSeconds::MIN_SELF,
t::FractionalNanosecond::MIN_SELF,
);
assert_eq!(t.to_parts(), (t::SpanSeconds::MIN_REPR, -999_999_999));
let t = TimeDuration::new(
t::SpanSeconds::MIN_SELF,
t::FractionalNanosecond::MAX_SELF,
);
assert_eq!(t.to_parts(), (t::SpanSeconds::MIN_REPR + 1, -1));
}
#[test]
fn mul() {
let td = TimeDuration::new(C(5), C(400_000_000));
let got = td * C(2);
assert_eq!(got.to_parts(), (10, 800_000_000));
let td = TimeDuration::new(C(5), C(400_000_000));
let got = td * C(3);
assert_eq!(got.to_parts(), (16, 200_000_000));
let td = TimeDuration::new(C(-5), C(-400_000_000));
let got = td * C(2);
assert_eq!(got.to_parts(), (-10, -800_000_000));
let td = TimeDuration::new(C(-5), C(-400_000_000));
let got = td * C(3);
assert_eq!(got.to_parts(), (-16, -200_000_000));
let td = TimeDuration::new(C(5), C(400_000_000));
let got = td * C(-2);
assert_eq!(got.to_parts(), (-10, -800_000_000));
let td = TimeDuration::new(C(5), C(400_000_000));
let got = td * C(-3);
assert_eq!(got.to_parts(), (-16, -200_000_000));
let td = TimeDuration::new(C(-5), C(-400_000_000));
let got = td * C(-2);
assert_eq!(got.to_parts(), (10, 800_000_000));
let td = TimeDuration::new(C(-5), C(-400_000_000));
let got = td * C(-3);
assert_eq!(got.to_parts(), (16, 200_000_000));
}
#[test]
fn checked_mul() {
let td = TimeDuration::try_new(5, 400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(2).unwrap()).unwrap();
assert_eq!(got.to_parts(), (10, 800_000_000));
let td = TimeDuration::try_new(5, 400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(3).unwrap()).unwrap();
assert_eq!(got.to_parts(), (16, 200_000_000));
let td = TimeDuration::try_new(-5, -400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(2).unwrap()).unwrap();
assert_eq!(got.to_parts(), (-10, -800_000_000));
let td = TimeDuration::try_new(-5, -400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(3).unwrap()).unwrap();
assert_eq!(got.to_parts(), (-16, -200_000_000));
let td = TimeDuration::try_new(5, 400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(-2).unwrap()).unwrap();
assert_eq!(got.to_parts(), (-10, -800_000_000));
let td = TimeDuration::try_new(5, 400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(-3).unwrap()).unwrap();
assert_eq!(got.to_parts(), (-16, -200_000_000));
let td = TimeDuration::try_new(-5, -400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(-2).unwrap()).unwrap();
assert_eq!(got.to_parts(), (10, 800_000_000));
let td = TimeDuration::try_new(-5, -400_000_000).unwrap();
let got = td.checked_mul(t::NoUnits::new(-3).unwrap()).unwrap();
assert_eq!(got.to_parts(), (16, 200_000_000));
}
quickcheck::quickcheck! {
fn prop_roundtrip_nanoseconds(td: TimeDuration) -> bool {
let nanos = td.to_nanoseconds();
let got = TimeDuration::from_nanoseconds(nanos).unwrap();
td == got
}
}
}

1761
src/span/mod.rs Normal file

File diff suppressed because it is too large Load diff

116
src/tz/db/mod.rs Normal file
View file

@ -0,0 +1,116 @@
use crate::{
error::Error,
instant::{Instant, Tai, Unix},
tz::TimeZone,
util::t,
};
use self::zoneinfo::ZoneInfo;
#[cfg(feature = "std")]
mod zoneinfo;
pub fn db() -> &'static TimeZoneDatabase {
#[cfg(not(feature = "std"))]
{
static NONE: &'static TimeZoneDatabase = TimeZoneDatabase::none();
NONE
}
#[cfg(feature = "std")]
{
use std::sync::OnceLock;
static DB: OnceLock<TimeZoneDatabase> = OnceLock::new();
DB.get_or_init(|| TimeZoneDatabase::from_env())
}
}
#[derive(Debug)]
pub struct TimeZoneDatabase {
kind: TimeZoneDatabaseKind,
}
#[derive(Debug)]
enum TimeZoneDatabaseKind {
None,
ZoneInfo(ZoneInfo),
}
impl TimeZoneDatabase {
pub fn from_env() -> TimeZoneDatabase {
#[cfg(feature = "std")]
{
let zoneinfo = ZoneInfo::from_env();
let kind = TimeZoneDatabaseKind::ZoneInfo(zoneinfo);
TimeZoneDatabase { kind }
}
#[cfg(not(feature = "std"))]
{
TimeZoneDatabase::none()
}
}
pub const fn none() -> TimeZoneDatabase {
let kind = TimeZoneDatabaseKind::None;
TimeZoneDatabase { kind }
}
pub fn get(&self, name: &str) -> Result<TimeZone, Error> {
match self.kind {
TimeZoneDatabaseKind::None => {
Err(Error::time_zone_lookup("unavailable", name))
}
TimeZoneDatabaseKind::ZoneInfo(ref db) => db
.get(name)
.ok_or_else(|| Error::time_zone_lookup("zoneinfo", name)),
}
}
pub fn unix_to_tai(
&self,
instant: Instant<Unix>,
) -> Result<Instant<Tai>, Error> {
match self.kind {
TimeZoneDatabaseKind::None => todo!(),
TimeZoneDatabaseKind::ZoneInfo(ref db) => db.unix_to_tai(instant),
}
}
pub fn tai_to_unix(&self, instant: Instant<Tai>) -> Instant<Unix> {
match self.kind {
TimeZoneDatabaseKind::None => todo!(),
TimeZoneDatabaseKind::ZoneInfo(ref db) => db.tai_to_unix(instant),
}
}
pub fn reset(&self) {
match self.kind {
TimeZoneDatabaseKind::None => {}
TimeZoneDatabaseKind::ZoneInfo(ref db) => db.reset(),
}
}
pub(crate) fn unix_to_tai_timestamp(
&self,
unix_timestamp: t::UnixSeconds,
) -> Result<t::TaiSeconds, Error> {
match self.kind {
TimeZoneDatabaseKind::None => todo!(),
TimeZoneDatabaseKind::ZoneInfo(ref db) => {
db.unix_to_tai_timestamp(unix_timestamp)
}
}
}
pub(crate) fn tai_to_unix_timestamp(
&self,
tai_timestamp: t::TaiSeconds,
) -> (t::UnixSeconds, bool) {
match self.kind {
TimeZoneDatabaseKind::None => todo!(),
TimeZoneDatabaseKind::ZoneInfo(ref db) => {
db.tai_to_unix_timestamp(tai_timestamp)
}
}
}
}

980
src/tz/db/zoneinfo.rs Normal file
View file

@ -0,0 +1,980 @@
use std::{
fs::File,
io::{self, Read},
path::{Path, PathBuf},
sync::{Arc, RwLock},
time::{Duration, Instant as MonotonicInstant},
};
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
use crate::{
error::{err, Error},
instant::{Instant, Tai, Unix},
leapseconds::LeapSeconds,
tz::{tzif::is_possibly_tzif, TimeZone},
util::{escape::Bytes, parse, t},
};
const DEFAULT_TTL: Duration = Duration::new(5 * 60, 0);
const ZONEINFO_DIRECTORIES: &[&str] =
&["/usr/share/zoneinfo", "/etc/zoneinfo"];
#[derive(Debug)]
pub(crate) struct ZoneInfo {
names: Option<ZoneInfoNames>,
zones: RwLock<CachedZones>,
leaps: RwLock<Leaps>,
}
impl ZoneInfo {
pub(crate) fn from_env() -> ZoneInfo {
if let Some(tzdir) = std::env::var_os("TZDIR") {
let tzdir = PathBuf::from(tzdir);
debug!("opening zoneinfo database at TZDIR={}", tzdir.display());
match ZoneInfo::from_dir(&tzdir) {
Ok(db) => return db,
Err(err) => {
warn!("failed opening TZDIR={}: {err}", tzdir.display());
// fall through to attempt default directories
}
}
}
for dir in ZONEINFO_DIRECTORIES {
let tzdir = Path::new(dir);
debug!("opening zoneinfo database at {}", tzdir.display());
match ZoneInfo::from_dir(&tzdir) {
Ok(db) => return db,
Err(err) => {
debug!("failed opening TZDIR={}: {err}", tzdir.display());
}
}
}
warn!(
"could not find zoneinfo database at any of the following \
paths: {}",
ZONEINFO_DIRECTORIES.join(", "),
);
ZoneInfo::none()
}
pub(crate) fn from_dir(dir: &Path) -> Result<ZoneInfo, Error> {
let names = Some(ZoneInfoNames::new(dir)?);
let zones = RwLock::new(CachedZones::new());
// TODO: We shouldn't load leap data eagerly. Most users will probably
// never use it!
let leaps = RwLock::new(match Leaps::new(dir) {
Ok(leaps) => leaps,
Err(err) => {
warn!(
"failed to load leap second data from {}, \
falling back to builtin data: {err}",
dir.display()
);
Leaps::builtin()
}
});
Ok(ZoneInfo { names, zones, leaps })
}
/// Creates a "dummy" zoneinfo database in which all lookups fail.
pub(crate) fn none() -> ZoneInfo {
let names = None;
let zones = RwLock::new(CachedZones::new());
let leaps = RwLock::new(Leaps::builtin());
ZoneInfo { names, zones, leaps }
}
/// Returns true if it is known that all lookups will always fail.
pub(crate) fn is_none(&self) -> bool {
self.names.is_none()
}
pub(crate) fn reset(&self) {
let mut zones = self.zones.write().unwrap();
let mut leaps = self.leaps.write().unwrap();
if let Some(ref names) = self.names {
names.reset();
}
zones.reset();
leaps.reset();
}
pub(crate) fn get(&self, query: &str) -> Option<TimeZone> {
// If we couldn't build any time zone names, then every lookup will
// fail. So just bail now.
let names = self.names.as_ref()?;
// The fast path is when the query matches a pre-existing unexpired
// time zone.
{
let zones = self.zones.read().unwrap();
if let Some(czone) = zones.get(query) {
if !czone.is_expired() {
return Some(czone.tz.clone());
}
}
}
// At this point, one of three possible cases is true:
//
// 1. The given query does not match any time zone in this database.
// 2. A time zone exists, but isn't cached.
// 3. A zime exists and is cached, but needs to be revalidated.
//
// While (3) is probably the common case since our TTLs are pretty
// short, both (2) and (3) require write access. Thus we rule out (1)
// before acquiring a write lock on the entire database. Plus, we'll
// need the zone info for case (2) and possibly for (3) if cache
// revalidation fails.
//
// I feel kind of bad about all this because it seems to me like there
// is too much work being done while holding on to the write lock.
// In particular, it seems like bad juju to do any I/O of any kind
// while holding any lock at all. I think I could design something
// that avoids doing I/O while holding a lock, but it seems a lot more
// complicated. (And what happens if the I/O becomes outdated by the
// time you acquire the lock?)
let info = names.get(query)?;
let mut zones = self.zones.write().unwrap();
let ttl = zones.ttl;
match zones.get_zone_index(query) {
Ok(i) => {
let czone = &mut zones.zones[i];
if czone.revalidate(&info, ttl) {
// Metadata on the file didn't change, so we assume the
// file hasn't either.
return Some(czone.tz.clone());
}
// Revalidation failed. Re-read the TZif data.
let czone = match CachedTimeZone::new(&info, zones.ttl) {
Ok(czone) => czone,
Err(err) => {
warn!(
"failed to re-cache time zone from file {}: {err}",
info.inner.full.display(),
);
return None;
}
};
let tz = czone.tz.clone();
zones.zones[i] = czone;
Some(tz)
}
Err(i) => {
let czone = match CachedTimeZone::new(&info, ttl) {
Ok(czone) => czone,
Err(err) => {
warn!(
"failed to cache time zone from file {}: {err}",
info.inner.full.display(),
);
return None;
}
};
let tz = czone.tz.clone();
zones.zones.insert(i, czone);
Some(tz)
}
}
}
pub(crate) fn unix_to_tai(
&self,
instant: Instant<Unix>,
) -> Result<Instant<Tai>, Error> {
self.with_leap_seconds(|ls| ls.unix_to_tai(instant))
}
pub(crate) fn tai_to_unix(&self, instant: Instant<Tai>) -> Instant<Unix> {
self.with_leap_seconds(|ls| ls.tai_to_unix(instant))
}
pub(crate) fn unix_to_tai_timestamp(
&self,
unix_timestamp: t::UnixSeconds,
) -> Result<t::TaiSeconds, Error> {
self.with_leap_seconds(|ls| ls.unix_to_tai_timestamp(unix_timestamp))
}
pub(crate) fn tai_to_unix_timestamp(
&self,
tai_timestamp: t::TaiSeconds,
) -> (t::UnixSeconds, bool) {
self.with_leap_seconds(|ls| ls.tai_to_unix_timestamp(tai_timestamp))
}
fn with_leap_seconds<T>(&self, mut f: impl FnMut(&LeapSeconds) -> T) -> T {
{
let leaps = self.leaps.read().unwrap();
if !leaps.expiration.is_expired() {
return f(&leaps.leapseconds);
}
}
let mut leaps = self.leaps.write().unwrap();
if leaps.revalidate() {
return f(&leaps.leapseconds);
}
// OK because we can only be here when we have a path.
let path = leaps.path.as_ref().unwrap().to_path_buf();
match Leaps::from_path(&path) {
Ok(new_leaps) => *leaps = new_leaps,
Err(err) => {
warn!(
"failed to update leap second data for {}: {err}",
path.display(),
);
leaps.expiration = Expiration::after(leaps.ttl);
}
}
f(&leaps.leapseconds)
}
}
#[derive(Debug)]
struct CachedZones {
zones: Vec<CachedTimeZone>,
ttl: Duration,
}
impl CachedZones {
const DEFAULT_TTL: Duration = DEFAULT_TTL;
fn new() -> CachedZones {
CachedZones { zones: vec![], ttl: CachedZones::DEFAULT_TTL }
}
fn get(&self, query: &str) -> Option<&CachedTimeZone> {
self.get_zone_index(query).ok().map(|i| &self.zones[i])
}
fn get_zone_index(&self, query: &str) -> Result<usize, usize> {
self.zones.binary_search_by(|zone| {
cmp_ignore_ascii_case(zone.tz.name(), query)
})
}
fn reset(&mut self) {
self.zones.clear();
}
}
#[derive(Clone, Debug)]
struct CachedTimeZone {
tz: TimeZone,
expiration: Expiration,
last_modified: Option<Instant>,
}
impl CachedTimeZone {
/// Create a new cached time zone.
///
/// The `info` says which time zone to create and where to find it. The
/// `ttl` says how long the cached time zone should minimally remain fresh
/// for.
fn new(
info: &ZoneInfoName,
ttl: Duration,
) -> Result<CachedTimeZone, Error> {
let path = &info.inner.full;
let mut file = File::open(path).map_err(|e| Error::fs(path, e))?;
let mut data = vec![];
file.read_to_end(&mut data).map_err(|e| Error::fs(path, e))?;
let tz = TimeZone::tzif(&info.inner.original, &data)
.map_err(|e| e.path(path))?;
let last_modified = last_modified_from_file(path, &file);
let expiration = Expiration::after(ttl);
Ok(CachedTimeZone { tz, expiration, last_modified })
}
/// Returns true if this time zone has gone stale and should, at minimum,
/// be revalidated.
fn is_expired(&self) -> bool {
self.expiration.is_expired()
}
/// Attempts to revalidate this cached time zone.
///
/// Upon successful revalidation (that is, the cached time zone is still
/// fresh and okay to use), this returns true. Otherwise, the cached time
/// zone should be considered stale and must be re-created.
///
/// Note that technically another layer of revalidation could be done.
/// For example, we could keep a checksum of the TZif data, and only
/// consider rebuilding the time zone when the checksum changes. But I
/// think the last modified metadata will in practice be good enough, and
/// parsing a TZif file should be quite fast.
fn revalidate(&mut self, info: &ZoneInfoName, ttl: Duration) -> bool {
// If we started with no last modified timestamp, then I guess we
// should always fail revalidation? I suppose a case could be made to
// do the opposite: always pass revalidation.
let Some(old_last_modified) = self.last_modified else {
info!(
"revalidation for {} failed because old last modified time \
is unavailable",
info.inner.full.display(),
);
return false;
};
let Some(new_last_modified) =
last_modified_from_path(&info.inner.full)
else {
info!(
"revalidation for {} failed because new last modified time \
is unavailable",
info.inner.full.display(),
);
return false;
};
// We consider any change to invalidate cache.
if old_last_modified != new_last_modified {
info!(
"revalidation for {} failed because last modified times \
do not match: old = {} != {} = new",
info.inner.full.display(),
old_last_modified,
new_last_modified,
);
return false;
}
trace!(
"revalidation for {} succeeded because last modified times \
match: old = {} == {} = new",
info.inner.full.display(),
old_last_modified,
new_last_modified,
);
self.expiration = Expiration::after(ttl);
true
}
}
/// A collection of time zone names extracted from a zoneinfo directory.
///
/// Each time zone name maps to a full path on the file system corresponding
/// to the TZif formatted data file for that time zone.
///
/// This type is responsible not just for providing the names, but also for
/// updating them periodically.
#[derive(Debug)]
struct ZoneInfoNames {
inner: RwLock<ZoneInfoNamesInner>,
}
#[derive(Debug)]
struct ZoneInfoNamesInner {
/// The directory from which we collected time zone names.
dir: PathBuf,
/// All available names from the `zoneinfo` directory.
///
/// Each name corresponds to the suffix of a file path
/// starting with `dir`. For example, `America/New_York` in
/// `/usr/share/zoneinfo/America/New_York`. Each name also has a normalized
/// lowercase version of the name for easy case insensitive lookup.
names: Vec<ZoneInfoName>,
/// The expiration time of this cached value.
///
/// Note that this is a necessary but not sufficient criterion for
/// invalidating the cached value.
ttl: Duration,
/// The time at which the data in `names` becomes stale.
///
/// When `None`, it implies that the expiration time is at some arbitrary
/// point in the past beyond any possible `ttl` value. i.e., A `None` value
/// invalidates the cache at the next failed lookup.
expiration: Expiration,
}
impl ZoneInfoNames {
/// The default amount of time to wait before checking for added/removed
/// time zones.
///
/// Note that this TTL is a necessary but not sufficient criterion to
/// provoke cache invalidation. Namely, since we don't expect the set of
/// possible time zone names to change often, we only invalidate the cache
/// under these circumstances:
///
/// 1. The TTL or more has passed since the last time the names were
/// attempted to be refreshed (even if it wasn't successful).
/// 2. A name lookup is attempted and it isn't found. This is required
/// because otherwise there isn't much point in refreshing the names.
///
/// This logic does not deal as well with removals from the underlying time
/// zone database. That in turn is covered by the TTL on constructing the
/// `TimeZone` values themselves.
///
/// We could just use the second criterion on its own, but we require the
/// TTL to expire out of "good sense." Namely, if there is something borked
/// in the environment, the TTL will prevent doing a full scan of the
/// zoneinfo directory for every missed time zone lookup.
const DEFAULT_TTL: Duration = DEFAULT_TTL;
/// Create a new collection of names from the zoneinfo database directory
/// given.
///
/// If no names of time zones with corresponding TZif data files could be
/// found in the given directory, then an error is returned.
fn new(dir: &Path) -> Result<ZoneInfoNames, Error> {
let names = walk(dir)?;
let dir = dir.to_path_buf();
let ttl = ZoneInfoNames::DEFAULT_TTL;
let expiration = Expiration::after(ttl);
let inner = ZoneInfoNamesInner { dir, names, ttl, expiration };
Ok(ZoneInfoNames { inner: RwLock::new(inner) })
}
/// Attempts to find the name entry for the given query using a case
/// insensitive search.
///
/// If no match is found and the data is stale, then the time zone names
/// are refreshed from the file system before doing another check.
fn get(&self, query: &str) -> Option<ZoneInfoName> {
{
let inner = self.inner.read().unwrap();
if let Some(zone_info_name) = inner.get(query) {
return Some(zone_info_name);
}
drop(inner); // unlock
}
let mut inner = self.inner.write().unwrap();
inner.attempt_refresh();
inner.get(query)
}
fn reset(&self) {
self.inner.write().unwrap().reset();
}
}
impl ZoneInfoNamesInner {
/// Attempts to find the name entry for the given query using a case
/// insensitive search.
///
/// `None` is returned if one isn't found.
fn get(&self, query: &str) -> Option<ZoneInfoName> {
self.names
.binary_search_by(|n| cmp_ignore_ascii_case(&n.inner.lower, query))
.ok()
.map(|i| self.names[i].clone())
}
/// Attempts a refresh, but only follows through if the TTL has been
/// exceeded.
///
/// The caller must ensure that the other cache invalidation criteria
/// have been upheld. For example, this should only be called for a missed
/// zone name lookup.
fn attempt_refresh(&mut self) {
if self.expiration.is_expired() {
self.refresh();
}
}
/// Forcefully refreshes the cached names with possibly new data from disk.
/// If an error occurs when fetching the names, then no names are updated
/// (but the `expires_at` is updated). This will also emit a warning log on
/// failure.
fn refresh(&mut self) {
// PERF: Should we try to move this `walk` call to run outside of a
// lock? It probably happens pretty rarely, so it might not matter.
let result = walk(&self.dir);
self.expiration = Expiration::after(self.ttl);
match result {
Ok(names) => {
self.names = names;
}
Err(err) => {
warn!(
"failed to refresh zoneinfo time zone name cache \
for {}: {err}",
self.dir.display(),
)
}
}
}
/// Resets the state such that the next lookup is guaranteed to force a
/// cache refresh, and that it is impossible for any data to be stale.
fn reset(&mut self) {
// This will force the next lookup to fail.
self.names.clear();
// And this will force the next failed lookup to result in a refresh.
self.expiration = Expiration::expired();
}
}
/// A single TZif entry in a zoneinfo database directory.
#[derive(Clone, Debug)]
struct ZoneInfoName {
inner: Arc<ZoneInfoNameInner>,
}
#[derive(Clone, Debug)]
struct ZoneInfoNameInner {
/// A file path resolvable to the corresponding file relative to the
/// working directory of this program.
///
/// Should we canonicalize this to a absolute path? I guess in practice it
/// is an absolute path in most cases.
full: PathBuf,
/// The original name of this time zone taken from the file path with
/// no additional changes.
original: String,
/// The lowercase version of `original`. This is how we determine name
/// equality.
lower: String,
}
impl ZoneInfoName {
/// Create a new time zone info name.
///
/// `base` should corresponding to the zoneinfo directory from which the
/// suffix `time_zone_name` path was returned.
fn new(base: &Path, time_zone_name: &Path) -> Result<ZoneInfoName, Error> {
let full = base.join(time_zone_name);
let original = parse::os_str_utf8(time_zone_name.as_os_str())
.map_err(|err| err.path(base))?;
let lower = original.to_ascii_lowercase();
let inner =
ZoneInfoNameInner { full, original: original.to_string(), lower };
Ok(ZoneInfoName { inner: Arc::new(inner) })
}
}
impl Eq for ZoneInfoName {}
impl PartialEq for ZoneInfoName {
fn eq(&self, rhs: &ZoneInfoName) -> bool {
self.inner.lower == rhs.inner.lower
}
}
impl Ord for ZoneInfoName {
fn cmp(&self, rhs: &ZoneInfoName) -> core::cmp::Ordering {
self.inner.lower.cmp(&rhs.inner.lower)
}
}
impl PartialOrd for ZoneInfoName {
fn partial_cmp(&self, rhs: &ZoneInfoName) -> Option<core::cmp::Ordering> {
Some(self.cmp(rhs))
}
}
impl core::hash::Hash for ZoneInfoName {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.inner.lower.hash(state);
}
}
// TODO: I think Leaps should perhaps be an enum which is either a builtin
// or something that is updated from the file system. And the RwLock should
// only exist in the latter. Otherwise, the builtin should be used. Although,
// maybe we want something more flexible, since we might still want to use the
// builtin if it's equivalent to what's on disk, since it will likely be faster
// since it is forever immutable. So maybe:
//
// struct Leaps {
// builtin: LeapSeconds,
// builtin_active: AtomicBool,
// fs: RwLock<LeapsFromFile>,
// }
//
// Or something like that.
#[derive(Debug)]
struct Leaps {
/// The actual table of leap seconds. This permits Unix<->TAI conversions.
leapseconds: LeapSeconds,
/// The file path to the specific leap second data file we are using.
///
/// This is `None` when using builtin leap second data.
path: Option<PathBuf>,
/// The length of time that we should consider leap second data fresh for.
ttl: Duration,
/// A point in the future where leap second data should be considered
/// potentially stale.
expiration: Expiration,
/// The last modified time of the file we've read leap second data from.
last_modified: Option<Instant>,
}
impl Leaps {
const DEFAULT_TTL: Duration = DEFAULT_TTL;
fn builtin() -> Leaps {
let ttl = Duration::MAX;
Leaps {
leapseconds: LeapSeconds::builtin(),
path: None,
// builtin leap seconds will never change
ttl,
expiration: Expiration::after(ttl),
last_modified: None,
}
}
fn new(dir: &Path) -> Result<Leaps, Error> {
// The IANA formatted `leapseconds` seems to be more ubiquitous. e.g.,
// My mac and Linux machines both have it, but only my Linux machine
// has the NIST formatted `leap-seconds.list`.
let tries = [
("iana", dir.join("leapseconds")),
("nist", dir.join("leap-seconds.list")),
];
for (parse, path) in tries {
match Leaps::from_path(&path) {
Ok(leaps) => return Ok(leaps),
Err(err) => {
debug!(
"could not open leap second data file candidate \
{}: {err}",
path.display(),
);
continue;
}
}
}
Err(err!("could not find leap second data file").path(dir))
}
fn from_path(path: &Path) -> Result<Leaps, Error> {
let mut file = File::open(&path).map_err(|e| Error::fs(path, e))?;
let mut data = vec![];
file.read_to_end(&mut data).map_err(|e| Error::fs(path, e))?;
let leapseconds = if path.ends_with("leapseconds") {
LeapSeconds::from_iana_bytes(&data).map_err(|e| e.path(path))?
} else if path.ends_with("leap-seconds.list") {
LeapSeconds::from_nist_bytes(&data).map_err(|e| e.path(path))?
} else {
return Err(err!(
"unrecognized leap second data file format, \
expected either `leapseconds` or `leap-seconds.list`",
)
.path(path));
};
let last_modified = last_modified_from_file(&path, &file);
Ok(Leaps {
leapseconds,
path: Some(path.to_path_buf()),
ttl: Leaps::DEFAULT_TTL,
expiration: Expiration::after(Leaps::DEFAULT_TTL),
last_modified,
})
}
fn revalidate(&mut self) -> bool {
// TODO: This can be factored out and shared with time zone
// revalidation.
// No path implies builtin, and builtin is always valid.
let Some(ref path) = self.path else { return true };
// If we started with no last modified timestamp, then I guess we
// should always fail revalidation? I suppose a case could be made to
// do the opposite: always pass revalidation.
let Some(old_last_modified) = self.last_modified else {
info!(
"revalidation for {} failed because old last modified time \
is unavailable",
path.display(),
);
return false;
};
let Some(new_last_modified) = last_modified_from_path(path) else {
info!(
"revalidation for {} failed because new last modified time \
is unavailable",
path.display(),
);
return false;
};
// We consider any change to invalidate cache.
if old_last_modified != new_last_modified {
info!(
"revalidation for {} failed because last modified times \
do not match: old = {} != {} = new",
path.display(),
old_last_modified,
new_last_modified,
);
return false;
}
trace!(
"revalidation for {} succeeded because last modified times \
match: old = {} == {} = new",
path.display(),
old_last_modified,
new_last_modified,
);
self.expiration = Expiration::after(self.ttl);
true
}
fn reset(&mut self) {
self.expiration = Expiration::expired();
}
}
/// A little helper for representation expiration time.
///
/// An overflowing expiration time is treated identically to a time that is
/// always expired.
#[derive(Clone, Copy, Debug)]
struct Expiration(Option<MonotonicInstant>);
impl Expiration {
/// Returns an expiration time for which `is_expired` returns true after
/// the given duration has elapsed from this instant.
fn after(ttl: Duration) -> Expiration {
Expiration(MonotonicInstant::now().checked_add(ttl))
}
/// Returns an expiration time for which `is_expired` always returns true.
fn expired() -> Expiration {
Expiration(None)
}
/// Whether expiration has occurred or not.
fn is_expired(self) -> bool {
self.0.map_or(true, |t| MonotonicInstant::now() > t)
}
}
/// Returns the last modified time for the given file path as a Jiff Instant.
///
/// If there was a problem accessing the last modified time or if it could not
/// fit in a Jiff instant, then a warning message is logged and `None` is
/// returned.
fn last_modified_from_path(path: &Path) -> Option<Instant> {
let file = match File::open(path) {
Ok(file) => file,
Err(err) => {
warn!(
"failed to open file to get last modified time {}: {err}",
path.display(),
);
return None;
}
};
last_modified_from_file(path, &file)
}
/// Returns the last modified time for the given file as a Jiff Instant.
///
/// If there was a problem accessing the last modified time or if it could not
/// fit in a Jiff instant, then a warning message is logged and `None` is
/// returned.
///
/// The path given should be the path to the given file. It is used for
/// diagnostic purposes.
fn last_modified_from_file(path: &Path, file: &File) -> Option<Instant> {
let md = match file.metadata() {
Ok(md) => md,
Err(err) => {
warn!(
"failed to get metadata (for last modified time) \
for {}: {err}",
path.display(),
);
return None;
}
};
let systime = match md.modified() {
Ok(systime) => systime,
Err(err) => {
warn!(
"failed to get last modified time for {}: {err}",
path.display()
);
return None;
}
};
let instant = match Instant::try_from(systime) {
Ok(instant) => instant,
Err(err) => {
warn!(
"system time {systime:?} out of bounds \
for Jiff Instant for last modified time \
from {}: {err}",
path.display(),
);
return None;
}
};
Some(instant)
}
/// Recursively walks the given directory and returns the names of all time
/// zones found.
///
/// This is guaranteed to return either one or more time zone names OR an
/// error. That is, `Ok(vec![])` is an impossible result.
///
/// This will attempt to collect as many names as possible, even if some I/O
/// operations fail.
///
/// The names returned are sorted in lexicographic order according to the
/// lowercase form of each name.
fn walk(start: &Path) -> Result<Vec<ZoneInfoName>, Error> {
let mut first_err: Option<Error> = None;
let mut seterr = |path: &Path, err: Error| {
if first_err.is_none() {
first_err = Some(err.path(path));
}
};
let mut names = vec![];
let mut stack = vec![start.to_path_buf()];
while let Some(dir) = stack.pop() {
let readdir = match dir.read_dir() {
Ok(readdir) => readdir,
Err(err) => {
info!(
"error when reading {} as a directory: {err}",
dir.display()
);
seterr(&dir, Error::io(err));
continue;
}
};
for result in readdir {
let dent = match result {
Ok(dent) => dent,
Err(err) => {
info!(
"error when reading directory entry from {}: {err}",
dir.display()
);
seterr(&dir, Error::io(err));
continue;
}
};
let file_type = match dent.file_type() {
Ok(file_type) => file_type,
Err(err) => {
let path = dent.path();
info!(
"error when reading file type from {}: {err}",
path.display()
);
seterr(&path, Error::io(err));
continue;
}
};
let path = dent.path();
if file_type.is_dir() {
stack.push(path);
continue;
}
// We assume symlinks are files, although this may not be
// appropriate. If we need to also handle the case when they're
// directories, then we'll need to add symlink loop detection.
//
// Otherwise, at this point, we peek at the first few bytes of a
// file to do a low false positive and never false negative check
// for a TZif file.
let mut f = match File::open(&path) {
Ok(f) => f,
Err(err) => {
info!("failed to open {}: {err}", path.display());
seterr(&path, Error::io(err));
continue;
}
};
let mut buf = [0; 4];
if let Err(err) = f.read_exact(&mut buf) {
info!(
"failed to read first 4 bytes of {}: {err}",
path.display()
);
seterr(&path, Error::io(err));
continue;
}
if !is_possibly_tzif(&buf) {
// This is a trace because it's perfectly normal for a
// non-TZif file to be in a zoneinfo directory. But it could
// still be potentially useful debugging info.
trace!(
"found file {} that isn't TZif since its first \
four bytes are {:?}",
path.display(),
Bytes(&buf),
);
continue;
}
let time_zone_name = match path.strip_prefix(start) {
Ok(time_zone_name) => time_zone_name,
Err(err) => {
info!(
"failed to extract time zone name from {} \
using {} as a base: {err}",
path.display(),
start.display(),
);
// FIXME: Set an `Error` here instead.
seterr(&path, Error::adhoc(err.to_string()));
continue;
}
};
let zone_info_name =
match ZoneInfoName::new(&start, time_zone_name) {
Ok(zone_info_name) => zone_info_name,
Err(err) => {
seterr(&path, err);
continue;
}
};
names.push(zone_info_name);
}
}
if names.is_empty() {
let err = first_err
.take()
.unwrap_or_else(|| err!("{}: no TZif files", start.display()));
Err(err)
} else {
// If we found at least one valid name, then we declare success and
// drop any error we might have found. They do all get logged above
// though.
names.sort();
Ok(names)
}
}
/// Like std's `eq_ignore_ascii_case`, but returns a full `Ordering`.
fn cmp_ignore_ascii_case(s1: &str, s2: &str) -> core::cmp::Ordering {
let it1 = s1.as_bytes().iter().map(|&b| b.to_ascii_lowercase());
let it2 = s2.as_bytes().iter().map(|&b| b.to_ascii_lowercase());
it1.cmp(it2)
}
#[cfg(test)]
mod tests {
use super::*;
/// DEBUG COMMAND
///
/// Takes environment variable `JIFF_DEBUG_ZONEINFO_DIR` as input and
/// prints a list of all time zone names in the directory (one per line).
///
/// Callers may also set `RUST_LOG` to get extra debugging output.
#[test]
fn debug_zoneinfo_walk() -> anyhow::Result<()> {
use anyhow::Context;
let _ = env_logger::try_init();
const ENV: &str = "JIFF_DEBUG_ZONEINFO_DIR";
let Some(val) = std::env::var_os(ENV) else { return Ok(()) };
let dir = PathBuf::from(val);
let names = walk(&dir)?;
for n in names {
std::eprintln!("{}", n.inner.original);
}
Ok(())
}
}

1455
src/tz/mod.rs Normal file

File diff suppressed because it is too large Load diff

893
src/tz/offset.rs Normal file
View file

@ -0,0 +1,893 @@
use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use crate::{
civil::DateTime,
error::Error,
instant::{Instant, TimeScale, Unix},
span::{Span, TimeDuration},
util::{
rangeint::{RFrom, RInto, TryRFrom},
t::{self, C},
},
};
/// An enum indicating whether a particular datetime or instant is in DST or
/// not.
///
/// DST stands for "daylight savings time." It is a label used to apply to
/// points in time as a way to contrast it with "standard time." DST is
/// usually, but not always, one hour ahead of standard time. When DST takes
/// effect is usually determined by governments, and the rules can vary
/// depending on the location. DST is typically used as a means to maximize
/// "sunlight" time during typical working hours, and as a cost cutting measure
/// by reducing energy consumption. (The effectiveness of DST and whether it
/// is overall worth it is a separate question entirely.)
///
/// In general, most users should never need to deal with this type. But it can
/// be occasionally useful in circumstances where callers need to know whether
/// DST is active or not for a particular point in time.
///
/// This type has a `From<bool>` trait implementation, where the bool is
/// interpreted as being `true` when DST is active.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum Dst {
/// DST is not in effect. In other words, standard time is in effect.
No,
/// DST is in effect.
Yes,
}
impl Dst {
/// Returns true when this value is equal to `Dst::Yes`.
pub fn is_dst(self) -> bool {
matches!(self, Dst::Yes)
}
/// Returns true when this value is equal to `Dst::No`.
///
/// `std` in this context refers to "standard time." That is, it is the
/// offset from UTC used when DST is not in effect.
pub fn is_std(self) -> bool {
matches!(self, Dst::No)
}
}
impl From<bool> for Dst {
fn from(is_dst: bool) -> Dst {
if is_dst {
Dst::Yes
} else {
Dst::No
}
}
}
/// Represents a fixed time zone offset.
///
/// Negative offsets correspond to time zones west of the prime meridian, while
/// positive offsets correspond to time zones east of the prime meridian.
/// Equivalently, in all cases, `civil-time - offset = UTC`.
///
/// # Display format
///
/// This type implements the `std::fmt::Display` trait. It
/// will convert the offset to a string format in the form
/// `{sign}{hours}[:{minutes}[:{seconds}]]`, where `minutes` and `seconds` are
/// only present when non-zero. For example:
///
/// ```
/// use jiff::tz::Offset;
///
/// let o = Offset::constant(-5);
/// assert_eq!(o.to_string(), "-05");
/// let o = Offset::constant_seconds(-18_000);
/// assert_eq!(o.to_string(), "-05");
/// let o = Offset::constant_seconds(-18_060);
/// assert_eq!(o.to_string(), "-05:01");
/// let o = Offset::constant_seconds(-18_062);
/// assert_eq!(o.to_string(), "-05:01:02");
///
/// // The min value.
/// let o = Offset::constant_seconds(-93_599);
/// assert_eq!(o.to_string(), "-25:59:59");
/// // The max value.
/// let o = Offset::constant_seconds(93_599);
/// assert_eq!(o.to_string(), "+25:59:59");
/// // No offset.
/// let o = Offset::constant(0);
/// assert_eq!(o.to_string(), "+00");
/// ```
///
/// # Examples
///
/// TODO: Write some examples here using them with real times.
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Offset {
span: t::SpanZoneOffset,
}
impl Offset {
/// The minimum possible time zone offset.
///
/// This corresponds to the offset `-25:59:59`.
pub const MIN: Offset = Offset { span: t::SpanZoneOffset::MIN_SELF };
/// The maximum possible time zone offset.
///
/// This corresponds to the offset `25:59:59`.
pub const MAX: Offset = Offset { span: t::SpanZoneOffset::MAX_SELF };
/// The offset corresponding to UTC. That is, no offset at all.
///
/// This is defined to always be equivalent to `Offset::ZERO`, but it is
/// semantically distinct. This ought to be used when UTC is desired
/// specifically, while `Offset::ZERO` ought to be used when one wants to
/// express "no offset." For example, when adding offsets, `Offset::ZERO`
/// corresponds to the identity.
pub const UTC: Offset = Offset::ZERO;
/// The offset corresponding to no offset at all.
///
/// This is defined to always be equivalent to `Offset::UTC`, but it is
/// semantically distinct. This ought to be used when a zero offset is
/// desired specifically, while `Offset::UTC` ought to be used when one
/// wants to express UTC. For example, when adding offsets, `Offset::ZERO`
/// corresponds to the identity.
pub const ZERO: Offset = Offset::constant(0);
/// Creates a new time zone offset in a `const` context from a given number
/// of hours.
///
/// Negative offsets correspond to time zones west of the prime meridian,
/// while positive offsets correspond to time zones east of the prime
/// meridian. Equivalently, in all cases, `civil-time - offset = UTC`.
///
/// The fallible non-const version of this constructor is [`Offset::new`].
///
/// # Panics
///
/// This routine panics when the given number of hours is out of range.
/// Namely, `hours` must be in the range `-25..=25`.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// let o = Offset::constant(-5);
/// assert_eq!(o.seconds(), -18_000);
/// let o = Offset::constant(5);
/// assert_eq!(o.seconds(), 18_000);
/// ```
#[inline]
pub const fn constant(hours: i8) -> Offset {
if !t::SpanZoneOffsetHours::contains(hours) {
panic!("invalid time zone offset hours")
}
Offset::constant_seconds((hours as i32) * 60 * 60)
}
/// Creates a new time zone offset in a `const` context from a given number
/// of seconds.
///
/// Negative offsets correspond to time zones west of the prime meridian,
/// while positive offsets correspond to time zones east of the prime
/// meridian. Equivalently, in all cases, `civil-time - offset = UTC`.
///
/// The fallible non-const version of this constructor is
/// [`Offset::new_seconds`].
///
/// # Panics
///
/// This routine panics when the given number of seconds is out of range.
/// The range corresponds to the offsets `-25:59:59..=25:59:59`. In units
/// of seconds, that corresponds to `-93,599..=93,599`.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// let o = Offset::constant_seconds(-18_000);
/// assert_eq!(o.seconds(), -18_000);
/// let o = Offset::constant_seconds(18_000);
/// assert_eq!(o.seconds(), 18_000);
/// ```
#[inline]
pub const fn constant_seconds(seconds: i32) -> Offset {
if !t::SpanZoneOffset::contains(seconds) {
panic!("invalid time zone offset seconds")
}
Offset { span: t::SpanZoneOffset::new_unchecked(seconds) }
}
/// Creates a new time zone offset from a given number of hours.
///
/// Negative offsets correspond to time zones west of the prime meridian,
/// while positive offsets correspond to time zones east of the prime
/// meridian. Equivalently, in all cases, `civil-time - offset = UTC`.
///
/// # Errors
///
/// This routine returns an error when the given number of hours is out of
/// range. Namely, `hours` must be in the range `-25..=25`.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// let o = Offset::new(-5)?;
/// assert_eq!(o.seconds(), -18_000);
/// let o = Offset::new(5)?;
/// assert_eq!(o.seconds(), 18_000);
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn new(hours: i8) -> Result<Offset, Error> {
let hours = t::SpanZoneOffsetHours::try_new("offset-hours", hours)?;
Ok(Offset::new_ranged(hours))
}
/// Creates a new time zone offset in a `const` context from a given number
/// of seconds.
///
/// Negative offsets correspond to time zones west of the prime meridian,
/// while positive offsets correspond to time zones east of the prime
/// meridian. Equivalently, in all cases, `civil-time - offset = UTC`.
///
/// # Errors
///
/// This routine returns an error when the given number of seconds is out
/// of range. The range corresponds to the offsets `-25:59:59..=25:59:59`.
/// In units of seconds, that corresponds to `-93,599..=93,599`.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// let o = Offset::new_seconds(-18_000)?;
/// assert_eq!(o.seconds(), -18_000);
/// let o = Offset::new_seconds(18_000)?;
/// assert_eq!(o.seconds(), 18_000);
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn new_seconds(seconds: i32) -> Result<Offset, Error> {
let seconds = t::SpanZoneOffset::try_new("offset-seconds", seconds)?;
Ok(Offset::new_seconds_ranged(seconds))
}
/// Returns the total number of seconds in this offset.
///
/// The value returned is guaranteed to represent an offset in the range
/// `-25:59:59..=25:59:59`. Or more precisely, the value will be in units
/// of seconds in the range `-93,599..=93,599`.
///
/// Negative offsets correspond to time zones west of the prime meridian,
/// while positive offsets correspond to time zones east of the prime
/// meridian. Equivalently, in all cases, `civil-time - offset = UTC`.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// let o = Offset::constant(-5);
/// assert_eq!(o.seconds(), -18_000);
/// let o = Offset::constant(5);
/// assert_eq!(o.seconds(), 18_000);
/// ```
#[inline]
pub fn seconds(self) -> i32 {
self.seconds_ranged().get()
}
/// Returns the negation of this offset.
///
/// A negative offset will become positive and vice versa. This is a no-op
/// if the offset is zero.
///
/// This never panics.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// assert_eq!(Offset::constant(-5).negate(), Offset::constant(5));
/// // It's also available via the `-` operator:
/// assert_eq!(-Offset::constant(-5), Offset::constant(5));
/// ```
pub fn negate(self) -> Offset {
Offset { span: -self.span }
}
/// Returns true if and only if this offset is less than zero.
///
/// # Example
///
/// ```
/// use jiff::tz::Offset;
///
/// assert!(!Offset::constant(5).is_negative());
/// assert!(!Offset::constant(0).is_negative());
/// assert!(Offset::constant(-5).is_negative());
/// ```
pub fn is_negative(self) -> bool {
self.seconds_ranged() < 0
}
/// Converts the given instant to a civil datetime using this offset.
///
/// # Example
///
/// ```
/// use jiff::{civil::DateTime, tz::Offset, Instant};
///
/// assert_eq!(
/// Offset::constant(-8).to_datetime(Instant::UNIX_EPOCH),
/// DateTime::constant(1969, 12, 31, 16, 0, 0, 0),
/// );
/// ```
#[inline]
pub fn to_datetime<S: TimeScale>(self, instant: Instant<S>) -> DateTime {
instant.to_datetime_with_offset(self)
}
/// Converts the given civil datetime to an instant using this offset.
///
/// # Errors
///
/// This returns an error if this would have returned an instant outside
/// of its minimum and maximum values.
///
/// # Example
///
/// This example shows how to find the instant corresponding to
/// `1969-12-31T16:00:00-08`.
///
/// ```
/// use jiff::{civil::DateTime, tz::Offset, Instant};
///
/// let dt = DateTime::constant(1969, 12, 31, 16, 0, 0, 0);
/// assert_eq!(
/// Offset::constant(-8).to_instant(dt).unwrap(),
/// Instant::UNIX_EPOCH,
/// );
/// ```
///
/// This example shows some maximum boundary conditions where this routine
/// will fail:
///
/// ```
/// use jiff::{civil::DateTime, tz::Offset, Instant, ToSpan};
///
/// let dt = DateTime::constant(9999, 12, 31, 23, 0, 0, 0);
/// assert!(Offset::constant(-8).to_instant(dt).is_err());
///
/// // If the offset is big enough, then converting it to a UTC
/// // instant will fit, even when using the maximum civil datetime.
/// let dt = DateTime::constant(9999, 12, 31, 23, 59, 59, 999_999_999);
/// assert_eq!(Offset::MAX.to_instant(dt).unwrap(), Instant::MAX);
/// // But adjust the offset down 1 second is enough to go out-of-bounds.
/// assert!((Offset::MAX - 1.seconds()).to_instant(dt).is_err());
/// ```
///
/// Same as above, but for minimum values:
///
/// ```
/// use jiff::{civil::DateTime, tz::Offset, Instant, ToSpan};
///
/// let dt = DateTime::constant(-9999, 1, 1, 1, 0, 0, 0);
/// assert!(Offset::constant(8).to_instant(dt).is_err());
///
/// // If the offset is small enough, then converting it to a UTC
/// // instant will fit, even when using the minimum civil datetime.
/// let dt = DateTime::constant(-9999, 1, 1, 0, 0, 0, 0);
/// assert_eq!(Offset::MIN.to_instant(dt).unwrap(), Instant::MIN);
/// // But adjust the offset up 1 second is enough to go out-of-bounds.
/// assert!((Offset::MIN + 1.seconds()).to_instant(dt).is_err());
/// ```
#[inline]
pub fn to_instant(self, dt: DateTime) -> Result<Instant, Error> {
self.to_instant_with_scale(dt)
}
#[inline]
pub fn to_instant_with_scale<S: TimeScale>(
self,
dt: DateTime,
) -> Result<Instant<S>, Error> {
Instant::from_datetime_zulu_ranged(dt)?.checked_sub(self.to_span())
}
/// Returns the span of time since the other offset given from this offset.
///
/// When the `other` is more east (i.e., more positive) of the prime
/// meridian than this offset, then the span returned will be negative.
///
/// # Properties
///
/// Adding the span returned to the `other` offset will always equal this
/// offset.
///
/// # Examples
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// assert_eq!(
/// Offset::UTC.since(Offset::constant(-5)),
/// (5 * 60 * 60).seconds(),
/// );
/// // Flipping the operands in this case results in a negative span.
/// assert_eq!(
/// Offset::constant(-5).since(Offset::UTC),
/// -(5 * 60 * 60).seconds(),
/// );
/// ```
#[inline]
pub fn since(self, other: Offset) -> Span {
self.until(other).negate()
}
/// Returns the span of time from this offset until the other given.
///
/// When the `other` offset is more west (i.e., more negative) of the prime
/// meridian than this offset, then the span returned will be negative.
///
/// # Properties
///
/// Adding the span returned to this offset will always equal the `other`
/// offset given.
///
/// # Examples
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// assert_eq!(
/// Offset::constant(-5).until(Offset::UTC),
/// (5 * 60 * 60).seconds(),
/// );
/// // Flipping the operands in this case results in a negative span.
/// assert_eq!(
/// Offset::UTC.until(Offset::constant(-5)),
/// -(5 * 60 * 60).seconds(),
/// );
/// ```
#[inline]
pub fn until(self, other: Offset) -> Span {
Span::new()
.seconds_ranged(other.seconds_ranged() - self.seconds_ranged())
}
/// Adds the given span of time to this offset.
///
/// Since time zone offsets have second resolution, any fractional seconds
/// in the span given are ignored.
///
/// # Errors
///
/// This returns an error if the result of adding the given span would
/// exceed the minimum or maximum allowed `Offset` value.
///
/// # Example
///
/// This example shows how to add one hour to an offset (if the offset
/// corresponds to standard time, then adding an hour will usually give
/// you DST time):
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// let off = Offset::constant(-5);
/// assert_eq!(off.checked_add(1.hours()).unwrap(), Offset::constant(-4));
/// ```
///
/// While unusual, adding a full day to an offset is allowed as long as it
/// doesn't overflow.
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// let off = Offset::constant(-5);
///
/// assert_eq!(off.checked_add(1.days()).unwrap(), Offset::constant(19));
/// // Adding 1 day is always the same as adding 24 hours.
/// assert_eq!(off.checked_add(24.hours()).unwrap(), Offset::constant(19));
/// ```
///
/// And note that while fractional seconds are ignored, units less than
/// seconds aren't ignored if they sum up to a duration at least as big
/// as one second:
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// let off = Offset::constant(5);
/// let span = 900.milliseconds()
/// .microseconds(50_000)
/// .nanoseconds(50_000_000);
/// assert_eq!(
/// off.checked_add(span).unwrap(),
/// Offset::constant_seconds((5 * 60 * 60) + 1),
/// );
/// // Any leftover fractional part is ignored.
/// let span = 901.milliseconds()
/// .microseconds(50_001)
/// .nanoseconds(50_000_001);
/// assert_eq!(
/// off.checked_add(span).unwrap(),
/// Offset::constant_seconds((5 * 60 * 60) + 1),
/// );
/// ```
///
/// This example shows some cases where checked addition will fail.
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// // Adding units above 'day' always results in an error.
/// assert!(Offset::UTC.checked_add(1.weeks()).is_err());
/// assert!(Offset::UTC.checked_add(1.months()).is_err());
/// assert!(Offset::UTC.checked_add(1.years()).is_err());
///
/// // Adding even 1 second to the max, or subtracting 1 from the min,
/// // will result in overflow and thus an error will be returned.
/// assert!(Offset::MIN.checked_add(-1.seconds()).is_err());
/// assert!(Offset::MAX.checked_add(1.seconds()).is_err());
/// ```
#[inline]
pub fn checked_add(self, span: Span) -> Result<Offset, Error> {
let td = span.time_civil_day_parts_to_duration()?;
// A non-zero number of weeks will always lead to overflow, but I found
// it clearer to just do the math here and let it bubble an error.
let span_weeks = span
.get_weeks_ranged()
.try_checked_mul("weeks-as-seconds", t::SECONDS_PER_CIVIL_WEEK)?;
let span_days = span
.get_days_ranged()
.try_checked_mul("days-as-seconds", t::SECONDS_PER_CIVIL_DAY)?;
let span_seconds =
t::SpanZoneOffset::try_rfrom("span-as-seconds", td.seconds())?
.try_checked_add("weeks", span_weeks)?
.try_checked_add("days", span_days)?;
let seconds = self
.seconds_ranged()
.try_checked_add("offset-as-seconds", span_seconds)?;
Ok(Offset::new_seconds_ranged(seconds))
}
/// Subtracts the given span of time from this offset.
///
/// Since time zone offsets have second resolution, any fractional seconds
/// in the span given are ignored.
///
/// # Errors
///
/// This returns an error if the result of subtracting the given span would
/// exceed the minimum or maximum allowed `Offset` value.
///
/// # Example
///
/// This example shows how to subtract one hour to an offset (if the offset
/// corresponds to DST, then subtracting an hour will usually give you
/// standard time):
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// let off = Offset::constant(-4);
/// assert_eq!(off.checked_sub(1.hours()).unwrap(), Offset::constant(-5));
/// ```
///
/// While unusual, subtracting a full day from an offset is allowed as long
/// as it doesn't overflow.
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// let off = Offset::constant(5);
///
/// assert_eq!(off.checked_sub(1.days()).unwrap(), Offset::constant(-19));
/// // Subtracting 1 day is always the same as subtracting 24 hours.
/// assert_eq!(off.checked_sub(24.hours()).unwrap(), Offset::constant(-19));
/// ```
///
/// And note that while fractional seconds are ignored, units less than
/// seconds aren't ignored if they sum up to a duration at least as big
/// as one second:
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// let off = Offset::constant(5);
/// let span = 900.milliseconds()
/// .microseconds(50_000)
/// .nanoseconds(50_000_000);
/// assert_eq!(
/// off.checked_sub(span).unwrap(),
/// Offset::constant_seconds((5 * 60 * 60) - 1),
/// );
/// // Any leftover fractional part is ignored.
/// let span = 901.milliseconds()
/// .microseconds(50_001)
/// .nanoseconds(50_000_001);
/// assert_eq!(
/// off.checked_sub(span).unwrap(),
/// Offset::constant_seconds((5 * 60 * 60) - 1),
/// );
/// ```
///
/// This example shows some cases where checked subtraction will fail.
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// // Subtracting units above 'day' always results in an error.
/// assert!(Offset::UTC.checked_sub(1.weeks()).is_err());
/// assert!(Offset::UTC.checked_sub(1.months()).is_err());
/// assert!(Offset::UTC.checked_sub(1.years()).is_err());
///
/// // Adding even 1 second to the max, or subtracting 1 from the min,
/// // will result in overflow and thus an error will be returned.
/// assert!(Offset::MIN.checked_sub(1.seconds()).is_err());
/// assert!(Offset::MAX.checked_sub(-1.seconds()).is_err());
/// ```
#[inline]
pub fn checked_sub(self, span: Span) -> Result<Offset, Error> {
self.checked_add(span.negate())
}
/// Adds the given span of time to this offset, saturating on overflow.
///
/// Since time zone offsets have second resolution, any fractional seconds
/// in the span given are ignored.
///
/// # Example
///
/// This example shows some cases where saturation will occur.
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// // Adding units above 'day' always results in saturation.
/// assert_eq!(Offset::UTC.saturating_add(1.weeks()), Offset::MAX);
/// assert_eq!(Offset::UTC.saturating_add(1.months()), Offset::MAX);
/// assert_eq!(Offset::UTC.saturating_add(1.years()), Offset::MAX);
///
/// // Adding even 1 second to the max, or subtracting 1 from the min,
/// // will result in saturationg.
/// assert_eq!(Offset::MIN.saturating_add(-1.seconds()), Offset::MIN);
/// assert_eq!(Offset::MAX.saturating_add(1.seconds()), Offset::MAX);
/// ```
#[inline]
pub fn saturating_add(self, span: Span) -> Offset {
self.checked_add(span).unwrap_or_else(|_| {
if span.is_negative() {
Offset::MIN
} else {
Offset::MAX
}
})
}
/// Subtracts the given span of time from this offset, saturating on
/// overflow.
///
/// Since time zone offsets have second resolution, any fractional seconds
/// in the span given are ignored.
///
/// # Example
///
/// This example shows some cases where saturation will occur.
///
/// ```
/// use jiff::{tz::Offset, ToSpan};
///
/// // Adding units above 'day' always results in saturation.
/// assert_eq!(Offset::UTC.saturating_sub(1.weeks()), Offset::MIN);
/// assert_eq!(Offset::UTC.saturating_sub(1.months()), Offset::MIN);
/// assert_eq!(Offset::UTC.saturating_sub(1.years()), Offset::MIN);
///
/// // Adding even 1 second to the max, or subtracting 1 from the min,
/// // will result in saturationg.
/// assert_eq!(Offset::MIN.saturating_sub(1.seconds()), Offset::MIN);
/// assert_eq!(Offset::MAX.saturating_sub(-1.seconds()), Offset::MAX);
/// ```
#[inline]
pub fn saturating_sub(self, span: Span) -> Offset {
self.saturating_add(span.negate())
}
/// Returns this offset as a [`Span`].
#[inline]
pub(crate) fn to_span(self) -> Span {
Span::new().seconds_ranged(self.seconds_ranged())
}
/// Returns this offset as a [`TimeDuration`].
#[inline]
pub(crate) fn to_time_duration(self) -> TimeDuration {
TimeDuration::new(self.seconds_ranged(), C(0))
}
}
impl Offset {
/// This creates an `Offset` via hours/minutes/seconds components.
///
/// Currently, it exists because it's convenient for use in tests.
///
/// I originally wanted to expose this in the public API, but I couldn't
/// decide on how I wanted to treat signedness. There are a variety of
/// choices:
///
/// * Require all values to be positive, and ask the caller to use
/// `-offset` to negate it.
/// * Require all values to have the same sign. If any differs, either
/// panic or return an error.
/// * If any have a negative sign, then behave as if all have a negative
/// sign.
/// * Permit any combination of sign and combine them correctly. Similar
/// to how `TimeDuration::new(-1s, 1ns)` is turned into `-999,999,999ns`.
///
/// I think the last option is probably the right behavior, but also the
/// most annoying to implement. But if someone wants to take a crack at it,
/// a PR is welcome.
#[cfg(test)]
#[inline]
pub(crate) const fn hms(hours: i8, minutes: i8, seconds: i8) -> Offset {
let total = (hours as i32 * 60 * 60)
+ (minutes as i32 * 60)
+ (seconds as i32);
Offset { span: t::SpanZoneOffset::new_unchecked(total) }
}
#[inline]
pub(crate) fn new_ranged(
hours: impl RInto<t::SpanZoneOffsetHours>,
) -> Offset {
let hours: t::SpanZoneOffset = hours.rinto().rinto();
Offset::new_seconds_ranged(hours * t::SECONDS_PER_HOUR)
}
#[inline]
pub(crate) fn new_seconds_ranged(
seconds: impl RInto<t::SpanZoneOffset>,
) -> Offset {
Offset { span: seconds.rinto() }
}
#[inline]
pub(crate) fn seconds_ranged(self) -> t::SpanZoneOffset {
self.span
}
#[inline]
pub(crate) fn part_hours_ranged(self) -> t::SpanZoneOffsetHours {
self.span.div_ceil(t::SECONDS_PER_HOUR).rinto()
}
#[inline]
pub(crate) fn part_minutes_ranged(self) -> t::SpanZoneOffsetMinutes {
self.span
.div_ceil(t::SECONDS_PER_MINUTE)
.rem_ceil(t::MINUTES_PER_HOUR)
.rinto()
}
#[inline]
pub(crate) fn part_seconds_ranged(self) -> t::SpanZoneOffsetSeconds {
self.span.rem_ceil(t::SECONDS_PER_MINUTE).rinto()
}
}
impl core::fmt::Debug for Offset {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let sign = if self.seconds_ranged() < 0 { "-" } else { "" };
write!(
f,
"Offset({sign}{:02}:{:02}:{:02})",
self.part_hours_ranged().abs(),
self.part_minutes_ranged().abs(),
self.part_seconds_ranged().abs(),
)
}
}
impl core::fmt::Display for Offset {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let sign = if self.span < 0 { "-" } else { "+" };
let hours = self.part_hours_ranged().abs().get();
let minutes = self.part_minutes_ranged().abs().get();
let seconds = self.part_seconds_ranged().abs().get();
if hours == 0 && minutes == 0 && seconds == 0 {
write!(f, "+00")
} else if hours != 0 && minutes == 0 && seconds == 0 {
write!(f, "{sign}{hours:02}")
} else if minutes != 0 && seconds == 0 {
write!(f, "{sign}{hours:02}:{minutes:02}")
} else {
write!(f, "{sign}{hours:02}:{minutes:02}:{seconds:02}")
}
}
}
/// Adds a span of time to an offset. This panics on overflow.
///
/// For checked arithmetic, see [`Offset::checked_add`].
impl Add<Span> for Offset {
type Output = Offset;
#[inline]
fn add(self, rhs: Span) -> Offset {
self.checked_add(rhs)
.expect("adding span to offset should not overflow")
}
}
/// Adds a span of time to an offset in place. This panics on overflow.
///
/// For checked arithmetic, see [`Offset::checked_add`].
impl AddAssign<Span> for Offset {
#[inline]
fn add_assign(&mut self, rhs: Span) {
*self = self.add(rhs);
}
}
/// Subtracts a span of time from an offset. This panics on overflow.
///
/// For checked arithmetic, see [`Offset::checked_sub`].
impl Sub<Span> for Offset {
type Output = Offset;
#[inline]
fn sub(self, rhs: Span) -> Offset {
self.checked_sub(rhs)
.expect("subtracting span from offsetsshould not overflow")
}
}
/// Subtracts a span of time from an offset in place. This panics on overflow.
///
/// For checked arithmetic, see [`Offset::checked_sub`].
impl SubAssign<Span> for Offset {
#[inline]
fn sub_assign(&mut self, rhs: Span) {
*self = self.sub(rhs);
}
}
/// Computes the span of time between two offsets.
///
/// This will return a negative span when the offset being subtracted is
/// greater (i.e., more east with respect to the prime meridian).
impl Sub for Offset {
type Output = Span;
#[inline]
fn sub(self, rhs: Offset) -> Span {
self.since(rhs)
}
}
/// Negate this offset.
///
/// A positive offset becomes negative and vice versa. This is a no-op for the
/// zero offset.
///
/// This never panics.
impl Neg for Offset {
type Output = Offset;
#[inline]
fn neg(self) -> Offset {
self.negate()
}
}

3242
src/tz/posix.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,251 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
America/New_York
LOCAL TIME TYPES
000: offset=-04:56:02 designation=LMT indicator=local/wall
001: offset=-04 designation=EDT dst indicator=local/wall
002: offset=-05 designation=EST indicator=local/wall
003: offset=-05 designation=EST indicator=ut/std
004: offset=-04 designation=EWT dst indicator=local/wall
005: offset=-04 designation=EPT dst indicator=ut/std
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T21:03:57 unambiguous type=0 -04:56:02 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-13T15:45:52 fold-until(1901-12-13T15:49:50) type=3 -05 EST
0002: 1918-03-31T07:00:00Z unix=-1633280400 wall=1918-03-31T02:00:00 gap-until(1918-03-31T03:00:00) type=1 -04 EDT dst
0003: 1918-10-27T06:00:00Z unix=-1615140000 wall=1918-10-27T01:00:00 fold-until(1918-10-27T02:00:00) type=2 -05 EST
0004: 1919-03-30T07:00:00Z unix=-1601830800 wall=1919-03-30T02:00:00 gap-until(1919-03-30T03:00:00) type=1 -04 EDT dst
0005: 1919-10-26T06:00:00Z unix=-1583690400 wall=1919-10-26T01:00:00 fold-until(1919-10-26T02:00:00) type=2 -05 EST
0006: 1920-03-28T07:00:00Z unix=-1570381200 wall=1920-03-28T02:00:00 gap-until(1920-03-28T03:00:00) type=1 -04 EDT dst
0007: 1920-10-31T06:00:00Z unix=-1551636000 wall=1920-10-31T01:00:00 fold-until(1920-10-31T02:00:00) type=2 -05 EST
0008: 1921-04-24T07:00:00Z unix=-1536512400 wall=1921-04-24T02:00:00 gap-until(1921-04-24T03:00:00) type=1 -04 EDT dst
0009: 1921-09-25T06:00:00Z unix=-1523210400 wall=1921-09-25T01:00:00 fold-until(1921-09-25T02:00:00) type=2 -05 EST
0010: 1922-04-30T07:00:00Z unix=-1504458000 wall=1922-04-30T02:00:00 gap-until(1922-04-30T03:00:00) type=1 -04 EDT dst
0011: 1922-09-24T06:00:00Z unix=-1491760800 wall=1922-09-24T01:00:00 fold-until(1922-09-24T02:00:00) type=2 -05 EST
0012: 1923-04-29T07:00:00Z unix=-1473008400 wall=1923-04-29T02:00:00 gap-until(1923-04-29T03:00:00) type=1 -04 EDT dst
0013: 1923-09-30T06:00:00Z unix=-1459706400 wall=1923-09-30T01:00:00 fold-until(1923-09-30T02:00:00) type=2 -05 EST
0014: 1924-04-27T07:00:00Z unix=-1441558800 wall=1924-04-27T02:00:00 gap-until(1924-04-27T03:00:00) type=1 -04 EDT dst
0015: 1924-09-28T06:00:00Z unix=-1428256800 wall=1924-09-28T01:00:00 fold-until(1924-09-28T02:00:00) type=2 -05 EST
0016: 1925-04-26T07:00:00Z unix=-1410109200 wall=1925-04-26T02:00:00 gap-until(1925-04-26T03:00:00) type=1 -04 EDT dst
0017: 1925-09-27T06:00:00Z unix=-1396807200 wall=1925-09-27T01:00:00 fold-until(1925-09-27T02:00:00) type=2 -05 EST
0018: 1926-04-25T07:00:00Z unix=-1378659600 wall=1926-04-25T02:00:00 gap-until(1926-04-25T03:00:00) type=1 -04 EDT dst
0019: 1926-09-26T06:00:00Z unix=-1365357600 wall=1926-09-26T01:00:00 fold-until(1926-09-26T02:00:00) type=2 -05 EST
0020: 1927-04-24T07:00:00Z unix=-1347210000 wall=1927-04-24T02:00:00 gap-until(1927-04-24T03:00:00) type=1 -04 EDT dst
0021: 1927-09-25T06:00:00Z unix=-1333908000 wall=1927-09-25T01:00:00 fold-until(1927-09-25T02:00:00) type=2 -05 EST
0022: 1928-04-29T07:00:00Z unix=-1315155600 wall=1928-04-29T02:00:00 gap-until(1928-04-29T03:00:00) type=1 -04 EDT dst
0023: 1928-09-30T06:00:00Z unix=-1301853600 wall=1928-09-30T01:00:00 fold-until(1928-09-30T02:00:00) type=2 -05 EST
0024: 1929-04-28T07:00:00Z unix=-1283706000 wall=1929-04-28T02:00:00 gap-until(1929-04-28T03:00:00) type=1 -04 EDT dst
0025: 1929-09-29T06:00:00Z unix=-1270404000 wall=1929-09-29T01:00:00 fold-until(1929-09-29T02:00:00) type=2 -05 EST
0026: 1930-04-27T07:00:00Z unix=-1252256400 wall=1930-04-27T02:00:00 gap-until(1930-04-27T03:00:00) type=1 -04 EDT dst
0027: 1930-09-28T06:00:00Z unix=-1238954400 wall=1930-09-28T01:00:00 fold-until(1930-09-28T02:00:00) type=2 -05 EST
0028: 1931-04-26T07:00:00Z unix=-1220806800 wall=1931-04-26T02:00:00 gap-until(1931-04-26T03:00:00) type=1 -04 EDT dst
0029: 1931-09-27T06:00:00Z unix=-1207504800 wall=1931-09-27T01:00:00 fold-until(1931-09-27T02:00:00) type=2 -05 EST
0030: 1932-04-24T07:00:00Z unix=-1189357200 wall=1932-04-24T02:00:00 gap-until(1932-04-24T03:00:00) type=1 -04 EDT dst
0031: 1932-09-25T06:00:00Z unix=-1176055200 wall=1932-09-25T01:00:00 fold-until(1932-09-25T02:00:00) type=2 -05 EST
0032: 1933-04-30T07:00:00Z unix=-1157302800 wall=1933-04-30T02:00:00 gap-until(1933-04-30T03:00:00) type=1 -04 EDT dst
0033: 1933-09-24T06:00:00Z unix=-1144605600 wall=1933-09-24T01:00:00 fold-until(1933-09-24T02:00:00) type=2 -05 EST
0034: 1934-04-29T07:00:00Z unix=-1125853200 wall=1934-04-29T02:00:00 gap-until(1934-04-29T03:00:00) type=1 -04 EDT dst
0035: 1934-09-30T06:00:00Z unix=-1112551200 wall=1934-09-30T01:00:00 fold-until(1934-09-30T02:00:00) type=2 -05 EST
0036: 1935-04-28T07:00:00Z unix=-1094403600 wall=1935-04-28T02:00:00 gap-until(1935-04-28T03:00:00) type=1 -04 EDT dst
0037: 1935-09-29T06:00:00Z unix=-1081101600 wall=1935-09-29T01:00:00 fold-until(1935-09-29T02:00:00) type=2 -05 EST
0038: 1936-04-26T07:00:00Z unix=-1062954000 wall=1936-04-26T02:00:00 gap-until(1936-04-26T03:00:00) type=1 -04 EDT dst
0039: 1936-09-27T06:00:00Z unix=-1049652000 wall=1936-09-27T01:00:00 fold-until(1936-09-27T02:00:00) type=2 -05 EST
0040: 1937-04-25T07:00:00Z unix=-1031504400 wall=1937-04-25T02:00:00 gap-until(1937-04-25T03:00:00) type=1 -04 EDT dst
0041: 1937-09-26T06:00:00Z unix=-1018202400 wall=1937-09-26T01:00:00 fold-until(1937-09-26T02:00:00) type=2 -05 EST
0042: 1938-04-24T07:00:00Z unix=-1000054800 wall=1938-04-24T02:00:00 gap-until(1938-04-24T03:00:00) type=1 -04 EDT dst
0043: 1938-09-25T06:00:00Z unix=-986752800 wall=1938-09-25T01:00:00 fold-until(1938-09-25T02:00:00) type=2 -05 EST
0044: 1939-04-30T07:00:00Z unix=-968000400 wall=1939-04-30T02:00:00 gap-until(1939-04-30T03:00:00) type=1 -04 EDT dst
0045: 1939-09-24T06:00:00Z unix=-955303200 wall=1939-09-24T01:00:00 fold-until(1939-09-24T02:00:00) type=2 -05 EST
0046: 1940-04-28T07:00:00Z unix=-936550800 wall=1940-04-28T02:00:00 gap-until(1940-04-28T03:00:00) type=1 -04 EDT dst
0047: 1940-09-29T06:00:00Z unix=-923248800 wall=1940-09-29T01:00:00 fold-until(1940-09-29T02:00:00) type=2 -05 EST
0048: 1941-04-27T07:00:00Z unix=-905101200 wall=1941-04-27T02:00:00 gap-until(1941-04-27T03:00:00) type=1 -04 EDT dst
0049: 1941-09-28T06:00:00Z unix=-891799200 wall=1941-09-28T01:00:00 fold-until(1941-09-28T02:00:00) type=2 -05 EST
0050: 1942-02-09T07:00:00Z unix=-880218000 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=4 -04 EWT dst
0051: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T19:00:00 unambiguous type=5 -04 EPT dst
0052: 1945-09-30T06:00:00Z unix=-765396000 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=2 -05 EST
0053: 1946-04-28T07:00:00Z unix=-747248400 wall=1946-04-28T02:00:00 gap-until(1946-04-28T03:00:00) type=1 -04 EDT dst
0054: 1946-09-29T06:00:00Z unix=-733946400 wall=1946-09-29T01:00:00 fold-until(1946-09-29T02:00:00) type=2 -05 EST
0055: 1947-04-27T07:00:00Z unix=-715798800 wall=1947-04-27T02:00:00 gap-until(1947-04-27T03:00:00) type=1 -04 EDT dst
0056: 1947-09-28T06:00:00Z unix=-702496800 wall=1947-09-28T01:00:00 fold-until(1947-09-28T02:00:00) type=2 -05 EST
0057: 1948-04-25T07:00:00Z unix=-684349200 wall=1948-04-25T02:00:00 gap-until(1948-04-25T03:00:00) type=1 -04 EDT dst
0058: 1948-09-26T06:00:00Z unix=-671047200 wall=1948-09-26T01:00:00 fold-until(1948-09-26T02:00:00) type=2 -05 EST
0059: 1949-04-24T07:00:00Z unix=-652899600 wall=1949-04-24T02:00:00 gap-until(1949-04-24T03:00:00) type=1 -04 EDT dst
0060: 1949-09-25T06:00:00Z unix=-639597600 wall=1949-09-25T01:00:00 fold-until(1949-09-25T02:00:00) type=2 -05 EST
0061: 1950-04-30T07:00:00Z unix=-620845200 wall=1950-04-30T02:00:00 gap-until(1950-04-30T03:00:00) type=1 -04 EDT dst
0062: 1950-09-24T06:00:00Z unix=-608148000 wall=1950-09-24T01:00:00 fold-until(1950-09-24T02:00:00) type=2 -05 EST
0063: 1951-04-29T07:00:00Z unix=-589395600 wall=1951-04-29T02:00:00 gap-until(1951-04-29T03:00:00) type=1 -04 EDT dst
0064: 1951-09-30T06:00:00Z unix=-576093600 wall=1951-09-30T01:00:00 fold-until(1951-09-30T02:00:00) type=2 -05 EST
0065: 1952-04-27T07:00:00Z unix=-557946000 wall=1952-04-27T02:00:00 gap-until(1952-04-27T03:00:00) type=1 -04 EDT dst
0066: 1952-09-28T06:00:00Z unix=-544644000 wall=1952-09-28T01:00:00 fold-until(1952-09-28T02:00:00) type=2 -05 EST
0067: 1953-04-26T07:00:00Z unix=-526496400 wall=1953-04-26T02:00:00 gap-until(1953-04-26T03:00:00) type=1 -04 EDT dst
0068: 1953-09-27T06:00:00Z unix=-513194400 wall=1953-09-27T01:00:00 fold-until(1953-09-27T02:00:00) type=2 -05 EST
0069: 1954-04-25T07:00:00Z unix=-495046800 wall=1954-04-25T02:00:00 gap-until(1954-04-25T03:00:00) type=1 -04 EDT dst
0070: 1954-09-26T06:00:00Z unix=-481744800 wall=1954-09-26T01:00:00 fold-until(1954-09-26T02:00:00) type=2 -05 EST
0071: 1955-04-24T07:00:00Z unix=-463597200 wall=1955-04-24T02:00:00 gap-until(1955-04-24T03:00:00) type=1 -04 EDT dst
0072: 1955-10-30T06:00:00Z unix=-447271200 wall=1955-10-30T01:00:00 fold-until(1955-10-30T02:00:00) type=2 -05 EST
0073: 1956-04-29T07:00:00Z unix=-431542800 wall=1956-04-29T02:00:00 gap-until(1956-04-29T03:00:00) type=1 -04 EDT dst
0074: 1956-10-28T06:00:00Z unix=-415821600 wall=1956-10-28T01:00:00 fold-until(1956-10-28T02:00:00) type=2 -05 EST
0075: 1957-04-28T07:00:00Z unix=-400093200 wall=1957-04-28T02:00:00 gap-until(1957-04-28T03:00:00) type=1 -04 EDT dst
0076: 1957-10-27T06:00:00Z unix=-384372000 wall=1957-10-27T01:00:00 fold-until(1957-10-27T02:00:00) type=2 -05 EST
0077: 1958-04-27T07:00:00Z unix=-368643600 wall=1958-04-27T02:00:00 gap-until(1958-04-27T03:00:00) type=1 -04 EDT dst
0078: 1958-10-26T06:00:00Z unix=-352922400 wall=1958-10-26T01:00:00 fold-until(1958-10-26T02:00:00) type=2 -05 EST
0079: 1959-04-26T07:00:00Z unix=-337194000 wall=1959-04-26T02:00:00 gap-until(1959-04-26T03:00:00) type=1 -04 EDT dst
0080: 1959-10-25T06:00:00Z unix=-321472800 wall=1959-10-25T01:00:00 fold-until(1959-10-25T02:00:00) type=2 -05 EST
0081: 1960-04-24T07:00:00Z unix=-305744400 wall=1960-04-24T02:00:00 gap-until(1960-04-24T03:00:00) type=1 -04 EDT dst
0082: 1960-10-30T06:00:00Z unix=-289418400 wall=1960-10-30T01:00:00 fold-until(1960-10-30T02:00:00) type=2 -05 EST
0083: 1961-04-30T07:00:00Z unix=-273690000 wall=1961-04-30T02:00:00 gap-until(1961-04-30T03:00:00) type=1 -04 EDT dst
0084: 1961-10-29T06:00:00Z unix=-257968800 wall=1961-10-29T01:00:00 fold-until(1961-10-29T02:00:00) type=2 -05 EST
0085: 1962-04-29T07:00:00Z unix=-242240400 wall=1962-04-29T02:00:00 gap-until(1962-04-29T03:00:00) type=1 -04 EDT dst
0086: 1962-10-28T06:00:00Z unix=-226519200 wall=1962-10-28T01:00:00 fold-until(1962-10-28T02:00:00) type=2 -05 EST
0087: 1963-04-28T07:00:00Z unix=-210790800 wall=1963-04-28T02:00:00 gap-until(1963-04-28T03:00:00) type=1 -04 EDT dst
0088: 1963-10-27T06:00:00Z unix=-195069600 wall=1963-10-27T01:00:00 fold-until(1963-10-27T02:00:00) type=2 -05 EST
0089: 1964-04-26T07:00:00Z unix=-179341200 wall=1964-04-26T02:00:00 gap-until(1964-04-26T03:00:00) type=1 -04 EDT dst
0090: 1964-10-25T06:00:00Z unix=-163620000 wall=1964-10-25T01:00:00 fold-until(1964-10-25T02:00:00) type=2 -05 EST
0091: 1965-04-25T07:00:00Z unix=-147891600 wall=1965-04-25T02:00:00 gap-until(1965-04-25T03:00:00) type=1 -04 EDT dst
0092: 1965-10-31T06:00:00Z unix=-131565600 wall=1965-10-31T01:00:00 fold-until(1965-10-31T02:00:00) type=2 -05 EST
0093: 1966-04-24T07:00:00Z unix=-116442000 wall=1966-04-24T02:00:00 gap-until(1966-04-24T03:00:00) type=1 -04 EDT dst
0094: 1966-10-30T06:00:00Z unix=-100116000 wall=1966-10-30T01:00:00 fold-until(1966-10-30T02:00:00) type=2 -05 EST
0095: 1967-04-30T07:00:00Z unix=-84387600 wall=1967-04-30T02:00:00 gap-until(1967-04-30T03:00:00) type=1 -04 EDT dst
0096: 1967-10-29T06:00:00Z unix=-68666400 wall=1967-10-29T01:00:00 fold-until(1967-10-29T02:00:00) type=2 -05 EST
0097: 1968-04-28T07:00:00Z unix=-52938000 wall=1968-04-28T02:00:00 gap-until(1968-04-28T03:00:00) type=1 -04 EDT dst
0098: 1968-10-27T06:00:00Z unix=-37216800 wall=1968-10-27T01:00:00 fold-until(1968-10-27T02:00:00) type=2 -05 EST
0099: 1969-04-27T07:00:00Z unix=-21488400 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=1 -04 EDT dst
0100: 1969-10-26T06:00:00Z unix=-5767200 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=2 -05 EST
0101: 1970-04-26T07:00:00Z unix=9961200 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=1 -04 EDT dst
0102: 1970-10-25T06:00:00Z unix=25682400 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=2 -05 EST
0103: 1971-04-25T07:00:00Z unix=41410800 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=1 -04 EDT dst
0104: 1971-10-31T06:00:00Z unix=57736800 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=2 -05 EST
0105: 1972-04-30T07:00:00Z unix=73465200 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=1 -04 EDT dst
0106: 1972-10-29T06:00:00Z unix=89186400 wall=1972-10-29T01:00:00 fold-until(1972-10-29T02:00:00) type=2 -05 EST
0107: 1973-04-29T07:00:00Z unix=104914800 wall=1973-04-29T02:00:00 gap-until(1973-04-29T03:00:00) type=1 -04 EDT dst
0108: 1973-10-28T06:00:00Z unix=120636000 wall=1973-10-28T01:00:00 fold-until(1973-10-28T02:00:00) type=2 -05 EST
0109: 1974-01-06T07:00:00Z unix=126687600 wall=1974-01-06T02:00:00 gap-until(1974-01-06T03:00:00) type=1 -04 EDT dst
0110: 1974-10-27T06:00:00Z unix=152085600 wall=1974-10-27T01:00:00 fold-until(1974-10-27T02:00:00) type=2 -05 EST
0111: 1975-02-23T07:00:00Z unix=162370800 wall=1975-02-23T02:00:00 gap-until(1975-02-23T03:00:00) type=1 -04 EDT dst
0112: 1975-10-26T06:00:00Z unix=183535200 wall=1975-10-26T01:00:00 fold-until(1975-10-26T02:00:00) type=2 -05 EST
0113: 1976-04-25T07:00:00Z unix=199263600 wall=1976-04-25T02:00:00 gap-until(1976-04-25T03:00:00) type=1 -04 EDT dst
0114: 1976-10-31T06:00:00Z unix=215589600 wall=1976-10-31T01:00:00 fold-until(1976-10-31T02:00:00) type=2 -05 EST
0115: 1977-04-24T07:00:00Z unix=230713200 wall=1977-04-24T02:00:00 gap-until(1977-04-24T03:00:00) type=1 -04 EDT dst
0116: 1977-10-30T06:00:00Z unix=247039200 wall=1977-10-30T01:00:00 fold-until(1977-10-30T02:00:00) type=2 -05 EST
0117: 1978-04-30T07:00:00Z unix=262767600 wall=1978-04-30T02:00:00 gap-until(1978-04-30T03:00:00) type=1 -04 EDT dst
0118: 1978-10-29T06:00:00Z unix=278488800 wall=1978-10-29T01:00:00 fold-until(1978-10-29T02:00:00) type=2 -05 EST
0119: 1979-04-29T07:00:00Z unix=294217200 wall=1979-04-29T02:00:00 gap-until(1979-04-29T03:00:00) type=1 -04 EDT dst
0120: 1979-10-28T06:00:00Z unix=309938400 wall=1979-10-28T01:00:00 fold-until(1979-10-28T02:00:00) type=2 -05 EST
0121: 1980-04-27T07:00:00Z unix=325666800 wall=1980-04-27T02:00:00 gap-until(1980-04-27T03:00:00) type=1 -04 EDT dst
0122: 1980-10-26T06:00:00Z unix=341388000 wall=1980-10-26T01:00:00 fold-until(1980-10-26T02:00:00) type=2 -05 EST
0123: 1981-04-26T07:00:00Z unix=357116400 wall=1981-04-26T02:00:00 gap-until(1981-04-26T03:00:00) type=1 -04 EDT dst
0124: 1981-10-25T06:00:00Z unix=372837600 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=2 -05 EST
0125: 1982-04-25T07:00:00Z unix=388566000 wall=1982-04-25T02:00:00 gap-until(1982-04-25T03:00:00) type=1 -04 EDT dst
0126: 1982-10-31T06:00:00Z unix=404892000 wall=1982-10-31T01:00:00 fold-until(1982-10-31T02:00:00) type=2 -05 EST
0127: 1983-04-24T07:00:00Z unix=420015600 wall=1983-04-24T02:00:00 gap-until(1983-04-24T03:00:00) type=1 -04 EDT dst
0128: 1983-10-30T06:00:00Z unix=436341600 wall=1983-10-30T01:00:00 fold-until(1983-10-30T02:00:00) type=2 -05 EST
0129: 1984-04-29T07:00:00Z unix=452070000 wall=1984-04-29T02:00:00 gap-until(1984-04-29T03:00:00) type=1 -04 EDT dst
0130: 1984-10-28T06:00:00Z unix=467791200 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=2 -05 EST
0131: 1985-04-28T07:00:00Z unix=483519600 wall=1985-04-28T02:00:00 gap-until(1985-04-28T03:00:00) type=1 -04 EDT dst
0132: 1985-10-27T06:00:00Z unix=499240800 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=2 -05 EST
0133: 1986-04-27T07:00:00Z unix=514969200 wall=1986-04-27T02:00:00 gap-until(1986-04-27T03:00:00) type=1 -04 EDT dst
0134: 1986-10-26T06:00:00Z unix=530690400 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=2 -05 EST
0135: 1987-04-05T07:00:00Z unix=544604400 wall=1987-04-05T02:00:00 gap-until(1987-04-05T03:00:00) type=1 -04 EDT dst
0136: 1987-10-25T06:00:00Z unix=562140000 wall=1987-10-25T01:00:00 fold-until(1987-10-25T02:00:00) type=2 -05 EST
0137: 1988-04-03T07:00:00Z unix=576054000 wall=1988-04-03T02:00:00 gap-until(1988-04-03T03:00:00) type=1 -04 EDT dst
0138: 1988-10-30T06:00:00Z unix=594194400 wall=1988-10-30T01:00:00 fold-until(1988-10-30T02:00:00) type=2 -05 EST
0139: 1989-04-02T07:00:00Z unix=607503600 wall=1989-04-02T02:00:00 gap-until(1989-04-02T03:00:00) type=1 -04 EDT dst
0140: 1989-10-29T06:00:00Z unix=625644000 wall=1989-10-29T01:00:00 fold-until(1989-10-29T02:00:00) type=2 -05 EST
0141: 1990-04-01T07:00:00Z unix=638953200 wall=1990-04-01T02:00:00 gap-until(1990-04-01T03:00:00) type=1 -04 EDT dst
0142: 1990-10-28T06:00:00Z unix=657093600 wall=1990-10-28T01:00:00 fold-until(1990-10-28T02:00:00) type=2 -05 EST
0143: 1991-04-07T07:00:00Z unix=671007600 wall=1991-04-07T02:00:00 gap-until(1991-04-07T03:00:00) type=1 -04 EDT dst
0144: 1991-10-27T06:00:00Z unix=688543200 wall=1991-10-27T01:00:00 fold-until(1991-10-27T02:00:00) type=2 -05 EST
0145: 1992-04-05T07:00:00Z unix=702457200 wall=1992-04-05T02:00:00 gap-until(1992-04-05T03:00:00) type=1 -04 EDT dst
0146: 1992-10-25T06:00:00Z unix=719992800 wall=1992-10-25T01:00:00 fold-until(1992-10-25T02:00:00) type=2 -05 EST
0147: 1993-04-04T07:00:00Z unix=733906800 wall=1993-04-04T02:00:00 gap-until(1993-04-04T03:00:00) type=1 -04 EDT dst
0148: 1993-10-31T06:00:00Z unix=752047200 wall=1993-10-31T01:00:00 fold-until(1993-10-31T02:00:00) type=2 -05 EST
0149: 1994-04-03T07:00:00Z unix=765356400 wall=1994-04-03T02:00:00 gap-until(1994-04-03T03:00:00) type=1 -04 EDT dst
0150: 1994-10-30T06:00:00Z unix=783496800 wall=1994-10-30T01:00:00 fold-until(1994-10-30T02:00:00) type=2 -05 EST
0151: 1995-04-02T07:00:00Z unix=796806000 wall=1995-04-02T02:00:00 gap-until(1995-04-02T03:00:00) type=1 -04 EDT dst
0152: 1995-10-29T06:00:00Z unix=814946400 wall=1995-10-29T01:00:00 fold-until(1995-10-29T02:00:00) type=2 -05 EST
0153: 1996-04-07T07:00:00Z unix=828860400 wall=1996-04-07T02:00:00 gap-until(1996-04-07T03:00:00) type=1 -04 EDT dst
0154: 1996-10-27T06:00:00Z unix=846396000 wall=1996-10-27T01:00:00 fold-until(1996-10-27T02:00:00) type=2 -05 EST
0155: 1997-04-06T07:00:00Z unix=860310000 wall=1997-04-06T02:00:00 gap-until(1997-04-06T03:00:00) type=1 -04 EDT dst
0156: 1997-10-26T06:00:00Z unix=877845600 wall=1997-10-26T01:00:00 fold-until(1997-10-26T02:00:00) type=2 -05 EST
0157: 1998-04-05T07:00:00Z unix=891759600 wall=1998-04-05T02:00:00 gap-until(1998-04-05T03:00:00) type=1 -04 EDT dst
0158: 1998-10-25T06:00:00Z unix=909295200 wall=1998-10-25T01:00:00 fold-until(1998-10-25T02:00:00) type=2 -05 EST
0159: 1999-04-04T07:00:00Z unix=923209200 wall=1999-04-04T02:00:00 gap-until(1999-04-04T03:00:00) type=1 -04 EDT dst
0160: 1999-10-31T06:00:00Z unix=941349600 wall=1999-10-31T01:00:00 fold-until(1999-10-31T02:00:00) type=2 -05 EST
0161: 2000-04-02T07:00:00Z unix=954658800 wall=2000-04-02T02:00:00 gap-until(2000-04-02T03:00:00) type=1 -04 EDT dst
0162: 2000-10-29T06:00:00Z unix=972799200 wall=2000-10-29T01:00:00 fold-until(2000-10-29T02:00:00) type=2 -05 EST
0163: 2001-04-01T07:00:00Z unix=986108400 wall=2001-04-01T02:00:00 gap-until(2001-04-01T03:00:00) type=1 -04 EDT dst
0164: 2001-10-28T06:00:00Z unix=1004248800 wall=2001-10-28T01:00:00 fold-until(2001-10-28T02:00:00) type=2 -05 EST
0165: 2002-04-07T07:00:00Z unix=1018162800 wall=2002-04-07T02:00:00 gap-until(2002-04-07T03:00:00) type=1 -04 EDT dst
0166: 2002-10-27T06:00:00Z unix=1035698400 wall=2002-10-27T01:00:00 fold-until(2002-10-27T02:00:00) type=2 -05 EST
0167: 2003-04-06T07:00:00Z unix=1049612400 wall=2003-04-06T02:00:00 gap-until(2003-04-06T03:00:00) type=1 -04 EDT dst
0168: 2003-10-26T06:00:00Z unix=1067148000 wall=2003-10-26T01:00:00 fold-until(2003-10-26T02:00:00) type=2 -05 EST
0169: 2004-04-04T07:00:00Z unix=1081062000 wall=2004-04-04T02:00:00 gap-until(2004-04-04T03:00:00) type=1 -04 EDT dst
0170: 2004-10-31T06:00:00Z unix=1099202400 wall=2004-10-31T01:00:00 fold-until(2004-10-31T02:00:00) type=2 -05 EST
0171: 2005-04-03T07:00:00Z unix=1112511600 wall=2005-04-03T02:00:00 gap-until(2005-04-03T03:00:00) type=1 -04 EDT dst
0172: 2005-10-30T06:00:00Z unix=1130652000 wall=2005-10-30T01:00:00 fold-until(2005-10-30T02:00:00) type=2 -05 EST
0173: 2006-04-02T07:00:00Z unix=1143961200 wall=2006-04-02T02:00:00 gap-until(2006-04-02T03:00:00) type=1 -04 EDT dst
0174: 2006-10-29T06:00:00Z unix=1162101600 wall=2006-10-29T01:00:00 fold-until(2006-10-29T02:00:00) type=2 -05 EST
0175: 2007-03-11T07:00:00Z unix=1173596400 wall=2007-03-11T02:00:00 gap-until(2007-03-11T03:00:00) type=1 -04 EDT dst
0176: 2007-11-04T06:00:00Z unix=1194156000 wall=2007-11-04T01:00:00 fold-until(2007-11-04T02:00:00) type=2 -05 EST
0177: 2008-03-09T07:00:00Z unix=1205046000 wall=2008-03-09T02:00:00 gap-until(2008-03-09T03:00:00) type=1 -04 EDT dst
0178: 2008-11-02T06:00:00Z unix=1225605600 wall=2008-11-02T01:00:00 fold-until(2008-11-02T02:00:00) type=2 -05 EST
0179: 2009-03-08T07:00:00Z unix=1236495600 wall=2009-03-08T02:00:00 gap-until(2009-03-08T03:00:00) type=1 -04 EDT dst
0180: 2009-11-01T06:00:00Z unix=1257055200 wall=2009-11-01T01:00:00 fold-until(2009-11-01T02:00:00) type=2 -05 EST
0181: 2010-03-14T07:00:00Z unix=1268550000 wall=2010-03-14T02:00:00 gap-until(2010-03-14T03:00:00) type=1 -04 EDT dst
0182: 2010-11-07T06:00:00Z unix=1289109600 wall=2010-11-07T01:00:00 fold-until(2010-11-07T02:00:00) type=2 -05 EST
0183: 2011-03-13T07:00:00Z unix=1299999600 wall=2011-03-13T02:00:00 gap-until(2011-03-13T03:00:00) type=1 -04 EDT dst
0184: 2011-11-06T06:00:00Z unix=1320559200 wall=2011-11-06T01:00:00 fold-until(2011-11-06T02:00:00) type=2 -05 EST
0185: 2012-03-11T07:00:00Z unix=1331449200 wall=2012-03-11T02:00:00 gap-until(2012-03-11T03:00:00) type=1 -04 EDT dst
0186: 2012-11-04T06:00:00Z unix=1352008800 wall=2012-11-04T01:00:00 fold-until(2012-11-04T02:00:00) type=2 -05 EST
0187: 2013-03-10T07:00:00Z unix=1362898800 wall=2013-03-10T02:00:00 gap-until(2013-03-10T03:00:00) type=1 -04 EDT dst
0188: 2013-11-03T06:00:00Z unix=1383458400 wall=2013-11-03T01:00:00 fold-until(2013-11-03T02:00:00) type=2 -05 EST
0189: 2014-03-09T07:00:00Z unix=1394348400 wall=2014-03-09T02:00:00 gap-until(2014-03-09T03:00:00) type=1 -04 EDT dst
0190: 2014-11-02T06:00:00Z unix=1414908000 wall=2014-11-02T01:00:00 fold-until(2014-11-02T02:00:00) type=2 -05 EST
0191: 2015-03-08T07:00:00Z unix=1425798000 wall=2015-03-08T02:00:00 gap-until(2015-03-08T03:00:00) type=1 -04 EDT dst
0192: 2015-11-01T06:00:00Z unix=1446357600 wall=2015-11-01T01:00:00 fold-until(2015-11-01T02:00:00) type=2 -05 EST
0193: 2016-03-13T07:00:00Z unix=1457852400 wall=2016-03-13T02:00:00 gap-until(2016-03-13T03:00:00) type=1 -04 EDT dst
0194: 2016-11-06T06:00:00Z unix=1478412000 wall=2016-11-06T01:00:00 fold-until(2016-11-06T02:00:00) type=2 -05 EST
0195: 2017-03-12T07:00:00Z unix=1489302000 wall=2017-03-12T02:00:00 gap-until(2017-03-12T03:00:00) type=1 -04 EDT dst
0196: 2017-11-05T06:00:00Z unix=1509861600 wall=2017-11-05T01:00:00 fold-until(2017-11-05T02:00:00) type=2 -05 EST
0197: 2018-03-11T07:00:00Z unix=1520751600 wall=2018-03-11T02:00:00 gap-until(2018-03-11T03:00:00) type=1 -04 EDT dst
0198: 2018-11-04T06:00:00Z unix=1541311200 wall=2018-11-04T01:00:00 fold-until(2018-11-04T02:00:00) type=2 -05 EST
0199: 2019-03-10T07:00:00Z unix=1552201200 wall=2019-03-10T02:00:00 gap-until(2019-03-10T03:00:00) type=1 -04 EDT dst
0200: 2019-11-03T06:00:00Z unix=1572760800 wall=2019-11-03T01:00:00 fold-until(2019-11-03T02:00:00) type=2 -05 EST
0201: 2020-03-08T07:00:00Z unix=1583650800 wall=2020-03-08T02:00:00 gap-until(2020-03-08T03:00:00) type=1 -04 EDT dst
0202: 2020-11-01T06:00:00Z unix=1604210400 wall=2020-11-01T01:00:00 fold-until(2020-11-01T02:00:00) type=2 -05 EST
0203: 2021-03-14T07:00:00Z unix=1615705200 wall=2021-03-14T02:00:00 gap-until(2021-03-14T03:00:00) type=1 -04 EDT dst
0204: 2021-11-07T06:00:00Z unix=1636264800 wall=2021-11-07T01:00:00 fold-until(2021-11-07T02:00:00) type=2 -05 EST
0205: 2022-03-13T07:00:00Z unix=1647154800 wall=2022-03-13T02:00:00 gap-until(2022-03-13T03:00:00) type=1 -04 EDT dst
0206: 2022-11-06T06:00:00Z unix=1667714400 wall=2022-11-06T01:00:00 fold-until(2022-11-06T02:00:00) type=2 -05 EST
0207: 2023-03-12T07:00:00Z unix=1678604400 wall=2023-03-12T02:00:00 gap-until(2023-03-12T03:00:00) type=1 -04 EDT dst
0208: 2023-11-05T06:00:00Z unix=1699164000 wall=2023-11-05T01:00:00 fold-until(2023-11-05T02:00:00) type=2 -05 EST
0209: 2024-03-10T07:00:00Z unix=1710054000 wall=2024-03-10T02:00:00 gap-until(2024-03-10T03:00:00) type=1 -04 EDT dst
0210: 2024-11-03T06:00:00Z unix=1730613600 wall=2024-11-03T01:00:00 fold-until(2024-11-03T02:00:00) type=2 -05 EST
0211: 2025-03-09T07:00:00Z unix=1741503600 wall=2025-03-09T02:00:00 gap-until(2025-03-09T03:00:00) type=1 -04 EDT dst
0212: 2025-11-02T06:00:00Z unix=1762063200 wall=2025-11-02T01:00:00 fold-until(2025-11-02T02:00:00) type=2 -05 EST
0213: 2026-03-08T07:00:00Z unix=1772953200 wall=2026-03-08T02:00:00 gap-until(2026-03-08T03:00:00) type=1 -04 EDT dst
0214: 2026-11-01T06:00:00Z unix=1793512800 wall=2026-11-01T01:00:00 fold-until(2026-11-01T02:00:00) type=2 -05 EST
0215: 2027-03-14T07:00:00Z unix=1805007600 wall=2027-03-14T02:00:00 gap-until(2027-03-14T03:00:00) type=1 -04 EDT dst
0216: 2027-11-07T06:00:00Z unix=1825567200 wall=2027-11-07T01:00:00 fold-until(2027-11-07T02:00:00) type=2 -05 EST
0217: 2028-03-12T07:00:00Z unix=1836457200 wall=2028-03-12T02:00:00 gap-until(2028-03-12T03:00:00) type=1 -04 EDT dst
0218: 2028-11-05T06:00:00Z unix=1857016800 wall=2028-11-05T01:00:00 fold-until(2028-11-05T02:00:00) type=2 -05 EST
0219: 2029-03-11T07:00:00Z unix=1867906800 wall=2029-03-11T02:00:00 gap-until(2029-03-11T03:00:00) type=1 -04 EDT dst
0220: 2029-11-04T06:00:00Z unix=1888466400 wall=2029-11-04T01:00:00 fold-until(2029-11-04T02:00:00) type=2 -05 EST
0221: 2030-03-10T07:00:00Z unix=1899356400 wall=2030-03-10T02:00:00 gap-until(2030-03-10T03:00:00) type=1 -04 EDT dst
0222: 2030-11-03T06:00:00Z unix=1919916000 wall=2030-11-03T01:00:00 fold-until(2030-11-03T02:00:00) type=2 -05 EST
0223: 2031-03-09T07:00:00Z unix=1930806000 wall=2031-03-09T02:00:00 gap-until(2031-03-09T03:00:00) type=1 -04 EDT dst
0224: 2031-11-02T06:00:00Z unix=1951365600 wall=2031-11-02T01:00:00 fold-until(2031-11-02T02:00:00) type=2 -05 EST
0225: 2032-03-14T07:00:00Z unix=1962860400 wall=2032-03-14T02:00:00 gap-until(2032-03-14T03:00:00) type=1 -04 EDT dst
0226: 2032-11-07T06:00:00Z unix=1983420000 wall=2032-11-07T01:00:00 fold-until(2032-11-07T02:00:00) type=2 -05 EST
0227: 2033-03-13T07:00:00Z unix=1994310000 wall=2033-03-13T02:00:00 gap-until(2033-03-13T03:00:00) type=1 -04 EDT dst
0228: 2033-11-06T06:00:00Z unix=2014869600 wall=2033-11-06T01:00:00 fold-until(2033-11-06T02:00:00) type=2 -05 EST
0229: 2034-03-12T07:00:00Z unix=2025759600 wall=2034-03-12T02:00:00 gap-until(2034-03-12T03:00:00) type=1 -04 EDT dst
0230: 2034-11-05T06:00:00Z unix=2046319200 wall=2034-11-05T01:00:00 fold-until(2034-11-05T02:00:00) type=2 -05 EST
0231: 2035-03-11T07:00:00Z unix=2057209200 wall=2035-03-11T02:00:00 gap-until(2035-03-11T03:00:00) type=1 -04 EDT dst
0232: 2035-11-04T06:00:00Z unix=2077768800 wall=2035-11-04T01:00:00 fold-until(2035-11-04T02:00:00) type=2 -05 EST
0233: 2036-03-09T07:00:00Z unix=2088658800 wall=2036-03-09T02:00:00 gap-until(2036-03-09T03:00:00) type=1 -04 EDT dst
0234: 2036-11-02T06:00:00Z unix=2109218400 wall=2036-11-02T01:00:00 fold-until(2036-11-02T02:00:00) type=2 -05 EST
0235: 2037-03-08T07:00:00Z unix=2120108400 wall=2037-03-08T02:00:00 gap-until(2037-03-08T03:00:00) type=1 -04 EDT dst
0236: 2037-11-01T06:00:00Z unix=2140668000 wall=2037-11-01T01:00:00 fold-until(2037-11-01T02:00:00) type=2 -05 EST

View file

@ -0,0 +1,253 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
America/New_York
LOCAL TIME TYPES
000: offset=-04:56:02 designation=LMT indicator=local/wall
001: offset=-04 designation=EDT dst indicator=local/wall
002: offset=-05 designation=EST indicator=local/wall
003: offset=-05 designation=EST indicator=ut/std
004: offset=-04 designation=EWT dst indicator=local/wall
005: offset=-04 designation=EPT dst indicator=ut/std
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T21:03:57 unambiguous type=0 -04:56:02 LMT
0001: 1883-11-18T17:00:00Z unix=-2717650800 wall=1883-11-18T12:00:00 fold-until(1883-11-18T12:03:58) type=3 -05 EST
0002: 1918-03-31T07:00:00Z unix=-1633280400 wall=1918-03-31T02:00:00 gap-until(1918-03-31T03:00:00) type=1 -04 EDT dst
0003: 1918-10-27T06:00:00Z unix=-1615140000 wall=1918-10-27T01:00:00 fold-until(1918-10-27T02:00:00) type=2 -05 EST
0004: 1919-03-30T07:00:00Z unix=-1601830800 wall=1919-03-30T02:00:00 gap-until(1919-03-30T03:00:00) type=1 -04 EDT dst
0005: 1919-10-26T06:00:00Z unix=-1583690400 wall=1919-10-26T01:00:00 fold-until(1919-10-26T02:00:00) type=2 -05 EST
0006: 1920-03-28T07:00:00Z unix=-1570381200 wall=1920-03-28T02:00:00 gap-until(1920-03-28T03:00:00) type=1 -04 EDT dst
0007: 1920-10-31T06:00:00Z unix=-1551636000 wall=1920-10-31T01:00:00 fold-until(1920-10-31T02:00:00) type=2 -05 EST
0008: 1921-04-24T07:00:00Z unix=-1536512400 wall=1921-04-24T02:00:00 gap-until(1921-04-24T03:00:00) type=1 -04 EDT dst
0009: 1921-09-25T06:00:00Z unix=-1523210400 wall=1921-09-25T01:00:00 fold-until(1921-09-25T02:00:00) type=2 -05 EST
0010: 1922-04-30T07:00:00Z unix=-1504458000 wall=1922-04-30T02:00:00 gap-until(1922-04-30T03:00:00) type=1 -04 EDT dst
0011: 1922-09-24T06:00:00Z unix=-1491760800 wall=1922-09-24T01:00:00 fold-until(1922-09-24T02:00:00) type=2 -05 EST
0012: 1923-04-29T07:00:00Z unix=-1473008400 wall=1923-04-29T02:00:00 gap-until(1923-04-29T03:00:00) type=1 -04 EDT dst
0013: 1923-09-30T06:00:00Z unix=-1459706400 wall=1923-09-30T01:00:00 fold-until(1923-09-30T02:00:00) type=2 -05 EST
0014: 1924-04-27T07:00:00Z unix=-1441558800 wall=1924-04-27T02:00:00 gap-until(1924-04-27T03:00:00) type=1 -04 EDT dst
0015: 1924-09-28T06:00:00Z unix=-1428256800 wall=1924-09-28T01:00:00 fold-until(1924-09-28T02:00:00) type=2 -05 EST
0016: 1925-04-26T07:00:00Z unix=-1410109200 wall=1925-04-26T02:00:00 gap-until(1925-04-26T03:00:00) type=1 -04 EDT dst
0017: 1925-09-27T06:00:00Z unix=-1396807200 wall=1925-09-27T01:00:00 fold-until(1925-09-27T02:00:00) type=2 -05 EST
0018: 1926-04-25T07:00:00Z unix=-1378659600 wall=1926-04-25T02:00:00 gap-until(1926-04-25T03:00:00) type=1 -04 EDT dst
0019: 1926-09-26T06:00:00Z unix=-1365357600 wall=1926-09-26T01:00:00 fold-until(1926-09-26T02:00:00) type=2 -05 EST
0020: 1927-04-24T07:00:00Z unix=-1347210000 wall=1927-04-24T02:00:00 gap-until(1927-04-24T03:00:00) type=1 -04 EDT dst
0021: 1927-09-25T06:00:00Z unix=-1333908000 wall=1927-09-25T01:00:00 fold-until(1927-09-25T02:00:00) type=2 -05 EST
0022: 1928-04-29T07:00:00Z unix=-1315155600 wall=1928-04-29T02:00:00 gap-until(1928-04-29T03:00:00) type=1 -04 EDT dst
0023: 1928-09-30T06:00:00Z unix=-1301853600 wall=1928-09-30T01:00:00 fold-until(1928-09-30T02:00:00) type=2 -05 EST
0024: 1929-04-28T07:00:00Z unix=-1283706000 wall=1929-04-28T02:00:00 gap-until(1929-04-28T03:00:00) type=1 -04 EDT dst
0025: 1929-09-29T06:00:00Z unix=-1270404000 wall=1929-09-29T01:00:00 fold-until(1929-09-29T02:00:00) type=2 -05 EST
0026: 1930-04-27T07:00:00Z unix=-1252256400 wall=1930-04-27T02:00:00 gap-until(1930-04-27T03:00:00) type=1 -04 EDT dst
0027: 1930-09-28T06:00:00Z unix=-1238954400 wall=1930-09-28T01:00:00 fold-until(1930-09-28T02:00:00) type=2 -05 EST
0028: 1931-04-26T07:00:00Z unix=-1220806800 wall=1931-04-26T02:00:00 gap-until(1931-04-26T03:00:00) type=1 -04 EDT dst
0029: 1931-09-27T06:00:00Z unix=-1207504800 wall=1931-09-27T01:00:00 fold-until(1931-09-27T02:00:00) type=2 -05 EST
0030: 1932-04-24T07:00:00Z unix=-1189357200 wall=1932-04-24T02:00:00 gap-until(1932-04-24T03:00:00) type=1 -04 EDT dst
0031: 1932-09-25T06:00:00Z unix=-1176055200 wall=1932-09-25T01:00:00 fold-until(1932-09-25T02:00:00) type=2 -05 EST
0032: 1933-04-30T07:00:00Z unix=-1157302800 wall=1933-04-30T02:00:00 gap-until(1933-04-30T03:00:00) type=1 -04 EDT dst
0033: 1933-09-24T06:00:00Z unix=-1144605600 wall=1933-09-24T01:00:00 fold-until(1933-09-24T02:00:00) type=2 -05 EST
0034: 1934-04-29T07:00:00Z unix=-1125853200 wall=1934-04-29T02:00:00 gap-until(1934-04-29T03:00:00) type=1 -04 EDT dst
0035: 1934-09-30T06:00:00Z unix=-1112551200 wall=1934-09-30T01:00:00 fold-until(1934-09-30T02:00:00) type=2 -05 EST
0036: 1935-04-28T07:00:00Z unix=-1094403600 wall=1935-04-28T02:00:00 gap-until(1935-04-28T03:00:00) type=1 -04 EDT dst
0037: 1935-09-29T06:00:00Z unix=-1081101600 wall=1935-09-29T01:00:00 fold-until(1935-09-29T02:00:00) type=2 -05 EST
0038: 1936-04-26T07:00:00Z unix=-1062954000 wall=1936-04-26T02:00:00 gap-until(1936-04-26T03:00:00) type=1 -04 EDT dst
0039: 1936-09-27T06:00:00Z unix=-1049652000 wall=1936-09-27T01:00:00 fold-until(1936-09-27T02:00:00) type=2 -05 EST
0040: 1937-04-25T07:00:00Z unix=-1031504400 wall=1937-04-25T02:00:00 gap-until(1937-04-25T03:00:00) type=1 -04 EDT dst
0041: 1937-09-26T06:00:00Z unix=-1018202400 wall=1937-09-26T01:00:00 fold-until(1937-09-26T02:00:00) type=2 -05 EST
0042: 1938-04-24T07:00:00Z unix=-1000054800 wall=1938-04-24T02:00:00 gap-until(1938-04-24T03:00:00) type=1 -04 EDT dst
0043: 1938-09-25T06:00:00Z unix=-986752800 wall=1938-09-25T01:00:00 fold-until(1938-09-25T02:00:00) type=2 -05 EST
0044: 1939-04-30T07:00:00Z unix=-968000400 wall=1939-04-30T02:00:00 gap-until(1939-04-30T03:00:00) type=1 -04 EDT dst
0045: 1939-09-24T06:00:00Z unix=-955303200 wall=1939-09-24T01:00:00 fold-until(1939-09-24T02:00:00) type=2 -05 EST
0046: 1940-04-28T07:00:00Z unix=-936550800 wall=1940-04-28T02:00:00 gap-until(1940-04-28T03:00:00) type=1 -04 EDT dst
0047: 1940-09-29T06:00:00Z unix=-923248800 wall=1940-09-29T01:00:00 fold-until(1940-09-29T02:00:00) type=2 -05 EST
0048: 1941-04-27T07:00:00Z unix=-905101200 wall=1941-04-27T02:00:00 gap-until(1941-04-27T03:00:00) type=1 -04 EDT dst
0049: 1941-09-28T06:00:00Z unix=-891799200 wall=1941-09-28T01:00:00 fold-until(1941-09-28T02:00:00) type=2 -05 EST
0050: 1942-02-09T07:00:00Z unix=-880218000 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=4 -04 EWT dst
0051: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T19:00:00 unambiguous type=5 -04 EPT dst
0052: 1945-09-30T06:00:00Z unix=-765396000 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=2 -05 EST
0053: 1946-04-28T07:00:00Z unix=-747248400 wall=1946-04-28T02:00:00 gap-until(1946-04-28T03:00:00) type=1 -04 EDT dst
0054: 1946-09-29T06:00:00Z unix=-733946400 wall=1946-09-29T01:00:00 fold-until(1946-09-29T02:00:00) type=2 -05 EST
0055: 1947-04-27T07:00:00Z unix=-715798800 wall=1947-04-27T02:00:00 gap-until(1947-04-27T03:00:00) type=1 -04 EDT dst
0056: 1947-09-28T06:00:00Z unix=-702496800 wall=1947-09-28T01:00:00 fold-until(1947-09-28T02:00:00) type=2 -05 EST
0057: 1948-04-25T07:00:00Z unix=-684349200 wall=1948-04-25T02:00:00 gap-until(1948-04-25T03:00:00) type=1 -04 EDT dst
0058: 1948-09-26T06:00:00Z unix=-671047200 wall=1948-09-26T01:00:00 fold-until(1948-09-26T02:00:00) type=2 -05 EST
0059: 1949-04-24T07:00:00Z unix=-652899600 wall=1949-04-24T02:00:00 gap-until(1949-04-24T03:00:00) type=1 -04 EDT dst
0060: 1949-09-25T06:00:00Z unix=-639597600 wall=1949-09-25T01:00:00 fold-until(1949-09-25T02:00:00) type=2 -05 EST
0061: 1950-04-30T07:00:00Z unix=-620845200 wall=1950-04-30T02:00:00 gap-until(1950-04-30T03:00:00) type=1 -04 EDT dst
0062: 1950-09-24T06:00:00Z unix=-608148000 wall=1950-09-24T01:00:00 fold-until(1950-09-24T02:00:00) type=2 -05 EST
0063: 1951-04-29T07:00:00Z unix=-589395600 wall=1951-04-29T02:00:00 gap-until(1951-04-29T03:00:00) type=1 -04 EDT dst
0064: 1951-09-30T06:00:00Z unix=-576093600 wall=1951-09-30T01:00:00 fold-until(1951-09-30T02:00:00) type=2 -05 EST
0065: 1952-04-27T07:00:00Z unix=-557946000 wall=1952-04-27T02:00:00 gap-until(1952-04-27T03:00:00) type=1 -04 EDT dst
0066: 1952-09-28T06:00:00Z unix=-544644000 wall=1952-09-28T01:00:00 fold-until(1952-09-28T02:00:00) type=2 -05 EST
0067: 1953-04-26T07:00:00Z unix=-526496400 wall=1953-04-26T02:00:00 gap-until(1953-04-26T03:00:00) type=1 -04 EDT dst
0068: 1953-09-27T06:00:00Z unix=-513194400 wall=1953-09-27T01:00:00 fold-until(1953-09-27T02:00:00) type=2 -05 EST
0069: 1954-04-25T07:00:00Z unix=-495046800 wall=1954-04-25T02:00:00 gap-until(1954-04-25T03:00:00) type=1 -04 EDT dst
0070: 1954-09-26T06:00:00Z unix=-481744800 wall=1954-09-26T01:00:00 fold-until(1954-09-26T02:00:00) type=2 -05 EST
0071: 1955-04-24T07:00:00Z unix=-463597200 wall=1955-04-24T02:00:00 gap-until(1955-04-24T03:00:00) type=1 -04 EDT dst
0072: 1955-10-30T06:00:00Z unix=-447271200 wall=1955-10-30T01:00:00 fold-until(1955-10-30T02:00:00) type=2 -05 EST
0073: 1956-04-29T07:00:00Z unix=-431542800 wall=1956-04-29T02:00:00 gap-until(1956-04-29T03:00:00) type=1 -04 EDT dst
0074: 1956-10-28T06:00:00Z unix=-415821600 wall=1956-10-28T01:00:00 fold-until(1956-10-28T02:00:00) type=2 -05 EST
0075: 1957-04-28T07:00:00Z unix=-400093200 wall=1957-04-28T02:00:00 gap-until(1957-04-28T03:00:00) type=1 -04 EDT dst
0076: 1957-10-27T06:00:00Z unix=-384372000 wall=1957-10-27T01:00:00 fold-until(1957-10-27T02:00:00) type=2 -05 EST
0077: 1958-04-27T07:00:00Z unix=-368643600 wall=1958-04-27T02:00:00 gap-until(1958-04-27T03:00:00) type=1 -04 EDT dst
0078: 1958-10-26T06:00:00Z unix=-352922400 wall=1958-10-26T01:00:00 fold-until(1958-10-26T02:00:00) type=2 -05 EST
0079: 1959-04-26T07:00:00Z unix=-337194000 wall=1959-04-26T02:00:00 gap-until(1959-04-26T03:00:00) type=1 -04 EDT dst
0080: 1959-10-25T06:00:00Z unix=-321472800 wall=1959-10-25T01:00:00 fold-until(1959-10-25T02:00:00) type=2 -05 EST
0081: 1960-04-24T07:00:00Z unix=-305744400 wall=1960-04-24T02:00:00 gap-until(1960-04-24T03:00:00) type=1 -04 EDT dst
0082: 1960-10-30T06:00:00Z unix=-289418400 wall=1960-10-30T01:00:00 fold-until(1960-10-30T02:00:00) type=2 -05 EST
0083: 1961-04-30T07:00:00Z unix=-273690000 wall=1961-04-30T02:00:00 gap-until(1961-04-30T03:00:00) type=1 -04 EDT dst
0084: 1961-10-29T06:00:00Z unix=-257968800 wall=1961-10-29T01:00:00 fold-until(1961-10-29T02:00:00) type=2 -05 EST
0085: 1962-04-29T07:00:00Z unix=-242240400 wall=1962-04-29T02:00:00 gap-until(1962-04-29T03:00:00) type=1 -04 EDT dst
0086: 1962-10-28T06:00:00Z unix=-226519200 wall=1962-10-28T01:00:00 fold-until(1962-10-28T02:00:00) type=2 -05 EST
0087: 1963-04-28T07:00:00Z unix=-210790800 wall=1963-04-28T02:00:00 gap-until(1963-04-28T03:00:00) type=1 -04 EDT dst
0088: 1963-10-27T06:00:00Z unix=-195069600 wall=1963-10-27T01:00:00 fold-until(1963-10-27T02:00:00) type=2 -05 EST
0089: 1964-04-26T07:00:00Z unix=-179341200 wall=1964-04-26T02:00:00 gap-until(1964-04-26T03:00:00) type=1 -04 EDT dst
0090: 1964-10-25T06:00:00Z unix=-163620000 wall=1964-10-25T01:00:00 fold-until(1964-10-25T02:00:00) type=2 -05 EST
0091: 1965-04-25T07:00:00Z unix=-147891600 wall=1965-04-25T02:00:00 gap-until(1965-04-25T03:00:00) type=1 -04 EDT dst
0092: 1965-10-31T06:00:00Z unix=-131565600 wall=1965-10-31T01:00:00 fold-until(1965-10-31T02:00:00) type=2 -05 EST
0093: 1966-04-24T07:00:00Z unix=-116442000 wall=1966-04-24T02:00:00 gap-until(1966-04-24T03:00:00) type=1 -04 EDT dst
0094: 1966-10-30T06:00:00Z unix=-100116000 wall=1966-10-30T01:00:00 fold-until(1966-10-30T02:00:00) type=2 -05 EST
0095: 1967-04-30T07:00:00Z unix=-84387600 wall=1967-04-30T02:00:00 gap-until(1967-04-30T03:00:00) type=1 -04 EDT dst
0096: 1967-10-29T06:00:00Z unix=-68666400 wall=1967-10-29T01:00:00 fold-until(1967-10-29T02:00:00) type=2 -05 EST
0097: 1968-04-28T07:00:00Z unix=-52938000 wall=1968-04-28T02:00:00 gap-until(1968-04-28T03:00:00) type=1 -04 EDT dst
0098: 1968-10-27T06:00:00Z unix=-37216800 wall=1968-10-27T01:00:00 fold-until(1968-10-27T02:00:00) type=2 -05 EST
0099: 1969-04-27T07:00:00Z unix=-21488400 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=1 -04 EDT dst
0100: 1969-10-26T06:00:00Z unix=-5767200 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=2 -05 EST
0101: 1970-04-26T07:00:00Z unix=9961200 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=1 -04 EDT dst
0102: 1970-10-25T06:00:00Z unix=25682400 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=2 -05 EST
0103: 1971-04-25T07:00:00Z unix=41410800 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=1 -04 EDT dst
0104: 1971-10-31T06:00:00Z unix=57736800 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=2 -05 EST
0105: 1972-04-30T07:00:00Z unix=73465200 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=1 -04 EDT dst
0106: 1972-10-29T06:00:00Z unix=89186400 wall=1972-10-29T01:00:00 fold-until(1972-10-29T02:00:00) type=2 -05 EST
0107: 1973-04-29T07:00:00Z unix=104914800 wall=1973-04-29T02:00:00 gap-until(1973-04-29T03:00:00) type=1 -04 EDT dst
0108: 1973-10-28T06:00:00Z unix=120636000 wall=1973-10-28T01:00:00 fold-until(1973-10-28T02:00:00) type=2 -05 EST
0109: 1974-01-06T07:00:00Z unix=126687600 wall=1974-01-06T02:00:00 gap-until(1974-01-06T03:00:00) type=1 -04 EDT dst
0110: 1974-10-27T06:00:00Z unix=152085600 wall=1974-10-27T01:00:00 fold-until(1974-10-27T02:00:00) type=2 -05 EST
0111: 1975-02-23T07:00:00Z unix=162370800 wall=1975-02-23T02:00:00 gap-until(1975-02-23T03:00:00) type=1 -04 EDT dst
0112: 1975-10-26T06:00:00Z unix=183535200 wall=1975-10-26T01:00:00 fold-until(1975-10-26T02:00:00) type=2 -05 EST
0113: 1976-04-25T07:00:00Z unix=199263600 wall=1976-04-25T02:00:00 gap-until(1976-04-25T03:00:00) type=1 -04 EDT dst
0114: 1976-10-31T06:00:00Z unix=215589600 wall=1976-10-31T01:00:00 fold-until(1976-10-31T02:00:00) type=2 -05 EST
0115: 1977-04-24T07:00:00Z unix=230713200 wall=1977-04-24T02:00:00 gap-until(1977-04-24T03:00:00) type=1 -04 EDT dst
0116: 1977-10-30T06:00:00Z unix=247039200 wall=1977-10-30T01:00:00 fold-until(1977-10-30T02:00:00) type=2 -05 EST
0117: 1978-04-30T07:00:00Z unix=262767600 wall=1978-04-30T02:00:00 gap-until(1978-04-30T03:00:00) type=1 -04 EDT dst
0118: 1978-10-29T06:00:00Z unix=278488800 wall=1978-10-29T01:00:00 fold-until(1978-10-29T02:00:00) type=2 -05 EST
0119: 1979-04-29T07:00:00Z unix=294217200 wall=1979-04-29T02:00:00 gap-until(1979-04-29T03:00:00) type=1 -04 EDT dst
0120: 1979-10-28T06:00:00Z unix=309938400 wall=1979-10-28T01:00:00 fold-until(1979-10-28T02:00:00) type=2 -05 EST
0121: 1980-04-27T07:00:00Z unix=325666800 wall=1980-04-27T02:00:00 gap-until(1980-04-27T03:00:00) type=1 -04 EDT dst
0122: 1980-10-26T06:00:00Z unix=341388000 wall=1980-10-26T01:00:00 fold-until(1980-10-26T02:00:00) type=2 -05 EST
0123: 1981-04-26T07:00:00Z unix=357116400 wall=1981-04-26T02:00:00 gap-until(1981-04-26T03:00:00) type=1 -04 EDT dst
0124: 1981-10-25T06:00:00Z unix=372837600 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=2 -05 EST
0125: 1982-04-25T07:00:00Z unix=388566000 wall=1982-04-25T02:00:00 gap-until(1982-04-25T03:00:00) type=1 -04 EDT dst
0126: 1982-10-31T06:00:00Z unix=404892000 wall=1982-10-31T01:00:00 fold-until(1982-10-31T02:00:00) type=2 -05 EST
0127: 1983-04-24T07:00:00Z unix=420015600 wall=1983-04-24T02:00:00 gap-until(1983-04-24T03:00:00) type=1 -04 EDT dst
0128: 1983-10-30T06:00:00Z unix=436341600 wall=1983-10-30T01:00:00 fold-until(1983-10-30T02:00:00) type=2 -05 EST
0129: 1984-04-29T07:00:00Z unix=452070000 wall=1984-04-29T02:00:00 gap-until(1984-04-29T03:00:00) type=1 -04 EDT dst
0130: 1984-10-28T06:00:00Z unix=467791200 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=2 -05 EST
0131: 1985-04-28T07:00:00Z unix=483519600 wall=1985-04-28T02:00:00 gap-until(1985-04-28T03:00:00) type=1 -04 EDT dst
0132: 1985-10-27T06:00:00Z unix=499240800 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=2 -05 EST
0133: 1986-04-27T07:00:00Z unix=514969200 wall=1986-04-27T02:00:00 gap-until(1986-04-27T03:00:00) type=1 -04 EDT dst
0134: 1986-10-26T06:00:00Z unix=530690400 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=2 -05 EST
0135: 1987-04-05T07:00:00Z unix=544604400 wall=1987-04-05T02:00:00 gap-until(1987-04-05T03:00:00) type=1 -04 EDT dst
0136: 1987-10-25T06:00:00Z unix=562140000 wall=1987-10-25T01:00:00 fold-until(1987-10-25T02:00:00) type=2 -05 EST
0137: 1988-04-03T07:00:00Z unix=576054000 wall=1988-04-03T02:00:00 gap-until(1988-04-03T03:00:00) type=1 -04 EDT dst
0138: 1988-10-30T06:00:00Z unix=594194400 wall=1988-10-30T01:00:00 fold-until(1988-10-30T02:00:00) type=2 -05 EST
0139: 1989-04-02T07:00:00Z unix=607503600 wall=1989-04-02T02:00:00 gap-until(1989-04-02T03:00:00) type=1 -04 EDT dst
0140: 1989-10-29T06:00:00Z unix=625644000 wall=1989-10-29T01:00:00 fold-until(1989-10-29T02:00:00) type=2 -05 EST
0141: 1990-04-01T07:00:00Z unix=638953200 wall=1990-04-01T02:00:00 gap-until(1990-04-01T03:00:00) type=1 -04 EDT dst
0142: 1990-10-28T06:00:00Z unix=657093600 wall=1990-10-28T01:00:00 fold-until(1990-10-28T02:00:00) type=2 -05 EST
0143: 1991-04-07T07:00:00Z unix=671007600 wall=1991-04-07T02:00:00 gap-until(1991-04-07T03:00:00) type=1 -04 EDT dst
0144: 1991-10-27T06:00:00Z unix=688543200 wall=1991-10-27T01:00:00 fold-until(1991-10-27T02:00:00) type=2 -05 EST
0145: 1992-04-05T07:00:00Z unix=702457200 wall=1992-04-05T02:00:00 gap-until(1992-04-05T03:00:00) type=1 -04 EDT dst
0146: 1992-10-25T06:00:00Z unix=719992800 wall=1992-10-25T01:00:00 fold-until(1992-10-25T02:00:00) type=2 -05 EST
0147: 1993-04-04T07:00:00Z unix=733906800 wall=1993-04-04T02:00:00 gap-until(1993-04-04T03:00:00) type=1 -04 EDT dst
0148: 1993-10-31T06:00:00Z unix=752047200 wall=1993-10-31T01:00:00 fold-until(1993-10-31T02:00:00) type=2 -05 EST
0149: 1994-04-03T07:00:00Z unix=765356400 wall=1994-04-03T02:00:00 gap-until(1994-04-03T03:00:00) type=1 -04 EDT dst
0150: 1994-10-30T06:00:00Z unix=783496800 wall=1994-10-30T01:00:00 fold-until(1994-10-30T02:00:00) type=2 -05 EST
0151: 1995-04-02T07:00:00Z unix=796806000 wall=1995-04-02T02:00:00 gap-until(1995-04-02T03:00:00) type=1 -04 EDT dst
0152: 1995-10-29T06:00:00Z unix=814946400 wall=1995-10-29T01:00:00 fold-until(1995-10-29T02:00:00) type=2 -05 EST
0153: 1996-04-07T07:00:00Z unix=828860400 wall=1996-04-07T02:00:00 gap-until(1996-04-07T03:00:00) type=1 -04 EDT dst
0154: 1996-10-27T06:00:00Z unix=846396000 wall=1996-10-27T01:00:00 fold-until(1996-10-27T02:00:00) type=2 -05 EST
0155: 1997-04-06T07:00:00Z unix=860310000 wall=1997-04-06T02:00:00 gap-until(1997-04-06T03:00:00) type=1 -04 EDT dst
0156: 1997-10-26T06:00:00Z unix=877845600 wall=1997-10-26T01:00:00 fold-until(1997-10-26T02:00:00) type=2 -05 EST
0157: 1998-04-05T07:00:00Z unix=891759600 wall=1998-04-05T02:00:00 gap-until(1998-04-05T03:00:00) type=1 -04 EDT dst
0158: 1998-10-25T06:00:00Z unix=909295200 wall=1998-10-25T01:00:00 fold-until(1998-10-25T02:00:00) type=2 -05 EST
0159: 1999-04-04T07:00:00Z unix=923209200 wall=1999-04-04T02:00:00 gap-until(1999-04-04T03:00:00) type=1 -04 EDT dst
0160: 1999-10-31T06:00:00Z unix=941349600 wall=1999-10-31T01:00:00 fold-until(1999-10-31T02:00:00) type=2 -05 EST
0161: 2000-04-02T07:00:00Z unix=954658800 wall=2000-04-02T02:00:00 gap-until(2000-04-02T03:00:00) type=1 -04 EDT dst
0162: 2000-10-29T06:00:00Z unix=972799200 wall=2000-10-29T01:00:00 fold-until(2000-10-29T02:00:00) type=2 -05 EST
0163: 2001-04-01T07:00:00Z unix=986108400 wall=2001-04-01T02:00:00 gap-until(2001-04-01T03:00:00) type=1 -04 EDT dst
0164: 2001-10-28T06:00:00Z unix=1004248800 wall=2001-10-28T01:00:00 fold-until(2001-10-28T02:00:00) type=2 -05 EST
0165: 2002-04-07T07:00:00Z unix=1018162800 wall=2002-04-07T02:00:00 gap-until(2002-04-07T03:00:00) type=1 -04 EDT dst
0166: 2002-10-27T06:00:00Z unix=1035698400 wall=2002-10-27T01:00:00 fold-until(2002-10-27T02:00:00) type=2 -05 EST
0167: 2003-04-06T07:00:00Z unix=1049612400 wall=2003-04-06T02:00:00 gap-until(2003-04-06T03:00:00) type=1 -04 EDT dst
0168: 2003-10-26T06:00:00Z unix=1067148000 wall=2003-10-26T01:00:00 fold-until(2003-10-26T02:00:00) type=2 -05 EST
0169: 2004-04-04T07:00:00Z unix=1081062000 wall=2004-04-04T02:00:00 gap-until(2004-04-04T03:00:00) type=1 -04 EDT dst
0170: 2004-10-31T06:00:00Z unix=1099202400 wall=2004-10-31T01:00:00 fold-until(2004-10-31T02:00:00) type=2 -05 EST
0171: 2005-04-03T07:00:00Z unix=1112511600 wall=2005-04-03T02:00:00 gap-until(2005-04-03T03:00:00) type=1 -04 EDT dst
0172: 2005-10-30T06:00:00Z unix=1130652000 wall=2005-10-30T01:00:00 fold-until(2005-10-30T02:00:00) type=2 -05 EST
0173: 2006-04-02T07:00:00Z unix=1143961200 wall=2006-04-02T02:00:00 gap-until(2006-04-02T03:00:00) type=1 -04 EDT dst
0174: 2006-10-29T06:00:00Z unix=1162101600 wall=2006-10-29T01:00:00 fold-until(2006-10-29T02:00:00) type=2 -05 EST
0175: 2007-03-11T07:00:00Z unix=1173596400 wall=2007-03-11T02:00:00 gap-until(2007-03-11T03:00:00) type=1 -04 EDT dst
0176: 2007-11-04T06:00:00Z unix=1194156000 wall=2007-11-04T01:00:00 fold-until(2007-11-04T02:00:00) type=2 -05 EST
0177: 2008-03-09T07:00:00Z unix=1205046000 wall=2008-03-09T02:00:00 gap-until(2008-03-09T03:00:00) type=1 -04 EDT dst
0178: 2008-11-02T06:00:00Z unix=1225605600 wall=2008-11-02T01:00:00 fold-until(2008-11-02T02:00:00) type=2 -05 EST
0179: 2009-03-08T07:00:00Z unix=1236495600 wall=2009-03-08T02:00:00 gap-until(2009-03-08T03:00:00) type=1 -04 EDT dst
0180: 2009-11-01T06:00:00Z unix=1257055200 wall=2009-11-01T01:00:00 fold-until(2009-11-01T02:00:00) type=2 -05 EST
0181: 2010-03-14T07:00:00Z unix=1268550000 wall=2010-03-14T02:00:00 gap-until(2010-03-14T03:00:00) type=1 -04 EDT dst
0182: 2010-11-07T06:00:00Z unix=1289109600 wall=2010-11-07T01:00:00 fold-until(2010-11-07T02:00:00) type=2 -05 EST
0183: 2011-03-13T07:00:00Z unix=1299999600 wall=2011-03-13T02:00:00 gap-until(2011-03-13T03:00:00) type=1 -04 EDT dst
0184: 2011-11-06T06:00:00Z unix=1320559200 wall=2011-11-06T01:00:00 fold-until(2011-11-06T02:00:00) type=2 -05 EST
0185: 2012-03-11T07:00:00Z unix=1331449200 wall=2012-03-11T02:00:00 gap-until(2012-03-11T03:00:00) type=1 -04 EDT dst
0186: 2012-11-04T06:00:00Z unix=1352008800 wall=2012-11-04T01:00:00 fold-until(2012-11-04T02:00:00) type=2 -05 EST
0187: 2013-03-10T07:00:00Z unix=1362898800 wall=2013-03-10T02:00:00 gap-until(2013-03-10T03:00:00) type=1 -04 EDT dst
0188: 2013-11-03T06:00:00Z unix=1383458400 wall=2013-11-03T01:00:00 fold-until(2013-11-03T02:00:00) type=2 -05 EST
0189: 2014-03-09T07:00:00Z unix=1394348400 wall=2014-03-09T02:00:00 gap-until(2014-03-09T03:00:00) type=1 -04 EDT dst
0190: 2014-11-02T06:00:00Z unix=1414908000 wall=2014-11-02T01:00:00 fold-until(2014-11-02T02:00:00) type=2 -05 EST
0191: 2015-03-08T07:00:00Z unix=1425798000 wall=2015-03-08T02:00:00 gap-until(2015-03-08T03:00:00) type=1 -04 EDT dst
0192: 2015-11-01T06:00:00Z unix=1446357600 wall=2015-11-01T01:00:00 fold-until(2015-11-01T02:00:00) type=2 -05 EST
0193: 2016-03-13T07:00:00Z unix=1457852400 wall=2016-03-13T02:00:00 gap-until(2016-03-13T03:00:00) type=1 -04 EDT dst
0194: 2016-11-06T06:00:00Z unix=1478412000 wall=2016-11-06T01:00:00 fold-until(2016-11-06T02:00:00) type=2 -05 EST
0195: 2017-03-12T07:00:00Z unix=1489302000 wall=2017-03-12T02:00:00 gap-until(2017-03-12T03:00:00) type=1 -04 EDT dst
0196: 2017-11-05T06:00:00Z unix=1509861600 wall=2017-11-05T01:00:00 fold-until(2017-11-05T02:00:00) type=2 -05 EST
0197: 2018-03-11T07:00:00Z unix=1520751600 wall=2018-03-11T02:00:00 gap-until(2018-03-11T03:00:00) type=1 -04 EDT dst
0198: 2018-11-04T06:00:00Z unix=1541311200 wall=2018-11-04T01:00:00 fold-until(2018-11-04T02:00:00) type=2 -05 EST
0199: 2019-03-10T07:00:00Z unix=1552201200 wall=2019-03-10T02:00:00 gap-until(2019-03-10T03:00:00) type=1 -04 EDT dst
0200: 2019-11-03T06:00:00Z unix=1572760800 wall=2019-11-03T01:00:00 fold-until(2019-11-03T02:00:00) type=2 -05 EST
0201: 2020-03-08T07:00:00Z unix=1583650800 wall=2020-03-08T02:00:00 gap-until(2020-03-08T03:00:00) type=1 -04 EDT dst
0202: 2020-11-01T06:00:00Z unix=1604210400 wall=2020-11-01T01:00:00 fold-until(2020-11-01T02:00:00) type=2 -05 EST
0203: 2021-03-14T07:00:00Z unix=1615705200 wall=2021-03-14T02:00:00 gap-until(2021-03-14T03:00:00) type=1 -04 EDT dst
0204: 2021-11-07T06:00:00Z unix=1636264800 wall=2021-11-07T01:00:00 fold-until(2021-11-07T02:00:00) type=2 -05 EST
0205: 2022-03-13T07:00:00Z unix=1647154800 wall=2022-03-13T02:00:00 gap-until(2022-03-13T03:00:00) type=1 -04 EDT dst
0206: 2022-11-06T06:00:00Z unix=1667714400 wall=2022-11-06T01:00:00 fold-until(2022-11-06T02:00:00) type=2 -05 EST
0207: 2023-03-12T07:00:00Z unix=1678604400 wall=2023-03-12T02:00:00 gap-until(2023-03-12T03:00:00) type=1 -04 EDT dst
0208: 2023-11-05T06:00:00Z unix=1699164000 wall=2023-11-05T01:00:00 fold-until(2023-11-05T02:00:00) type=2 -05 EST
0209: 2024-03-10T07:00:00Z unix=1710054000 wall=2024-03-10T02:00:00 gap-until(2024-03-10T03:00:00) type=1 -04 EDT dst
0210: 2024-11-03T06:00:00Z unix=1730613600 wall=2024-11-03T01:00:00 fold-until(2024-11-03T02:00:00) type=2 -05 EST
0211: 2025-03-09T07:00:00Z unix=1741503600 wall=2025-03-09T02:00:00 gap-until(2025-03-09T03:00:00) type=1 -04 EDT dst
0212: 2025-11-02T06:00:00Z unix=1762063200 wall=2025-11-02T01:00:00 fold-until(2025-11-02T02:00:00) type=2 -05 EST
0213: 2026-03-08T07:00:00Z unix=1772953200 wall=2026-03-08T02:00:00 gap-until(2026-03-08T03:00:00) type=1 -04 EDT dst
0214: 2026-11-01T06:00:00Z unix=1793512800 wall=2026-11-01T01:00:00 fold-until(2026-11-01T02:00:00) type=2 -05 EST
0215: 2027-03-14T07:00:00Z unix=1805007600 wall=2027-03-14T02:00:00 gap-until(2027-03-14T03:00:00) type=1 -04 EDT dst
0216: 2027-11-07T06:00:00Z unix=1825567200 wall=2027-11-07T01:00:00 fold-until(2027-11-07T02:00:00) type=2 -05 EST
0217: 2028-03-12T07:00:00Z unix=1836457200 wall=2028-03-12T02:00:00 gap-until(2028-03-12T03:00:00) type=1 -04 EDT dst
0218: 2028-11-05T06:00:00Z unix=1857016800 wall=2028-11-05T01:00:00 fold-until(2028-11-05T02:00:00) type=2 -05 EST
0219: 2029-03-11T07:00:00Z unix=1867906800 wall=2029-03-11T02:00:00 gap-until(2029-03-11T03:00:00) type=1 -04 EDT dst
0220: 2029-11-04T06:00:00Z unix=1888466400 wall=2029-11-04T01:00:00 fold-until(2029-11-04T02:00:00) type=2 -05 EST
0221: 2030-03-10T07:00:00Z unix=1899356400 wall=2030-03-10T02:00:00 gap-until(2030-03-10T03:00:00) type=1 -04 EDT dst
0222: 2030-11-03T06:00:00Z unix=1919916000 wall=2030-11-03T01:00:00 fold-until(2030-11-03T02:00:00) type=2 -05 EST
0223: 2031-03-09T07:00:00Z unix=1930806000 wall=2031-03-09T02:00:00 gap-until(2031-03-09T03:00:00) type=1 -04 EDT dst
0224: 2031-11-02T06:00:00Z unix=1951365600 wall=2031-11-02T01:00:00 fold-until(2031-11-02T02:00:00) type=2 -05 EST
0225: 2032-03-14T07:00:00Z unix=1962860400 wall=2032-03-14T02:00:00 gap-until(2032-03-14T03:00:00) type=1 -04 EDT dst
0226: 2032-11-07T06:00:00Z unix=1983420000 wall=2032-11-07T01:00:00 fold-until(2032-11-07T02:00:00) type=2 -05 EST
0227: 2033-03-13T07:00:00Z unix=1994310000 wall=2033-03-13T02:00:00 gap-until(2033-03-13T03:00:00) type=1 -04 EDT dst
0228: 2033-11-06T06:00:00Z unix=2014869600 wall=2033-11-06T01:00:00 fold-until(2033-11-06T02:00:00) type=2 -05 EST
0229: 2034-03-12T07:00:00Z unix=2025759600 wall=2034-03-12T02:00:00 gap-until(2034-03-12T03:00:00) type=1 -04 EDT dst
0230: 2034-11-05T06:00:00Z unix=2046319200 wall=2034-11-05T01:00:00 fold-until(2034-11-05T02:00:00) type=2 -05 EST
0231: 2035-03-11T07:00:00Z unix=2057209200 wall=2035-03-11T02:00:00 gap-until(2035-03-11T03:00:00) type=1 -04 EDT dst
0232: 2035-11-04T06:00:00Z unix=2077768800 wall=2035-11-04T01:00:00 fold-until(2035-11-04T02:00:00) type=2 -05 EST
0233: 2036-03-09T07:00:00Z unix=2088658800 wall=2036-03-09T02:00:00 gap-until(2036-03-09T03:00:00) type=1 -04 EDT dst
0234: 2036-11-02T06:00:00Z unix=2109218400 wall=2036-11-02T01:00:00 fold-until(2036-11-02T02:00:00) type=2 -05 EST
0235: 2037-03-08T07:00:00Z unix=2120108400 wall=2037-03-08T02:00:00 gap-until(2037-03-08T03:00:00) type=1 -04 EDT dst
0236: 2037-11-01T06:00:00Z unix=2140668000 wall=2037-11-01T01:00:00 fold-until(2037-11-01T02:00:00) type=2 -05 EST
POSIX TIME ZONE STRING
EST5EDT,M3.2.0,M11.1.0

View file

@ -0,0 +1,160 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
America/Sitka
LOCAL TIME TYPES
000: offset=+14:58:47 designation=LMT indicator=local/wall
001: offset=-08 designation=PST indicator=local/wall
002: offset=-07 designation=PWT dst indicator=local/wall
003: offset=-07 designation=PPT dst indicator=ut/std
004: offset=-07 designation=PDT dst indicator=local/wall
005: offset=-09 designation=YST indicator=local/wall
006: offset=-08 designation=AKDT dst indicator=local/wall
007: offset=-09 designation=AKST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T16:58:46 unambiguous type=0 +14:58:47 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-13T12:45:52 fold-until(1901-12-14T11:44:39) type=1 -08 PST
0002: 1942-02-09T10:00:00Z unix=-880207200 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=2 -07 PWT dst
0003: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T16:00:00 unambiguous type=3 -07 PPT dst
0004: 1945-09-30T09:00:00Z unix=-765385200 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=1 -08 PST
0005: 1969-04-27T10:00:00Z unix=-21477600 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=4 -07 PDT dst
0006: 1969-10-26T09:00:00Z unix=-5756400 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=1 -08 PST
0007: 1970-04-26T10:00:00Z unix=9972000 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=4 -07 PDT dst
0008: 1970-10-25T09:00:00Z unix=25693200 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=1 -08 PST
0009: 1971-04-25T10:00:00Z unix=41421600 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=4 -07 PDT dst
0010: 1971-10-31T09:00:00Z unix=57747600 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=1 -08 PST
0011: 1972-04-30T10:00:00Z unix=73476000 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=4 -07 PDT dst
0012: 1972-10-29T09:00:00Z unix=89197200 wall=1972-10-29T01:00:00 fold-until(1972-10-29T02:00:00) type=1 -08 PST
0013: 1973-04-29T10:00:00Z unix=104925600 wall=1973-04-29T02:00:00 gap-until(1973-04-29T03:00:00) type=4 -07 PDT dst
0014: 1973-10-28T09:00:00Z unix=120646800 wall=1973-10-28T01:00:00 fold-until(1973-10-28T02:00:00) type=1 -08 PST
0015: 1974-01-06T10:00:00Z unix=126698400 wall=1974-01-06T02:00:00 gap-until(1974-01-06T03:00:00) type=4 -07 PDT dst
0016: 1974-10-27T09:00:00Z unix=152096400 wall=1974-10-27T01:00:00 fold-until(1974-10-27T02:00:00) type=1 -08 PST
0017: 1975-02-23T10:00:00Z unix=162381600 wall=1975-02-23T02:00:00 gap-until(1975-02-23T03:00:00) type=4 -07 PDT dst
0018: 1975-10-26T09:00:00Z unix=183546000 wall=1975-10-26T01:00:00 fold-until(1975-10-26T02:00:00) type=1 -08 PST
0019: 1976-04-25T10:00:00Z unix=199274400 wall=1976-04-25T02:00:00 gap-until(1976-04-25T03:00:00) type=4 -07 PDT dst
0020: 1976-10-31T09:00:00Z unix=215600400 wall=1976-10-31T01:00:00 fold-until(1976-10-31T02:00:00) type=1 -08 PST
0021: 1977-04-24T10:00:00Z unix=230724000 wall=1977-04-24T02:00:00 gap-until(1977-04-24T03:00:00) type=4 -07 PDT dst
0022: 1977-10-30T09:00:00Z unix=247050000 wall=1977-10-30T01:00:00 fold-until(1977-10-30T02:00:00) type=1 -08 PST
0023: 1978-04-30T10:00:00Z unix=262778400 wall=1978-04-30T02:00:00 gap-until(1978-04-30T03:00:00) type=4 -07 PDT dst
0024: 1978-10-29T09:00:00Z unix=278499600 wall=1978-10-29T01:00:00 fold-until(1978-10-29T02:00:00) type=1 -08 PST
0025: 1979-04-29T10:00:00Z unix=294228000 wall=1979-04-29T02:00:00 gap-until(1979-04-29T03:00:00) type=4 -07 PDT dst
0026: 1979-10-28T09:00:00Z unix=309949200 wall=1979-10-28T01:00:00 fold-until(1979-10-28T02:00:00) type=1 -08 PST
0027: 1980-04-27T10:00:00Z unix=325677600 wall=1980-04-27T02:00:00 gap-until(1980-04-27T03:00:00) type=4 -07 PDT dst
0028: 1980-10-26T09:00:00Z unix=341398800 wall=1980-10-26T01:00:00 fold-until(1980-10-26T02:00:00) type=1 -08 PST
0029: 1981-04-26T10:00:00Z unix=357127200 wall=1981-04-26T02:00:00 gap-until(1981-04-26T03:00:00) type=4 -07 PDT dst
0030: 1981-10-25T09:00:00Z unix=372848400 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=1 -08 PST
0031: 1982-04-25T10:00:00Z unix=388576800 wall=1982-04-25T02:00:00 gap-until(1982-04-25T03:00:00) type=4 -07 PDT dst
0032: 1982-10-31T09:00:00Z unix=404902800 wall=1982-10-31T01:00:00 fold-until(1982-10-31T02:00:00) type=1 -08 PST
0033: 1983-04-24T10:00:00Z unix=420026400 wall=1983-04-24T02:00:00 gap-until(1983-04-24T03:00:00) type=4 -07 PDT dst
0034: 1983-10-30T09:00:00Z unix=436352400 wall=1983-10-30T00:00:00 fold-until(1983-10-30T02:00:00) type=5 -09 YST
0035: 1983-11-30T09:00:00Z unix=439030800 wall=1983-11-30T00:00:00 unambiguous type=7 -09 AKST
0036: 1984-04-29T11:00:00Z unix=452084400 wall=1984-04-29T02:00:00 gap-until(1984-04-29T03:00:00) type=6 -08 AKDT dst
0037: 1984-10-28T10:00:00Z unix=467805600 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=7 -09 AKST
0038: 1985-04-28T11:00:00Z unix=483534000 wall=1985-04-28T02:00:00 gap-until(1985-04-28T03:00:00) type=6 -08 AKDT dst
0039: 1985-10-27T10:00:00Z unix=499255200 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=7 -09 AKST
0040: 1986-04-27T11:00:00Z unix=514983600 wall=1986-04-27T02:00:00 gap-until(1986-04-27T03:00:00) type=6 -08 AKDT dst
0041: 1986-10-26T10:00:00Z unix=530704800 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=7 -09 AKST
0042: 1987-04-05T11:00:00Z unix=544618800 wall=1987-04-05T02:00:00 gap-until(1987-04-05T03:00:00) type=6 -08 AKDT dst
0043: 1987-10-25T10:00:00Z unix=562154400 wall=1987-10-25T01:00:00 fold-until(1987-10-25T02:00:00) type=7 -09 AKST
0044: 1988-04-03T11:00:00Z unix=576068400 wall=1988-04-03T02:00:00 gap-until(1988-04-03T03:00:00) type=6 -08 AKDT dst
0045: 1988-10-30T10:00:00Z unix=594208800 wall=1988-10-30T01:00:00 fold-until(1988-10-30T02:00:00) type=7 -09 AKST
0046: 1989-04-02T11:00:00Z unix=607518000 wall=1989-04-02T02:00:00 gap-until(1989-04-02T03:00:00) type=6 -08 AKDT dst
0047: 1989-10-29T10:00:00Z unix=625658400 wall=1989-10-29T01:00:00 fold-until(1989-10-29T02:00:00) type=7 -09 AKST
0048: 1990-04-01T11:00:00Z unix=638967600 wall=1990-04-01T02:00:00 gap-until(1990-04-01T03:00:00) type=6 -08 AKDT dst
0049: 1990-10-28T10:00:00Z unix=657108000 wall=1990-10-28T01:00:00 fold-until(1990-10-28T02:00:00) type=7 -09 AKST
0050: 1991-04-07T11:00:00Z unix=671022000 wall=1991-04-07T02:00:00 gap-until(1991-04-07T03:00:00) type=6 -08 AKDT dst
0051: 1991-10-27T10:00:00Z unix=688557600 wall=1991-10-27T01:00:00 fold-until(1991-10-27T02:00:00) type=7 -09 AKST
0052: 1992-04-05T11:00:00Z unix=702471600 wall=1992-04-05T02:00:00 gap-until(1992-04-05T03:00:00) type=6 -08 AKDT dst
0053: 1992-10-25T10:00:00Z unix=720007200 wall=1992-10-25T01:00:00 fold-until(1992-10-25T02:00:00) type=7 -09 AKST
0054: 1993-04-04T11:00:00Z unix=733921200 wall=1993-04-04T02:00:00 gap-until(1993-04-04T03:00:00) type=6 -08 AKDT dst
0055: 1993-10-31T10:00:00Z unix=752061600 wall=1993-10-31T01:00:00 fold-until(1993-10-31T02:00:00) type=7 -09 AKST
0056: 1994-04-03T11:00:00Z unix=765370800 wall=1994-04-03T02:00:00 gap-until(1994-04-03T03:00:00) type=6 -08 AKDT dst
0057: 1994-10-30T10:00:00Z unix=783511200 wall=1994-10-30T01:00:00 fold-until(1994-10-30T02:00:00) type=7 -09 AKST
0058: 1995-04-02T11:00:00Z unix=796820400 wall=1995-04-02T02:00:00 gap-until(1995-04-02T03:00:00) type=6 -08 AKDT dst
0059: 1995-10-29T10:00:00Z unix=814960800 wall=1995-10-29T01:00:00 fold-until(1995-10-29T02:00:00) type=7 -09 AKST
0060: 1996-04-07T11:00:00Z unix=828874800 wall=1996-04-07T02:00:00 gap-until(1996-04-07T03:00:00) type=6 -08 AKDT dst
0061: 1996-10-27T10:00:00Z unix=846410400 wall=1996-10-27T01:00:00 fold-until(1996-10-27T02:00:00) type=7 -09 AKST
0062: 1997-04-06T11:00:00Z unix=860324400 wall=1997-04-06T02:00:00 gap-until(1997-04-06T03:00:00) type=6 -08 AKDT dst
0063: 1997-10-26T10:00:00Z unix=877860000 wall=1997-10-26T01:00:00 fold-until(1997-10-26T02:00:00) type=7 -09 AKST
0064: 1998-04-05T11:00:00Z unix=891774000 wall=1998-04-05T02:00:00 gap-until(1998-04-05T03:00:00) type=6 -08 AKDT dst
0065: 1998-10-25T10:00:00Z unix=909309600 wall=1998-10-25T01:00:00 fold-until(1998-10-25T02:00:00) type=7 -09 AKST
0066: 1999-04-04T11:00:00Z unix=923223600 wall=1999-04-04T02:00:00 gap-until(1999-04-04T03:00:00) type=6 -08 AKDT dst
0067: 1999-10-31T10:00:00Z unix=941364000 wall=1999-10-31T01:00:00 fold-until(1999-10-31T02:00:00) type=7 -09 AKST
0068: 2000-04-02T11:00:00Z unix=954673200 wall=2000-04-02T02:00:00 gap-until(2000-04-02T03:00:00) type=6 -08 AKDT dst
0069: 2000-10-29T10:00:00Z unix=972813600 wall=2000-10-29T01:00:00 fold-until(2000-10-29T02:00:00) type=7 -09 AKST
0070: 2001-04-01T11:00:00Z unix=986122800 wall=2001-04-01T02:00:00 gap-until(2001-04-01T03:00:00) type=6 -08 AKDT dst
0071: 2001-10-28T10:00:00Z unix=1004263200 wall=2001-10-28T01:00:00 fold-until(2001-10-28T02:00:00) type=7 -09 AKST
0072: 2002-04-07T11:00:00Z unix=1018177200 wall=2002-04-07T02:00:00 gap-until(2002-04-07T03:00:00) type=6 -08 AKDT dst
0073: 2002-10-27T10:00:00Z unix=1035712800 wall=2002-10-27T01:00:00 fold-until(2002-10-27T02:00:00) type=7 -09 AKST
0074: 2003-04-06T11:00:00Z unix=1049626800 wall=2003-04-06T02:00:00 gap-until(2003-04-06T03:00:00) type=6 -08 AKDT dst
0075: 2003-10-26T10:00:00Z unix=1067162400 wall=2003-10-26T01:00:00 fold-until(2003-10-26T02:00:00) type=7 -09 AKST
0076: 2004-04-04T11:00:00Z unix=1081076400 wall=2004-04-04T02:00:00 gap-until(2004-04-04T03:00:00) type=6 -08 AKDT dst
0077: 2004-10-31T10:00:00Z unix=1099216800 wall=2004-10-31T01:00:00 fold-until(2004-10-31T02:00:00) type=7 -09 AKST
0078: 2005-04-03T11:00:00Z unix=1112526000 wall=2005-04-03T02:00:00 gap-until(2005-04-03T03:00:00) type=6 -08 AKDT dst
0079: 2005-10-30T10:00:00Z unix=1130666400 wall=2005-10-30T01:00:00 fold-until(2005-10-30T02:00:00) type=7 -09 AKST
0080: 2006-04-02T11:00:00Z unix=1143975600 wall=2006-04-02T02:00:00 gap-until(2006-04-02T03:00:00) type=6 -08 AKDT dst
0081: 2006-10-29T10:00:00Z unix=1162116000 wall=2006-10-29T01:00:00 fold-until(2006-10-29T02:00:00) type=7 -09 AKST
0082: 2007-03-11T11:00:00Z unix=1173610800 wall=2007-03-11T02:00:00 gap-until(2007-03-11T03:00:00) type=6 -08 AKDT dst
0083: 2007-11-04T10:00:00Z unix=1194170400 wall=2007-11-04T01:00:00 fold-until(2007-11-04T02:00:00) type=7 -09 AKST
0084: 2008-03-09T11:00:00Z unix=1205060400 wall=2008-03-09T02:00:00 gap-until(2008-03-09T03:00:00) type=6 -08 AKDT dst
0085: 2008-11-02T10:00:00Z unix=1225620000 wall=2008-11-02T01:00:00 fold-until(2008-11-02T02:00:00) type=7 -09 AKST
0086: 2009-03-08T11:00:00Z unix=1236510000 wall=2009-03-08T02:00:00 gap-until(2009-03-08T03:00:00) type=6 -08 AKDT dst
0087: 2009-11-01T10:00:00Z unix=1257069600 wall=2009-11-01T01:00:00 fold-until(2009-11-01T02:00:00) type=7 -09 AKST
0088: 2010-03-14T11:00:00Z unix=1268564400 wall=2010-03-14T02:00:00 gap-until(2010-03-14T03:00:00) type=6 -08 AKDT dst
0089: 2010-11-07T10:00:00Z unix=1289124000 wall=2010-11-07T01:00:00 fold-until(2010-11-07T02:00:00) type=7 -09 AKST
0090: 2011-03-13T11:00:00Z unix=1300014000 wall=2011-03-13T02:00:00 gap-until(2011-03-13T03:00:00) type=6 -08 AKDT dst
0091: 2011-11-06T10:00:00Z unix=1320573600 wall=2011-11-06T01:00:00 fold-until(2011-11-06T02:00:00) type=7 -09 AKST
0092: 2012-03-11T11:00:00Z unix=1331463600 wall=2012-03-11T02:00:00 gap-until(2012-03-11T03:00:00) type=6 -08 AKDT dst
0093: 2012-11-04T10:00:00Z unix=1352023200 wall=2012-11-04T01:00:00 fold-until(2012-11-04T02:00:00) type=7 -09 AKST
0094: 2013-03-10T11:00:00Z unix=1362913200 wall=2013-03-10T02:00:00 gap-until(2013-03-10T03:00:00) type=6 -08 AKDT dst
0095: 2013-11-03T10:00:00Z unix=1383472800 wall=2013-11-03T01:00:00 fold-until(2013-11-03T02:00:00) type=7 -09 AKST
0096: 2014-03-09T11:00:00Z unix=1394362800 wall=2014-03-09T02:00:00 gap-until(2014-03-09T03:00:00) type=6 -08 AKDT dst
0097: 2014-11-02T10:00:00Z unix=1414922400 wall=2014-11-02T01:00:00 fold-until(2014-11-02T02:00:00) type=7 -09 AKST
0098: 2015-03-08T11:00:00Z unix=1425812400 wall=2015-03-08T02:00:00 gap-until(2015-03-08T03:00:00) type=6 -08 AKDT dst
0099: 2015-11-01T10:00:00Z unix=1446372000 wall=2015-11-01T01:00:00 fold-until(2015-11-01T02:00:00) type=7 -09 AKST
0100: 2016-03-13T11:00:00Z unix=1457866800 wall=2016-03-13T02:00:00 gap-until(2016-03-13T03:00:00) type=6 -08 AKDT dst
0101: 2016-11-06T10:00:00Z unix=1478426400 wall=2016-11-06T01:00:00 fold-until(2016-11-06T02:00:00) type=7 -09 AKST
0102: 2017-03-12T11:00:00Z unix=1489316400 wall=2017-03-12T02:00:00 gap-until(2017-03-12T03:00:00) type=6 -08 AKDT dst
0103: 2017-11-05T10:00:00Z unix=1509876000 wall=2017-11-05T01:00:00 fold-until(2017-11-05T02:00:00) type=7 -09 AKST
0104: 2018-03-11T11:00:00Z unix=1520766000 wall=2018-03-11T02:00:00 gap-until(2018-03-11T03:00:00) type=6 -08 AKDT dst
0105: 2018-11-04T10:00:00Z unix=1541325600 wall=2018-11-04T01:00:00 fold-until(2018-11-04T02:00:00) type=7 -09 AKST
0106: 2019-03-10T11:00:00Z unix=1552215600 wall=2019-03-10T02:00:00 gap-until(2019-03-10T03:00:00) type=6 -08 AKDT dst
0107: 2019-11-03T10:00:00Z unix=1572775200 wall=2019-11-03T01:00:00 fold-until(2019-11-03T02:00:00) type=7 -09 AKST
0108: 2020-03-08T11:00:00Z unix=1583665200 wall=2020-03-08T02:00:00 gap-until(2020-03-08T03:00:00) type=6 -08 AKDT dst
0109: 2020-11-01T10:00:00Z unix=1604224800 wall=2020-11-01T01:00:00 fold-until(2020-11-01T02:00:00) type=7 -09 AKST
0110: 2021-03-14T11:00:00Z unix=1615719600 wall=2021-03-14T02:00:00 gap-until(2021-03-14T03:00:00) type=6 -08 AKDT dst
0111: 2021-11-07T10:00:00Z unix=1636279200 wall=2021-11-07T01:00:00 fold-until(2021-11-07T02:00:00) type=7 -09 AKST
0112: 2022-03-13T11:00:00Z unix=1647169200 wall=2022-03-13T02:00:00 gap-until(2022-03-13T03:00:00) type=6 -08 AKDT dst
0113: 2022-11-06T10:00:00Z unix=1667728800 wall=2022-11-06T01:00:00 fold-until(2022-11-06T02:00:00) type=7 -09 AKST
0114: 2023-03-12T11:00:00Z unix=1678618800 wall=2023-03-12T02:00:00 gap-until(2023-03-12T03:00:00) type=6 -08 AKDT dst
0115: 2023-11-05T10:00:00Z unix=1699178400 wall=2023-11-05T01:00:00 fold-until(2023-11-05T02:00:00) type=7 -09 AKST
0116: 2024-03-10T11:00:00Z unix=1710068400 wall=2024-03-10T02:00:00 gap-until(2024-03-10T03:00:00) type=6 -08 AKDT dst
0117: 2024-11-03T10:00:00Z unix=1730628000 wall=2024-11-03T01:00:00 fold-until(2024-11-03T02:00:00) type=7 -09 AKST
0118: 2025-03-09T11:00:00Z unix=1741518000 wall=2025-03-09T02:00:00 gap-until(2025-03-09T03:00:00) type=6 -08 AKDT dst
0119: 2025-11-02T10:00:00Z unix=1762077600 wall=2025-11-02T01:00:00 fold-until(2025-11-02T02:00:00) type=7 -09 AKST
0120: 2026-03-08T11:00:00Z unix=1772967600 wall=2026-03-08T02:00:00 gap-until(2026-03-08T03:00:00) type=6 -08 AKDT dst
0121: 2026-11-01T10:00:00Z unix=1793527200 wall=2026-11-01T01:00:00 fold-until(2026-11-01T02:00:00) type=7 -09 AKST
0122: 2027-03-14T11:00:00Z unix=1805022000 wall=2027-03-14T02:00:00 gap-until(2027-03-14T03:00:00) type=6 -08 AKDT dst
0123: 2027-11-07T10:00:00Z unix=1825581600 wall=2027-11-07T01:00:00 fold-until(2027-11-07T02:00:00) type=7 -09 AKST
0124: 2028-03-12T11:00:00Z unix=1836471600 wall=2028-03-12T02:00:00 gap-until(2028-03-12T03:00:00) type=6 -08 AKDT dst
0125: 2028-11-05T10:00:00Z unix=1857031200 wall=2028-11-05T01:00:00 fold-until(2028-11-05T02:00:00) type=7 -09 AKST
0126: 2029-03-11T11:00:00Z unix=1867921200 wall=2029-03-11T02:00:00 gap-until(2029-03-11T03:00:00) type=6 -08 AKDT dst
0127: 2029-11-04T10:00:00Z unix=1888480800 wall=2029-11-04T01:00:00 fold-until(2029-11-04T02:00:00) type=7 -09 AKST
0128: 2030-03-10T11:00:00Z unix=1899370800 wall=2030-03-10T02:00:00 gap-until(2030-03-10T03:00:00) type=6 -08 AKDT dst
0129: 2030-11-03T10:00:00Z unix=1919930400 wall=2030-11-03T01:00:00 fold-until(2030-11-03T02:00:00) type=7 -09 AKST
0130: 2031-03-09T11:00:00Z unix=1930820400 wall=2031-03-09T02:00:00 gap-until(2031-03-09T03:00:00) type=6 -08 AKDT dst
0131: 2031-11-02T10:00:00Z unix=1951380000 wall=2031-11-02T01:00:00 fold-until(2031-11-02T02:00:00) type=7 -09 AKST
0132: 2032-03-14T11:00:00Z unix=1962874800 wall=2032-03-14T02:00:00 gap-until(2032-03-14T03:00:00) type=6 -08 AKDT dst
0133: 2032-11-07T10:00:00Z unix=1983434400 wall=2032-11-07T01:00:00 fold-until(2032-11-07T02:00:00) type=7 -09 AKST
0134: 2033-03-13T11:00:00Z unix=1994324400 wall=2033-03-13T02:00:00 gap-until(2033-03-13T03:00:00) type=6 -08 AKDT dst
0135: 2033-11-06T10:00:00Z unix=2014884000 wall=2033-11-06T01:00:00 fold-until(2033-11-06T02:00:00) type=7 -09 AKST
0136: 2034-03-12T11:00:00Z unix=2025774000 wall=2034-03-12T02:00:00 gap-until(2034-03-12T03:00:00) type=6 -08 AKDT dst
0137: 2034-11-05T10:00:00Z unix=2046333600 wall=2034-11-05T01:00:00 fold-until(2034-11-05T02:00:00) type=7 -09 AKST
0138: 2035-03-11T11:00:00Z unix=2057223600 wall=2035-03-11T02:00:00 gap-until(2035-03-11T03:00:00) type=6 -08 AKDT dst
0139: 2035-11-04T10:00:00Z unix=2077783200 wall=2035-11-04T01:00:00 fold-until(2035-11-04T02:00:00) type=7 -09 AKST
0140: 2036-03-09T11:00:00Z unix=2088673200 wall=2036-03-09T02:00:00 gap-until(2036-03-09T03:00:00) type=6 -08 AKDT dst
0141: 2036-11-02T10:00:00Z unix=2109232800 wall=2036-11-02T01:00:00 fold-until(2036-11-02T02:00:00) type=7 -09 AKST
0142: 2037-03-08T11:00:00Z unix=2120122800 wall=2037-03-08T02:00:00 gap-until(2037-03-08T03:00:00) type=6 -08 AKDT dst
0143: 2037-11-01T10:00:00Z unix=2140682400 wall=2037-11-01T01:00:00 fold-until(2037-11-01T02:00:00) type=7 -09 AKST

View file

@ -0,0 +1,164 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
America/Sitka
LOCAL TIME TYPES
000: offset=+14:58:47 designation=LMT indicator=local/wall
001: offset=-09:01:13 designation=LMT indicator=local/wall
002: offset=-08 designation=PST indicator=local/wall
003: offset=-07 designation=PWT dst indicator=local/wall
004: offset=-07 designation=PPT dst indicator=ut/std
005: offset=-07 designation=PDT dst indicator=local/wall
006: offset=-09 designation=YST indicator=local/wall
007: offset=-08 designation=AKDT dst indicator=local/wall
008: offset=-09 designation=AKST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T16:58:46 unambiguous type=0 +14:58:47 LMT
0001: 1867-10-19T00:31:13Z unix=-3225223727 wall=1867-10-18T15:30:00 fold-until(1867-10-19T15:30:00) type=1 -09:01:13 LMT
0002: 1900-08-20T21:01:13Z unix=-2188954727 wall=1900-08-20T12:00:00 gap-until(1900-08-20T13:01:13) type=2 -08 PST
0003: 1942-02-09T10:00:00Z unix=-880207200 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=3 -07 PWT dst
0004: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T16:00:00 unambiguous type=4 -07 PPT dst
0005: 1945-09-30T09:00:00Z unix=-765385200 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=2 -08 PST
0006: 1969-04-27T10:00:00Z unix=-21477600 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=5 -07 PDT dst
0007: 1969-10-26T09:00:00Z unix=-5756400 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=2 -08 PST
0008: 1970-04-26T10:00:00Z unix=9972000 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=5 -07 PDT dst
0009: 1970-10-25T09:00:00Z unix=25693200 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=2 -08 PST
0010: 1971-04-25T10:00:00Z unix=41421600 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=5 -07 PDT dst
0011: 1971-10-31T09:00:00Z unix=57747600 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=2 -08 PST
0012: 1972-04-30T10:00:00Z unix=73476000 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=5 -07 PDT dst
0013: 1972-10-29T09:00:00Z unix=89197200 wall=1972-10-29T01:00:00 fold-until(1972-10-29T02:00:00) type=2 -08 PST
0014: 1973-04-29T10:00:00Z unix=104925600 wall=1973-04-29T02:00:00 gap-until(1973-04-29T03:00:00) type=5 -07 PDT dst
0015: 1973-10-28T09:00:00Z unix=120646800 wall=1973-10-28T01:00:00 fold-until(1973-10-28T02:00:00) type=2 -08 PST
0016: 1974-01-06T10:00:00Z unix=126698400 wall=1974-01-06T02:00:00 gap-until(1974-01-06T03:00:00) type=5 -07 PDT dst
0017: 1974-10-27T09:00:00Z unix=152096400 wall=1974-10-27T01:00:00 fold-until(1974-10-27T02:00:00) type=2 -08 PST
0018: 1975-02-23T10:00:00Z unix=162381600 wall=1975-02-23T02:00:00 gap-until(1975-02-23T03:00:00) type=5 -07 PDT dst
0019: 1975-10-26T09:00:00Z unix=183546000 wall=1975-10-26T01:00:00 fold-until(1975-10-26T02:00:00) type=2 -08 PST
0020: 1976-04-25T10:00:00Z unix=199274400 wall=1976-04-25T02:00:00 gap-until(1976-04-25T03:00:00) type=5 -07 PDT dst
0021: 1976-10-31T09:00:00Z unix=215600400 wall=1976-10-31T01:00:00 fold-until(1976-10-31T02:00:00) type=2 -08 PST
0022: 1977-04-24T10:00:00Z unix=230724000 wall=1977-04-24T02:00:00 gap-until(1977-04-24T03:00:00) type=5 -07 PDT dst
0023: 1977-10-30T09:00:00Z unix=247050000 wall=1977-10-30T01:00:00 fold-until(1977-10-30T02:00:00) type=2 -08 PST
0024: 1978-04-30T10:00:00Z unix=262778400 wall=1978-04-30T02:00:00 gap-until(1978-04-30T03:00:00) type=5 -07 PDT dst
0025: 1978-10-29T09:00:00Z unix=278499600 wall=1978-10-29T01:00:00 fold-until(1978-10-29T02:00:00) type=2 -08 PST
0026: 1979-04-29T10:00:00Z unix=294228000 wall=1979-04-29T02:00:00 gap-until(1979-04-29T03:00:00) type=5 -07 PDT dst
0027: 1979-10-28T09:00:00Z unix=309949200 wall=1979-10-28T01:00:00 fold-until(1979-10-28T02:00:00) type=2 -08 PST
0028: 1980-04-27T10:00:00Z unix=325677600 wall=1980-04-27T02:00:00 gap-until(1980-04-27T03:00:00) type=5 -07 PDT dst
0029: 1980-10-26T09:00:00Z unix=341398800 wall=1980-10-26T01:00:00 fold-until(1980-10-26T02:00:00) type=2 -08 PST
0030: 1981-04-26T10:00:00Z unix=357127200 wall=1981-04-26T02:00:00 gap-until(1981-04-26T03:00:00) type=5 -07 PDT dst
0031: 1981-10-25T09:00:00Z unix=372848400 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=2 -08 PST
0032: 1982-04-25T10:00:00Z unix=388576800 wall=1982-04-25T02:00:00 gap-until(1982-04-25T03:00:00) type=5 -07 PDT dst
0033: 1982-10-31T09:00:00Z unix=404902800 wall=1982-10-31T01:00:00 fold-until(1982-10-31T02:00:00) type=2 -08 PST
0034: 1983-04-24T10:00:00Z unix=420026400 wall=1983-04-24T02:00:00 gap-until(1983-04-24T03:00:00) type=5 -07 PDT dst
0035: 1983-10-30T09:00:00Z unix=436352400 wall=1983-10-30T00:00:00 fold-until(1983-10-30T02:00:00) type=6 -09 YST
0036: 1983-11-30T09:00:00Z unix=439030800 wall=1983-11-30T00:00:00 unambiguous type=8 -09 AKST
0037: 1984-04-29T11:00:00Z unix=452084400 wall=1984-04-29T02:00:00 gap-until(1984-04-29T03:00:00) type=7 -08 AKDT dst
0038: 1984-10-28T10:00:00Z unix=467805600 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=8 -09 AKST
0039: 1985-04-28T11:00:00Z unix=483534000 wall=1985-04-28T02:00:00 gap-until(1985-04-28T03:00:00) type=7 -08 AKDT dst
0040: 1985-10-27T10:00:00Z unix=499255200 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=8 -09 AKST
0041: 1986-04-27T11:00:00Z unix=514983600 wall=1986-04-27T02:00:00 gap-until(1986-04-27T03:00:00) type=7 -08 AKDT dst
0042: 1986-10-26T10:00:00Z unix=530704800 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=8 -09 AKST
0043: 1987-04-05T11:00:00Z unix=544618800 wall=1987-04-05T02:00:00 gap-until(1987-04-05T03:00:00) type=7 -08 AKDT dst
0044: 1987-10-25T10:00:00Z unix=562154400 wall=1987-10-25T01:00:00 fold-until(1987-10-25T02:00:00) type=8 -09 AKST
0045: 1988-04-03T11:00:00Z unix=576068400 wall=1988-04-03T02:00:00 gap-until(1988-04-03T03:00:00) type=7 -08 AKDT dst
0046: 1988-10-30T10:00:00Z unix=594208800 wall=1988-10-30T01:00:00 fold-until(1988-10-30T02:00:00) type=8 -09 AKST
0047: 1989-04-02T11:00:00Z unix=607518000 wall=1989-04-02T02:00:00 gap-until(1989-04-02T03:00:00) type=7 -08 AKDT dst
0048: 1989-10-29T10:00:00Z unix=625658400 wall=1989-10-29T01:00:00 fold-until(1989-10-29T02:00:00) type=8 -09 AKST
0049: 1990-04-01T11:00:00Z unix=638967600 wall=1990-04-01T02:00:00 gap-until(1990-04-01T03:00:00) type=7 -08 AKDT dst
0050: 1990-10-28T10:00:00Z unix=657108000 wall=1990-10-28T01:00:00 fold-until(1990-10-28T02:00:00) type=8 -09 AKST
0051: 1991-04-07T11:00:00Z unix=671022000 wall=1991-04-07T02:00:00 gap-until(1991-04-07T03:00:00) type=7 -08 AKDT dst
0052: 1991-10-27T10:00:00Z unix=688557600 wall=1991-10-27T01:00:00 fold-until(1991-10-27T02:00:00) type=8 -09 AKST
0053: 1992-04-05T11:00:00Z unix=702471600 wall=1992-04-05T02:00:00 gap-until(1992-04-05T03:00:00) type=7 -08 AKDT dst
0054: 1992-10-25T10:00:00Z unix=720007200 wall=1992-10-25T01:00:00 fold-until(1992-10-25T02:00:00) type=8 -09 AKST
0055: 1993-04-04T11:00:00Z unix=733921200 wall=1993-04-04T02:00:00 gap-until(1993-04-04T03:00:00) type=7 -08 AKDT dst
0056: 1993-10-31T10:00:00Z unix=752061600 wall=1993-10-31T01:00:00 fold-until(1993-10-31T02:00:00) type=8 -09 AKST
0057: 1994-04-03T11:00:00Z unix=765370800 wall=1994-04-03T02:00:00 gap-until(1994-04-03T03:00:00) type=7 -08 AKDT dst
0058: 1994-10-30T10:00:00Z unix=783511200 wall=1994-10-30T01:00:00 fold-until(1994-10-30T02:00:00) type=8 -09 AKST
0059: 1995-04-02T11:00:00Z unix=796820400 wall=1995-04-02T02:00:00 gap-until(1995-04-02T03:00:00) type=7 -08 AKDT dst
0060: 1995-10-29T10:00:00Z unix=814960800 wall=1995-10-29T01:00:00 fold-until(1995-10-29T02:00:00) type=8 -09 AKST
0061: 1996-04-07T11:00:00Z unix=828874800 wall=1996-04-07T02:00:00 gap-until(1996-04-07T03:00:00) type=7 -08 AKDT dst
0062: 1996-10-27T10:00:00Z unix=846410400 wall=1996-10-27T01:00:00 fold-until(1996-10-27T02:00:00) type=8 -09 AKST
0063: 1997-04-06T11:00:00Z unix=860324400 wall=1997-04-06T02:00:00 gap-until(1997-04-06T03:00:00) type=7 -08 AKDT dst
0064: 1997-10-26T10:00:00Z unix=877860000 wall=1997-10-26T01:00:00 fold-until(1997-10-26T02:00:00) type=8 -09 AKST
0065: 1998-04-05T11:00:00Z unix=891774000 wall=1998-04-05T02:00:00 gap-until(1998-04-05T03:00:00) type=7 -08 AKDT dst
0066: 1998-10-25T10:00:00Z unix=909309600 wall=1998-10-25T01:00:00 fold-until(1998-10-25T02:00:00) type=8 -09 AKST
0067: 1999-04-04T11:00:00Z unix=923223600 wall=1999-04-04T02:00:00 gap-until(1999-04-04T03:00:00) type=7 -08 AKDT dst
0068: 1999-10-31T10:00:00Z unix=941364000 wall=1999-10-31T01:00:00 fold-until(1999-10-31T02:00:00) type=8 -09 AKST
0069: 2000-04-02T11:00:00Z unix=954673200 wall=2000-04-02T02:00:00 gap-until(2000-04-02T03:00:00) type=7 -08 AKDT dst
0070: 2000-10-29T10:00:00Z unix=972813600 wall=2000-10-29T01:00:00 fold-until(2000-10-29T02:00:00) type=8 -09 AKST
0071: 2001-04-01T11:00:00Z unix=986122800 wall=2001-04-01T02:00:00 gap-until(2001-04-01T03:00:00) type=7 -08 AKDT dst
0072: 2001-10-28T10:00:00Z unix=1004263200 wall=2001-10-28T01:00:00 fold-until(2001-10-28T02:00:00) type=8 -09 AKST
0073: 2002-04-07T11:00:00Z unix=1018177200 wall=2002-04-07T02:00:00 gap-until(2002-04-07T03:00:00) type=7 -08 AKDT dst
0074: 2002-10-27T10:00:00Z unix=1035712800 wall=2002-10-27T01:00:00 fold-until(2002-10-27T02:00:00) type=8 -09 AKST
0075: 2003-04-06T11:00:00Z unix=1049626800 wall=2003-04-06T02:00:00 gap-until(2003-04-06T03:00:00) type=7 -08 AKDT dst
0076: 2003-10-26T10:00:00Z unix=1067162400 wall=2003-10-26T01:00:00 fold-until(2003-10-26T02:00:00) type=8 -09 AKST
0077: 2004-04-04T11:00:00Z unix=1081076400 wall=2004-04-04T02:00:00 gap-until(2004-04-04T03:00:00) type=7 -08 AKDT dst
0078: 2004-10-31T10:00:00Z unix=1099216800 wall=2004-10-31T01:00:00 fold-until(2004-10-31T02:00:00) type=8 -09 AKST
0079: 2005-04-03T11:00:00Z unix=1112526000 wall=2005-04-03T02:00:00 gap-until(2005-04-03T03:00:00) type=7 -08 AKDT dst
0080: 2005-10-30T10:00:00Z unix=1130666400 wall=2005-10-30T01:00:00 fold-until(2005-10-30T02:00:00) type=8 -09 AKST
0081: 2006-04-02T11:00:00Z unix=1143975600 wall=2006-04-02T02:00:00 gap-until(2006-04-02T03:00:00) type=7 -08 AKDT dst
0082: 2006-10-29T10:00:00Z unix=1162116000 wall=2006-10-29T01:00:00 fold-until(2006-10-29T02:00:00) type=8 -09 AKST
0083: 2007-03-11T11:00:00Z unix=1173610800 wall=2007-03-11T02:00:00 gap-until(2007-03-11T03:00:00) type=7 -08 AKDT dst
0084: 2007-11-04T10:00:00Z unix=1194170400 wall=2007-11-04T01:00:00 fold-until(2007-11-04T02:00:00) type=8 -09 AKST
0085: 2008-03-09T11:00:00Z unix=1205060400 wall=2008-03-09T02:00:00 gap-until(2008-03-09T03:00:00) type=7 -08 AKDT dst
0086: 2008-11-02T10:00:00Z unix=1225620000 wall=2008-11-02T01:00:00 fold-until(2008-11-02T02:00:00) type=8 -09 AKST
0087: 2009-03-08T11:00:00Z unix=1236510000 wall=2009-03-08T02:00:00 gap-until(2009-03-08T03:00:00) type=7 -08 AKDT dst
0088: 2009-11-01T10:00:00Z unix=1257069600 wall=2009-11-01T01:00:00 fold-until(2009-11-01T02:00:00) type=8 -09 AKST
0089: 2010-03-14T11:00:00Z unix=1268564400 wall=2010-03-14T02:00:00 gap-until(2010-03-14T03:00:00) type=7 -08 AKDT dst
0090: 2010-11-07T10:00:00Z unix=1289124000 wall=2010-11-07T01:00:00 fold-until(2010-11-07T02:00:00) type=8 -09 AKST
0091: 2011-03-13T11:00:00Z unix=1300014000 wall=2011-03-13T02:00:00 gap-until(2011-03-13T03:00:00) type=7 -08 AKDT dst
0092: 2011-11-06T10:00:00Z unix=1320573600 wall=2011-11-06T01:00:00 fold-until(2011-11-06T02:00:00) type=8 -09 AKST
0093: 2012-03-11T11:00:00Z unix=1331463600 wall=2012-03-11T02:00:00 gap-until(2012-03-11T03:00:00) type=7 -08 AKDT dst
0094: 2012-11-04T10:00:00Z unix=1352023200 wall=2012-11-04T01:00:00 fold-until(2012-11-04T02:00:00) type=8 -09 AKST
0095: 2013-03-10T11:00:00Z unix=1362913200 wall=2013-03-10T02:00:00 gap-until(2013-03-10T03:00:00) type=7 -08 AKDT dst
0096: 2013-11-03T10:00:00Z unix=1383472800 wall=2013-11-03T01:00:00 fold-until(2013-11-03T02:00:00) type=8 -09 AKST
0097: 2014-03-09T11:00:00Z unix=1394362800 wall=2014-03-09T02:00:00 gap-until(2014-03-09T03:00:00) type=7 -08 AKDT dst
0098: 2014-11-02T10:00:00Z unix=1414922400 wall=2014-11-02T01:00:00 fold-until(2014-11-02T02:00:00) type=8 -09 AKST
0099: 2015-03-08T11:00:00Z unix=1425812400 wall=2015-03-08T02:00:00 gap-until(2015-03-08T03:00:00) type=7 -08 AKDT dst
0100: 2015-11-01T10:00:00Z unix=1446372000 wall=2015-11-01T01:00:00 fold-until(2015-11-01T02:00:00) type=8 -09 AKST
0101: 2016-03-13T11:00:00Z unix=1457866800 wall=2016-03-13T02:00:00 gap-until(2016-03-13T03:00:00) type=7 -08 AKDT dst
0102: 2016-11-06T10:00:00Z unix=1478426400 wall=2016-11-06T01:00:00 fold-until(2016-11-06T02:00:00) type=8 -09 AKST
0103: 2017-03-12T11:00:00Z unix=1489316400 wall=2017-03-12T02:00:00 gap-until(2017-03-12T03:00:00) type=7 -08 AKDT dst
0104: 2017-11-05T10:00:00Z unix=1509876000 wall=2017-11-05T01:00:00 fold-until(2017-11-05T02:00:00) type=8 -09 AKST
0105: 2018-03-11T11:00:00Z unix=1520766000 wall=2018-03-11T02:00:00 gap-until(2018-03-11T03:00:00) type=7 -08 AKDT dst
0106: 2018-11-04T10:00:00Z unix=1541325600 wall=2018-11-04T01:00:00 fold-until(2018-11-04T02:00:00) type=8 -09 AKST
0107: 2019-03-10T11:00:00Z unix=1552215600 wall=2019-03-10T02:00:00 gap-until(2019-03-10T03:00:00) type=7 -08 AKDT dst
0108: 2019-11-03T10:00:00Z unix=1572775200 wall=2019-11-03T01:00:00 fold-until(2019-11-03T02:00:00) type=8 -09 AKST
0109: 2020-03-08T11:00:00Z unix=1583665200 wall=2020-03-08T02:00:00 gap-until(2020-03-08T03:00:00) type=7 -08 AKDT dst
0110: 2020-11-01T10:00:00Z unix=1604224800 wall=2020-11-01T01:00:00 fold-until(2020-11-01T02:00:00) type=8 -09 AKST
0111: 2021-03-14T11:00:00Z unix=1615719600 wall=2021-03-14T02:00:00 gap-until(2021-03-14T03:00:00) type=7 -08 AKDT dst
0112: 2021-11-07T10:00:00Z unix=1636279200 wall=2021-11-07T01:00:00 fold-until(2021-11-07T02:00:00) type=8 -09 AKST
0113: 2022-03-13T11:00:00Z unix=1647169200 wall=2022-03-13T02:00:00 gap-until(2022-03-13T03:00:00) type=7 -08 AKDT dst
0114: 2022-11-06T10:00:00Z unix=1667728800 wall=2022-11-06T01:00:00 fold-until(2022-11-06T02:00:00) type=8 -09 AKST
0115: 2023-03-12T11:00:00Z unix=1678618800 wall=2023-03-12T02:00:00 gap-until(2023-03-12T03:00:00) type=7 -08 AKDT dst
0116: 2023-11-05T10:00:00Z unix=1699178400 wall=2023-11-05T01:00:00 fold-until(2023-11-05T02:00:00) type=8 -09 AKST
0117: 2024-03-10T11:00:00Z unix=1710068400 wall=2024-03-10T02:00:00 gap-until(2024-03-10T03:00:00) type=7 -08 AKDT dst
0118: 2024-11-03T10:00:00Z unix=1730628000 wall=2024-11-03T01:00:00 fold-until(2024-11-03T02:00:00) type=8 -09 AKST
0119: 2025-03-09T11:00:00Z unix=1741518000 wall=2025-03-09T02:00:00 gap-until(2025-03-09T03:00:00) type=7 -08 AKDT dst
0120: 2025-11-02T10:00:00Z unix=1762077600 wall=2025-11-02T01:00:00 fold-until(2025-11-02T02:00:00) type=8 -09 AKST
0121: 2026-03-08T11:00:00Z unix=1772967600 wall=2026-03-08T02:00:00 gap-until(2026-03-08T03:00:00) type=7 -08 AKDT dst
0122: 2026-11-01T10:00:00Z unix=1793527200 wall=2026-11-01T01:00:00 fold-until(2026-11-01T02:00:00) type=8 -09 AKST
0123: 2027-03-14T11:00:00Z unix=1805022000 wall=2027-03-14T02:00:00 gap-until(2027-03-14T03:00:00) type=7 -08 AKDT dst
0124: 2027-11-07T10:00:00Z unix=1825581600 wall=2027-11-07T01:00:00 fold-until(2027-11-07T02:00:00) type=8 -09 AKST
0125: 2028-03-12T11:00:00Z unix=1836471600 wall=2028-03-12T02:00:00 gap-until(2028-03-12T03:00:00) type=7 -08 AKDT dst
0126: 2028-11-05T10:00:00Z unix=1857031200 wall=2028-11-05T01:00:00 fold-until(2028-11-05T02:00:00) type=8 -09 AKST
0127: 2029-03-11T11:00:00Z unix=1867921200 wall=2029-03-11T02:00:00 gap-until(2029-03-11T03:00:00) type=7 -08 AKDT dst
0128: 2029-11-04T10:00:00Z unix=1888480800 wall=2029-11-04T01:00:00 fold-until(2029-11-04T02:00:00) type=8 -09 AKST
0129: 2030-03-10T11:00:00Z unix=1899370800 wall=2030-03-10T02:00:00 gap-until(2030-03-10T03:00:00) type=7 -08 AKDT dst
0130: 2030-11-03T10:00:00Z unix=1919930400 wall=2030-11-03T01:00:00 fold-until(2030-11-03T02:00:00) type=8 -09 AKST
0131: 2031-03-09T11:00:00Z unix=1930820400 wall=2031-03-09T02:00:00 gap-until(2031-03-09T03:00:00) type=7 -08 AKDT dst
0132: 2031-11-02T10:00:00Z unix=1951380000 wall=2031-11-02T01:00:00 fold-until(2031-11-02T02:00:00) type=8 -09 AKST
0133: 2032-03-14T11:00:00Z unix=1962874800 wall=2032-03-14T02:00:00 gap-until(2032-03-14T03:00:00) type=7 -08 AKDT dst
0134: 2032-11-07T10:00:00Z unix=1983434400 wall=2032-11-07T01:00:00 fold-until(2032-11-07T02:00:00) type=8 -09 AKST
0135: 2033-03-13T11:00:00Z unix=1994324400 wall=2033-03-13T02:00:00 gap-until(2033-03-13T03:00:00) type=7 -08 AKDT dst
0136: 2033-11-06T10:00:00Z unix=2014884000 wall=2033-11-06T01:00:00 fold-until(2033-11-06T02:00:00) type=8 -09 AKST
0137: 2034-03-12T11:00:00Z unix=2025774000 wall=2034-03-12T02:00:00 gap-until(2034-03-12T03:00:00) type=7 -08 AKDT dst
0138: 2034-11-05T10:00:00Z unix=2046333600 wall=2034-11-05T01:00:00 fold-until(2034-11-05T02:00:00) type=8 -09 AKST
0139: 2035-03-11T11:00:00Z unix=2057223600 wall=2035-03-11T02:00:00 gap-until(2035-03-11T03:00:00) type=7 -08 AKDT dst
0140: 2035-11-04T10:00:00Z unix=2077783200 wall=2035-11-04T01:00:00 fold-until(2035-11-04T02:00:00) type=8 -09 AKST
0141: 2036-03-09T11:00:00Z unix=2088673200 wall=2036-03-09T02:00:00 gap-until(2036-03-09T03:00:00) type=7 -08 AKDT dst
0142: 2036-11-02T10:00:00Z unix=2109232800 wall=2036-11-02T01:00:00 fold-until(2036-11-02T02:00:00) type=8 -09 AKST
0143: 2037-03-08T11:00:00Z unix=2120122800 wall=2037-03-08T02:00:00 gap-until(2037-03-08T03:00:00) type=7 -08 AKDT dst
0144: 2037-11-01T10:00:00Z unix=2140682400 wall=2037-11-01T01:00:00 fold-until(2037-11-01T02:00:00) type=8 -09 AKST
POSIX TIME ZONE STRING
AKST9AKDT,M3.2.0,M11.1.0

View file

@ -0,0 +1,257 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
America/St_Johns
LOCAL TIME TYPES
000: offset=-03:30:52 designation=LMT indicator=local/wall
001: offset=-02:30:52 designation=NDT dst indicator=local/wall
002: offset=-03:30:52 designation=NST indicator=local/wall
003: offset=-02:30 designation=NDT dst indicator=local/wall
004: offset=-03:30 designation=NST indicator=local/wall
005: offset=-02:30 designation=NPT dst indicator=ut/std
006: offset=-02:30 designation=NWT dst indicator=local/wall
007: offset=-01:30 designation=NDDT dst indicator=local/wall
008: offset=-02:30 designation=NDT dst indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T22:29:07 unambiguous type=0 -03:30:52 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-13T17:15:00 unambiguous type=2 -03:30:52 NST
0002: 1917-04-08T05:30:52Z unix=-1664130548 wall=1917-04-08T02:00:00 gap-until(1917-04-08T03:00:00) type=1 -02:30:52 NDT dst
0003: 1917-09-17T04:30:52Z unix=-1650137348 wall=1917-09-17T01:00:00 fold-until(1917-09-17T02:00:00) type=2 -03:30:52 NST
0004: 1918-04-14T05:30:52Z unix=-1632076148 wall=1918-04-14T02:00:00 gap-until(1918-04-14T03:00:00) type=1 -02:30:52 NDT dst
0005: 1918-10-27T04:30:52Z unix=-1615145348 wall=1918-10-27T01:00:00 fold-until(1918-10-27T02:00:00) type=2 -03:30:52 NST
0006: 1919-05-06T02:30:52Z unix=-1598650148 wall=1919-05-05T23:00:00 gap-until(1919-05-06T00:00:00) type=1 -02:30:52 NDT dst
0007: 1919-08-13T01:30:52Z unix=-1590100148 wall=1919-08-12T22:00:00 fold-until(1919-08-12T23:00:00) type=2 -03:30:52 NST
0008: 1920-05-03T02:30:52Z unix=-1567286948 wall=1920-05-02T23:00:00 gap-until(1920-05-03T00:00:00) type=1 -02:30:52 NDT dst
0009: 1920-11-01T01:30:52Z unix=-1551565748 wall=1920-10-31T22:00:00 fold-until(1920-10-31T23:00:00) type=2 -03:30:52 NST
0010: 1921-05-02T02:30:52Z unix=-1535837348 wall=1921-05-01T23:00:00 gap-until(1921-05-02T00:00:00) type=1 -02:30:52 NDT dst
0011: 1921-10-31T01:30:52Z unix=-1520116148 wall=1921-10-30T22:00:00 fold-until(1921-10-30T23:00:00) type=2 -03:30:52 NST
0012: 1922-05-08T02:30:52Z unix=-1503782948 wall=1922-05-07T23:00:00 gap-until(1922-05-08T00:00:00) type=1 -02:30:52 NDT dst
0013: 1922-10-30T01:30:52Z unix=-1488666548 wall=1922-10-29T22:00:00 fold-until(1922-10-29T23:00:00) type=2 -03:30:52 NST
0014: 1923-05-07T02:30:52Z unix=-1472333348 wall=1923-05-06T23:00:00 gap-until(1923-05-07T00:00:00) type=1 -02:30:52 NDT dst
0015: 1923-10-29T01:30:52Z unix=-1457216948 wall=1923-10-28T22:00:00 fold-until(1923-10-28T23:00:00) type=2 -03:30:52 NST
0016: 1924-05-05T02:30:52Z unix=-1440883748 wall=1924-05-04T23:00:00 gap-until(1924-05-05T00:00:00) type=1 -02:30:52 NDT dst
0017: 1924-10-27T01:30:52Z unix=-1425767348 wall=1924-10-26T22:00:00 fold-until(1924-10-26T23:00:00) type=2 -03:30:52 NST
0018: 1925-05-04T02:30:52Z unix=-1409434148 wall=1925-05-03T23:00:00 gap-until(1925-05-04T00:00:00) type=1 -02:30:52 NDT dst
0019: 1925-10-26T01:30:52Z unix=-1394317748 wall=1925-10-25T22:00:00 fold-until(1925-10-25T23:00:00) type=2 -03:30:52 NST
0020: 1926-05-03T02:30:52Z unix=-1377984548 wall=1926-05-02T23:00:00 gap-until(1926-05-03T00:00:00) type=1 -02:30:52 NDT dst
0021: 1926-11-01T01:30:52Z unix=-1362263348 wall=1926-10-31T22:00:00 fold-until(1926-10-31T23:00:00) type=2 -03:30:52 NST
0022: 1927-05-02T02:30:52Z unix=-1346534948 wall=1927-05-01T23:00:00 gap-until(1927-05-02T00:00:00) type=1 -02:30:52 NDT dst
0023: 1927-10-31T01:30:52Z unix=-1330813748 wall=1927-10-30T22:00:00 fold-until(1927-10-30T23:00:00) type=2 -03:30:52 NST
0024: 1928-05-07T02:30:52Z unix=-1314480548 wall=1928-05-06T23:00:00 gap-until(1928-05-07T00:00:00) type=1 -02:30:52 NDT dst
0025: 1928-10-29T01:30:52Z unix=-1299364148 wall=1928-10-28T22:00:00 fold-until(1928-10-28T23:00:00) type=2 -03:30:52 NST
0026: 1929-05-06T02:30:52Z unix=-1283030948 wall=1929-05-05T23:00:00 gap-until(1929-05-06T00:00:00) type=1 -02:30:52 NDT dst
0027: 1929-10-28T01:30:52Z unix=-1267914548 wall=1929-10-27T22:00:00 fold-until(1929-10-27T23:00:00) type=2 -03:30:52 NST
0028: 1930-05-05T02:30:52Z unix=-1251581348 wall=1930-05-04T23:00:00 gap-until(1930-05-05T00:00:00) type=1 -02:30:52 NDT dst
0029: 1930-10-27T01:30:52Z unix=-1236464948 wall=1930-10-26T22:00:00 fold-until(1930-10-26T23:00:00) type=2 -03:30:52 NST
0030: 1931-05-04T02:30:52Z unix=-1220131748 wall=1931-05-03T23:00:00 gap-until(1931-05-04T00:00:00) type=1 -02:30:52 NDT dst
0031: 1931-10-26T01:30:52Z unix=-1205015348 wall=1931-10-25T22:00:00 fold-until(1931-10-25T23:00:00) type=2 -03:30:52 NST
0032: 1932-05-02T02:30:52Z unix=-1188682148 wall=1932-05-01T23:00:00 gap-until(1932-05-02T00:00:00) type=1 -02:30:52 NDT dst
0033: 1932-10-31T01:30:52Z unix=-1172960948 wall=1932-10-30T22:00:00 fold-until(1932-10-30T23:00:00) type=2 -03:30:52 NST
0034: 1933-05-08T02:30:52Z unix=-1156627748 wall=1933-05-07T23:00:00 gap-until(1933-05-08T00:00:00) type=1 -02:30:52 NDT dst
0035: 1933-10-30T01:30:52Z unix=-1141511348 wall=1933-10-29T22:00:00 fold-until(1933-10-29T23:00:00) type=2 -03:30:52 NST
0036: 1934-05-07T02:30:52Z unix=-1125178148 wall=1934-05-06T23:00:00 gap-until(1934-05-07T00:00:00) type=1 -02:30:52 NDT dst
0037: 1934-10-29T01:30:52Z unix=-1110061748 wall=1934-10-28T22:00:00 fold-until(1934-10-28T23:00:00) type=2 -03:30:52 NST
0038: 1935-03-30T03:30:52Z unix=-1096921748 wall=1935-03-30T00:00:00 gap-until(1935-03-30T00:00:52) type=4 -03:30 NST
0039: 1935-05-06T02:30:00Z unix=-1093728600 wall=1935-05-05T23:00:00 gap-until(1935-05-06T00:00:00) type=3 -02:30 NDT dst
0040: 1935-10-28T01:30:00Z unix=-1078612200 wall=1935-10-27T22:00:00 fold-until(1935-10-27T23:00:00) type=4 -03:30 NST
0041: 1936-05-11T03:30:00Z unix=-1061670600 wall=1936-05-11T00:00:00 gap-until(1936-05-11T01:00:00) type=3 -02:30 NDT dst
0042: 1936-10-05T02:30:00Z unix=-1048973400 wall=1936-10-04T23:00:00 fold-until(1936-10-05T00:00:00) type=4 -03:30 NST
0043: 1937-05-10T03:30:00Z unix=-1030221000 wall=1937-05-10T00:00:00 gap-until(1937-05-10T01:00:00) type=3 -02:30 NDT dst
0044: 1937-10-04T02:30:00Z unix=-1017523800 wall=1937-10-03T23:00:00 fold-until(1937-10-04T00:00:00) type=4 -03:30 NST
0045: 1938-05-09T03:30:00Z unix=-998771400 wall=1938-05-09T00:00:00 gap-until(1938-05-09T01:00:00) type=3 -02:30 NDT dst
0046: 1938-10-03T02:30:00Z unix=-986074200 wall=1938-10-02T23:00:00 fold-until(1938-10-03T00:00:00) type=4 -03:30 NST
0047: 1939-05-15T03:30:00Z unix=-966717000 wall=1939-05-15T00:00:00 gap-until(1939-05-15T01:00:00) type=3 -02:30 NDT dst
0048: 1939-10-02T02:30:00Z unix=-954624600 wall=1939-10-01T23:00:00 fold-until(1939-10-02T00:00:00) type=4 -03:30 NST
0049: 1940-05-13T03:30:00Z unix=-935267400 wall=1940-05-13T00:00:00 gap-until(1940-05-13T01:00:00) type=3 -02:30 NDT dst
0050: 1940-10-07T02:30:00Z unix=-922570200 wall=1940-10-06T23:00:00 fold-until(1940-10-07T00:00:00) type=4 -03:30 NST
0051: 1941-05-12T03:30:00Z unix=-903817800 wall=1941-05-12T00:00:00 gap-until(1941-05-12T01:00:00) type=3 -02:30 NDT dst
0052: 1941-10-06T02:30:00Z unix=-891120600 wall=1941-10-05T23:00:00 fold-until(1941-10-06T00:00:00) type=4 -03:30 NST
0053: 1942-05-11T03:30:00Z unix=-872368200 wall=1942-05-11T00:00:00 gap-until(1942-05-11T01:00:00) type=6 -02:30 NWT dst
0054: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T20:30:00 unambiguous type=5 -02:30 NPT dst
0055: 1945-09-30T04:30:00Z unix=-765401400 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=4 -03:30 NST
0056: 1946-05-12T05:30:00Z unix=-746044200 wall=1946-05-12T02:00:00 gap-until(1946-05-12T03:00:00) type=3 -02:30 NDT dst
0057: 1946-10-06T04:30:00Z unix=-733347000 wall=1946-10-06T01:00:00 fold-until(1946-10-06T02:00:00) type=4 -03:30 NST
0058: 1947-05-11T05:30:00Z unix=-714594600 wall=1947-05-11T02:00:00 gap-until(1947-05-11T03:00:00) type=3 -02:30 NDT dst
0059: 1947-10-05T04:30:00Z unix=-701897400 wall=1947-10-05T01:00:00 fold-until(1947-10-05T02:00:00) type=4 -03:30 NST
0060: 1948-05-09T05:30:00Z unix=-683145000 wall=1948-05-09T02:00:00 gap-until(1948-05-09T03:00:00) type=3 -02:30 NDT dst
0061: 1948-10-03T04:30:00Z unix=-670447800 wall=1948-10-03T01:00:00 fold-until(1948-10-03T02:00:00) type=4 -03:30 NST
0062: 1949-05-08T05:30:00Z unix=-651695400 wall=1949-05-08T02:00:00 gap-until(1949-05-08T03:00:00) type=3 -02:30 NDT dst
0063: 1949-10-02T04:30:00Z unix=-638998200 wall=1949-10-02T01:00:00 fold-until(1949-10-02T02:00:00) type=4 -03:30 NST
0064: 1950-05-14T05:30:00Z unix=-619641000 wall=1950-05-14T02:00:00 gap-until(1950-05-14T03:00:00) type=3 -02:30 NDT dst
0065: 1950-10-08T04:30:00Z unix=-606943800 wall=1950-10-08T01:00:00 fold-until(1950-10-08T02:00:00) type=4 -03:30 NST
0066: 1951-04-29T05:30:00Z unix=-589401000 wall=1951-04-29T02:00:00 gap-until(1951-04-29T03:00:00) type=3 -02:30 NDT dst
0067: 1951-09-30T04:30:00Z unix=-576099000 wall=1951-09-30T01:00:00 fold-until(1951-09-30T02:00:00) type=4 -03:30 NST
0068: 1952-04-27T05:30:00Z unix=-557951400 wall=1952-04-27T02:00:00 gap-until(1952-04-27T03:00:00) type=3 -02:30 NDT dst
0069: 1952-09-28T04:30:00Z unix=-544649400 wall=1952-09-28T01:00:00 fold-until(1952-09-28T02:00:00) type=4 -03:30 NST
0070: 1953-04-26T05:30:00Z unix=-526501800 wall=1953-04-26T02:00:00 gap-until(1953-04-26T03:00:00) type=3 -02:30 NDT dst
0071: 1953-09-27T04:30:00Z unix=-513199800 wall=1953-09-27T01:00:00 fold-until(1953-09-27T02:00:00) type=4 -03:30 NST
0072: 1954-04-25T05:30:00Z unix=-495052200 wall=1954-04-25T02:00:00 gap-until(1954-04-25T03:00:00) type=3 -02:30 NDT dst
0073: 1954-09-26T04:30:00Z unix=-481750200 wall=1954-09-26T01:00:00 fold-until(1954-09-26T02:00:00) type=4 -03:30 NST
0074: 1955-04-24T05:30:00Z unix=-463602600 wall=1955-04-24T02:00:00 gap-until(1955-04-24T03:00:00) type=3 -02:30 NDT dst
0075: 1955-09-25T04:30:00Z unix=-450300600 wall=1955-09-25T01:00:00 fold-until(1955-09-25T02:00:00) type=4 -03:30 NST
0076: 1956-04-29T05:30:00Z unix=-431548200 wall=1956-04-29T02:00:00 gap-until(1956-04-29T03:00:00) type=3 -02:30 NDT dst
0077: 1956-09-30T04:30:00Z unix=-418246200 wall=1956-09-30T01:00:00 fold-until(1956-09-30T02:00:00) type=4 -03:30 NST
0078: 1957-04-28T05:30:00Z unix=-400098600 wall=1957-04-28T02:00:00 gap-until(1957-04-28T03:00:00) type=3 -02:30 NDT dst
0079: 1957-09-29T04:30:00Z unix=-386796600 wall=1957-09-29T01:00:00 fold-until(1957-09-29T02:00:00) type=4 -03:30 NST
0080: 1958-04-27T05:30:00Z unix=-368649000 wall=1958-04-27T02:00:00 gap-until(1958-04-27T03:00:00) type=3 -02:30 NDT dst
0081: 1958-09-28T04:30:00Z unix=-355347000 wall=1958-09-28T01:00:00 fold-until(1958-09-28T02:00:00) type=4 -03:30 NST
0082: 1959-04-26T05:30:00Z unix=-337199400 wall=1959-04-26T02:00:00 gap-until(1959-04-26T03:00:00) type=3 -02:30 NDT dst
0083: 1959-09-27T04:30:00Z unix=-323897400 wall=1959-09-27T01:00:00 fold-until(1959-09-27T02:00:00) type=4 -03:30 NST
0084: 1960-04-24T05:30:00Z unix=-305749800 wall=1960-04-24T02:00:00 gap-until(1960-04-24T03:00:00) type=3 -02:30 NDT dst
0085: 1960-10-30T04:30:00Z unix=-289423800 wall=1960-10-30T01:00:00 fold-until(1960-10-30T02:00:00) type=4 -03:30 NST
0086: 1961-04-30T05:30:00Z unix=-273695400 wall=1961-04-30T02:00:00 gap-until(1961-04-30T03:00:00) type=3 -02:30 NDT dst
0087: 1961-10-29T04:30:00Z unix=-257974200 wall=1961-10-29T01:00:00 fold-until(1961-10-29T02:00:00) type=4 -03:30 NST
0088: 1962-04-29T05:30:00Z unix=-242245800 wall=1962-04-29T02:00:00 gap-until(1962-04-29T03:00:00) type=3 -02:30 NDT dst
0089: 1962-10-28T04:30:00Z unix=-226524600 wall=1962-10-28T01:00:00 fold-until(1962-10-28T02:00:00) type=4 -03:30 NST
0090: 1963-04-28T05:30:00Z unix=-210796200 wall=1963-04-28T02:00:00 gap-until(1963-04-28T03:00:00) type=3 -02:30 NDT dst
0091: 1963-10-27T04:30:00Z unix=-195075000 wall=1963-10-27T01:00:00 fold-until(1963-10-27T02:00:00) type=4 -03:30 NST
0092: 1964-04-26T05:30:00Z unix=-179346600 wall=1964-04-26T02:00:00 gap-until(1964-04-26T03:00:00) type=3 -02:30 NDT dst
0093: 1964-10-25T04:30:00Z unix=-163625400 wall=1964-10-25T01:00:00 fold-until(1964-10-25T02:00:00) type=4 -03:30 NST
0094: 1965-04-25T05:30:00Z unix=-147897000 wall=1965-04-25T02:00:00 gap-until(1965-04-25T03:00:00) type=3 -02:30 NDT dst
0095: 1965-10-31T04:30:00Z unix=-131571000 wall=1965-10-31T01:00:00 fold-until(1965-10-31T02:00:00) type=4 -03:30 NST
0096: 1966-04-24T05:30:00Z unix=-116447400 wall=1966-04-24T02:00:00 gap-until(1966-04-24T03:00:00) type=3 -02:30 NDT dst
0097: 1966-10-30T04:30:00Z unix=-100121400 wall=1966-10-30T01:00:00 fold-until(1966-10-30T02:00:00) type=4 -03:30 NST
0098: 1967-04-30T05:30:00Z unix=-84393000 wall=1967-04-30T02:00:00 gap-until(1967-04-30T03:00:00) type=3 -02:30 NDT dst
0099: 1967-10-29T04:30:00Z unix=-68671800 wall=1967-10-29T01:00:00 fold-until(1967-10-29T02:00:00) type=4 -03:30 NST
0100: 1968-04-28T05:30:00Z unix=-52943400 wall=1968-04-28T02:00:00 gap-until(1968-04-28T03:00:00) type=3 -02:30 NDT dst
0101: 1968-10-27T04:30:00Z unix=-37222200 wall=1968-10-27T01:00:00 fold-until(1968-10-27T02:00:00) type=4 -03:30 NST
0102: 1969-04-27T05:30:00Z unix=-21493800 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=3 -02:30 NDT dst
0103: 1969-10-26T04:30:00Z unix=-5772600 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=4 -03:30 NST
0104: 1970-04-26T05:30:00Z unix=9955800 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=3 -02:30 NDT dst
0105: 1970-10-25T04:30:00Z unix=25677000 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=4 -03:30 NST
0106: 1971-04-25T05:30:00Z unix=41405400 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=3 -02:30 NDT dst
0107: 1971-10-31T04:30:00Z unix=57731400 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=4 -03:30 NST
0108: 1972-04-30T05:30:00Z unix=73459800 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=3 -02:30 NDT dst
0109: 1972-10-29T04:30:00Z unix=89181000 wall=1972-10-29T01:00:00 fold-until(1972-10-29T02:00:00) type=4 -03:30 NST
0110: 1973-04-29T05:30:00Z unix=104909400 wall=1973-04-29T02:00:00 gap-until(1973-04-29T03:00:00) type=3 -02:30 NDT dst
0111: 1973-10-28T04:30:00Z unix=120630600 wall=1973-10-28T01:00:00 fold-until(1973-10-28T02:00:00) type=4 -03:30 NST
0112: 1974-04-28T05:30:00Z unix=136359000 wall=1974-04-28T02:00:00 gap-until(1974-04-28T03:00:00) type=3 -02:30 NDT dst
0113: 1974-10-27T04:30:00Z unix=152080200 wall=1974-10-27T01:00:00 fold-until(1974-10-27T02:00:00) type=4 -03:30 NST
0114: 1975-04-27T05:30:00Z unix=167808600 wall=1975-04-27T02:00:00 gap-until(1975-04-27T03:00:00) type=3 -02:30 NDT dst
0115: 1975-10-26T04:30:00Z unix=183529800 wall=1975-10-26T01:00:00 fold-until(1975-10-26T02:00:00) type=4 -03:30 NST
0116: 1976-04-25T05:30:00Z unix=199258200 wall=1976-04-25T02:00:00 gap-until(1976-04-25T03:00:00) type=3 -02:30 NDT dst
0117: 1976-10-31T04:30:00Z unix=215584200 wall=1976-10-31T01:00:00 fold-until(1976-10-31T02:00:00) type=4 -03:30 NST
0118: 1977-04-24T05:30:00Z unix=230707800 wall=1977-04-24T02:00:00 gap-until(1977-04-24T03:00:00) type=3 -02:30 NDT dst
0119: 1977-10-30T04:30:00Z unix=247033800 wall=1977-10-30T01:00:00 fold-until(1977-10-30T02:00:00) type=4 -03:30 NST
0120: 1978-04-30T05:30:00Z unix=262762200 wall=1978-04-30T02:00:00 gap-until(1978-04-30T03:00:00) type=3 -02:30 NDT dst
0121: 1978-10-29T04:30:00Z unix=278483400 wall=1978-10-29T01:00:00 fold-until(1978-10-29T02:00:00) type=4 -03:30 NST
0122: 1979-04-29T05:30:00Z unix=294211800 wall=1979-04-29T02:00:00 gap-until(1979-04-29T03:00:00) type=3 -02:30 NDT dst
0123: 1979-10-28T04:30:00Z unix=309933000 wall=1979-10-28T01:00:00 fold-until(1979-10-28T02:00:00) type=4 -03:30 NST
0124: 1980-04-27T05:30:00Z unix=325661400 wall=1980-04-27T02:00:00 gap-until(1980-04-27T03:00:00) type=3 -02:30 NDT dst
0125: 1980-10-26T04:30:00Z unix=341382600 wall=1980-10-26T01:00:00 fold-until(1980-10-26T02:00:00) type=4 -03:30 NST
0126: 1981-04-26T05:30:00Z unix=357111000 wall=1981-04-26T02:00:00 gap-until(1981-04-26T03:00:00) type=3 -02:30 NDT dst
0127: 1981-10-25T04:30:00Z unix=372832200 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=4 -03:30 NST
0128: 1982-04-25T05:30:00Z unix=388560600 wall=1982-04-25T02:00:00 gap-until(1982-04-25T03:00:00) type=3 -02:30 NDT dst
0129: 1982-10-31T04:30:00Z unix=404886600 wall=1982-10-31T01:00:00 fold-until(1982-10-31T02:00:00) type=4 -03:30 NST
0130: 1983-04-24T05:30:00Z unix=420010200 wall=1983-04-24T02:00:00 gap-until(1983-04-24T03:00:00) type=3 -02:30 NDT dst
0131: 1983-10-30T04:30:00Z unix=436336200 wall=1983-10-30T01:00:00 fold-until(1983-10-30T02:00:00) type=4 -03:30 NST
0132: 1984-04-29T05:30:00Z unix=452064600 wall=1984-04-29T02:00:00 gap-until(1984-04-29T03:00:00) type=3 -02:30 NDT dst
0133: 1984-10-28T04:30:00Z unix=467785800 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=4 -03:30 NST
0134: 1985-04-28T05:30:00Z unix=483514200 wall=1985-04-28T02:00:00 gap-until(1985-04-28T03:00:00) type=3 -02:30 NDT dst
0135: 1985-10-27T04:30:00Z unix=499235400 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=4 -03:30 NST
0136: 1986-04-27T05:30:00Z unix=514963800 wall=1986-04-27T02:00:00 gap-until(1986-04-27T03:00:00) type=3 -02:30 NDT dst
0137: 1986-10-26T04:30:00Z unix=530685000 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=4 -03:30 NST
0138: 1987-04-05T03:31:00Z unix=544591860 wall=1987-04-05T00:01:00 gap-until(1987-04-05T01:01:00) type=3 -02:30 NDT dst
0139: 1987-10-25T02:31:00Z unix=562127460 wall=1987-10-24T23:01:00 fold-until(1987-10-25T00:01:00) type=4 -03:30 NST
0140: 1988-04-03T03:31:00Z unix=576041460 wall=1988-04-03T00:01:00 gap-until(1988-04-03T02:01:00) type=7 -01:30 NDDT dst
0141: 1988-10-30T01:31:00Z unix=594178260 wall=1988-10-29T22:01:00 fold-until(1988-10-30T00:01:00) type=4 -03:30 NST
0142: 1989-04-02T03:31:00Z unix=607491060 wall=1989-04-02T00:01:00 gap-until(1989-04-02T01:01:00) type=3 -02:30 NDT dst
0143: 1989-10-29T02:31:00Z unix=625631460 wall=1989-10-28T23:01:00 fold-until(1989-10-29T00:01:00) type=4 -03:30 NST
0144: 1990-04-01T03:31:00Z unix=638940660 wall=1990-04-01T00:01:00 gap-until(1990-04-01T01:01:00) type=3 -02:30 NDT dst
0145: 1990-10-28T02:31:00Z unix=657081060 wall=1990-10-27T23:01:00 fold-until(1990-10-28T00:01:00) type=4 -03:30 NST
0146: 1991-04-07T03:31:00Z unix=670995060 wall=1991-04-07T00:01:00 gap-until(1991-04-07T01:01:00) type=3 -02:30 NDT dst
0147: 1991-10-27T02:31:00Z unix=688530660 wall=1991-10-26T23:01:00 fold-until(1991-10-27T00:01:00) type=4 -03:30 NST
0148: 1992-04-05T03:31:00Z unix=702444660 wall=1992-04-05T00:01:00 gap-until(1992-04-05T01:01:00) type=3 -02:30 NDT dst
0149: 1992-10-25T02:31:00Z unix=719980260 wall=1992-10-24T23:01:00 fold-until(1992-10-25T00:01:00) type=4 -03:30 NST
0150: 1993-04-04T03:31:00Z unix=733894260 wall=1993-04-04T00:01:00 gap-until(1993-04-04T01:01:00) type=3 -02:30 NDT dst
0151: 1993-10-31T02:31:00Z unix=752034660 wall=1993-10-30T23:01:00 fold-until(1993-10-31T00:01:00) type=4 -03:30 NST
0152: 1994-04-03T03:31:00Z unix=765343860 wall=1994-04-03T00:01:00 gap-until(1994-04-03T01:01:00) type=3 -02:30 NDT dst
0153: 1994-10-30T02:31:00Z unix=783484260 wall=1994-10-29T23:01:00 fold-until(1994-10-30T00:01:00) type=4 -03:30 NST
0154: 1995-04-02T03:31:00Z unix=796793460 wall=1995-04-02T00:01:00 gap-until(1995-04-02T01:01:00) type=3 -02:30 NDT dst
0155: 1995-10-29T02:31:00Z unix=814933860 wall=1995-10-28T23:01:00 fold-until(1995-10-29T00:01:00) type=4 -03:30 NST
0156: 1996-04-07T03:31:00Z unix=828847860 wall=1996-04-07T00:01:00 gap-until(1996-04-07T01:01:00) type=3 -02:30 NDT dst
0157: 1996-10-27T02:31:00Z unix=846383460 wall=1996-10-26T23:01:00 fold-until(1996-10-27T00:01:00) type=4 -03:30 NST
0158: 1997-04-06T03:31:00Z unix=860297460 wall=1997-04-06T00:01:00 gap-until(1997-04-06T01:01:00) type=3 -02:30 NDT dst
0159: 1997-10-26T02:31:00Z unix=877833060 wall=1997-10-25T23:01:00 fold-until(1997-10-26T00:01:00) type=4 -03:30 NST
0160: 1998-04-05T03:31:00Z unix=891747060 wall=1998-04-05T00:01:00 gap-until(1998-04-05T01:01:00) type=3 -02:30 NDT dst
0161: 1998-10-25T02:31:00Z unix=909282660 wall=1998-10-24T23:01:00 fold-until(1998-10-25T00:01:00) type=4 -03:30 NST
0162: 1999-04-04T03:31:00Z unix=923196660 wall=1999-04-04T00:01:00 gap-until(1999-04-04T01:01:00) type=3 -02:30 NDT dst
0163: 1999-10-31T02:31:00Z unix=941337060 wall=1999-10-30T23:01:00 fold-until(1999-10-31T00:01:00) type=4 -03:30 NST
0164: 2000-04-02T03:31:00Z unix=954646260 wall=2000-04-02T00:01:00 gap-until(2000-04-02T01:01:00) type=3 -02:30 NDT dst
0165: 2000-10-29T02:31:00Z unix=972786660 wall=2000-10-28T23:01:00 fold-until(2000-10-29T00:01:00) type=4 -03:30 NST
0166: 2001-04-01T03:31:00Z unix=986095860 wall=2001-04-01T00:01:00 gap-until(2001-04-01T01:01:00) type=3 -02:30 NDT dst
0167: 2001-10-28T02:31:00Z unix=1004236260 wall=2001-10-27T23:01:00 fold-until(2001-10-28T00:01:00) type=4 -03:30 NST
0168: 2002-04-07T03:31:00Z unix=1018150260 wall=2002-04-07T00:01:00 gap-until(2002-04-07T01:01:00) type=3 -02:30 NDT dst
0169: 2002-10-27T02:31:00Z unix=1035685860 wall=2002-10-26T23:01:00 fold-until(2002-10-27T00:01:00) type=4 -03:30 NST
0170: 2003-04-06T03:31:00Z unix=1049599860 wall=2003-04-06T00:01:00 gap-until(2003-04-06T01:01:00) type=3 -02:30 NDT dst
0171: 2003-10-26T02:31:00Z unix=1067135460 wall=2003-10-25T23:01:00 fold-until(2003-10-26T00:01:00) type=4 -03:30 NST
0172: 2004-04-04T03:31:00Z unix=1081049460 wall=2004-04-04T00:01:00 gap-until(2004-04-04T01:01:00) type=3 -02:30 NDT dst
0173: 2004-10-31T02:31:00Z unix=1099189860 wall=2004-10-30T23:01:00 fold-until(2004-10-31T00:01:00) type=4 -03:30 NST
0174: 2005-04-03T03:31:00Z unix=1112499060 wall=2005-04-03T00:01:00 gap-until(2005-04-03T01:01:00) type=3 -02:30 NDT dst
0175: 2005-10-30T02:31:00Z unix=1130639460 wall=2005-10-29T23:01:00 fold-until(2005-10-30T00:01:00) type=4 -03:30 NST
0176: 2006-04-02T03:31:00Z unix=1143948660 wall=2006-04-02T00:01:00 gap-until(2006-04-02T01:01:00) type=3 -02:30 NDT dst
0177: 2006-10-29T02:31:00Z unix=1162089060 wall=2006-10-28T23:01:00 fold-until(2006-10-29T00:01:00) type=4 -03:30 NST
0178: 2007-03-11T03:31:00Z unix=1173583860 wall=2007-03-11T00:01:00 gap-until(2007-03-11T01:01:00) type=3 -02:30 NDT dst
0179: 2007-11-04T02:31:00Z unix=1194143460 wall=2007-11-03T23:01:00 fold-until(2007-11-04T00:01:00) type=4 -03:30 NST
0180: 2008-03-09T03:31:00Z unix=1205033460 wall=2008-03-09T00:01:00 gap-until(2008-03-09T01:01:00) type=3 -02:30 NDT dst
0181: 2008-11-02T02:31:00Z unix=1225593060 wall=2008-11-01T23:01:00 fold-until(2008-11-02T00:01:00) type=4 -03:30 NST
0182: 2009-03-08T03:31:00Z unix=1236483060 wall=2009-03-08T00:01:00 gap-until(2009-03-08T01:01:00) type=3 -02:30 NDT dst
0183: 2009-11-01T02:31:00Z unix=1257042660 wall=2009-10-31T23:01:00 fold-until(2009-11-01T00:01:00) type=4 -03:30 NST
0184: 2010-03-14T03:31:00Z unix=1268537460 wall=2010-03-14T00:01:00 gap-until(2010-03-14T01:01:00) type=3 -02:30 NDT dst
0185: 2010-11-07T02:31:00Z unix=1289097060 wall=2010-11-06T23:01:00 fold-until(2010-11-07T00:01:00) type=4 -03:30 NST
0186: 2011-03-13T03:31:00Z unix=1299987060 wall=2011-03-13T00:01:00 gap-until(2011-03-13T01:01:00) type=3 -02:30 NDT dst
0187: 2011-11-06T04:30:00Z unix=1320553800 wall=2011-11-06T01:00:00 fold-until(2011-11-06T02:00:00) type=4 -03:30 NST
0188: 2012-03-11T05:30:00Z unix=1331443800 wall=2012-03-11T02:00:00 gap-until(2012-03-11T03:00:00) type=3 -02:30 NDT dst
0189: 2012-11-04T04:30:00Z unix=1352003400 wall=2012-11-04T01:00:00 fold-until(2012-11-04T02:00:00) type=4 -03:30 NST
0190: 2013-03-10T05:30:00Z unix=1362893400 wall=2013-03-10T02:00:00 gap-until(2013-03-10T03:00:00) type=3 -02:30 NDT dst
0191: 2013-11-03T04:30:00Z unix=1383453000 wall=2013-11-03T01:00:00 fold-until(2013-11-03T02:00:00) type=4 -03:30 NST
0192: 2014-03-09T05:30:00Z unix=1394343000 wall=2014-03-09T02:00:00 gap-until(2014-03-09T03:00:00) type=3 -02:30 NDT dst
0193: 2014-11-02T04:30:00Z unix=1414902600 wall=2014-11-02T01:00:00 fold-until(2014-11-02T02:00:00) type=4 -03:30 NST
0194: 2015-03-08T05:30:00Z unix=1425792600 wall=2015-03-08T02:00:00 gap-until(2015-03-08T03:00:00) type=3 -02:30 NDT dst
0195: 2015-11-01T04:30:00Z unix=1446352200 wall=2015-11-01T01:00:00 fold-until(2015-11-01T02:00:00) type=4 -03:30 NST
0196: 2016-03-13T05:30:00Z unix=1457847000 wall=2016-03-13T02:00:00 gap-until(2016-03-13T03:00:00) type=3 -02:30 NDT dst
0197: 2016-11-06T04:30:00Z unix=1478406600 wall=2016-11-06T01:00:00 fold-until(2016-11-06T02:00:00) type=4 -03:30 NST
0198: 2017-03-12T05:30:00Z unix=1489296600 wall=2017-03-12T02:00:00 gap-until(2017-03-12T03:00:00) type=3 -02:30 NDT dst
0199: 2017-11-05T04:30:00Z unix=1509856200 wall=2017-11-05T01:00:00 fold-until(2017-11-05T02:00:00) type=4 -03:30 NST
0200: 2018-03-11T05:30:00Z unix=1520746200 wall=2018-03-11T02:00:00 gap-until(2018-03-11T03:00:00) type=3 -02:30 NDT dst
0201: 2018-11-04T04:30:00Z unix=1541305800 wall=2018-11-04T01:00:00 fold-until(2018-11-04T02:00:00) type=4 -03:30 NST
0202: 2019-03-10T05:30:00Z unix=1552195800 wall=2019-03-10T02:00:00 gap-until(2019-03-10T03:00:00) type=3 -02:30 NDT dst
0203: 2019-11-03T04:30:00Z unix=1572755400 wall=2019-11-03T01:00:00 fold-until(2019-11-03T02:00:00) type=4 -03:30 NST
0204: 2020-03-08T05:30:00Z unix=1583645400 wall=2020-03-08T02:00:00 gap-until(2020-03-08T03:00:00) type=3 -02:30 NDT dst
0205: 2020-11-01T04:30:00Z unix=1604205000 wall=2020-11-01T01:00:00 fold-until(2020-11-01T02:00:00) type=4 -03:30 NST
0206: 2021-03-14T05:30:00Z unix=1615699800 wall=2021-03-14T02:00:00 gap-until(2021-03-14T03:00:00) type=3 -02:30 NDT dst
0207: 2021-11-07T04:30:00Z unix=1636259400 wall=2021-11-07T01:00:00 fold-until(2021-11-07T02:00:00) type=4 -03:30 NST
0208: 2022-03-13T05:30:00Z unix=1647149400 wall=2022-03-13T02:00:00 gap-until(2022-03-13T03:00:00) type=3 -02:30 NDT dst
0209: 2022-11-06T04:30:00Z unix=1667709000 wall=2022-11-06T01:00:00 fold-until(2022-11-06T02:00:00) type=4 -03:30 NST
0210: 2023-03-12T05:30:00Z unix=1678599000 wall=2023-03-12T02:00:00 gap-until(2023-03-12T03:00:00) type=3 -02:30 NDT dst
0211: 2023-11-05T04:30:00Z unix=1699158600 wall=2023-11-05T01:00:00 fold-until(2023-11-05T02:00:00) type=4 -03:30 NST
0212: 2024-03-10T05:30:00Z unix=1710048600 wall=2024-03-10T02:00:00 gap-until(2024-03-10T03:00:00) type=3 -02:30 NDT dst
0213: 2024-11-03T04:30:00Z unix=1730608200 wall=2024-11-03T01:00:00 fold-until(2024-11-03T02:00:00) type=4 -03:30 NST
0214: 2025-03-09T05:30:00Z unix=1741498200 wall=2025-03-09T02:00:00 gap-until(2025-03-09T03:00:00) type=3 -02:30 NDT dst
0215: 2025-11-02T04:30:00Z unix=1762057800 wall=2025-11-02T01:00:00 fold-until(2025-11-02T02:00:00) type=4 -03:30 NST
0216: 2026-03-08T05:30:00Z unix=1772947800 wall=2026-03-08T02:00:00 gap-until(2026-03-08T03:00:00) type=3 -02:30 NDT dst
0217: 2026-11-01T04:30:00Z unix=1793507400 wall=2026-11-01T01:00:00 fold-until(2026-11-01T02:00:00) type=4 -03:30 NST
0218: 2027-03-14T05:30:00Z unix=1805002200 wall=2027-03-14T02:00:00 gap-until(2027-03-14T03:00:00) type=3 -02:30 NDT dst
0219: 2027-11-07T04:30:00Z unix=1825561800 wall=2027-11-07T01:00:00 fold-until(2027-11-07T02:00:00) type=4 -03:30 NST
0220: 2028-03-12T05:30:00Z unix=1836451800 wall=2028-03-12T02:00:00 gap-until(2028-03-12T03:00:00) type=3 -02:30 NDT dst
0221: 2028-11-05T04:30:00Z unix=1857011400 wall=2028-11-05T01:00:00 fold-until(2028-11-05T02:00:00) type=4 -03:30 NST
0222: 2029-03-11T05:30:00Z unix=1867901400 wall=2029-03-11T02:00:00 gap-until(2029-03-11T03:00:00) type=3 -02:30 NDT dst
0223: 2029-11-04T04:30:00Z unix=1888461000 wall=2029-11-04T01:00:00 fold-until(2029-11-04T02:00:00) type=4 -03:30 NST
0224: 2030-03-10T05:30:00Z unix=1899351000 wall=2030-03-10T02:00:00 gap-until(2030-03-10T03:00:00) type=3 -02:30 NDT dst
0225: 2030-11-03T04:30:00Z unix=1919910600 wall=2030-11-03T01:00:00 fold-until(2030-11-03T02:00:00) type=4 -03:30 NST
0226: 2031-03-09T05:30:00Z unix=1930800600 wall=2031-03-09T02:00:00 gap-until(2031-03-09T03:00:00) type=3 -02:30 NDT dst
0227: 2031-11-02T04:30:00Z unix=1951360200 wall=2031-11-02T01:00:00 fold-until(2031-11-02T02:00:00) type=4 -03:30 NST
0228: 2032-03-14T05:30:00Z unix=1962855000 wall=2032-03-14T02:00:00 gap-until(2032-03-14T03:00:00) type=3 -02:30 NDT dst
0229: 2032-11-07T04:30:00Z unix=1983414600 wall=2032-11-07T01:00:00 fold-until(2032-11-07T02:00:00) type=4 -03:30 NST
0230: 2033-03-13T05:30:00Z unix=1994304600 wall=2033-03-13T02:00:00 gap-until(2033-03-13T03:00:00) type=3 -02:30 NDT dst
0231: 2033-11-06T04:30:00Z unix=2014864200 wall=2033-11-06T01:00:00 fold-until(2033-11-06T02:00:00) type=4 -03:30 NST
0232: 2034-03-12T05:30:00Z unix=2025754200 wall=2034-03-12T02:00:00 gap-until(2034-03-12T03:00:00) type=3 -02:30 NDT dst
0233: 2034-11-05T04:30:00Z unix=2046313800 wall=2034-11-05T01:00:00 fold-until(2034-11-05T02:00:00) type=4 -03:30 NST
0234: 2035-03-11T05:30:00Z unix=2057203800 wall=2035-03-11T02:00:00 gap-until(2035-03-11T03:00:00) type=3 -02:30 NDT dst
0235: 2035-11-04T04:30:00Z unix=2077763400 wall=2035-11-04T01:00:00 fold-until(2035-11-04T02:00:00) type=4 -03:30 NST
0236: 2036-03-09T05:30:00Z unix=2088653400 wall=2036-03-09T02:00:00 gap-until(2036-03-09T03:00:00) type=3 -02:30 NDT dst
0237: 2036-11-02T04:30:00Z unix=2109213000 wall=2036-11-02T01:00:00 fold-until(2036-11-02T02:00:00) type=4 -03:30 NST
0238: 2037-03-08T05:30:00Z unix=2120103000 wall=2037-03-08T02:00:00 gap-until(2037-03-08T03:00:00) type=3 -02:30 NDT dst
0239: 2037-11-01T04:30:00Z unix=2140662600 wall=2037-11-01T01:00:00 fold-until(2037-11-01T02:00:00) type=4 -03:30 NST

View file

@ -0,0 +1,259 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
America/St_Johns
LOCAL TIME TYPES
000: offset=-03:30:52 designation=LMT indicator=local/wall
001: offset=-02:30:52 designation=NDT dst indicator=local/wall
002: offset=-03:30:52 designation=NST indicator=local/wall
003: offset=-02:30 designation=NDT dst indicator=local/wall
004: offset=-03:30 designation=NST indicator=local/wall
005: offset=-02:30 designation=NPT dst indicator=ut/std
006: offset=-02:30 designation=NWT dst indicator=local/wall
007: offset=-01:30 designation=NDDT dst indicator=local/wall
008: offset=-02:30 designation=NDT dst indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T22:29:07 unambiguous type=0 -03:30:52 LMT
0001: 1884-01-01T03:30:52Z unix=-2713897748 wall=1884-01-01T00:00:00 unambiguous type=2 -03:30:52 NST
0002: 1917-04-08T05:30:52Z unix=-1664130548 wall=1917-04-08T02:00:00 gap-until(1917-04-08T03:00:00) type=1 -02:30:52 NDT dst
0003: 1917-09-17T04:30:52Z unix=-1650137348 wall=1917-09-17T01:00:00 fold-until(1917-09-17T02:00:00) type=2 -03:30:52 NST
0004: 1918-04-14T05:30:52Z unix=-1632076148 wall=1918-04-14T02:00:00 gap-until(1918-04-14T03:00:00) type=1 -02:30:52 NDT dst
0005: 1918-10-27T04:30:52Z unix=-1615145348 wall=1918-10-27T01:00:00 fold-until(1918-10-27T02:00:00) type=2 -03:30:52 NST
0006: 1919-05-06T02:30:52Z unix=-1598650148 wall=1919-05-05T23:00:00 gap-until(1919-05-06T00:00:00) type=1 -02:30:52 NDT dst
0007: 1919-08-13T01:30:52Z unix=-1590100148 wall=1919-08-12T22:00:00 fold-until(1919-08-12T23:00:00) type=2 -03:30:52 NST
0008: 1920-05-03T02:30:52Z unix=-1567286948 wall=1920-05-02T23:00:00 gap-until(1920-05-03T00:00:00) type=1 -02:30:52 NDT dst
0009: 1920-11-01T01:30:52Z unix=-1551565748 wall=1920-10-31T22:00:00 fold-until(1920-10-31T23:00:00) type=2 -03:30:52 NST
0010: 1921-05-02T02:30:52Z unix=-1535837348 wall=1921-05-01T23:00:00 gap-until(1921-05-02T00:00:00) type=1 -02:30:52 NDT dst
0011: 1921-10-31T01:30:52Z unix=-1520116148 wall=1921-10-30T22:00:00 fold-until(1921-10-30T23:00:00) type=2 -03:30:52 NST
0012: 1922-05-08T02:30:52Z unix=-1503782948 wall=1922-05-07T23:00:00 gap-until(1922-05-08T00:00:00) type=1 -02:30:52 NDT dst
0013: 1922-10-30T01:30:52Z unix=-1488666548 wall=1922-10-29T22:00:00 fold-until(1922-10-29T23:00:00) type=2 -03:30:52 NST
0014: 1923-05-07T02:30:52Z unix=-1472333348 wall=1923-05-06T23:00:00 gap-until(1923-05-07T00:00:00) type=1 -02:30:52 NDT dst
0015: 1923-10-29T01:30:52Z unix=-1457216948 wall=1923-10-28T22:00:00 fold-until(1923-10-28T23:00:00) type=2 -03:30:52 NST
0016: 1924-05-05T02:30:52Z unix=-1440883748 wall=1924-05-04T23:00:00 gap-until(1924-05-05T00:00:00) type=1 -02:30:52 NDT dst
0017: 1924-10-27T01:30:52Z unix=-1425767348 wall=1924-10-26T22:00:00 fold-until(1924-10-26T23:00:00) type=2 -03:30:52 NST
0018: 1925-05-04T02:30:52Z unix=-1409434148 wall=1925-05-03T23:00:00 gap-until(1925-05-04T00:00:00) type=1 -02:30:52 NDT dst
0019: 1925-10-26T01:30:52Z unix=-1394317748 wall=1925-10-25T22:00:00 fold-until(1925-10-25T23:00:00) type=2 -03:30:52 NST
0020: 1926-05-03T02:30:52Z unix=-1377984548 wall=1926-05-02T23:00:00 gap-until(1926-05-03T00:00:00) type=1 -02:30:52 NDT dst
0021: 1926-11-01T01:30:52Z unix=-1362263348 wall=1926-10-31T22:00:00 fold-until(1926-10-31T23:00:00) type=2 -03:30:52 NST
0022: 1927-05-02T02:30:52Z unix=-1346534948 wall=1927-05-01T23:00:00 gap-until(1927-05-02T00:00:00) type=1 -02:30:52 NDT dst
0023: 1927-10-31T01:30:52Z unix=-1330813748 wall=1927-10-30T22:00:00 fold-until(1927-10-30T23:00:00) type=2 -03:30:52 NST
0024: 1928-05-07T02:30:52Z unix=-1314480548 wall=1928-05-06T23:00:00 gap-until(1928-05-07T00:00:00) type=1 -02:30:52 NDT dst
0025: 1928-10-29T01:30:52Z unix=-1299364148 wall=1928-10-28T22:00:00 fold-until(1928-10-28T23:00:00) type=2 -03:30:52 NST
0026: 1929-05-06T02:30:52Z unix=-1283030948 wall=1929-05-05T23:00:00 gap-until(1929-05-06T00:00:00) type=1 -02:30:52 NDT dst
0027: 1929-10-28T01:30:52Z unix=-1267914548 wall=1929-10-27T22:00:00 fold-until(1929-10-27T23:00:00) type=2 -03:30:52 NST
0028: 1930-05-05T02:30:52Z unix=-1251581348 wall=1930-05-04T23:00:00 gap-until(1930-05-05T00:00:00) type=1 -02:30:52 NDT dst
0029: 1930-10-27T01:30:52Z unix=-1236464948 wall=1930-10-26T22:00:00 fold-until(1930-10-26T23:00:00) type=2 -03:30:52 NST
0030: 1931-05-04T02:30:52Z unix=-1220131748 wall=1931-05-03T23:00:00 gap-until(1931-05-04T00:00:00) type=1 -02:30:52 NDT dst
0031: 1931-10-26T01:30:52Z unix=-1205015348 wall=1931-10-25T22:00:00 fold-until(1931-10-25T23:00:00) type=2 -03:30:52 NST
0032: 1932-05-02T02:30:52Z unix=-1188682148 wall=1932-05-01T23:00:00 gap-until(1932-05-02T00:00:00) type=1 -02:30:52 NDT dst
0033: 1932-10-31T01:30:52Z unix=-1172960948 wall=1932-10-30T22:00:00 fold-until(1932-10-30T23:00:00) type=2 -03:30:52 NST
0034: 1933-05-08T02:30:52Z unix=-1156627748 wall=1933-05-07T23:00:00 gap-until(1933-05-08T00:00:00) type=1 -02:30:52 NDT dst
0035: 1933-10-30T01:30:52Z unix=-1141511348 wall=1933-10-29T22:00:00 fold-until(1933-10-29T23:00:00) type=2 -03:30:52 NST
0036: 1934-05-07T02:30:52Z unix=-1125178148 wall=1934-05-06T23:00:00 gap-until(1934-05-07T00:00:00) type=1 -02:30:52 NDT dst
0037: 1934-10-29T01:30:52Z unix=-1110061748 wall=1934-10-28T22:00:00 fold-until(1934-10-28T23:00:00) type=2 -03:30:52 NST
0038: 1935-03-30T03:30:52Z unix=-1096921748 wall=1935-03-30T00:00:00 gap-until(1935-03-30T00:00:52) type=4 -03:30 NST
0039: 1935-05-06T02:30:00Z unix=-1093728600 wall=1935-05-05T23:00:00 gap-until(1935-05-06T00:00:00) type=3 -02:30 NDT dst
0040: 1935-10-28T01:30:00Z unix=-1078612200 wall=1935-10-27T22:00:00 fold-until(1935-10-27T23:00:00) type=4 -03:30 NST
0041: 1936-05-11T03:30:00Z unix=-1061670600 wall=1936-05-11T00:00:00 gap-until(1936-05-11T01:00:00) type=3 -02:30 NDT dst
0042: 1936-10-05T02:30:00Z unix=-1048973400 wall=1936-10-04T23:00:00 fold-until(1936-10-05T00:00:00) type=4 -03:30 NST
0043: 1937-05-10T03:30:00Z unix=-1030221000 wall=1937-05-10T00:00:00 gap-until(1937-05-10T01:00:00) type=3 -02:30 NDT dst
0044: 1937-10-04T02:30:00Z unix=-1017523800 wall=1937-10-03T23:00:00 fold-until(1937-10-04T00:00:00) type=4 -03:30 NST
0045: 1938-05-09T03:30:00Z unix=-998771400 wall=1938-05-09T00:00:00 gap-until(1938-05-09T01:00:00) type=3 -02:30 NDT dst
0046: 1938-10-03T02:30:00Z unix=-986074200 wall=1938-10-02T23:00:00 fold-until(1938-10-03T00:00:00) type=4 -03:30 NST
0047: 1939-05-15T03:30:00Z unix=-966717000 wall=1939-05-15T00:00:00 gap-until(1939-05-15T01:00:00) type=3 -02:30 NDT dst
0048: 1939-10-02T02:30:00Z unix=-954624600 wall=1939-10-01T23:00:00 fold-until(1939-10-02T00:00:00) type=4 -03:30 NST
0049: 1940-05-13T03:30:00Z unix=-935267400 wall=1940-05-13T00:00:00 gap-until(1940-05-13T01:00:00) type=3 -02:30 NDT dst
0050: 1940-10-07T02:30:00Z unix=-922570200 wall=1940-10-06T23:00:00 fold-until(1940-10-07T00:00:00) type=4 -03:30 NST
0051: 1941-05-12T03:30:00Z unix=-903817800 wall=1941-05-12T00:00:00 gap-until(1941-05-12T01:00:00) type=3 -02:30 NDT dst
0052: 1941-10-06T02:30:00Z unix=-891120600 wall=1941-10-05T23:00:00 fold-until(1941-10-06T00:00:00) type=4 -03:30 NST
0053: 1942-05-11T03:30:00Z unix=-872368200 wall=1942-05-11T00:00:00 gap-until(1942-05-11T01:00:00) type=6 -02:30 NWT dst
0054: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T20:30:00 unambiguous type=5 -02:30 NPT dst
0055: 1945-09-30T04:30:00Z unix=-765401400 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=4 -03:30 NST
0056: 1946-05-12T05:30:00Z unix=-746044200 wall=1946-05-12T02:00:00 gap-until(1946-05-12T03:00:00) type=3 -02:30 NDT dst
0057: 1946-10-06T04:30:00Z unix=-733347000 wall=1946-10-06T01:00:00 fold-until(1946-10-06T02:00:00) type=4 -03:30 NST
0058: 1947-05-11T05:30:00Z unix=-714594600 wall=1947-05-11T02:00:00 gap-until(1947-05-11T03:00:00) type=3 -02:30 NDT dst
0059: 1947-10-05T04:30:00Z unix=-701897400 wall=1947-10-05T01:00:00 fold-until(1947-10-05T02:00:00) type=4 -03:30 NST
0060: 1948-05-09T05:30:00Z unix=-683145000 wall=1948-05-09T02:00:00 gap-until(1948-05-09T03:00:00) type=3 -02:30 NDT dst
0061: 1948-10-03T04:30:00Z unix=-670447800 wall=1948-10-03T01:00:00 fold-until(1948-10-03T02:00:00) type=4 -03:30 NST
0062: 1949-05-08T05:30:00Z unix=-651695400 wall=1949-05-08T02:00:00 gap-until(1949-05-08T03:00:00) type=3 -02:30 NDT dst
0063: 1949-10-02T04:30:00Z unix=-638998200 wall=1949-10-02T01:00:00 fold-until(1949-10-02T02:00:00) type=4 -03:30 NST
0064: 1950-05-14T05:30:00Z unix=-619641000 wall=1950-05-14T02:00:00 gap-until(1950-05-14T03:00:00) type=3 -02:30 NDT dst
0065: 1950-10-08T04:30:00Z unix=-606943800 wall=1950-10-08T01:00:00 fold-until(1950-10-08T02:00:00) type=4 -03:30 NST
0066: 1951-04-29T05:30:00Z unix=-589401000 wall=1951-04-29T02:00:00 gap-until(1951-04-29T03:00:00) type=3 -02:30 NDT dst
0067: 1951-09-30T04:30:00Z unix=-576099000 wall=1951-09-30T01:00:00 fold-until(1951-09-30T02:00:00) type=4 -03:30 NST
0068: 1952-04-27T05:30:00Z unix=-557951400 wall=1952-04-27T02:00:00 gap-until(1952-04-27T03:00:00) type=3 -02:30 NDT dst
0069: 1952-09-28T04:30:00Z unix=-544649400 wall=1952-09-28T01:00:00 fold-until(1952-09-28T02:00:00) type=4 -03:30 NST
0070: 1953-04-26T05:30:00Z unix=-526501800 wall=1953-04-26T02:00:00 gap-until(1953-04-26T03:00:00) type=3 -02:30 NDT dst
0071: 1953-09-27T04:30:00Z unix=-513199800 wall=1953-09-27T01:00:00 fold-until(1953-09-27T02:00:00) type=4 -03:30 NST
0072: 1954-04-25T05:30:00Z unix=-495052200 wall=1954-04-25T02:00:00 gap-until(1954-04-25T03:00:00) type=3 -02:30 NDT dst
0073: 1954-09-26T04:30:00Z unix=-481750200 wall=1954-09-26T01:00:00 fold-until(1954-09-26T02:00:00) type=4 -03:30 NST
0074: 1955-04-24T05:30:00Z unix=-463602600 wall=1955-04-24T02:00:00 gap-until(1955-04-24T03:00:00) type=3 -02:30 NDT dst
0075: 1955-09-25T04:30:00Z unix=-450300600 wall=1955-09-25T01:00:00 fold-until(1955-09-25T02:00:00) type=4 -03:30 NST
0076: 1956-04-29T05:30:00Z unix=-431548200 wall=1956-04-29T02:00:00 gap-until(1956-04-29T03:00:00) type=3 -02:30 NDT dst
0077: 1956-09-30T04:30:00Z unix=-418246200 wall=1956-09-30T01:00:00 fold-until(1956-09-30T02:00:00) type=4 -03:30 NST
0078: 1957-04-28T05:30:00Z unix=-400098600 wall=1957-04-28T02:00:00 gap-until(1957-04-28T03:00:00) type=3 -02:30 NDT dst
0079: 1957-09-29T04:30:00Z unix=-386796600 wall=1957-09-29T01:00:00 fold-until(1957-09-29T02:00:00) type=4 -03:30 NST
0080: 1958-04-27T05:30:00Z unix=-368649000 wall=1958-04-27T02:00:00 gap-until(1958-04-27T03:00:00) type=3 -02:30 NDT dst
0081: 1958-09-28T04:30:00Z unix=-355347000 wall=1958-09-28T01:00:00 fold-until(1958-09-28T02:00:00) type=4 -03:30 NST
0082: 1959-04-26T05:30:00Z unix=-337199400 wall=1959-04-26T02:00:00 gap-until(1959-04-26T03:00:00) type=3 -02:30 NDT dst
0083: 1959-09-27T04:30:00Z unix=-323897400 wall=1959-09-27T01:00:00 fold-until(1959-09-27T02:00:00) type=4 -03:30 NST
0084: 1960-04-24T05:30:00Z unix=-305749800 wall=1960-04-24T02:00:00 gap-until(1960-04-24T03:00:00) type=3 -02:30 NDT dst
0085: 1960-10-30T04:30:00Z unix=-289423800 wall=1960-10-30T01:00:00 fold-until(1960-10-30T02:00:00) type=4 -03:30 NST
0086: 1961-04-30T05:30:00Z unix=-273695400 wall=1961-04-30T02:00:00 gap-until(1961-04-30T03:00:00) type=3 -02:30 NDT dst
0087: 1961-10-29T04:30:00Z unix=-257974200 wall=1961-10-29T01:00:00 fold-until(1961-10-29T02:00:00) type=4 -03:30 NST
0088: 1962-04-29T05:30:00Z unix=-242245800 wall=1962-04-29T02:00:00 gap-until(1962-04-29T03:00:00) type=3 -02:30 NDT dst
0089: 1962-10-28T04:30:00Z unix=-226524600 wall=1962-10-28T01:00:00 fold-until(1962-10-28T02:00:00) type=4 -03:30 NST
0090: 1963-04-28T05:30:00Z unix=-210796200 wall=1963-04-28T02:00:00 gap-until(1963-04-28T03:00:00) type=3 -02:30 NDT dst
0091: 1963-10-27T04:30:00Z unix=-195075000 wall=1963-10-27T01:00:00 fold-until(1963-10-27T02:00:00) type=4 -03:30 NST
0092: 1964-04-26T05:30:00Z unix=-179346600 wall=1964-04-26T02:00:00 gap-until(1964-04-26T03:00:00) type=3 -02:30 NDT dst
0093: 1964-10-25T04:30:00Z unix=-163625400 wall=1964-10-25T01:00:00 fold-until(1964-10-25T02:00:00) type=4 -03:30 NST
0094: 1965-04-25T05:30:00Z unix=-147897000 wall=1965-04-25T02:00:00 gap-until(1965-04-25T03:00:00) type=3 -02:30 NDT dst
0095: 1965-10-31T04:30:00Z unix=-131571000 wall=1965-10-31T01:00:00 fold-until(1965-10-31T02:00:00) type=4 -03:30 NST
0096: 1966-04-24T05:30:00Z unix=-116447400 wall=1966-04-24T02:00:00 gap-until(1966-04-24T03:00:00) type=3 -02:30 NDT dst
0097: 1966-10-30T04:30:00Z unix=-100121400 wall=1966-10-30T01:00:00 fold-until(1966-10-30T02:00:00) type=4 -03:30 NST
0098: 1967-04-30T05:30:00Z unix=-84393000 wall=1967-04-30T02:00:00 gap-until(1967-04-30T03:00:00) type=3 -02:30 NDT dst
0099: 1967-10-29T04:30:00Z unix=-68671800 wall=1967-10-29T01:00:00 fold-until(1967-10-29T02:00:00) type=4 -03:30 NST
0100: 1968-04-28T05:30:00Z unix=-52943400 wall=1968-04-28T02:00:00 gap-until(1968-04-28T03:00:00) type=3 -02:30 NDT dst
0101: 1968-10-27T04:30:00Z unix=-37222200 wall=1968-10-27T01:00:00 fold-until(1968-10-27T02:00:00) type=4 -03:30 NST
0102: 1969-04-27T05:30:00Z unix=-21493800 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=3 -02:30 NDT dst
0103: 1969-10-26T04:30:00Z unix=-5772600 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=4 -03:30 NST
0104: 1970-04-26T05:30:00Z unix=9955800 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=3 -02:30 NDT dst
0105: 1970-10-25T04:30:00Z unix=25677000 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=4 -03:30 NST
0106: 1971-04-25T05:30:00Z unix=41405400 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=3 -02:30 NDT dst
0107: 1971-10-31T04:30:00Z unix=57731400 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=4 -03:30 NST
0108: 1972-04-30T05:30:00Z unix=73459800 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=3 -02:30 NDT dst
0109: 1972-10-29T04:30:00Z unix=89181000 wall=1972-10-29T01:00:00 fold-until(1972-10-29T02:00:00) type=4 -03:30 NST
0110: 1973-04-29T05:30:00Z unix=104909400 wall=1973-04-29T02:00:00 gap-until(1973-04-29T03:00:00) type=3 -02:30 NDT dst
0111: 1973-10-28T04:30:00Z unix=120630600 wall=1973-10-28T01:00:00 fold-until(1973-10-28T02:00:00) type=4 -03:30 NST
0112: 1974-04-28T05:30:00Z unix=136359000 wall=1974-04-28T02:00:00 gap-until(1974-04-28T03:00:00) type=3 -02:30 NDT dst
0113: 1974-10-27T04:30:00Z unix=152080200 wall=1974-10-27T01:00:00 fold-until(1974-10-27T02:00:00) type=4 -03:30 NST
0114: 1975-04-27T05:30:00Z unix=167808600 wall=1975-04-27T02:00:00 gap-until(1975-04-27T03:00:00) type=3 -02:30 NDT dst
0115: 1975-10-26T04:30:00Z unix=183529800 wall=1975-10-26T01:00:00 fold-until(1975-10-26T02:00:00) type=4 -03:30 NST
0116: 1976-04-25T05:30:00Z unix=199258200 wall=1976-04-25T02:00:00 gap-until(1976-04-25T03:00:00) type=3 -02:30 NDT dst
0117: 1976-10-31T04:30:00Z unix=215584200 wall=1976-10-31T01:00:00 fold-until(1976-10-31T02:00:00) type=4 -03:30 NST
0118: 1977-04-24T05:30:00Z unix=230707800 wall=1977-04-24T02:00:00 gap-until(1977-04-24T03:00:00) type=3 -02:30 NDT dst
0119: 1977-10-30T04:30:00Z unix=247033800 wall=1977-10-30T01:00:00 fold-until(1977-10-30T02:00:00) type=4 -03:30 NST
0120: 1978-04-30T05:30:00Z unix=262762200 wall=1978-04-30T02:00:00 gap-until(1978-04-30T03:00:00) type=3 -02:30 NDT dst
0121: 1978-10-29T04:30:00Z unix=278483400 wall=1978-10-29T01:00:00 fold-until(1978-10-29T02:00:00) type=4 -03:30 NST
0122: 1979-04-29T05:30:00Z unix=294211800 wall=1979-04-29T02:00:00 gap-until(1979-04-29T03:00:00) type=3 -02:30 NDT dst
0123: 1979-10-28T04:30:00Z unix=309933000 wall=1979-10-28T01:00:00 fold-until(1979-10-28T02:00:00) type=4 -03:30 NST
0124: 1980-04-27T05:30:00Z unix=325661400 wall=1980-04-27T02:00:00 gap-until(1980-04-27T03:00:00) type=3 -02:30 NDT dst
0125: 1980-10-26T04:30:00Z unix=341382600 wall=1980-10-26T01:00:00 fold-until(1980-10-26T02:00:00) type=4 -03:30 NST
0126: 1981-04-26T05:30:00Z unix=357111000 wall=1981-04-26T02:00:00 gap-until(1981-04-26T03:00:00) type=3 -02:30 NDT dst
0127: 1981-10-25T04:30:00Z unix=372832200 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=4 -03:30 NST
0128: 1982-04-25T05:30:00Z unix=388560600 wall=1982-04-25T02:00:00 gap-until(1982-04-25T03:00:00) type=3 -02:30 NDT dst
0129: 1982-10-31T04:30:00Z unix=404886600 wall=1982-10-31T01:00:00 fold-until(1982-10-31T02:00:00) type=4 -03:30 NST
0130: 1983-04-24T05:30:00Z unix=420010200 wall=1983-04-24T02:00:00 gap-until(1983-04-24T03:00:00) type=3 -02:30 NDT dst
0131: 1983-10-30T04:30:00Z unix=436336200 wall=1983-10-30T01:00:00 fold-until(1983-10-30T02:00:00) type=4 -03:30 NST
0132: 1984-04-29T05:30:00Z unix=452064600 wall=1984-04-29T02:00:00 gap-until(1984-04-29T03:00:00) type=3 -02:30 NDT dst
0133: 1984-10-28T04:30:00Z unix=467785800 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=4 -03:30 NST
0134: 1985-04-28T05:30:00Z unix=483514200 wall=1985-04-28T02:00:00 gap-until(1985-04-28T03:00:00) type=3 -02:30 NDT dst
0135: 1985-10-27T04:30:00Z unix=499235400 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=4 -03:30 NST
0136: 1986-04-27T05:30:00Z unix=514963800 wall=1986-04-27T02:00:00 gap-until(1986-04-27T03:00:00) type=3 -02:30 NDT dst
0137: 1986-10-26T04:30:00Z unix=530685000 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=4 -03:30 NST
0138: 1987-04-05T03:31:00Z unix=544591860 wall=1987-04-05T00:01:00 gap-until(1987-04-05T01:01:00) type=3 -02:30 NDT dst
0139: 1987-10-25T02:31:00Z unix=562127460 wall=1987-10-24T23:01:00 fold-until(1987-10-25T00:01:00) type=4 -03:30 NST
0140: 1988-04-03T03:31:00Z unix=576041460 wall=1988-04-03T00:01:00 gap-until(1988-04-03T02:01:00) type=7 -01:30 NDDT dst
0141: 1988-10-30T01:31:00Z unix=594178260 wall=1988-10-29T22:01:00 fold-until(1988-10-30T00:01:00) type=4 -03:30 NST
0142: 1989-04-02T03:31:00Z unix=607491060 wall=1989-04-02T00:01:00 gap-until(1989-04-02T01:01:00) type=3 -02:30 NDT dst
0143: 1989-10-29T02:31:00Z unix=625631460 wall=1989-10-28T23:01:00 fold-until(1989-10-29T00:01:00) type=4 -03:30 NST
0144: 1990-04-01T03:31:00Z unix=638940660 wall=1990-04-01T00:01:00 gap-until(1990-04-01T01:01:00) type=3 -02:30 NDT dst
0145: 1990-10-28T02:31:00Z unix=657081060 wall=1990-10-27T23:01:00 fold-until(1990-10-28T00:01:00) type=4 -03:30 NST
0146: 1991-04-07T03:31:00Z unix=670995060 wall=1991-04-07T00:01:00 gap-until(1991-04-07T01:01:00) type=3 -02:30 NDT dst
0147: 1991-10-27T02:31:00Z unix=688530660 wall=1991-10-26T23:01:00 fold-until(1991-10-27T00:01:00) type=4 -03:30 NST
0148: 1992-04-05T03:31:00Z unix=702444660 wall=1992-04-05T00:01:00 gap-until(1992-04-05T01:01:00) type=3 -02:30 NDT dst
0149: 1992-10-25T02:31:00Z unix=719980260 wall=1992-10-24T23:01:00 fold-until(1992-10-25T00:01:00) type=4 -03:30 NST
0150: 1993-04-04T03:31:00Z unix=733894260 wall=1993-04-04T00:01:00 gap-until(1993-04-04T01:01:00) type=3 -02:30 NDT dst
0151: 1993-10-31T02:31:00Z unix=752034660 wall=1993-10-30T23:01:00 fold-until(1993-10-31T00:01:00) type=4 -03:30 NST
0152: 1994-04-03T03:31:00Z unix=765343860 wall=1994-04-03T00:01:00 gap-until(1994-04-03T01:01:00) type=3 -02:30 NDT dst
0153: 1994-10-30T02:31:00Z unix=783484260 wall=1994-10-29T23:01:00 fold-until(1994-10-30T00:01:00) type=4 -03:30 NST
0154: 1995-04-02T03:31:00Z unix=796793460 wall=1995-04-02T00:01:00 gap-until(1995-04-02T01:01:00) type=3 -02:30 NDT dst
0155: 1995-10-29T02:31:00Z unix=814933860 wall=1995-10-28T23:01:00 fold-until(1995-10-29T00:01:00) type=4 -03:30 NST
0156: 1996-04-07T03:31:00Z unix=828847860 wall=1996-04-07T00:01:00 gap-until(1996-04-07T01:01:00) type=3 -02:30 NDT dst
0157: 1996-10-27T02:31:00Z unix=846383460 wall=1996-10-26T23:01:00 fold-until(1996-10-27T00:01:00) type=4 -03:30 NST
0158: 1997-04-06T03:31:00Z unix=860297460 wall=1997-04-06T00:01:00 gap-until(1997-04-06T01:01:00) type=3 -02:30 NDT dst
0159: 1997-10-26T02:31:00Z unix=877833060 wall=1997-10-25T23:01:00 fold-until(1997-10-26T00:01:00) type=4 -03:30 NST
0160: 1998-04-05T03:31:00Z unix=891747060 wall=1998-04-05T00:01:00 gap-until(1998-04-05T01:01:00) type=3 -02:30 NDT dst
0161: 1998-10-25T02:31:00Z unix=909282660 wall=1998-10-24T23:01:00 fold-until(1998-10-25T00:01:00) type=4 -03:30 NST
0162: 1999-04-04T03:31:00Z unix=923196660 wall=1999-04-04T00:01:00 gap-until(1999-04-04T01:01:00) type=3 -02:30 NDT dst
0163: 1999-10-31T02:31:00Z unix=941337060 wall=1999-10-30T23:01:00 fold-until(1999-10-31T00:01:00) type=4 -03:30 NST
0164: 2000-04-02T03:31:00Z unix=954646260 wall=2000-04-02T00:01:00 gap-until(2000-04-02T01:01:00) type=3 -02:30 NDT dst
0165: 2000-10-29T02:31:00Z unix=972786660 wall=2000-10-28T23:01:00 fold-until(2000-10-29T00:01:00) type=4 -03:30 NST
0166: 2001-04-01T03:31:00Z unix=986095860 wall=2001-04-01T00:01:00 gap-until(2001-04-01T01:01:00) type=3 -02:30 NDT dst
0167: 2001-10-28T02:31:00Z unix=1004236260 wall=2001-10-27T23:01:00 fold-until(2001-10-28T00:01:00) type=4 -03:30 NST
0168: 2002-04-07T03:31:00Z unix=1018150260 wall=2002-04-07T00:01:00 gap-until(2002-04-07T01:01:00) type=3 -02:30 NDT dst
0169: 2002-10-27T02:31:00Z unix=1035685860 wall=2002-10-26T23:01:00 fold-until(2002-10-27T00:01:00) type=4 -03:30 NST
0170: 2003-04-06T03:31:00Z unix=1049599860 wall=2003-04-06T00:01:00 gap-until(2003-04-06T01:01:00) type=3 -02:30 NDT dst
0171: 2003-10-26T02:31:00Z unix=1067135460 wall=2003-10-25T23:01:00 fold-until(2003-10-26T00:01:00) type=4 -03:30 NST
0172: 2004-04-04T03:31:00Z unix=1081049460 wall=2004-04-04T00:01:00 gap-until(2004-04-04T01:01:00) type=3 -02:30 NDT dst
0173: 2004-10-31T02:31:00Z unix=1099189860 wall=2004-10-30T23:01:00 fold-until(2004-10-31T00:01:00) type=4 -03:30 NST
0174: 2005-04-03T03:31:00Z unix=1112499060 wall=2005-04-03T00:01:00 gap-until(2005-04-03T01:01:00) type=3 -02:30 NDT dst
0175: 2005-10-30T02:31:00Z unix=1130639460 wall=2005-10-29T23:01:00 fold-until(2005-10-30T00:01:00) type=4 -03:30 NST
0176: 2006-04-02T03:31:00Z unix=1143948660 wall=2006-04-02T00:01:00 gap-until(2006-04-02T01:01:00) type=3 -02:30 NDT dst
0177: 2006-10-29T02:31:00Z unix=1162089060 wall=2006-10-28T23:01:00 fold-until(2006-10-29T00:01:00) type=4 -03:30 NST
0178: 2007-03-11T03:31:00Z unix=1173583860 wall=2007-03-11T00:01:00 gap-until(2007-03-11T01:01:00) type=3 -02:30 NDT dst
0179: 2007-11-04T02:31:00Z unix=1194143460 wall=2007-11-03T23:01:00 fold-until(2007-11-04T00:01:00) type=4 -03:30 NST
0180: 2008-03-09T03:31:00Z unix=1205033460 wall=2008-03-09T00:01:00 gap-until(2008-03-09T01:01:00) type=3 -02:30 NDT dst
0181: 2008-11-02T02:31:00Z unix=1225593060 wall=2008-11-01T23:01:00 fold-until(2008-11-02T00:01:00) type=4 -03:30 NST
0182: 2009-03-08T03:31:00Z unix=1236483060 wall=2009-03-08T00:01:00 gap-until(2009-03-08T01:01:00) type=3 -02:30 NDT dst
0183: 2009-11-01T02:31:00Z unix=1257042660 wall=2009-10-31T23:01:00 fold-until(2009-11-01T00:01:00) type=4 -03:30 NST
0184: 2010-03-14T03:31:00Z unix=1268537460 wall=2010-03-14T00:01:00 gap-until(2010-03-14T01:01:00) type=3 -02:30 NDT dst
0185: 2010-11-07T02:31:00Z unix=1289097060 wall=2010-11-06T23:01:00 fold-until(2010-11-07T00:01:00) type=4 -03:30 NST
0186: 2011-03-13T03:31:00Z unix=1299987060 wall=2011-03-13T00:01:00 gap-until(2011-03-13T01:01:00) type=3 -02:30 NDT dst
0187: 2011-11-06T04:30:00Z unix=1320553800 wall=2011-11-06T01:00:00 fold-until(2011-11-06T02:00:00) type=4 -03:30 NST
0188: 2012-03-11T05:30:00Z unix=1331443800 wall=2012-03-11T02:00:00 gap-until(2012-03-11T03:00:00) type=3 -02:30 NDT dst
0189: 2012-11-04T04:30:00Z unix=1352003400 wall=2012-11-04T01:00:00 fold-until(2012-11-04T02:00:00) type=4 -03:30 NST
0190: 2013-03-10T05:30:00Z unix=1362893400 wall=2013-03-10T02:00:00 gap-until(2013-03-10T03:00:00) type=3 -02:30 NDT dst
0191: 2013-11-03T04:30:00Z unix=1383453000 wall=2013-11-03T01:00:00 fold-until(2013-11-03T02:00:00) type=4 -03:30 NST
0192: 2014-03-09T05:30:00Z unix=1394343000 wall=2014-03-09T02:00:00 gap-until(2014-03-09T03:00:00) type=3 -02:30 NDT dst
0193: 2014-11-02T04:30:00Z unix=1414902600 wall=2014-11-02T01:00:00 fold-until(2014-11-02T02:00:00) type=4 -03:30 NST
0194: 2015-03-08T05:30:00Z unix=1425792600 wall=2015-03-08T02:00:00 gap-until(2015-03-08T03:00:00) type=3 -02:30 NDT dst
0195: 2015-11-01T04:30:00Z unix=1446352200 wall=2015-11-01T01:00:00 fold-until(2015-11-01T02:00:00) type=4 -03:30 NST
0196: 2016-03-13T05:30:00Z unix=1457847000 wall=2016-03-13T02:00:00 gap-until(2016-03-13T03:00:00) type=3 -02:30 NDT dst
0197: 2016-11-06T04:30:00Z unix=1478406600 wall=2016-11-06T01:00:00 fold-until(2016-11-06T02:00:00) type=4 -03:30 NST
0198: 2017-03-12T05:30:00Z unix=1489296600 wall=2017-03-12T02:00:00 gap-until(2017-03-12T03:00:00) type=3 -02:30 NDT dst
0199: 2017-11-05T04:30:00Z unix=1509856200 wall=2017-11-05T01:00:00 fold-until(2017-11-05T02:00:00) type=4 -03:30 NST
0200: 2018-03-11T05:30:00Z unix=1520746200 wall=2018-03-11T02:00:00 gap-until(2018-03-11T03:00:00) type=3 -02:30 NDT dst
0201: 2018-11-04T04:30:00Z unix=1541305800 wall=2018-11-04T01:00:00 fold-until(2018-11-04T02:00:00) type=4 -03:30 NST
0202: 2019-03-10T05:30:00Z unix=1552195800 wall=2019-03-10T02:00:00 gap-until(2019-03-10T03:00:00) type=3 -02:30 NDT dst
0203: 2019-11-03T04:30:00Z unix=1572755400 wall=2019-11-03T01:00:00 fold-until(2019-11-03T02:00:00) type=4 -03:30 NST
0204: 2020-03-08T05:30:00Z unix=1583645400 wall=2020-03-08T02:00:00 gap-until(2020-03-08T03:00:00) type=3 -02:30 NDT dst
0205: 2020-11-01T04:30:00Z unix=1604205000 wall=2020-11-01T01:00:00 fold-until(2020-11-01T02:00:00) type=4 -03:30 NST
0206: 2021-03-14T05:30:00Z unix=1615699800 wall=2021-03-14T02:00:00 gap-until(2021-03-14T03:00:00) type=3 -02:30 NDT dst
0207: 2021-11-07T04:30:00Z unix=1636259400 wall=2021-11-07T01:00:00 fold-until(2021-11-07T02:00:00) type=4 -03:30 NST
0208: 2022-03-13T05:30:00Z unix=1647149400 wall=2022-03-13T02:00:00 gap-until(2022-03-13T03:00:00) type=3 -02:30 NDT dst
0209: 2022-11-06T04:30:00Z unix=1667709000 wall=2022-11-06T01:00:00 fold-until(2022-11-06T02:00:00) type=4 -03:30 NST
0210: 2023-03-12T05:30:00Z unix=1678599000 wall=2023-03-12T02:00:00 gap-until(2023-03-12T03:00:00) type=3 -02:30 NDT dst
0211: 2023-11-05T04:30:00Z unix=1699158600 wall=2023-11-05T01:00:00 fold-until(2023-11-05T02:00:00) type=4 -03:30 NST
0212: 2024-03-10T05:30:00Z unix=1710048600 wall=2024-03-10T02:00:00 gap-until(2024-03-10T03:00:00) type=3 -02:30 NDT dst
0213: 2024-11-03T04:30:00Z unix=1730608200 wall=2024-11-03T01:00:00 fold-until(2024-11-03T02:00:00) type=4 -03:30 NST
0214: 2025-03-09T05:30:00Z unix=1741498200 wall=2025-03-09T02:00:00 gap-until(2025-03-09T03:00:00) type=3 -02:30 NDT dst
0215: 2025-11-02T04:30:00Z unix=1762057800 wall=2025-11-02T01:00:00 fold-until(2025-11-02T02:00:00) type=4 -03:30 NST
0216: 2026-03-08T05:30:00Z unix=1772947800 wall=2026-03-08T02:00:00 gap-until(2026-03-08T03:00:00) type=3 -02:30 NDT dst
0217: 2026-11-01T04:30:00Z unix=1793507400 wall=2026-11-01T01:00:00 fold-until(2026-11-01T02:00:00) type=4 -03:30 NST
0218: 2027-03-14T05:30:00Z unix=1805002200 wall=2027-03-14T02:00:00 gap-until(2027-03-14T03:00:00) type=3 -02:30 NDT dst
0219: 2027-11-07T04:30:00Z unix=1825561800 wall=2027-11-07T01:00:00 fold-until(2027-11-07T02:00:00) type=4 -03:30 NST
0220: 2028-03-12T05:30:00Z unix=1836451800 wall=2028-03-12T02:00:00 gap-until(2028-03-12T03:00:00) type=3 -02:30 NDT dst
0221: 2028-11-05T04:30:00Z unix=1857011400 wall=2028-11-05T01:00:00 fold-until(2028-11-05T02:00:00) type=4 -03:30 NST
0222: 2029-03-11T05:30:00Z unix=1867901400 wall=2029-03-11T02:00:00 gap-until(2029-03-11T03:00:00) type=3 -02:30 NDT dst
0223: 2029-11-04T04:30:00Z unix=1888461000 wall=2029-11-04T01:00:00 fold-until(2029-11-04T02:00:00) type=4 -03:30 NST
0224: 2030-03-10T05:30:00Z unix=1899351000 wall=2030-03-10T02:00:00 gap-until(2030-03-10T03:00:00) type=3 -02:30 NDT dst
0225: 2030-11-03T04:30:00Z unix=1919910600 wall=2030-11-03T01:00:00 fold-until(2030-11-03T02:00:00) type=4 -03:30 NST
0226: 2031-03-09T05:30:00Z unix=1930800600 wall=2031-03-09T02:00:00 gap-until(2031-03-09T03:00:00) type=3 -02:30 NDT dst
0227: 2031-11-02T04:30:00Z unix=1951360200 wall=2031-11-02T01:00:00 fold-until(2031-11-02T02:00:00) type=4 -03:30 NST
0228: 2032-03-14T05:30:00Z unix=1962855000 wall=2032-03-14T02:00:00 gap-until(2032-03-14T03:00:00) type=3 -02:30 NDT dst
0229: 2032-11-07T04:30:00Z unix=1983414600 wall=2032-11-07T01:00:00 fold-until(2032-11-07T02:00:00) type=4 -03:30 NST
0230: 2033-03-13T05:30:00Z unix=1994304600 wall=2033-03-13T02:00:00 gap-until(2033-03-13T03:00:00) type=3 -02:30 NDT dst
0231: 2033-11-06T04:30:00Z unix=2014864200 wall=2033-11-06T01:00:00 fold-until(2033-11-06T02:00:00) type=4 -03:30 NST
0232: 2034-03-12T05:30:00Z unix=2025754200 wall=2034-03-12T02:00:00 gap-until(2034-03-12T03:00:00) type=3 -02:30 NDT dst
0233: 2034-11-05T04:30:00Z unix=2046313800 wall=2034-11-05T01:00:00 fold-until(2034-11-05T02:00:00) type=4 -03:30 NST
0234: 2035-03-11T05:30:00Z unix=2057203800 wall=2035-03-11T02:00:00 gap-until(2035-03-11T03:00:00) type=3 -02:30 NDT dst
0235: 2035-11-04T04:30:00Z unix=2077763400 wall=2035-11-04T01:00:00 fold-until(2035-11-04T02:00:00) type=4 -03:30 NST
0236: 2036-03-09T05:30:00Z unix=2088653400 wall=2036-03-09T02:00:00 gap-until(2036-03-09T03:00:00) type=3 -02:30 NDT dst
0237: 2036-11-02T04:30:00Z unix=2109213000 wall=2036-11-02T01:00:00 fold-until(2036-11-02T02:00:00) type=4 -03:30 NST
0238: 2037-03-08T05:30:00Z unix=2120103000 wall=2037-03-08T02:00:00 gap-until(2037-03-08T03:00:00) type=3 -02:30 NDT dst
0239: 2037-11-01T04:30:00Z unix=2140662600 wall=2037-11-01T01:00:00 fold-until(2037-11-01T02:00:00) type=4 -03:30 NST
POSIX TIME ZONE STRING
NST3:30NDT,M3.2.0,M11.1.0

View file

@ -0,0 +1,80 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
Antarctica/Troll
LOCAL TIME TYPES
000: offset=+00 designation=-00 indicator=local/wall
001: offset=+02 designation=+02 dst indicator=ut/std
002: offset=+00 designation=+00 indicator=ut/std
003: offset=+00 designation=+00 indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T01:59:59 unambiguous type=0 +00 -00
0001: 2005-02-12T00:00:00Z unix=1108166400 wall=2005-02-12T00:00:00 unambiguous type=3 +00 +00
0002: 2005-03-27T01:00:00Z unix=1111885200 wall=2005-03-27T01:00:00 gap-until(2005-03-27T03:00:00) type=1 +02 +02 dst
0003: 2005-10-30T01:00:00Z unix=1130634000 wall=2005-10-30T01:00:00 fold-until(2005-10-30T03:00:00) type=2 +00 +00
0004: 2006-03-26T01:00:00Z unix=1143334800 wall=2006-03-26T01:00:00 gap-until(2006-03-26T03:00:00) type=1 +02 +02 dst
0005: 2006-10-29T01:00:00Z unix=1162083600 wall=2006-10-29T01:00:00 fold-until(2006-10-29T03:00:00) type=2 +00 +00
0006: 2007-03-25T01:00:00Z unix=1174784400 wall=2007-03-25T01:00:00 gap-until(2007-03-25T03:00:00) type=1 +02 +02 dst
0007: 2007-10-28T01:00:00Z unix=1193533200 wall=2007-10-28T01:00:00 fold-until(2007-10-28T03:00:00) type=2 +00 +00
0008: 2008-03-30T01:00:00Z unix=1206838800 wall=2008-03-30T01:00:00 gap-until(2008-03-30T03:00:00) type=1 +02 +02 dst
0009: 2008-10-26T01:00:00Z unix=1224982800 wall=2008-10-26T01:00:00 fold-until(2008-10-26T03:00:00) type=2 +00 +00
0010: 2009-03-29T01:00:00Z unix=1238288400 wall=2009-03-29T01:00:00 gap-until(2009-03-29T03:00:00) type=1 +02 +02 dst
0011: 2009-10-25T01:00:00Z unix=1256432400 wall=2009-10-25T01:00:00 fold-until(2009-10-25T03:00:00) type=2 +00 +00
0012: 2010-03-28T01:00:00Z unix=1269738000 wall=2010-03-28T01:00:00 gap-until(2010-03-28T03:00:00) type=1 +02 +02 dst
0013: 2010-10-31T01:00:00Z unix=1288486800 wall=2010-10-31T01:00:00 fold-until(2010-10-31T03:00:00) type=2 +00 +00
0014: 2011-03-27T01:00:00Z unix=1301187600 wall=2011-03-27T01:00:00 gap-until(2011-03-27T03:00:00) type=1 +02 +02 dst
0015: 2011-10-30T01:00:00Z unix=1319936400 wall=2011-10-30T01:00:00 fold-until(2011-10-30T03:00:00) type=2 +00 +00
0016: 2012-03-25T01:00:00Z unix=1332637200 wall=2012-03-25T01:00:00 gap-until(2012-03-25T03:00:00) type=1 +02 +02 dst
0017: 2012-10-28T01:00:00Z unix=1351386000 wall=2012-10-28T01:00:00 fold-until(2012-10-28T03:00:00) type=2 +00 +00
0018: 2013-03-31T01:00:00Z unix=1364691600 wall=2013-03-31T01:00:00 gap-until(2013-03-31T03:00:00) type=1 +02 +02 dst
0019: 2013-10-27T01:00:00Z unix=1382835600 wall=2013-10-27T01:00:00 fold-until(2013-10-27T03:00:00) type=2 +00 +00
0020: 2014-03-30T01:00:00Z unix=1396141200 wall=2014-03-30T01:00:00 gap-until(2014-03-30T03:00:00) type=1 +02 +02 dst
0021: 2014-10-26T01:00:00Z unix=1414285200 wall=2014-10-26T01:00:00 fold-until(2014-10-26T03:00:00) type=2 +00 +00
0022: 2015-03-29T01:00:00Z unix=1427590800 wall=2015-03-29T01:00:00 gap-until(2015-03-29T03:00:00) type=1 +02 +02 dst
0023: 2015-10-25T01:00:00Z unix=1445734800 wall=2015-10-25T01:00:00 fold-until(2015-10-25T03:00:00) type=2 +00 +00
0024: 2016-03-27T01:00:00Z unix=1459040400 wall=2016-03-27T01:00:00 gap-until(2016-03-27T03:00:00) type=1 +02 +02 dst
0025: 2016-10-30T01:00:00Z unix=1477789200 wall=2016-10-30T01:00:00 fold-until(2016-10-30T03:00:00) type=2 +00 +00
0026: 2017-03-26T01:00:00Z unix=1490490000 wall=2017-03-26T01:00:00 gap-until(2017-03-26T03:00:00) type=1 +02 +02 dst
0027: 2017-10-29T01:00:00Z unix=1509238800 wall=2017-10-29T01:00:00 fold-until(2017-10-29T03:00:00) type=2 +00 +00
0028: 2018-03-25T01:00:00Z unix=1521939600 wall=2018-03-25T01:00:00 gap-until(2018-03-25T03:00:00) type=1 +02 +02 dst
0029: 2018-10-28T01:00:00Z unix=1540688400 wall=2018-10-28T01:00:00 fold-until(2018-10-28T03:00:00) type=2 +00 +00
0030: 2019-03-31T01:00:00Z unix=1553994000 wall=2019-03-31T01:00:00 gap-until(2019-03-31T03:00:00) type=1 +02 +02 dst
0031: 2019-10-27T01:00:00Z unix=1572138000 wall=2019-10-27T01:00:00 fold-until(2019-10-27T03:00:00) type=2 +00 +00
0032: 2020-03-29T01:00:00Z unix=1585443600 wall=2020-03-29T01:00:00 gap-until(2020-03-29T03:00:00) type=1 +02 +02 dst
0033: 2020-10-25T01:00:00Z unix=1603587600 wall=2020-10-25T01:00:00 fold-until(2020-10-25T03:00:00) type=2 +00 +00
0034: 2021-03-28T01:00:00Z unix=1616893200 wall=2021-03-28T01:00:00 gap-until(2021-03-28T03:00:00) type=1 +02 +02 dst
0035: 2021-10-31T01:00:00Z unix=1635642000 wall=2021-10-31T01:00:00 fold-until(2021-10-31T03:00:00) type=2 +00 +00
0036: 2022-03-27T01:00:00Z unix=1648342800 wall=2022-03-27T01:00:00 gap-until(2022-03-27T03:00:00) type=1 +02 +02 dst
0037: 2022-10-30T01:00:00Z unix=1667091600 wall=2022-10-30T01:00:00 fold-until(2022-10-30T03:00:00) type=2 +00 +00
0038: 2023-03-26T01:00:00Z unix=1679792400 wall=2023-03-26T01:00:00 gap-until(2023-03-26T03:00:00) type=1 +02 +02 dst
0039: 2023-10-29T01:00:00Z unix=1698541200 wall=2023-10-29T01:00:00 fold-until(2023-10-29T03:00:00) type=2 +00 +00
0040: 2024-03-31T01:00:00Z unix=1711846800 wall=2024-03-31T01:00:00 gap-until(2024-03-31T03:00:00) type=1 +02 +02 dst
0041: 2024-10-27T01:00:00Z unix=1729990800 wall=2024-10-27T01:00:00 fold-until(2024-10-27T03:00:00) type=2 +00 +00
0042: 2025-03-30T01:00:00Z unix=1743296400 wall=2025-03-30T01:00:00 gap-until(2025-03-30T03:00:00) type=1 +02 +02 dst
0043: 2025-10-26T01:00:00Z unix=1761440400 wall=2025-10-26T01:00:00 fold-until(2025-10-26T03:00:00) type=2 +00 +00
0044: 2026-03-29T01:00:00Z unix=1774746000 wall=2026-03-29T01:00:00 gap-until(2026-03-29T03:00:00) type=1 +02 +02 dst
0045: 2026-10-25T01:00:00Z unix=1792890000 wall=2026-10-25T01:00:00 fold-until(2026-10-25T03:00:00) type=2 +00 +00
0046: 2027-03-28T01:00:00Z unix=1806195600 wall=2027-03-28T01:00:00 gap-until(2027-03-28T03:00:00) type=1 +02 +02 dst
0047: 2027-10-31T01:00:00Z unix=1824944400 wall=2027-10-31T01:00:00 fold-until(2027-10-31T03:00:00) type=2 +00 +00
0048: 2028-03-26T01:00:00Z unix=1837645200 wall=2028-03-26T01:00:00 gap-until(2028-03-26T03:00:00) type=1 +02 +02 dst
0049: 2028-10-29T01:00:00Z unix=1856394000 wall=2028-10-29T01:00:00 fold-until(2028-10-29T03:00:00) type=2 +00 +00
0050: 2029-03-25T01:00:00Z unix=1869094800 wall=2029-03-25T01:00:00 gap-until(2029-03-25T03:00:00) type=1 +02 +02 dst
0051: 2029-10-28T01:00:00Z unix=1887843600 wall=2029-10-28T01:00:00 fold-until(2029-10-28T03:00:00) type=2 +00 +00
0052: 2030-03-31T01:00:00Z unix=1901149200 wall=2030-03-31T01:00:00 gap-until(2030-03-31T03:00:00) type=1 +02 +02 dst
0053: 2030-10-27T01:00:00Z unix=1919293200 wall=2030-10-27T01:00:00 fold-until(2030-10-27T03:00:00) type=2 +00 +00
0054: 2031-03-30T01:00:00Z unix=1932598800 wall=2031-03-30T01:00:00 gap-until(2031-03-30T03:00:00) type=1 +02 +02 dst
0055: 2031-10-26T01:00:00Z unix=1950742800 wall=2031-10-26T01:00:00 fold-until(2031-10-26T03:00:00) type=2 +00 +00
0056: 2032-03-28T01:00:00Z unix=1964048400 wall=2032-03-28T01:00:00 gap-until(2032-03-28T03:00:00) type=1 +02 +02 dst
0057: 2032-10-31T01:00:00Z unix=1982797200 wall=2032-10-31T01:00:00 fold-until(2032-10-31T03:00:00) type=2 +00 +00
0058: 2033-03-27T01:00:00Z unix=1995498000 wall=2033-03-27T01:00:00 gap-until(2033-03-27T03:00:00) type=1 +02 +02 dst
0059: 2033-10-30T01:00:00Z unix=2014246800 wall=2033-10-30T01:00:00 fold-until(2033-10-30T03:00:00) type=2 +00 +00
0060: 2034-03-26T01:00:00Z unix=2026947600 wall=2034-03-26T01:00:00 gap-until(2034-03-26T03:00:00) type=1 +02 +02 dst
0061: 2034-10-29T01:00:00Z unix=2045696400 wall=2034-10-29T01:00:00 fold-until(2034-10-29T03:00:00) type=2 +00 +00
0062: 2035-03-25T01:00:00Z unix=2058397200 wall=2035-03-25T01:00:00 gap-until(2035-03-25T03:00:00) type=1 +02 +02 dst
0063: 2035-10-28T01:00:00Z unix=2077146000 wall=2035-10-28T01:00:00 fold-until(2035-10-28T03:00:00) type=2 +00 +00
0064: 2036-03-30T01:00:00Z unix=2090451600 wall=2036-03-30T01:00:00 gap-until(2036-03-30T03:00:00) type=1 +02 +02 dst
0065: 2036-10-26T01:00:00Z unix=2108595600 wall=2036-10-26T01:00:00 fold-until(2036-10-26T03:00:00) type=2 +00 +00
0066: 2037-03-29T01:00:00Z unix=2121901200 wall=2037-03-29T01:00:00 gap-until(2037-03-29T03:00:00) type=1 +02 +02 dst
0067: 2037-10-25T01:00:00Z unix=2140045200 wall=2037-10-25T01:00:00 fold-until(2037-10-25T03:00:00) type=2 +00 +00

View file

@ -0,0 +1,82 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
Antarctica/Troll
LOCAL TIME TYPES
000: offset=+00 designation=-00 indicator=local/wall
001: offset=+02 designation=+02 dst indicator=ut/std
002: offset=+00 designation=+00 indicator=ut/std
003: offset=+00 designation=+00 indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T01:59:59 unambiguous type=0 +00 -00
0001: 2005-02-12T00:00:00Z unix=1108166400 wall=2005-02-12T00:00:00 unambiguous type=3 +00 +00
0002: 2005-03-27T01:00:00Z unix=1111885200 wall=2005-03-27T01:00:00 gap-until(2005-03-27T03:00:00) type=1 +02 +02 dst
0003: 2005-10-30T01:00:00Z unix=1130634000 wall=2005-10-30T01:00:00 fold-until(2005-10-30T03:00:00) type=2 +00 +00
0004: 2006-03-26T01:00:00Z unix=1143334800 wall=2006-03-26T01:00:00 gap-until(2006-03-26T03:00:00) type=1 +02 +02 dst
0005: 2006-10-29T01:00:00Z unix=1162083600 wall=2006-10-29T01:00:00 fold-until(2006-10-29T03:00:00) type=2 +00 +00
0006: 2007-03-25T01:00:00Z unix=1174784400 wall=2007-03-25T01:00:00 gap-until(2007-03-25T03:00:00) type=1 +02 +02 dst
0007: 2007-10-28T01:00:00Z unix=1193533200 wall=2007-10-28T01:00:00 fold-until(2007-10-28T03:00:00) type=2 +00 +00
0008: 2008-03-30T01:00:00Z unix=1206838800 wall=2008-03-30T01:00:00 gap-until(2008-03-30T03:00:00) type=1 +02 +02 dst
0009: 2008-10-26T01:00:00Z unix=1224982800 wall=2008-10-26T01:00:00 fold-until(2008-10-26T03:00:00) type=2 +00 +00
0010: 2009-03-29T01:00:00Z unix=1238288400 wall=2009-03-29T01:00:00 gap-until(2009-03-29T03:00:00) type=1 +02 +02 dst
0011: 2009-10-25T01:00:00Z unix=1256432400 wall=2009-10-25T01:00:00 fold-until(2009-10-25T03:00:00) type=2 +00 +00
0012: 2010-03-28T01:00:00Z unix=1269738000 wall=2010-03-28T01:00:00 gap-until(2010-03-28T03:00:00) type=1 +02 +02 dst
0013: 2010-10-31T01:00:00Z unix=1288486800 wall=2010-10-31T01:00:00 fold-until(2010-10-31T03:00:00) type=2 +00 +00
0014: 2011-03-27T01:00:00Z unix=1301187600 wall=2011-03-27T01:00:00 gap-until(2011-03-27T03:00:00) type=1 +02 +02 dst
0015: 2011-10-30T01:00:00Z unix=1319936400 wall=2011-10-30T01:00:00 fold-until(2011-10-30T03:00:00) type=2 +00 +00
0016: 2012-03-25T01:00:00Z unix=1332637200 wall=2012-03-25T01:00:00 gap-until(2012-03-25T03:00:00) type=1 +02 +02 dst
0017: 2012-10-28T01:00:00Z unix=1351386000 wall=2012-10-28T01:00:00 fold-until(2012-10-28T03:00:00) type=2 +00 +00
0018: 2013-03-31T01:00:00Z unix=1364691600 wall=2013-03-31T01:00:00 gap-until(2013-03-31T03:00:00) type=1 +02 +02 dst
0019: 2013-10-27T01:00:00Z unix=1382835600 wall=2013-10-27T01:00:00 fold-until(2013-10-27T03:00:00) type=2 +00 +00
0020: 2014-03-30T01:00:00Z unix=1396141200 wall=2014-03-30T01:00:00 gap-until(2014-03-30T03:00:00) type=1 +02 +02 dst
0021: 2014-10-26T01:00:00Z unix=1414285200 wall=2014-10-26T01:00:00 fold-until(2014-10-26T03:00:00) type=2 +00 +00
0022: 2015-03-29T01:00:00Z unix=1427590800 wall=2015-03-29T01:00:00 gap-until(2015-03-29T03:00:00) type=1 +02 +02 dst
0023: 2015-10-25T01:00:00Z unix=1445734800 wall=2015-10-25T01:00:00 fold-until(2015-10-25T03:00:00) type=2 +00 +00
0024: 2016-03-27T01:00:00Z unix=1459040400 wall=2016-03-27T01:00:00 gap-until(2016-03-27T03:00:00) type=1 +02 +02 dst
0025: 2016-10-30T01:00:00Z unix=1477789200 wall=2016-10-30T01:00:00 fold-until(2016-10-30T03:00:00) type=2 +00 +00
0026: 2017-03-26T01:00:00Z unix=1490490000 wall=2017-03-26T01:00:00 gap-until(2017-03-26T03:00:00) type=1 +02 +02 dst
0027: 2017-10-29T01:00:00Z unix=1509238800 wall=2017-10-29T01:00:00 fold-until(2017-10-29T03:00:00) type=2 +00 +00
0028: 2018-03-25T01:00:00Z unix=1521939600 wall=2018-03-25T01:00:00 gap-until(2018-03-25T03:00:00) type=1 +02 +02 dst
0029: 2018-10-28T01:00:00Z unix=1540688400 wall=2018-10-28T01:00:00 fold-until(2018-10-28T03:00:00) type=2 +00 +00
0030: 2019-03-31T01:00:00Z unix=1553994000 wall=2019-03-31T01:00:00 gap-until(2019-03-31T03:00:00) type=1 +02 +02 dst
0031: 2019-10-27T01:00:00Z unix=1572138000 wall=2019-10-27T01:00:00 fold-until(2019-10-27T03:00:00) type=2 +00 +00
0032: 2020-03-29T01:00:00Z unix=1585443600 wall=2020-03-29T01:00:00 gap-until(2020-03-29T03:00:00) type=1 +02 +02 dst
0033: 2020-10-25T01:00:00Z unix=1603587600 wall=2020-10-25T01:00:00 fold-until(2020-10-25T03:00:00) type=2 +00 +00
0034: 2021-03-28T01:00:00Z unix=1616893200 wall=2021-03-28T01:00:00 gap-until(2021-03-28T03:00:00) type=1 +02 +02 dst
0035: 2021-10-31T01:00:00Z unix=1635642000 wall=2021-10-31T01:00:00 fold-until(2021-10-31T03:00:00) type=2 +00 +00
0036: 2022-03-27T01:00:00Z unix=1648342800 wall=2022-03-27T01:00:00 gap-until(2022-03-27T03:00:00) type=1 +02 +02 dst
0037: 2022-10-30T01:00:00Z unix=1667091600 wall=2022-10-30T01:00:00 fold-until(2022-10-30T03:00:00) type=2 +00 +00
0038: 2023-03-26T01:00:00Z unix=1679792400 wall=2023-03-26T01:00:00 gap-until(2023-03-26T03:00:00) type=1 +02 +02 dst
0039: 2023-10-29T01:00:00Z unix=1698541200 wall=2023-10-29T01:00:00 fold-until(2023-10-29T03:00:00) type=2 +00 +00
0040: 2024-03-31T01:00:00Z unix=1711846800 wall=2024-03-31T01:00:00 gap-until(2024-03-31T03:00:00) type=1 +02 +02 dst
0041: 2024-10-27T01:00:00Z unix=1729990800 wall=2024-10-27T01:00:00 fold-until(2024-10-27T03:00:00) type=2 +00 +00
0042: 2025-03-30T01:00:00Z unix=1743296400 wall=2025-03-30T01:00:00 gap-until(2025-03-30T03:00:00) type=1 +02 +02 dst
0043: 2025-10-26T01:00:00Z unix=1761440400 wall=2025-10-26T01:00:00 fold-until(2025-10-26T03:00:00) type=2 +00 +00
0044: 2026-03-29T01:00:00Z unix=1774746000 wall=2026-03-29T01:00:00 gap-until(2026-03-29T03:00:00) type=1 +02 +02 dst
0045: 2026-10-25T01:00:00Z unix=1792890000 wall=2026-10-25T01:00:00 fold-until(2026-10-25T03:00:00) type=2 +00 +00
0046: 2027-03-28T01:00:00Z unix=1806195600 wall=2027-03-28T01:00:00 gap-until(2027-03-28T03:00:00) type=1 +02 +02 dst
0047: 2027-10-31T01:00:00Z unix=1824944400 wall=2027-10-31T01:00:00 fold-until(2027-10-31T03:00:00) type=2 +00 +00
0048: 2028-03-26T01:00:00Z unix=1837645200 wall=2028-03-26T01:00:00 gap-until(2028-03-26T03:00:00) type=1 +02 +02 dst
0049: 2028-10-29T01:00:00Z unix=1856394000 wall=2028-10-29T01:00:00 fold-until(2028-10-29T03:00:00) type=2 +00 +00
0050: 2029-03-25T01:00:00Z unix=1869094800 wall=2029-03-25T01:00:00 gap-until(2029-03-25T03:00:00) type=1 +02 +02 dst
0051: 2029-10-28T01:00:00Z unix=1887843600 wall=2029-10-28T01:00:00 fold-until(2029-10-28T03:00:00) type=2 +00 +00
0052: 2030-03-31T01:00:00Z unix=1901149200 wall=2030-03-31T01:00:00 gap-until(2030-03-31T03:00:00) type=1 +02 +02 dst
0053: 2030-10-27T01:00:00Z unix=1919293200 wall=2030-10-27T01:00:00 fold-until(2030-10-27T03:00:00) type=2 +00 +00
0054: 2031-03-30T01:00:00Z unix=1932598800 wall=2031-03-30T01:00:00 gap-until(2031-03-30T03:00:00) type=1 +02 +02 dst
0055: 2031-10-26T01:00:00Z unix=1950742800 wall=2031-10-26T01:00:00 fold-until(2031-10-26T03:00:00) type=2 +00 +00
0056: 2032-03-28T01:00:00Z unix=1964048400 wall=2032-03-28T01:00:00 gap-until(2032-03-28T03:00:00) type=1 +02 +02 dst
0057: 2032-10-31T01:00:00Z unix=1982797200 wall=2032-10-31T01:00:00 fold-until(2032-10-31T03:00:00) type=2 +00 +00
0058: 2033-03-27T01:00:00Z unix=1995498000 wall=2033-03-27T01:00:00 gap-until(2033-03-27T03:00:00) type=1 +02 +02 dst
0059: 2033-10-30T01:00:00Z unix=2014246800 wall=2033-10-30T01:00:00 fold-until(2033-10-30T03:00:00) type=2 +00 +00
0060: 2034-03-26T01:00:00Z unix=2026947600 wall=2034-03-26T01:00:00 gap-until(2034-03-26T03:00:00) type=1 +02 +02 dst
0061: 2034-10-29T01:00:00Z unix=2045696400 wall=2034-10-29T01:00:00 fold-until(2034-10-29T03:00:00) type=2 +00 +00
0062: 2035-03-25T01:00:00Z unix=2058397200 wall=2035-03-25T01:00:00 gap-until(2035-03-25T03:00:00) type=1 +02 +02 dst
0063: 2035-10-28T01:00:00Z unix=2077146000 wall=2035-10-28T01:00:00 fold-until(2035-10-28T03:00:00) type=2 +00 +00
0064: 2036-03-30T01:00:00Z unix=2090451600 wall=2036-03-30T01:00:00 gap-until(2036-03-30T03:00:00) type=1 +02 +02 dst
0065: 2036-10-26T01:00:00Z unix=2108595600 wall=2036-10-26T01:00:00 fold-until(2036-10-26T03:00:00) type=2 +00 +00
0066: 2037-03-29T01:00:00Z unix=2121901200 wall=2037-03-29T01:00:00 gap-until(2037-03-29T03:00:00) type=1 +02 +02 dst
0067: 2037-10-25T01:00:00Z unix=2140045200 wall=2037-10-25T01:00:00 fold-until(2037-10-25T03:00:00) type=2 +00 +00
POSIX TIME ZONE STRING
<+00>0<+02>-2,M3.5.0/1,M10.5.0/3

View file

@ -0,0 +1,167 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
Australia/Tasmania
LOCAL TIME TYPES
000: offset=+09:49:16 designation=LMT indicator=local/wall
001: offset=+11 designation=AEDT dst indicator=local/std
002: offset=+10 designation=AEST indicator=local/std
003: offset=+10 designation=AEST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T11:49:15 unambiguous type=0 +09:49:16 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-14T06:35:08 gap-until(1901-12-14T06:45:52) type=3 +10 AEST
0002: 1916-09-30T16:00:00Z unix=-1680508800 wall=1916-10-01T02:00:00 gap-until(1916-10-01T03:00:00) type=1 +11 AEDT dst
0003: 1917-03-24T16:00:00Z unix=-1665388800 wall=1917-03-25T02:00:00 fold-until(1917-03-25T03:00:00) type=2 +10 AEST
0004: 1917-10-27T16:00:00Z unix=-1646640000 wall=1917-10-28T02:00:00 gap-until(1917-10-28T03:00:00) type=1 +11 AEDT dst
0005: 1918-03-02T16:00:00Z unix=-1635753600 wall=1918-03-03T02:00:00 fold-until(1918-03-03T03:00:00) type=2 +10 AEST
0006: 1918-10-26T16:00:00Z unix=-1615190400 wall=1918-10-27T02:00:00 gap-until(1918-10-27T03:00:00) type=1 +11 AEDT dst
0007: 1919-03-01T16:00:00Z unix=-1604304000 wall=1919-03-02T02:00:00 fold-until(1919-03-02T03:00:00) type=2 +10 AEST
0008: 1941-12-31T16:00:00Z unix=-883641600 wall=1942-01-01T02:00:00 gap-until(1942-01-01T03:00:00) type=1 +11 AEDT dst
0009: 1942-03-28T16:00:00Z unix=-876124800 wall=1942-03-29T02:00:00 fold-until(1942-03-29T03:00:00) type=2 +10 AEST
0010: 1942-09-26T16:00:00Z unix=-860400000 wall=1942-09-27T02:00:00 gap-until(1942-09-27T03:00:00) type=1 +11 AEDT dst
0011: 1943-03-27T16:00:00Z unix=-844675200 wall=1943-03-28T02:00:00 fold-until(1943-03-28T03:00:00) type=2 +10 AEST
0012: 1943-10-02T16:00:00Z unix=-828345600 wall=1943-10-03T02:00:00 gap-until(1943-10-03T03:00:00) type=1 +11 AEDT dst
0013: 1944-03-25T16:00:00Z unix=-813225600 wall=1944-03-26T02:00:00 fold-until(1944-03-26T03:00:00) type=2 +10 AEST
0014: 1967-09-30T16:00:00Z unix=-71136000 wall=1967-10-01T02:00:00 gap-until(1967-10-01T03:00:00) type=1 +11 AEDT dst
0015: 1968-03-30T16:00:00Z unix=-55411200 wall=1968-03-31T02:00:00 fold-until(1968-03-31T03:00:00) type=2 +10 AEST
0016: 1968-10-26T16:00:00Z unix=-37267200 wall=1968-10-27T02:00:00 gap-until(1968-10-27T03:00:00) type=1 +11 AEDT dst
0017: 1969-03-08T16:00:00Z unix=-25776000 wall=1969-03-09T02:00:00 fold-until(1969-03-09T03:00:00) type=2 +10 AEST
0018: 1969-10-25T16:00:00Z unix=-5817600 wall=1969-10-26T02:00:00 gap-until(1969-10-26T03:00:00) type=1 +11 AEDT dst
0019: 1970-03-07T16:00:00Z unix=5673600 wall=1970-03-08T02:00:00 fold-until(1970-03-08T03:00:00) type=2 +10 AEST
0020: 1970-10-24T16:00:00Z unix=25632000 wall=1970-10-25T02:00:00 gap-until(1970-10-25T03:00:00) type=1 +11 AEDT dst
0021: 1971-03-13T16:00:00Z unix=37728000 wall=1971-03-14T02:00:00 fold-until(1971-03-14T03:00:00) type=2 +10 AEST
0022: 1971-10-30T16:00:00Z unix=57686400 wall=1971-10-31T02:00:00 gap-until(1971-10-31T03:00:00) type=1 +11 AEDT dst
0023: 1972-02-26T16:00:00Z unix=67968000 wall=1972-02-27T02:00:00 fold-until(1972-02-27T03:00:00) type=2 +10 AEST
0024: 1972-10-28T16:00:00Z unix=89136000 wall=1972-10-29T02:00:00 gap-until(1972-10-29T03:00:00) type=1 +11 AEDT dst
0025: 1973-03-03T16:00:00Z unix=100022400 wall=1973-03-04T02:00:00 fold-until(1973-03-04T03:00:00) type=2 +10 AEST
0026: 1973-10-27T16:00:00Z unix=120585600 wall=1973-10-28T02:00:00 gap-until(1973-10-28T03:00:00) type=1 +11 AEDT dst
0027: 1974-03-02T16:00:00Z unix=131472000 wall=1974-03-03T02:00:00 fold-until(1974-03-03T03:00:00) type=2 +10 AEST
0028: 1974-10-26T16:00:00Z unix=152035200 wall=1974-10-27T02:00:00 gap-until(1974-10-27T03:00:00) type=1 +11 AEDT dst
0029: 1975-03-01T16:00:00Z unix=162921600 wall=1975-03-02T02:00:00 fold-until(1975-03-02T03:00:00) type=2 +10 AEST
0030: 1975-10-25T16:00:00Z unix=183484800 wall=1975-10-26T02:00:00 gap-until(1975-10-26T03:00:00) type=1 +11 AEDT dst
0031: 1976-03-06T16:00:00Z unix=194976000 wall=1976-03-07T02:00:00 fold-until(1976-03-07T03:00:00) type=2 +10 AEST
0032: 1976-10-30T16:00:00Z unix=215539200 wall=1976-10-31T02:00:00 gap-until(1976-10-31T03:00:00) type=1 +11 AEDT dst
0033: 1977-03-05T16:00:00Z unix=226425600 wall=1977-03-06T02:00:00 fold-until(1977-03-06T03:00:00) type=2 +10 AEST
0034: 1977-10-29T16:00:00Z unix=246988800 wall=1977-10-30T02:00:00 gap-until(1977-10-30T03:00:00) type=1 +11 AEDT dst
0035: 1978-03-04T16:00:00Z unix=257875200 wall=1978-03-05T02:00:00 fold-until(1978-03-05T03:00:00) type=2 +10 AEST
0036: 1978-10-28T16:00:00Z unix=278438400 wall=1978-10-29T02:00:00 gap-until(1978-10-29T03:00:00) type=1 +11 AEDT dst
0037: 1979-03-03T16:00:00Z unix=289324800 wall=1979-03-04T02:00:00 fold-until(1979-03-04T03:00:00) type=2 +10 AEST
0038: 1979-10-27T16:00:00Z unix=309888000 wall=1979-10-28T02:00:00 gap-until(1979-10-28T03:00:00) type=1 +11 AEDT dst
0039: 1980-03-01T16:00:00Z unix=320774400 wall=1980-03-02T02:00:00 fold-until(1980-03-02T03:00:00) type=2 +10 AEST
0040: 1980-10-25T16:00:00Z unix=341337600 wall=1980-10-26T02:00:00 gap-until(1980-10-26T03:00:00) type=1 +11 AEDT dst
0041: 1981-02-28T16:00:00Z unix=352224000 wall=1981-03-01T02:00:00 fold-until(1981-03-01T03:00:00) type=2 +10 AEST
0042: 1981-10-24T16:00:00Z unix=372787200 wall=1981-10-25T02:00:00 gap-until(1981-10-25T03:00:00) type=1 +11 AEDT dst
0043: 1982-03-27T16:00:00Z unix=386092800 wall=1982-03-28T02:00:00 fold-until(1982-03-28T03:00:00) type=2 +10 AEST
0044: 1982-10-30T16:00:00Z unix=404841600 wall=1982-10-31T02:00:00 gap-until(1982-10-31T03:00:00) type=1 +11 AEDT dst
0045: 1983-03-26T16:00:00Z unix=417542400 wall=1983-03-27T02:00:00 fold-until(1983-03-27T03:00:00) type=2 +10 AEST
0046: 1983-10-29T16:00:00Z unix=436291200 wall=1983-10-30T02:00:00 gap-until(1983-10-30T03:00:00) type=1 +11 AEDT dst
0047: 1984-03-03T16:00:00Z unix=447177600 wall=1984-03-04T02:00:00 fold-until(1984-03-04T03:00:00) type=2 +10 AEST
0048: 1984-10-27T16:00:00Z unix=467740800 wall=1984-10-28T02:00:00 gap-until(1984-10-28T03:00:00) type=1 +11 AEDT dst
0049: 1985-03-02T16:00:00Z unix=478627200 wall=1985-03-03T02:00:00 fold-until(1985-03-03T03:00:00) type=2 +10 AEST
0050: 1985-10-26T16:00:00Z unix=499190400 wall=1985-10-27T02:00:00 gap-until(1985-10-27T03:00:00) type=1 +11 AEDT dst
0051: 1986-03-01T16:00:00Z unix=510076800 wall=1986-03-02T02:00:00 fold-until(1986-03-02T03:00:00) type=2 +10 AEST
0052: 1986-10-18T16:00:00Z unix=530035200 wall=1986-10-19T02:00:00 gap-until(1986-10-19T03:00:00) type=1 +11 AEDT dst
0053: 1987-03-14T16:00:00Z unix=542736000 wall=1987-03-15T02:00:00 fold-until(1987-03-15T03:00:00) type=2 +10 AEST
0054: 1987-10-24T16:00:00Z unix=562089600 wall=1987-10-25T02:00:00 gap-until(1987-10-25T03:00:00) type=1 +11 AEDT dst
0055: 1988-03-19T16:00:00Z unix=574790400 wall=1988-03-20T02:00:00 fold-until(1988-03-20T03:00:00) type=2 +10 AEST
0056: 1988-10-29T16:00:00Z unix=594144000 wall=1988-10-30T02:00:00 gap-until(1988-10-30T03:00:00) type=1 +11 AEDT dst
0057: 1989-03-18T16:00:00Z unix=606240000 wall=1989-03-19T02:00:00 fold-until(1989-03-19T03:00:00) type=2 +10 AEST
0058: 1989-10-28T16:00:00Z unix=625593600 wall=1989-10-29T02:00:00 gap-until(1989-10-29T03:00:00) type=1 +11 AEDT dst
0059: 1990-03-17T16:00:00Z unix=637689600 wall=1990-03-18T02:00:00 fold-until(1990-03-18T03:00:00) type=2 +10 AEST
0060: 1990-10-27T16:00:00Z unix=657043200 wall=1990-10-28T02:00:00 gap-until(1990-10-28T03:00:00) type=1 +11 AEDT dst
0061: 1991-03-30T16:00:00Z unix=670348800 wall=1991-03-31T02:00:00 fold-until(1991-03-31T03:00:00) type=2 +10 AEST
0062: 1991-10-05T16:00:00Z unix=686678400 wall=1991-10-06T02:00:00 gap-until(1991-10-06T03:00:00) type=1 +11 AEDT dst
0063: 1992-03-28T16:00:00Z unix=701798400 wall=1992-03-29T02:00:00 fold-until(1992-03-29T03:00:00) type=2 +10 AEST
0064: 1992-10-03T16:00:00Z unix=718128000 wall=1992-10-04T02:00:00 gap-until(1992-10-04T03:00:00) type=1 +11 AEDT dst
0065: 1993-03-27T16:00:00Z unix=733248000 wall=1993-03-28T02:00:00 fold-until(1993-03-28T03:00:00) type=2 +10 AEST
0066: 1993-10-02T16:00:00Z unix=749577600 wall=1993-10-03T02:00:00 gap-until(1993-10-03T03:00:00) type=1 +11 AEDT dst
0067: 1994-03-26T16:00:00Z unix=764697600 wall=1994-03-27T02:00:00 fold-until(1994-03-27T03:00:00) type=2 +10 AEST
0068: 1994-10-01T16:00:00Z unix=781027200 wall=1994-10-02T02:00:00 gap-until(1994-10-02T03:00:00) type=1 +11 AEDT dst
0069: 1995-03-25T16:00:00Z unix=796147200 wall=1995-03-26T02:00:00 fold-until(1995-03-26T03:00:00) type=2 +10 AEST
0070: 1995-09-30T16:00:00Z unix=812476800 wall=1995-10-01T02:00:00 gap-until(1995-10-01T03:00:00) type=1 +11 AEDT dst
0071: 1996-03-30T16:00:00Z unix=828201600 wall=1996-03-31T02:00:00 fold-until(1996-03-31T03:00:00) type=2 +10 AEST
0072: 1996-10-05T16:00:00Z unix=844531200 wall=1996-10-06T02:00:00 gap-until(1996-10-06T03:00:00) type=1 +11 AEDT dst
0073: 1997-03-29T16:00:00Z unix=859651200 wall=1997-03-30T02:00:00 fold-until(1997-03-30T03:00:00) type=2 +10 AEST
0074: 1997-10-04T16:00:00Z unix=875980800 wall=1997-10-05T02:00:00 gap-until(1997-10-05T03:00:00) type=1 +11 AEDT dst
0075: 1998-03-28T16:00:00Z unix=891100800 wall=1998-03-29T02:00:00 fold-until(1998-03-29T03:00:00) type=2 +10 AEST
0076: 1998-10-03T16:00:00Z unix=907430400 wall=1998-10-04T02:00:00 gap-until(1998-10-04T03:00:00) type=1 +11 AEDT dst
0077: 1999-03-27T16:00:00Z unix=922550400 wall=1999-03-28T02:00:00 fold-until(1999-03-28T03:00:00) type=2 +10 AEST
0078: 1999-10-02T16:00:00Z unix=938880000 wall=1999-10-03T02:00:00 gap-until(1999-10-03T03:00:00) type=1 +11 AEDT dst
0079: 2000-03-25T16:00:00Z unix=954000000 wall=2000-03-26T02:00:00 fold-until(2000-03-26T03:00:00) type=2 +10 AEST
0080: 2000-08-26T16:00:00Z unix=967305600 wall=2000-08-27T02:00:00 gap-until(2000-08-27T03:00:00) type=1 +11 AEDT dst
0081: 2001-03-24T16:00:00Z unix=985449600 wall=2001-03-25T02:00:00 fold-until(2001-03-25T03:00:00) type=2 +10 AEST
0082: 2001-10-06T16:00:00Z unix=1002384000 wall=2001-10-07T02:00:00 gap-until(2001-10-07T03:00:00) type=1 +11 AEDT dst
0083: 2002-03-30T16:00:00Z unix=1017504000 wall=2002-03-31T02:00:00 fold-until(2002-03-31T03:00:00) type=2 +10 AEST
0084: 2002-10-05T16:00:00Z unix=1033833600 wall=2002-10-06T02:00:00 gap-until(2002-10-06T03:00:00) type=1 +11 AEDT dst
0085: 2003-03-29T16:00:00Z unix=1048953600 wall=2003-03-30T02:00:00 fold-until(2003-03-30T03:00:00) type=2 +10 AEST
0086: 2003-10-04T16:00:00Z unix=1065283200 wall=2003-10-05T02:00:00 gap-until(2003-10-05T03:00:00) type=1 +11 AEDT dst
0087: 2004-03-27T16:00:00Z unix=1080403200 wall=2004-03-28T02:00:00 fold-until(2004-03-28T03:00:00) type=2 +10 AEST
0088: 2004-10-02T16:00:00Z unix=1096732800 wall=2004-10-03T02:00:00 gap-until(2004-10-03T03:00:00) type=1 +11 AEDT dst
0089: 2005-03-26T16:00:00Z unix=1111852800 wall=2005-03-27T02:00:00 fold-until(2005-03-27T03:00:00) type=2 +10 AEST
0090: 2005-10-01T16:00:00Z unix=1128182400 wall=2005-10-02T02:00:00 gap-until(2005-10-02T03:00:00) type=1 +11 AEDT dst
0091: 2006-04-01T16:00:00Z unix=1143907200 wall=2006-04-02T02:00:00 fold-until(2006-04-02T03:00:00) type=2 +10 AEST
0092: 2006-09-30T16:00:00Z unix=1159632000 wall=2006-10-01T02:00:00 gap-until(2006-10-01T03:00:00) type=1 +11 AEDT dst
0093: 2007-03-24T16:00:00Z unix=1174752000 wall=2007-03-25T02:00:00 fold-until(2007-03-25T03:00:00) type=2 +10 AEST
0094: 2007-10-06T16:00:00Z unix=1191686400 wall=2007-10-07T02:00:00 gap-until(2007-10-07T03:00:00) type=1 +11 AEDT dst
0095: 2008-04-05T16:00:00Z unix=1207411200 wall=2008-04-06T02:00:00 fold-until(2008-04-06T03:00:00) type=2 +10 AEST
0096: 2008-10-04T16:00:00Z unix=1223136000 wall=2008-10-05T02:00:00 gap-until(2008-10-05T03:00:00) type=1 +11 AEDT dst
0097: 2009-04-04T16:00:00Z unix=1238860800 wall=2009-04-05T02:00:00 fold-until(2009-04-05T03:00:00) type=2 +10 AEST
0098: 2009-10-03T16:00:00Z unix=1254585600 wall=2009-10-04T02:00:00 gap-until(2009-10-04T03:00:00) type=1 +11 AEDT dst
0099: 2010-04-03T16:00:00Z unix=1270310400 wall=2010-04-04T02:00:00 fold-until(2010-04-04T03:00:00) type=2 +10 AEST
0100: 2010-10-02T16:00:00Z unix=1286035200 wall=2010-10-03T02:00:00 gap-until(2010-10-03T03:00:00) type=1 +11 AEDT dst
0101: 2011-04-02T16:00:00Z unix=1301760000 wall=2011-04-03T02:00:00 fold-until(2011-04-03T03:00:00) type=2 +10 AEST
0102: 2011-10-01T16:00:00Z unix=1317484800 wall=2011-10-02T02:00:00 gap-until(2011-10-02T03:00:00) type=1 +11 AEDT dst
0103: 2012-03-31T16:00:00Z unix=1333209600 wall=2012-04-01T02:00:00 fold-until(2012-04-01T03:00:00) type=2 +10 AEST
0104: 2012-10-06T16:00:00Z unix=1349539200 wall=2012-10-07T02:00:00 gap-until(2012-10-07T03:00:00) type=1 +11 AEDT dst
0105: 2013-04-06T16:00:00Z unix=1365264000 wall=2013-04-07T02:00:00 fold-until(2013-04-07T03:00:00) type=2 +10 AEST
0106: 2013-10-05T16:00:00Z unix=1380988800 wall=2013-10-06T02:00:00 gap-until(2013-10-06T03:00:00) type=1 +11 AEDT dst
0107: 2014-04-05T16:00:00Z unix=1396713600 wall=2014-04-06T02:00:00 fold-until(2014-04-06T03:00:00) type=2 +10 AEST
0108: 2014-10-04T16:00:00Z unix=1412438400 wall=2014-10-05T02:00:00 gap-until(2014-10-05T03:00:00) type=1 +11 AEDT dst
0109: 2015-04-04T16:00:00Z unix=1428163200 wall=2015-04-05T02:00:00 fold-until(2015-04-05T03:00:00) type=2 +10 AEST
0110: 2015-10-03T16:00:00Z unix=1443888000 wall=2015-10-04T02:00:00 gap-until(2015-10-04T03:00:00) type=1 +11 AEDT dst
0111: 2016-04-02T16:00:00Z unix=1459612800 wall=2016-04-03T02:00:00 fold-until(2016-04-03T03:00:00) type=2 +10 AEST
0112: 2016-10-01T16:00:00Z unix=1475337600 wall=2016-10-02T02:00:00 gap-until(2016-10-02T03:00:00) type=1 +11 AEDT dst
0113: 2017-04-01T16:00:00Z unix=1491062400 wall=2017-04-02T02:00:00 fold-until(2017-04-02T03:00:00) type=2 +10 AEST
0114: 2017-09-30T16:00:00Z unix=1506787200 wall=2017-10-01T02:00:00 gap-until(2017-10-01T03:00:00) type=1 +11 AEDT dst
0115: 2018-03-31T16:00:00Z unix=1522512000 wall=2018-04-01T02:00:00 fold-until(2018-04-01T03:00:00) type=2 +10 AEST
0116: 2018-10-06T16:00:00Z unix=1538841600 wall=2018-10-07T02:00:00 gap-until(2018-10-07T03:00:00) type=1 +11 AEDT dst
0117: 2019-04-06T16:00:00Z unix=1554566400 wall=2019-04-07T02:00:00 fold-until(2019-04-07T03:00:00) type=2 +10 AEST
0118: 2019-10-05T16:00:00Z unix=1570291200 wall=2019-10-06T02:00:00 gap-until(2019-10-06T03:00:00) type=1 +11 AEDT dst
0119: 2020-04-04T16:00:00Z unix=1586016000 wall=2020-04-05T02:00:00 fold-until(2020-04-05T03:00:00) type=2 +10 AEST
0120: 2020-10-03T16:00:00Z unix=1601740800 wall=2020-10-04T02:00:00 gap-until(2020-10-04T03:00:00) type=1 +11 AEDT dst
0121: 2021-04-03T16:00:00Z unix=1617465600 wall=2021-04-04T02:00:00 fold-until(2021-04-04T03:00:00) type=2 +10 AEST
0122: 2021-10-02T16:00:00Z unix=1633190400 wall=2021-10-03T02:00:00 gap-until(2021-10-03T03:00:00) type=1 +11 AEDT dst
0123: 2022-04-02T16:00:00Z unix=1648915200 wall=2022-04-03T02:00:00 fold-until(2022-04-03T03:00:00) type=2 +10 AEST
0124: 2022-10-01T16:00:00Z unix=1664640000 wall=2022-10-02T02:00:00 gap-until(2022-10-02T03:00:00) type=1 +11 AEDT dst
0125: 2023-04-01T16:00:00Z unix=1680364800 wall=2023-04-02T02:00:00 fold-until(2023-04-02T03:00:00) type=2 +10 AEST
0126: 2023-09-30T16:00:00Z unix=1696089600 wall=2023-10-01T02:00:00 gap-until(2023-10-01T03:00:00) type=1 +11 AEDT dst
0127: 2024-04-06T16:00:00Z unix=1712419200 wall=2024-04-07T02:00:00 fold-until(2024-04-07T03:00:00) type=2 +10 AEST
0128: 2024-10-05T16:00:00Z unix=1728144000 wall=2024-10-06T02:00:00 gap-until(2024-10-06T03:00:00) type=1 +11 AEDT dst
0129: 2025-04-05T16:00:00Z unix=1743868800 wall=2025-04-06T02:00:00 fold-until(2025-04-06T03:00:00) type=2 +10 AEST
0130: 2025-10-04T16:00:00Z unix=1759593600 wall=2025-10-05T02:00:00 gap-until(2025-10-05T03:00:00) type=1 +11 AEDT dst
0131: 2026-04-04T16:00:00Z unix=1775318400 wall=2026-04-05T02:00:00 fold-until(2026-04-05T03:00:00) type=2 +10 AEST
0132: 2026-10-03T16:00:00Z unix=1791043200 wall=2026-10-04T02:00:00 gap-until(2026-10-04T03:00:00) type=1 +11 AEDT dst
0133: 2027-04-03T16:00:00Z unix=1806768000 wall=2027-04-04T02:00:00 fold-until(2027-04-04T03:00:00) type=2 +10 AEST
0134: 2027-10-02T16:00:00Z unix=1822492800 wall=2027-10-03T02:00:00 gap-until(2027-10-03T03:00:00) type=1 +11 AEDT dst
0135: 2028-04-01T16:00:00Z unix=1838217600 wall=2028-04-02T02:00:00 fold-until(2028-04-02T03:00:00) type=2 +10 AEST
0136: 2028-09-30T16:00:00Z unix=1853942400 wall=2028-10-01T02:00:00 gap-until(2028-10-01T03:00:00) type=1 +11 AEDT dst
0137: 2029-03-31T16:00:00Z unix=1869667200 wall=2029-04-01T02:00:00 fold-until(2029-04-01T03:00:00) type=2 +10 AEST
0138: 2029-10-06T16:00:00Z unix=1885996800 wall=2029-10-07T02:00:00 gap-until(2029-10-07T03:00:00) type=1 +11 AEDT dst
0139: 2030-04-06T16:00:00Z unix=1901721600 wall=2030-04-07T02:00:00 fold-until(2030-04-07T03:00:00) type=2 +10 AEST
0140: 2030-10-05T16:00:00Z unix=1917446400 wall=2030-10-06T02:00:00 gap-until(2030-10-06T03:00:00) type=1 +11 AEDT dst
0141: 2031-04-05T16:00:00Z unix=1933171200 wall=2031-04-06T02:00:00 fold-until(2031-04-06T03:00:00) type=2 +10 AEST
0142: 2031-10-04T16:00:00Z unix=1948896000 wall=2031-10-05T02:00:00 gap-until(2031-10-05T03:00:00) type=1 +11 AEDT dst
0143: 2032-04-03T16:00:00Z unix=1964620800 wall=2032-04-04T02:00:00 fold-until(2032-04-04T03:00:00) type=2 +10 AEST
0144: 2032-10-02T16:00:00Z unix=1980345600 wall=2032-10-03T02:00:00 gap-until(2032-10-03T03:00:00) type=1 +11 AEDT dst
0145: 2033-04-02T16:00:00Z unix=1996070400 wall=2033-04-03T02:00:00 fold-until(2033-04-03T03:00:00) type=2 +10 AEST
0146: 2033-10-01T16:00:00Z unix=2011795200 wall=2033-10-02T02:00:00 gap-until(2033-10-02T03:00:00) type=1 +11 AEDT dst
0147: 2034-04-01T16:00:00Z unix=2027520000 wall=2034-04-02T02:00:00 fold-until(2034-04-02T03:00:00) type=2 +10 AEST
0148: 2034-09-30T16:00:00Z unix=2043244800 wall=2034-10-01T02:00:00 gap-until(2034-10-01T03:00:00) type=1 +11 AEDT dst
0149: 2035-03-31T16:00:00Z unix=2058969600 wall=2035-04-01T02:00:00 fold-until(2035-04-01T03:00:00) type=2 +10 AEST
0150: 2035-10-06T16:00:00Z unix=2075299200 wall=2035-10-07T02:00:00 gap-until(2035-10-07T03:00:00) type=1 +11 AEDT dst
0151: 2036-04-05T16:00:00Z unix=2091024000 wall=2036-04-06T02:00:00 fold-until(2036-04-06T03:00:00) type=2 +10 AEST
0152: 2036-10-04T16:00:00Z unix=2106748800 wall=2036-10-05T02:00:00 gap-until(2036-10-05T03:00:00) type=1 +11 AEDT dst
0153: 2037-04-04T16:00:00Z unix=2122473600 wall=2037-04-05T02:00:00 fold-until(2037-04-05T03:00:00) type=2 +10 AEST
0154: 2037-10-03T16:00:00Z unix=2138198400 wall=2037-10-04T02:00:00 gap-until(2037-10-04T03:00:00) type=1 +11 AEDT dst

View file

@ -0,0 +1,169 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
Australia/Tasmania
LOCAL TIME TYPES
000: offset=+09:49:16 designation=LMT indicator=local/wall
001: offset=+11 designation=AEDT dst indicator=local/std
002: offset=+10 designation=AEST indicator=local/std
003: offset=+10 designation=AEST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T11:49:15 unambiguous type=0 +09:49:16 LMT
0001: 1895-08-31T14:10:44Z unix=-2345795356 wall=1895-09-01T00:00:00 gap-until(1895-09-01T00:10:44) type=3 +10 AEST
0002: 1916-09-30T16:00:00Z unix=-1680508800 wall=1916-10-01T02:00:00 gap-until(1916-10-01T03:00:00) type=1 +11 AEDT dst
0003: 1917-03-24T16:00:00Z unix=-1665388800 wall=1917-03-25T02:00:00 fold-until(1917-03-25T03:00:00) type=2 +10 AEST
0004: 1917-10-27T16:00:00Z unix=-1646640000 wall=1917-10-28T02:00:00 gap-until(1917-10-28T03:00:00) type=1 +11 AEDT dst
0005: 1918-03-02T16:00:00Z unix=-1635753600 wall=1918-03-03T02:00:00 fold-until(1918-03-03T03:00:00) type=2 +10 AEST
0006: 1918-10-26T16:00:00Z unix=-1615190400 wall=1918-10-27T02:00:00 gap-until(1918-10-27T03:00:00) type=1 +11 AEDT dst
0007: 1919-03-01T16:00:00Z unix=-1604304000 wall=1919-03-02T02:00:00 fold-until(1919-03-02T03:00:00) type=2 +10 AEST
0008: 1941-12-31T16:00:00Z unix=-883641600 wall=1942-01-01T02:00:00 gap-until(1942-01-01T03:00:00) type=1 +11 AEDT dst
0009: 1942-03-28T16:00:00Z unix=-876124800 wall=1942-03-29T02:00:00 fold-until(1942-03-29T03:00:00) type=2 +10 AEST
0010: 1942-09-26T16:00:00Z unix=-860400000 wall=1942-09-27T02:00:00 gap-until(1942-09-27T03:00:00) type=1 +11 AEDT dst
0011: 1943-03-27T16:00:00Z unix=-844675200 wall=1943-03-28T02:00:00 fold-until(1943-03-28T03:00:00) type=2 +10 AEST
0012: 1943-10-02T16:00:00Z unix=-828345600 wall=1943-10-03T02:00:00 gap-until(1943-10-03T03:00:00) type=1 +11 AEDT dst
0013: 1944-03-25T16:00:00Z unix=-813225600 wall=1944-03-26T02:00:00 fold-until(1944-03-26T03:00:00) type=2 +10 AEST
0014: 1967-09-30T16:00:00Z unix=-71136000 wall=1967-10-01T02:00:00 gap-until(1967-10-01T03:00:00) type=1 +11 AEDT dst
0015: 1968-03-30T16:00:00Z unix=-55411200 wall=1968-03-31T02:00:00 fold-until(1968-03-31T03:00:00) type=2 +10 AEST
0016: 1968-10-26T16:00:00Z unix=-37267200 wall=1968-10-27T02:00:00 gap-until(1968-10-27T03:00:00) type=1 +11 AEDT dst
0017: 1969-03-08T16:00:00Z unix=-25776000 wall=1969-03-09T02:00:00 fold-until(1969-03-09T03:00:00) type=2 +10 AEST
0018: 1969-10-25T16:00:00Z unix=-5817600 wall=1969-10-26T02:00:00 gap-until(1969-10-26T03:00:00) type=1 +11 AEDT dst
0019: 1970-03-07T16:00:00Z unix=5673600 wall=1970-03-08T02:00:00 fold-until(1970-03-08T03:00:00) type=2 +10 AEST
0020: 1970-10-24T16:00:00Z unix=25632000 wall=1970-10-25T02:00:00 gap-until(1970-10-25T03:00:00) type=1 +11 AEDT dst
0021: 1971-03-13T16:00:00Z unix=37728000 wall=1971-03-14T02:00:00 fold-until(1971-03-14T03:00:00) type=2 +10 AEST
0022: 1971-10-30T16:00:00Z unix=57686400 wall=1971-10-31T02:00:00 gap-until(1971-10-31T03:00:00) type=1 +11 AEDT dst
0023: 1972-02-26T16:00:00Z unix=67968000 wall=1972-02-27T02:00:00 fold-until(1972-02-27T03:00:00) type=2 +10 AEST
0024: 1972-10-28T16:00:00Z unix=89136000 wall=1972-10-29T02:00:00 gap-until(1972-10-29T03:00:00) type=1 +11 AEDT dst
0025: 1973-03-03T16:00:00Z unix=100022400 wall=1973-03-04T02:00:00 fold-until(1973-03-04T03:00:00) type=2 +10 AEST
0026: 1973-10-27T16:00:00Z unix=120585600 wall=1973-10-28T02:00:00 gap-until(1973-10-28T03:00:00) type=1 +11 AEDT dst
0027: 1974-03-02T16:00:00Z unix=131472000 wall=1974-03-03T02:00:00 fold-until(1974-03-03T03:00:00) type=2 +10 AEST
0028: 1974-10-26T16:00:00Z unix=152035200 wall=1974-10-27T02:00:00 gap-until(1974-10-27T03:00:00) type=1 +11 AEDT dst
0029: 1975-03-01T16:00:00Z unix=162921600 wall=1975-03-02T02:00:00 fold-until(1975-03-02T03:00:00) type=2 +10 AEST
0030: 1975-10-25T16:00:00Z unix=183484800 wall=1975-10-26T02:00:00 gap-until(1975-10-26T03:00:00) type=1 +11 AEDT dst
0031: 1976-03-06T16:00:00Z unix=194976000 wall=1976-03-07T02:00:00 fold-until(1976-03-07T03:00:00) type=2 +10 AEST
0032: 1976-10-30T16:00:00Z unix=215539200 wall=1976-10-31T02:00:00 gap-until(1976-10-31T03:00:00) type=1 +11 AEDT dst
0033: 1977-03-05T16:00:00Z unix=226425600 wall=1977-03-06T02:00:00 fold-until(1977-03-06T03:00:00) type=2 +10 AEST
0034: 1977-10-29T16:00:00Z unix=246988800 wall=1977-10-30T02:00:00 gap-until(1977-10-30T03:00:00) type=1 +11 AEDT dst
0035: 1978-03-04T16:00:00Z unix=257875200 wall=1978-03-05T02:00:00 fold-until(1978-03-05T03:00:00) type=2 +10 AEST
0036: 1978-10-28T16:00:00Z unix=278438400 wall=1978-10-29T02:00:00 gap-until(1978-10-29T03:00:00) type=1 +11 AEDT dst
0037: 1979-03-03T16:00:00Z unix=289324800 wall=1979-03-04T02:00:00 fold-until(1979-03-04T03:00:00) type=2 +10 AEST
0038: 1979-10-27T16:00:00Z unix=309888000 wall=1979-10-28T02:00:00 gap-until(1979-10-28T03:00:00) type=1 +11 AEDT dst
0039: 1980-03-01T16:00:00Z unix=320774400 wall=1980-03-02T02:00:00 fold-until(1980-03-02T03:00:00) type=2 +10 AEST
0040: 1980-10-25T16:00:00Z unix=341337600 wall=1980-10-26T02:00:00 gap-until(1980-10-26T03:00:00) type=1 +11 AEDT dst
0041: 1981-02-28T16:00:00Z unix=352224000 wall=1981-03-01T02:00:00 fold-until(1981-03-01T03:00:00) type=2 +10 AEST
0042: 1981-10-24T16:00:00Z unix=372787200 wall=1981-10-25T02:00:00 gap-until(1981-10-25T03:00:00) type=1 +11 AEDT dst
0043: 1982-03-27T16:00:00Z unix=386092800 wall=1982-03-28T02:00:00 fold-until(1982-03-28T03:00:00) type=2 +10 AEST
0044: 1982-10-30T16:00:00Z unix=404841600 wall=1982-10-31T02:00:00 gap-until(1982-10-31T03:00:00) type=1 +11 AEDT dst
0045: 1983-03-26T16:00:00Z unix=417542400 wall=1983-03-27T02:00:00 fold-until(1983-03-27T03:00:00) type=2 +10 AEST
0046: 1983-10-29T16:00:00Z unix=436291200 wall=1983-10-30T02:00:00 gap-until(1983-10-30T03:00:00) type=1 +11 AEDT dst
0047: 1984-03-03T16:00:00Z unix=447177600 wall=1984-03-04T02:00:00 fold-until(1984-03-04T03:00:00) type=2 +10 AEST
0048: 1984-10-27T16:00:00Z unix=467740800 wall=1984-10-28T02:00:00 gap-until(1984-10-28T03:00:00) type=1 +11 AEDT dst
0049: 1985-03-02T16:00:00Z unix=478627200 wall=1985-03-03T02:00:00 fold-until(1985-03-03T03:00:00) type=2 +10 AEST
0050: 1985-10-26T16:00:00Z unix=499190400 wall=1985-10-27T02:00:00 gap-until(1985-10-27T03:00:00) type=1 +11 AEDT dst
0051: 1986-03-01T16:00:00Z unix=510076800 wall=1986-03-02T02:00:00 fold-until(1986-03-02T03:00:00) type=2 +10 AEST
0052: 1986-10-18T16:00:00Z unix=530035200 wall=1986-10-19T02:00:00 gap-until(1986-10-19T03:00:00) type=1 +11 AEDT dst
0053: 1987-03-14T16:00:00Z unix=542736000 wall=1987-03-15T02:00:00 fold-until(1987-03-15T03:00:00) type=2 +10 AEST
0054: 1987-10-24T16:00:00Z unix=562089600 wall=1987-10-25T02:00:00 gap-until(1987-10-25T03:00:00) type=1 +11 AEDT dst
0055: 1988-03-19T16:00:00Z unix=574790400 wall=1988-03-20T02:00:00 fold-until(1988-03-20T03:00:00) type=2 +10 AEST
0056: 1988-10-29T16:00:00Z unix=594144000 wall=1988-10-30T02:00:00 gap-until(1988-10-30T03:00:00) type=1 +11 AEDT dst
0057: 1989-03-18T16:00:00Z unix=606240000 wall=1989-03-19T02:00:00 fold-until(1989-03-19T03:00:00) type=2 +10 AEST
0058: 1989-10-28T16:00:00Z unix=625593600 wall=1989-10-29T02:00:00 gap-until(1989-10-29T03:00:00) type=1 +11 AEDT dst
0059: 1990-03-17T16:00:00Z unix=637689600 wall=1990-03-18T02:00:00 fold-until(1990-03-18T03:00:00) type=2 +10 AEST
0060: 1990-10-27T16:00:00Z unix=657043200 wall=1990-10-28T02:00:00 gap-until(1990-10-28T03:00:00) type=1 +11 AEDT dst
0061: 1991-03-30T16:00:00Z unix=670348800 wall=1991-03-31T02:00:00 fold-until(1991-03-31T03:00:00) type=2 +10 AEST
0062: 1991-10-05T16:00:00Z unix=686678400 wall=1991-10-06T02:00:00 gap-until(1991-10-06T03:00:00) type=1 +11 AEDT dst
0063: 1992-03-28T16:00:00Z unix=701798400 wall=1992-03-29T02:00:00 fold-until(1992-03-29T03:00:00) type=2 +10 AEST
0064: 1992-10-03T16:00:00Z unix=718128000 wall=1992-10-04T02:00:00 gap-until(1992-10-04T03:00:00) type=1 +11 AEDT dst
0065: 1993-03-27T16:00:00Z unix=733248000 wall=1993-03-28T02:00:00 fold-until(1993-03-28T03:00:00) type=2 +10 AEST
0066: 1993-10-02T16:00:00Z unix=749577600 wall=1993-10-03T02:00:00 gap-until(1993-10-03T03:00:00) type=1 +11 AEDT dst
0067: 1994-03-26T16:00:00Z unix=764697600 wall=1994-03-27T02:00:00 fold-until(1994-03-27T03:00:00) type=2 +10 AEST
0068: 1994-10-01T16:00:00Z unix=781027200 wall=1994-10-02T02:00:00 gap-until(1994-10-02T03:00:00) type=1 +11 AEDT dst
0069: 1995-03-25T16:00:00Z unix=796147200 wall=1995-03-26T02:00:00 fold-until(1995-03-26T03:00:00) type=2 +10 AEST
0070: 1995-09-30T16:00:00Z unix=812476800 wall=1995-10-01T02:00:00 gap-until(1995-10-01T03:00:00) type=1 +11 AEDT dst
0071: 1996-03-30T16:00:00Z unix=828201600 wall=1996-03-31T02:00:00 fold-until(1996-03-31T03:00:00) type=2 +10 AEST
0072: 1996-10-05T16:00:00Z unix=844531200 wall=1996-10-06T02:00:00 gap-until(1996-10-06T03:00:00) type=1 +11 AEDT dst
0073: 1997-03-29T16:00:00Z unix=859651200 wall=1997-03-30T02:00:00 fold-until(1997-03-30T03:00:00) type=2 +10 AEST
0074: 1997-10-04T16:00:00Z unix=875980800 wall=1997-10-05T02:00:00 gap-until(1997-10-05T03:00:00) type=1 +11 AEDT dst
0075: 1998-03-28T16:00:00Z unix=891100800 wall=1998-03-29T02:00:00 fold-until(1998-03-29T03:00:00) type=2 +10 AEST
0076: 1998-10-03T16:00:00Z unix=907430400 wall=1998-10-04T02:00:00 gap-until(1998-10-04T03:00:00) type=1 +11 AEDT dst
0077: 1999-03-27T16:00:00Z unix=922550400 wall=1999-03-28T02:00:00 fold-until(1999-03-28T03:00:00) type=2 +10 AEST
0078: 1999-10-02T16:00:00Z unix=938880000 wall=1999-10-03T02:00:00 gap-until(1999-10-03T03:00:00) type=1 +11 AEDT dst
0079: 2000-03-25T16:00:00Z unix=954000000 wall=2000-03-26T02:00:00 fold-until(2000-03-26T03:00:00) type=2 +10 AEST
0080: 2000-08-26T16:00:00Z unix=967305600 wall=2000-08-27T02:00:00 gap-until(2000-08-27T03:00:00) type=1 +11 AEDT dst
0081: 2001-03-24T16:00:00Z unix=985449600 wall=2001-03-25T02:00:00 fold-until(2001-03-25T03:00:00) type=2 +10 AEST
0082: 2001-10-06T16:00:00Z unix=1002384000 wall=2001-10-07T02:00:00 gap-until(2001-10-07T03:00:00) type=1 +11 AEDT dst
0083: 2002-03-30T16:00:00Z unix=1017504000 wall=2002-03-31T02:00:00 fold-until(2002-03-31T03:00:00) type=2 +10 AEST
0084: 2002-10-05T16:00:00Z unix=1033833600 wall=2002-10-06T02:00:00 gap-until(2002-10-06T03:00:00) type=1 +11 AEDT dst
0085: 2003-03-29T16:00:00Z unix=1048953600 wall=2003-03-30T02:00:00 fold-until(2003-03-30T03:00:00) type=2 +10 AEST
0086: 2003-10-04T16:00:00Z unix=1065283200 wall=2003-10-05T02:00:00 gap-until(2003-10-05T03:00:00) type=1 +11 AEDT dst
0087: 2004-03-27T16:00:00Z unix=1080403200 wall=2004-03-28T02:00:00 fold-until(2004-03-28T03:00:00) type=2 +10 AEST
0088: 2004-10-02T16:00:00Z unix=1096732800 wall=2004-10-03T02:00:00 gap-until(2004-10-03T03:00:00) type=1 +11 AEDT dst
0089: 2005-03-26T16:00:00Z unix=1111852800 wall=2005-03-27T02:00:00 fold-until(2005-03-27T03:00:00) type=2 +10 AEST
0090: 2005-10-01T16:00:00Z unix=1128182400 wall=2005-10-02T02:00:00 gap-until(2005-10-02T03:00:00) type=1 +11 AEDT dst
0091: 2006-04-01T16:00:00Z unix=1143907200 wall=2006-04-02T02:00:00 fold-until(2006-04-02T03:00:00) type=2 +10 AEST
0092: 2006-09-30T16:00:00Z unix=1159632000 wall=2006-10-01T02:00:00 gap-until(2006-10-01T03:00:00) type=1 +11 AEDT dst
0093: 2007-03-24T16:00:00Z unix=1174752000 wall=2007-03-25T02:00:00 fold-until(2007-03-25T03:00:00) type=2 +10 AEST
0094: 2007-10-06T16:00:00Z unix=1191686400 wall=2007-10-07T02:00:00 gap-until(2007-10-07T03:00:00) type=1 +11 AEDT dst
0095: 2008-04-05T16:00:00Z unix=1207411200 wall=2008-04-06T02:00:00 fold-until(2008-04-06T03:00:00) type=2 +10 AEST
0096: 2008-10-04T16:00:00Z unix=1223136000 wall=2008-10-05T02:00:00 gap-until(2008-10-05T03:00:00) type=1 +11 AEDT dst
0097: 2009-04-04T16:00:00Z unix=1238860800 wall=2009-04-05T02:00:00 fold-until(2009-04-05T03:00:00) type=2 +10 AEST
0098: 2009-10-03T16:00:00Z unix=1254585600 wall=2009-10-04T02:00:00 gap-until(2009-10-04T03:00:00) type=1 +11 AEDT dst
0099: 2010-04-03T16:00:00Z unix=1270310400 wall=2010-04-04T02:00:00 fold-until(2010-04-04T03:00:00) type=2 +10 AEST
0100: 2010-10-02T16:00:00Z unix=1286035200 wall=2010-10-03T02:00:00 gap-until(2010-10-03T03:00:00) type=1 +11 AEDT dst
0101: 2011-04-02T16:00:00Z unix=1301760000 wall=2011-04-03T02:00:00 fold-until(2011-04-03T03:00:00) type=2 +10 AEST
0102: 2011-10-01T16:00:00Z unix=1317484800 wall=2011-10-02T02:00:00 gap-until(2011-10-02T03:00:00) type=1 +11 AEDT dst
0103: 2012-03-31T16:00:00Z unix=1333209600 wall=2012-04-01T02:00:00 fold-until(2012-04-01T03:00:00) type=2 +10 AEST
0104: 2012-10-06T16:00:00Z unix=1349539200 wall=2012-10-07T02:00:00 gap-until(2012-10-07T03:00:00) type=1 +11 AEDT dst
0105: 2013-04-06T16:00:00Z unix=1365264000 wall=2013-04-07T02:00:00 fold-until(2013-04-07T03:00:00) type=2 +10 AEST
0106: 2013-10-05T16:00:00Z unix=1380988800 wall=2013-10-06T02:00:00 gap-until(2013-10-06T03:00:00) type=1 +11 AEDT dst
0107: 2014-04-05T16:00:00Z unix=1396713600 wall=2014-04-06T02:00:00 fold-until(2014-04-06T03:00:00) type=2 +10 AEST
0108: 2014-10-04T16:00:00Z unix=1412438400 wall=2014-10-05T02:00:00 gap-until(2014-10-05T03:00:00) type=1 +11 AEDT dst
0109: 2015-04-04T16:00:00Z unix=1428163200 wall=2015-04-05T02:00:00 fold-until(2015-04-05T03:00:00) type=2 +10 AEST
0110: 2015-10-03T16:00:00Z unix=1443888000 wall=2015-10-04T02:00:00 gap-until(2015-10-04T03:00:00) type=1 +11 AEDT dst
0111: 2016-04-02T16:00:00Z unix=1459612800 wall=2016-04-03T02:00:00 fold-until(2016-04-03T03:00:00) type=2 +10 AEST
0112: 2016-10-01T16:00:00Z unix=1475337600 wall=2016-10-02T02:00:00 gap-until(2016-10-02T03:00:00) type=1 +11 AEDT dst
0113: 2017-04-01T16:00:00Z unix=1491062400 wall=2017-04-02T02:00:00 fold-until(2017-04-02T03:00:00) type=2 +10 AEST
0114: 2017-09-30T16:00:00Z unix=1506787200 wall=2017-10-01T02:00:00 gap-until(2017-10-01T03:00:00) type=1 +11 AEDT dst
0115: 2018-03-31T16:00:00Z unix=1522512000 wall=2018-04-01T02:00:00 fold-until(2018-04-01T03:00:00) type=2 +10 AEST
0116: 2018-10-06T16:00:00Z unix=1538841600 wall=2018-10-07T02:00:00 gap-until(2018-10-07T03:00:00) type=1 +11 AEDT dst
0117: 2019-04-06T16:00:00Z unix=1554566400 wall=2019-04-07T02:00:00 fold-until(2019-04-07T03:00:00) type=2 +10 AEST
0118: 2019-10-05T16:00:00Z unix=1570291200 wall=2019-10-06T02:00:00 gap-until(2019-10-06T03:00:00) type=1 +11 AEDT dst
0119: 2020-04-04T16:00:00Z unix=1586016000 wall=2020-04-05T02:00:00 fold-until(2020-04-05T03:00:00) type=2 +10 AEST
0120: 2020-10-03T16:00:00Z unix=1601740800 wall=2020-10-04T02:00:00 gap-until(2020-10-04T03:00:00) type=1 +11 AEDT dst
0121: 2021-04-03T16:00:00Z unix=1617465600 wall=2021-04-04T02:00:00 fold-until(2021-04-04T03:00:00) type=2 +10 AEST
0122: 2021-10-02T16:00:00Z unix=1633190400 wall=2021-10-03T02:00:00 gap-until(2021-10-03T03:00:00) type=1 +11 AEDT dst
0123: 2022-04-02T16:00:00Z unix=1648915200 wall=2022-04-03T02:00:00 fold-until(2022-04-03T03:00:00) type=2 +10 AEST
0124: 2022-10-01T16:00:00Z unix=1664640000 wall=2022-10-02T02:00:00 gap-until(2022-10-02T03:00:00) type=1 +11 AEDT dst
0125: 2023-04-01T16:00:00Z unix=1680364800 wall=2023-04-02T02:00:00 fold-until(2023-04-02T03:00:00) type=2 +10 AEST
0126: 2023-09-30T16:00:00Z unix=1696089600 wall=2023-10-01T02:00:00 gap-until(2023-10-01T03:00:00) type=1 +11 AEDT dst
0127: 2024-04-06T16:00:00Z unix=1712419200 wall=2024-04-07T02:00:00 fold-until(2024-04-07T03:00:00) type=2 +10 AEST
0128: 2024-10-05T16:00:00Z unix=1728144000 wall=2024-10-06T02:00:00 gap-until(2024-10-06T03:00:00) type=1 +11 AEDT dst
0129: 2025-04-05T16:00:00Z unix=1743868800 wall=2025-04-06T02:00:00 fold-until(2025-04-06T03:00:00) type=2 +10 AEST
0130: 2025-10-04T16:00:00Z unix=1759593600 wall=2025-10-05T02:00:00 gap-until(2025-10-05T03:00:00) type=1 +11 AEDT dst
0131: 2026-04-04T16:00:00Z unix=1775318400 wall=2026-04-05T02:00:00 fold-until(2026-04-05T03:00:00) type=2 +10 AEST
0132: 2026-10-03T16:00:00Z unix=1791043200 wall=2026-10-04T02:00:00 gap-until(2026-10-04T03:00:00) type=1 +11 AEDT dst
0133: 2027-04-03T16:00:00Z unix=1806768000 wall=2027-04-04T02:00:00 fold-until(2027-04-04T03:00:00) type=2 +10 AEST
0134: 2027-10-02T16:00:00Z unix=1822492800 wall=2027-10-03T02:00:00 gap-until(2027-10-03T03:00:00) type=1 +11 AEDT dst
0135: 2028-04-01T16:00:00Z unix=1838217600 wall=2028-04-02T02:00:00 fold-until(2028-04-02T03:00:00) type=2 +10 AEST
0136: 2028-09-30T16:00:00Z unix=1853942400 wall=2028-10-01T02:00:00 gap-until(2028-10-01T03:00:00) type=1 +11 AEDT dst
0137: 2029-03-31T16:00:00Z unix=1869667200 wall=2029-04-01T02:00:00 fold-until(2029-04-01T03:00:00) type=2 +10 AEST
0138: 2029-10-06T16:00:00Z unix=1885996800 wall=2029-10-07T02:00:00 gap-until(2029-10-07T03:00:00) type=1 +11 AEDT dst
0139: 2030-04-06T16:00:00Z unix=1901721600 wall=2030-04-07T02:00:00 fold-until(2030-04-07T03:00:00) type=2 +10 AEST
0140: 2030-10-05T16:00:00Z unix=1917446400 wall=2030-10-06T02:00:00 gap-until(2030-10-06T03:00:00) type=1 +11 AEDT dst
0141: 2031-04-05T16:00:00Z unix=1933171200 wall=2031-04-06T02:00:00 fold-until(2031-04-06T03:00:00) type=2 +10 AEST
0142: 2031-10-04T16:00:00Z unix=1948896000 wall=2031-10-05T02:00:00 gap-until(2031-10-05T03:00:00) type=1 +11 AEDT dst
0143: 2032-04-03T16:00:00Z unix=1964620800 wall=2032-04-04T02:00:00 fold-until(2032-04-04T03:00:00) type=2 +10 AEST
0144: 2032-10-02T16:00:00Z unix=1980345600 wall=2032-10-03T02:00:00 gap-until(2032-10-03T03:00:00) type=1 +11 AEDT dst
0145: 2033-04-02T16:00:00Z unix=1996070400 wall=2033-04-03T02:00:00 fold-until(2033-04-03T03:00:00) type=2 +10 AEST
0146: 2033-10-01T16:00:00Z unix=2011795200 wall=2033-10-02T02:00:00 gap-until(2033-10-02T03:00:00) type=1 +11 AEDT dst
0147: 2034-04-01T16:00:00Z unix=2027520000 wall=2034-04-02T02:00:00 fold-until(2034-04-02T03:00:00) type=2 +10 AEST
0148: 2034-09-30T16:00:00Z unix=2043244800 wall=2034-10-01T02:00:00 gap-until(2034-10-01T03:00:00) type=1 +11 AEDT dst
0149: 2035-03-31T16:00:00Z unix=2058969600 wall=2035-04-01T02:00:00 fold-until(2035-04-01T03:00:00) type=2 +10 AEST
0150: 2035-10-06T16:00:00Z unix=2075299200 wall=2035-10-07T02:00:00 gap-until(2035-10-07T03:00:00) type=1 +11 AEDT dst
0151: 2036-04-05T16:00:00Z unix=2091024000 wall=2036-04-06T02:00:00 fold-until(2036-04-06T03:00:00) type=2 +10 AEST
0152: 2036-10-04T16:00:00Z unix=2106748800 wall=2036-10-05T02:00:00 gap-until(2036-10-05T03:00:00) type=1 +11 AEDT dst
0153: 2037-04-04T16:00:00Z unix=2122473600 wall=2037-04-05T02:00:00 fold-until(2037-04-05T03:00:00) type=2 +10 AEST
0154: 2037-10-03T16:00:00Z unix=2138198400 wall=2037-10-04T02:00:00 gap-until(2037-10-04T03:00:00) type=1 +11 AEDT dst
POSIX TIME ZONE STRING
AEST-10AEDT,M10.1.0,M4.1.0/3

View file

@ -0,0 +1,246 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
Europe/Dublin
LOCAL TIME TYPES
000: offset=-00:25:21 designation=LMT indicator=local/wall
001: offset=-00:25:21 designation=DMT indicator=local/wall
002: offset=+00:34:39 designation=IST dst indicator=local/std
003: offset=+01 designation=BST dst indicator=local/std
004: offset=+00 designation=GMT indicator=local/std
005: offset=+01 designation=IST dst indicator=local/std
006: offset=+00 designation=GMT dst indicator=ut/std
007: offset=+01 designation=IST indicator=ut/std
008: offset=+01 designation=IST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T01:34:38 unambiguous type=0 -00:25:21 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-13T20:20:31 unambiguous type=1 -00:25:21 DMT
0002: 1916-05-21T02:25:21Z unix=-1691962479 wall=1916-05-21T02:00:00 gap-until(1916-05-21T03:00:00) type=2 +00:34:39 IST dst
0003: 1916-10-01T02:25:21Z unix=-1680471279 wall=1916-10-01T02:25:21 fold-until(1916-10-01T03:00:00) type=4 +00 GMT
0004: 1917-04-08T02:00:00Z unix=-1664143200 wall=1917-04-08T02:00:00 gap-until(1917-04-08T03:00:00) type=3 +01 BST dst
0005: 1917-09-17T02:00:00Z unix=-1650146400 wall=1917-09-17T02:00:00 fold-until(1917-09-17T03:00:00) type=4 +00 GMT
0006: 1918-03-24T02:00:00Z unix=-1633903200 wall=1918-03-24T02:00:00 gap-until(1918-03-24T03:00:00) type=3 +01 BST dst
0007: 1918-09-30T02:00:00Z unix=-1617487200 wall=1918-09-30T02:00:00 fold-until(1918-09-30T03:00:00) type=4 +00 GMT
0008: 1919-03-30T02:00:00Z unix=-1601848800 wall=1919-03-30T02:00:00 gap-until(1919-03-30T03:00:00) type=3 +01 BST dst
0009: 1919-09-29T02:00:00Z unix=-1586037600 wall=1919-09-29T02:00:00 fold-until(1919-09-29T03:00:00) type=4 +00 GMT
0010: 1920-03-28T02:00:00Z unix=-1570399200 wall=1920-03-28T02:00:00 gap-until(1920-03-28T03:00:00) type=3 +01 BST dst
0011: 1920-10-25T02:00:00Z unix=-1552168800 wall=1920-10-25T02:00:00 fold-until(1920-10-25T03:00:00) type=4 +00 GMT
0012: 1921-04-03T02:00:00Z unix=-1538344800 wall=1921-04-03T02:00:00 gap-until(1921-04-03T03:00:00) type=3 +01 BST dst
0013: 1921-10-03T02:00:00Z unix=-1522533600 wall=1921-10-03T02:00:00 fold-until(1921-10-03T03:00:00) type=4 +00 GMT
0014: 1922-03-26T02:00:00Z unix=-1507500000 wall=1922-03-26T02:00:00 gap-until(1922-03-26T03:00:00) type=5 +01 IST dst
0015: 1922-10-08T02:00:00Z unix=-1490565600 wall=1922-10-08T02:00:00 fold-until(1922-10-08T03:00:00) type=4 +00 GMT
0016: 1923-04-22T02:00:00Z unix=-1473631200 wall=1923-04-22T02:00:00 gap-until(1923-04-22T03:00:00) type=5 +01 IST dst
0017: 1923-09-16T02:00:00Z unix=-1460930400 wall=1923-09-16T02:00:00 fold-until(1923-09-16T03:00:00) type=4 +00 GMT
0018: 1924-04-13T02:00:00Z unix=-1442786400 wall=1924-04-13T02:00:00 gap-until(1924-04-13T03:00:00) type=5 +01 IST dst
0019: 1924-09-21T02:00:00Z unix=-1428876000 wall=1924-09-21T02:00:00 fold-until(1924-09-21T03:00:00) type=4 +00 GMT
0020: 1925-04-19T02:00:00Z unix=-1410732000 wall=1925-04-19T02:00:00 gap-until(1925-04-19T03:00:00) type=5 +01 IST dst
0021: 1925-10-04T02:00:00Z unix=-1396216800 wall=1925-10-04T02:00:00 fold-until(1925-10-04T03:00:00) type=4 +00 GMT
0022: 1926-04-18T02:00:00Z unix=-1379282400 wall=1926-04-18T02:00:00 gap-until(1926-04-18T03:00:00) type=5 +01 IST dst
0023: 1926-10-03T02:00:00Z unix=-1364767200 wall=1926-10-03T02:00:00 fold-until(1926-10-03T03:00:00) type=4 +00 GMT
0024: 1927-04-10T02:00:00Z unix=-1348437600 wall=1927-04-10T02:00:00 gap-until(1927-04-10T03:00:00) type=5 +01 IST dst
0025: 1927-10-02T02:00:00Z unix=-1333317600 wall=1927-10-02T02:00:00 fold-until(1927-10-02T03:00:00) type=4 +00 GMT
0026: 1928-04-22T02:00:00Z unix=-1315778400 wall=1928-04-22T02:00:00 gap-until(1928-04-22T03:00:00) type=5 +01 IST dst
0027: 1928-10-07T02:00:00Z unix=-1301263200 wall=1928-10-07T02:00:00 fold-until(1928-10-07T03:00:00) type=4 +00 GMT
0028: 1929-04-21T02:00:00Z unix=-1284328800 wall=1929-04-21T02:00:00 gap-until(1929-04-21T03:00:00) type=5 +01 IST dst
0029: 1929-10-06T02:00:00Z unix=-1269813600 wall=1929-10-06T02:00:00 fold-until(1929-10-06T03:00:00) type=4 +00 GMT
0030: 1930-04-13T02:00:00Z unix=-1253484000 wall=1930-04-13T02:00:00 gap-until(1930-04-13T03:00:00) type=5 +01 IST dst
0031: 1930-10-05T02:00:00Z unix=-1238364000 wall=1930-10-05T02:00:00 fold-until(1930-10-05T03:00:00) type=4 +00 GMT
0032: 1931-04-19T02:00:00Z unix=-1221429600 wall=1931-04-19T02:00:00 gap-until(1931-04-19T03:00:00) type=5 +01 IST dst
0033: 1931-10-04T02:00:00Z unix=-1206914400 wall=1931-10-04T02:00:00 fold-until(1931-10-04T03:00:00) type=4 +00 GMT
0034: 1932-04-17T02:00:00Z unix=-1189980000 wall=1932-04-17T02:00:00 gap-until(1932-04-17T03:00:00) type=5 +01 IST dst
0035: 1932-10-02T02:00:00Z unix=-1175464800 wall=1932-10-02T02:00:00 fold-until(1932-10-02T03:00:00) type=4 +00 GMT
0036: 1933-04-09T02:00:00Z unix=-1159135200 wall=1933-04-09T02:00:00 gap-until(1933-04-09T03:00:00) type=5 +01 IST dst
0037: 1933-10-08T02:00:00Z unix=-1143410400 wall=1933-10-08T02:00:00 fold-until(1933-10-08T03:00:00) type=4 +00 GMT
0038: 1934-04-22T02:00:00Z unix=-1126476000 wall=1934-04-22T02:00:00 gap-until(1934-04-22T03:00:00) type=5 +01 IST dst
0039: 1934-10-07T02:00:00Z unix=-1111960800 wall=1934-10-07T02:00:00 fold-until(1934-10-07T03:00:00) type=4 +00 GMT
0040: 1935-04-14T02:00:00Z unix=-1095631200 wall=1935-04-14T02:00:00 gap-until(1935-04-14T03:00:00) type=5 +01 IST dst
0041: 1935-10-06T02:00:00Z unix=-1080511200 wall=1935-10-06T02:00:00 fold-until(1935-10-06T03:00:00) type=4 +00 GMT
0042: 1936-04-19T02:00:00Z unix=-1063576800 wall=1936-04-19T02:00:00 gap-until(1936-04-19T03:00:00) type=5 +01 IST dst
0043: 1936-10-04T02:00:00Z unix=-1049061600 wall=1936-10-04T02:00:00 fold-until(1936-10-04T03:00:00) type=4 +00 GMT
0044: 1937-04-18T02:00:00Z unix=-1032127200 wall=1937-04-18T02:00:00 gap-until(1937-04-18T03:00:00) type=5 +01 IST dst
0045: 1937-10-03T02:00:00Z unix=-1017612000 wall=1937-10-03T02:00:00 fold-until(1937-10-03T03:00:00) type=4 +00 GMT
0046: 1938-04-10T02:00:00Z unix=-1001282400 wall=1938-04-10T02:00:00 gap-until(1938-04-10T03:00:00) type=5 +01 IST dst
0047: 1938-10-02T02:00:00Z unix=-986162400 wall=1938-10-02T02:00:00 fold-until(1938-10-02T03:00:00) type=4 +00 GMT
0048: 1939-04-16T02:00:00Z unix=-969228000 wall=1939-04-16T02:00:00 gap-until(1939-04-16T03:00:00) type=5 +01 IST dst
0049: 1939-11-19T02:00:00Z unix=-950479200 wall=1939-11-19T02:00:00 fold-until(1939-11-19T03:00:00) type=4 +00 GMT
0050: 1940-02-25T02:00:00Z unix=-942012000 wall=1940-02-25T02:00:00 gap-until(1940-02-25T03:00:00) type=5 +01 IST dst
0051: 1946-10-06T02:00:00Z unix=-733356000 wall=1946-10-06T02:00:00 fold-until(1946-10-06T03:00:00) type=4 +00 GMT
0052: 1947-03-16T02:00:00Z unix=-719445600 wall=1947-03-16T02:00:00 gap-until(1947-03-16T03:00:00) type=5 +01 IST dst
0053: 1947-11-02T02:00:00Z unix=-699487200 wall=1947-11-02T02:00:00 fold-until(1947-11-02T03:00:00) type=4 +00 GMT
0054: 1948-04-18T02:00:00Z unix=-684972000 wall=1948-04-18T02:00:00 gap-until(1948-04-18T03:00:00) type=5 +01 IST dst
0055: 1948-10-31T02:00:00Z unix=-668037600 wall=1948-10-31T02:00:00 fold-until(1948-10-31T03:00:00) type=4 +00 GMT
0056: 1949-04-03T02:00:00Z unix=-654732000 wall=1949-04-03T02:00:00 gap-until(1949-04-03T03:00:00) type=5 +01 IST dst
0057: 1949-10-30T02:00:00Z unix=-636588000 wall=1949-10-30T02:00:00 fold-until(1949-10-30T03:00:00) type=4 +00 GMT
0058: 1950-04-16T02:00:00Z unix=-622072800 wall=1950-04-16T02:00:00 gap-until(1950-04-16T03:00:00) type=5 +01 IST dst
0059: 1950-10-22T02:00:00Z unix=-605743200 wall=1950-10-22T02:00:00 fold-until(1950-10-22T03:00:00) type=4 +00 GMT
0060: 1951-04-15T02:00:00Z unix=-590623200 wall=1951-04-15T02:00:00 gap-until(1951-04-15T03:00:00) type=5 +01 IST dst
0061: 1951-10-21T02:00:00Z unix=-574293600 wall=1951-10-21T02:00:00 fold-until(1951-10-21T03:00:00) type=4 +00 GMT
0062: 1952-04-20T02:00:00Z unix=-558568800 wall=1952-04-20T02:00:00 gap-until(1952-04-20T03:00:00) type=5 +01 IST dst
0063: 1952-10-26T02:00:00Z unix=-542239200 wall=1952-10-26T02:00:00 fold-until(1952-10-26T03:00:00) type=4 +00 GMT
0064: 1953-04-19T02:00:00Z unix=-527119200 wall=1953-04-19T02:00:00 gap-until(1953-04-19T03:00:00) type=5 +01 IST dst
0065: 1953-10-04T02:00:00Z unix=-512604000 wall=1953-10-04T02:00:00 fold-until(1953-10-04T03:00:00) type=4 +00 GMT
0066: 1954-04-11T02:00:00Z unix=-496274400 wall=1954-04-11T02:00:00 gap-until(1954-04-11T03:00:00) type=5 +01 IST dst
0067: 1954-10-03T02:00:00Z unix=-481154400 wall=1954-10-03T02:00:00 fold-until(1954-10-03T03:00:00) type=4 +00 GMT
0068: 1955-04-17T02:00:00Z unix=-464220000 wall=1955-04-17T02:00:00 gap-until(1955-04-17T03:00:00) type=5 +01 IST dst
0069: 1955-10-02T02:00:00Z unix=-449704800 wall=1955-10-02T02:00:00 fold-until(1955-10-02T03:00:00) type=4 +00 GMT
0070: 1956-04-22T02:00:00Z unix=-432165600 wall=1956-04-22T02:00:00 gap-until(1956-04-22T03:00:00) type=5 +01 IST dst
0071: 1956-10-07T02:00:00Z unix=-417650400 wall=1956-10-07T02:00:00 fold-until(1956-10-07T03:00:00) type=4 +00 GMT
0072: 1957-04-14T02:00:00Z unix=-401320800 wall=1957-04-14T02:00:00 gap-until(1957-04-14T03:00:00) type=5 +01 IST dst
0073: 1957-10-06T02:00:00Z unix=-386200800 wall=1957-10-06T02:00:00 fold-until(1957-10-06T03:00:00) type=4 +00 GMT
0074: 1958-04-20T02:00:00Z unix=-369266400 wall=1958-04-20T02:00:00 gap-until(1958-04-20T03:00:00) type=5 +01 IST dst
0075: 1958-10-05T02:00:00Z unix=-354751200 wall=1958-10-05T02:00:00 fold-until(1958-10-05T03:00:00) type=4 +00 GMT
0076: 1959-04-19T02:00:00Z unix=-337816800 wall=1959-04-19T02:00:00 gap-until(1959-04-19T03:00:00) type=5 +01 IST dst
0077: 1959-10-04T02:00:00Z unix=-323301600 wall=1959-10-04T02:00:00 fold-until(1959-10-04T03:00:00) type=4 +00 GMT
0078: 1960-04-10T02:00:00Z unix=-306972000 wall=1960-04-10T02:00:00 gap-until(1960-04-10T03:00:00) type=5 +01 IST dst
0079: 1960-10-02T02:00:00Z unix=-291852000 wall=1960-10-02T02:00:00 fold-until(1960-10-02T03:00:00) type=4 +00 GMT
0080: 1961-03-26T02:00:00Z unix=-276732000 wall=1961-03-26T02:00:00 gap-until(1961-03-26T03:00:00) type=5 +01 IST dst
0081: 1961-10-29T02:00:00Z unix=-257983200 wall=1961-10-29T02:00:00 fold-until(1961-10-29T03:00:00) type=4 +00 GMT
0082: 1962-03-25T02:00:00Z unix=-245282400 wall=1962-03-25T02:00:00 gap-until(1962-03-25T03:00:00) type=5 +01 IST dst
0083: 1962-10-28T02:00:00Z unix=-226533600 wall=1962-10-28T02:00:00 fold-until(1962-10-28T03:00:00) type=4 +00 GMT
0084: 1963-03-31T02:00:00Z unix=-213228000 wall=1963-03-31T02:00:00 gap-until(1963-03-31T03:00:00) type=5 +01 IST dst
0085: 1963-10-27T02:00:00Z unix=-195084000 wall=1963-10-27T02:00:00 fold-until(1963-10-27T03:00:00) type=4 +00 GMT
0086: 1964-03-22T02:00:00Z unix=-182383200 wall=1964-03-22T02:00:00 gap-until(1964-03-22T03:00:00) type=5 +01 IST dst
0087: 1964-10-25T02:00:00Z unix=-163634400 wall=1964-10-25T02:00:00 fold-until(1964-10-25T03:00:00) type=4 +00 GMT
0088: 1965-03-21T02:00:00Z unix=-150933600 wall=1965-03-21T02:00:00 gap-until(1965-03-21T03:00:00) type=5 +01 IST dst
0089: 1965-10-24T02:00:00Z unix=-132184800 wall=1965-10-24T02:00:00 fold-until(1965-10-24T03:00:00) type=4 +00 GMT
0090: 1966-03-20T02:00:00Z unix=-119484000 wall=1966-03-20T02:00:00 gap-until(1966-03-20T03:00:00) type=5 +01 IST dst
0091: 1966-10-23T02:00:00Z unix=-100735200 wall=1966-10-23T02:00:00 fold-until(1966-10-23T03:00:00) type=4 +00 GMT
0092: 1967-03-19T02:00:00Z unix=-88034400 wall=1967-03-19T02:00:00 gap-until(1967-03-19T03:00:00) type=5 +01 IST dst
0093: 1967-10-29T02:00:00Z unix=-68680800 wall=1967-10-29T02:00:00 fold-until(1967-10-29T03:00:00) type=4 +00 GMT
0094: 1968-02-18T02:00:00Z unix=-59004000 wall=1968-02-18T02:00:00 gap-until(1968-02-18T03:00:00) type=5 +01 IST dst
0095: 1968-10-26T23:00:00Z unix=-37242000 wall=1968-10-27T00:00:00 unambiguous type=8 +01 IST
0096: 1971-10-31T02:00:00Z unix=57722400 wall=1971-10-31T02:00:00 fold-until(1971-10-31T03:00:00) type=6 +00 GMT dst
0097: 1972-03-19T02:00:00Z unix=69818400 wall=1972-03-19T02:00:00 gap-until(1972-03-19T03:00:00) type=7 +01 IST
0098: 1972-10-29T02:00:00Z unix=89172000 wall=1972-10-29T02:00:00 fold-until(1972-10-29T03:00:00) type=6 +00 GMT dst
0099: 1973-03-18T02:00:00Z unix=101268000 wall=1973-03-18T02:00:00 gap-until(1973-03-18T03:00:00) type=7 +01 IST
0100: 1973-10-28T02:00:00Z unix=120621600 wall=1973-10-28T02:00:00 fold-until(1973-10-28T03:00:00) type=6 +00 GMT dst
0101: 1974-03-17T02:00:00Z unix=132717600 wall=1974-03-17T02:00:00 gap-until(1974-03-17T03:00:00) type=7 +01 IST
0102: 1974-10-27T02:00:00Z unix=152071200 wall=1974-10-27T02:00:00 fold-until(1974-10-27T03:00:00) type=6 +00 GMT dst
0103: 1975-03-16T02:00:00Z unix=164167200 wall=1975-03-16T02:00:00 gap-until(1975-03-16T03:00:00) type=7 +01 IST
0104: 1975-10-26T02:00:00Z unix=183520800 wall=1975-10-26T02:00:00 fold-until(1975-10-26T03:00:00) type=6 +00 GMT dst
0105: 1976-03-21T02:00:00Z unix=196221600 wall=1976-03-21T02:00:00 gap-until(1976-03-21T03:00:00) type=7 +01 IST
0106: 1976-10-24T02:00:00Z unix=214970400 wall=1976-10-24T02:00:00 fold-until(1976-10-24T03:00:00) type=6 +00 GMT dst
0107: 1977-03-20T02:00:00Z unix=227671200 wall=1977-03-20T02:00:00 gap-until(1977-03-20T03:00:00) type=7 +01 IST
0108: 1977-10-23T02:00:00Z unix=246420000 wall=1977-10-23T02:00:00 fold-until(1977-10-23T03:00:00) type=6 +00 GMT dst
0109: 1978-03-19T02:00:00Z unix=259120800 wall=1978-03-19T02:00:00 gap-until(1978-03-19T03:00:00) type=7 +01 IST
0110: 1978-10-29T02:00:00Z unix=278474400 wall=1978-10-29T02:00:00 fold-until(1978-10-29T03:00:00) type=6 +00 GMT dst
0111: 1979-03-18T02:00:00Z unix=290570400 wall=1979-03-18T02:00:00 gap-until(1979-03-18T03:00:00) type=7 +01 IST
0112: 1979-10-28T02:00:00Z unix=309924000 wall=1979-10-28T02:00:00 fold-until(1979-10-28T03:00:00) type=6 +00 GMT dst
0113: 1980-03-16T02:00:00Z unix=322020000 wall=1980-03-16T02:00:00 gap-until(1980-03-16T03:00:00) type=7 +01 IST
0114: 1980-10-26T02:00:00Z unix=341373600 wall=1980-10-26T02:00:00 fold-until(1980-10-26T03:00:00) type=6 +00 GMT dst
0115: 1981-03-29T01:00:00Z unix=354675600 wall=1981-03-29T01:00:00 gap-until(1981-03-29T02:00:00) type=7 +01 IST
0116: 1981-10-25T01:00:00Z unix=372819600 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=6 +00 GMT dst
0117: 1982-03-28T01:00:00Z unix=386125200 wall=1982-03-28T01:00:00 gap-until(1982-03-28T02:00:00) type=7 +01 IST
0118: 1982-10-24T01:00:00Z unix=404269200 wall=1982-10-24T01:00:00 fold-until(1982-10-24T02:00:00) type=6 +00 GMT dst
0119: 1983-03-27T01:00:00Z unix=417574800 wall=1983-03-27T01:00:00 gap-until(1983-03-27T02:00:00) type=7 +01 IST
0120: 1983-10-23T01:00:00Z unix=435718800 wall=1983-10-23T01:00:00 fold-until(1983-10-23T02:00:00) type=6 +00 GMT dst
0121: 1984-03-25T01:00:00Z unix=449024400 wall=1984-03-25T01:00:00 gap-until(1984-03-25T02:00:00) type=7 +01 IST
0122: 1984-10-28T01:00:00Z unix=467773200 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=6 +00 GMT dst
0123: 1985-03-31T01:00:00Z unix=481078800 wall=1985-03-31T01:00:00 gap-until(1985-03-31T02:00:00) type=7 +01 IST
0124: 1985-10-27T01:00:00Z unix=499222800 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=6 +00 GMT dst
0125: 1986-03-30T01:00:00Z unix=512528400 wall=1986-03-30T01:00:00 gap-until(1986-03-30T02:00:00) type=7 +01 IST
0126: 1986-10-26T01:00:00Z unix=530672400 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=6 +00 GMT dst
0127: 1987-03-29T01:00:00Z unix=543978000 wall=1987-03-29T01:00:00 gap-until(1987-03-29T02:00:00) type=7 +01 IST
0128: 1987-10-25T01:00:00Z unix=562122000 wall=1987-10-25T01:00:00 fold-until(1987-10-25T02:00:00) type=6 +00 GMT dst
0129: 1988-03-27T01:00:00Z unix=575427600 wall=1988-03-27T01:00:00 gap-until(1988-03-27T02:00:00) type=7 +01 IST
0130: 1988-10-23T01:00:00Z unix=593571600 wall=1988-10-23T01:00:00 fold-until(1988-10-23T02:00:00) type=6 +00 GMT dst
0131: 1989-03-26T01:00:00Z unix=606877200 wall=1989-03-26T01:00:00 gap-until(1989-03-26T02:00:00) type=7 +01 IST
0132: 1989-10-29T01:00:00Z unix=625626000 wall=1989-10-29T01:00:00 fold-until(1989-10-29T02:00:00) type=6 +00 GMT dst
0133: 1990-03-25T01:00:00Z unix=638326800 wall=1990-03-25T01:00:00 gap-until(1990-03-25T02:00:00) type=7 +01 IST
0134: 1990-10-28T01:00:00Z unix=657075600 wall=1990-10-28T01:00:00 fold-until(1990-10-28T02:00:00) type=6 +00 GMT dst
0135: 1991-03-31T01:00:00Z unix=670381200 wall=1991-03-31T01:00:00 gap-until(1991-03-31T02:00:00) type=7 +01 IST
0136: 1991-10-27T01:00:00Z unix=688525200 wall=1991-10-27T01:00:00 fold-until(1991-10-27T02:00:00) type=6 +00 GMT dst
0137: 1992-03-29T01:00:00Z unix=701830800 wall=1992-03-29T01:00:00 gap-until(1992-03-29T02:00:00) type=7 +01 IST
0138: 1992-10-25T01:00:00Z unix=719974800 wall=1992-10-25T01:00:00 fold-until(1992-10-25T02:00:00) type=6 +00 GMT dst
0139: 1993-03-28T01:00:00Z unix=733280400 wall=1993-03-28T01:00:00 gap-until(1993-03-28T02:00:00) type=7 +01 IST
0140: 1993-10-24T01:00:00Z unix=751424400 wall=1993-10-24T01:00:00 fold-until(1993-10-24T02:00:00) type=6 +00 GMT dst
0141: 1994-03-27T01:00:00Z unix=764730000 wall=1994-03-27T01:00:00 gap-until(1994-03-27T02:00:00) type=7 +01 IST
0142: 1994-10-23T01:00:00Z unix=782874000 wall=1994-10-23T01:00:00 fold-until(1994-10-23T02:00:00) type=6 +00 GMT dst
0143: 1995-03-26T01:00:00Z unix=796179600 wall=1995-03-26T01:00:00 gap-until(1995-03-26T02:00:00) type=7 +01 IST
0144: 1995-10-22T01:00:00Z unix=814323600 wall=1995-10-22T01:00:00 fold-until(1995-10-22T02:00:00) type=6 +00 GMT dst
0145: 1996-03-31T01:00:00Z unix=828234000 wall=1996-03-31T01:00:00 gap-until(1996-03-31T02:00:00) type=7 +01 IST
0146: 1996-10-27T01:00:00Z unix=846378000 wall=1996-10-27T01:00:00 fold-until(1996-10-27T02:00:00) type=6 +00 GMT dst
0147: 1997-03-30T01:00:00Z unix=859683600 wall=1997-03-30T01:00:00 gap-until(1997-03-30T02:00:00) type=7 +01 IST
0148: 1997-10-26T01:00:00Z unix=877827600 wall=1997-10-26T01:00:00 fold-until(1997-10-26T02:00:00) type=6 +00 GMT dst
0149: 1998-03-29T01:00:00Z unix=891133200 wall=1998-03-29T01:00:00 gap-until(1998-03-29T02:00:00) type=7 +01 IST
0150: 1998-10-25T01:00:00Z unix=909277200 wall=1998-10-25T01:00:00 fold-until(1998-10-25T02:00:00) type=6 +00 GMT dst
0151: 1999-03-28T01:00:00Z unix=922582800 wall=1999-03-28T01:00:00 gap-until(1999-03-28T02:00:00) type=7 +01 IST
0152: 1999-10-31T01:00:00Z unix=941331600 wall=1999-10-31T01:00:00 fold-until(1999-10-31T02:00:00) type=6 +00 GMT dst
0153: 2000-03-26T01:00:00Z unix=954032400 wall=2000-03-26T01:00:00 gap-until(2000-03-26T02:00:00) type=7 +01 IST
0154: 2000-10-29T01:00:00Z unix=972781200 wall=2000-10-29T01:00:00 fold-until(2000-10-29T02:00:00) type=6 +00 GMT dst
0155: 2001-03-25T01:00:00Z unix=985482000 wall=2001-03-25T01:00:00 gap-until(2001-03-25T02:00:00) type=7 +01 IST
0156: 2001-10-28T01:00:00Z unix=1004230800 wall=2001-10-28T01:00:00 fold-until(2001-10-28T02:00:00) type=6 +00 GMT dst
0157: 2002-03-31T01:00:00Z unix=1017536400 wall=2002-03-31T01:00:00 gap-until(2002-03-31T02:00:00) type=7 +01 IST
0158: 2002-10-27T01:00:00Z unix=1035680400 wall=2002-10-27T01:00:00 fold-until(2002-10-27T02:00:00) type=6 +00 GMT dst
0159: 2003-03-30T01:00:00Z unix=1048986000 wall=2003-03-30T01:00:00 gap-until(2003-03-30T02:00:00) type=7 +01 IST
0160: 2003-10-26T01:00:00Z unix=1067130000 wall=2003-10-26T01:00:00 fold-until(2003-10-26T02:00:00) type=6 +00 GMT dst
0161: 2004-03-28T01:00:00Z unix=1080435600 wall=2004-03-28T01:00:00 gap-until(2004-03-28T02:00:00) type=7 +01 IST
0162: 2004-10-31T01:00:00Z unix=1099184400 wall=2004-10-31T01:00:00 fold-until(2004-10-31T02:00:00) type=6 +00 GMT dst
0163: 2005-03-27T01:00:00Z unix=1111885200 wall=2005-03-27T01:00:00 gap-until(2005-03-27T02:00:00) type=7 +01 IST
0164: 2005-10-30T01:00:00Z unix=1130634000 wall=2005-10-30T01:00:00 fold-until(2005-10-30T02:00:00) type=6 +00 GMT dst
0165: 2006-03-26T01:00:00Z unix=1143334800 wall=2006-03-26T01:00:00 gap-until(2006-03-26T02:00:00) type=7 +01 IST
0166: 2006-10-29T01:00:00Z unix=1162083600 wall=2006-10-29T01:00:00 fold-until(2006-10-29T02:00:00) type=6 +00 GMT dst
0167: 2007-03-25T01:00:00Z unix=1174784400 wall=2007-03-25T01:00:00 gap-until(2007-03-25T02:00:00) type=7 +01 IST
0168: 2007-10-28T01:00:00Z unix=1193533200 wall=2007-10-28T01:00:00 fold-until(2007-10-28T02:00:00) type=6 +00 GMT dst
0169: 2008-03-30T01:00:00Z unix=1206838800 wall=2008-03-30T01:00:00 gap-until(2008-03-30T02:00:00) type=7 +01 IST
0170: 2008-10-26T01:00:00Z unix=1224982800 wall=2008-10-26T01:00:00 fold-until(2008-10-26T02:00:00) type=6 +00 GMT dst
0171: 2009-03-29T01:00:00Z unix=1238288400 wall=2009-03-29T01:00:00 gap-until(2009-03-29T02:00:00) type=7 +01 IST
0172: 2009-10-25T01:00:00Z unix=1256432400 wall=2009-10-25T01:00:00 fold-until(2009-10-25T02:00:00) type=6 +00 GMT dst
0173: 2010-03-28T01:00:00Z unix=1269738000 wall=2010-03-28T01:00:00 gap-until(2010-03-28T02:00:00) type=7 +01 IST
0174: 2010-10-31T01:00:00Z unix=1288486800 wall=2010-10-31T01:00:00 fold-until(2010-10-31T02:00:00) type=6 +00 GMT dst
0175: 2011-03-27T01:00:00Z unix=1301187600 wall=2011-03-27T01:00:00 gap-until(2011-03-27T02:00:00) type=7 +01 IST
0176: 2011-10-30T01:00:00Z unix=1319936400 wall=2011-10-30T01:00:00 fold-until(2011-10-30T02:00:00) type=6 +00 GMT dst
0177: 2012-03-25T01:00:00Z unix=1332637200 wall=2012-03-25T01:00:00 gap-until(2012-03-25T02:00:00) type=7 +01 IST
0178: 2012-10-28T01:00:00Z unix=1351386000 wall=2012-10-28T01:00:00 fold-until(2012-10-28T02:00:00) type=6 +00 GMT dst
0179: 2013-03-31T01:00:00Z unix=1364691600 wall=2013-03-31T01:00:00 gap-until(2013-03-31T02:00:00) type=7 +01 IST
0180: 2013-10-27T01:00:00Z unix=1382835600 wall=2013-10-27T01:00:00 fold-until(2013-10-27T02:00:00) type=6 +00 GMT dst
0181: 2014-03-30T01:00:00Z unix=1396141200 wall=2014-03-30T01:00:00 gap-until(2014-03-30T02:00:00) type=7 +01 IST
0182: 2014-10-26T01:00:00Z unix=1414285200 wall=2014-10-26T01:00:00 fold-until(2014-10-26T02:00:00) type=6 +00 GMT dst
0183: 2015-03-29T01:00:00Z unix=1427590800 wall=2015-03-29T01:00:00 gap-until(2015-03-29T02:00:00) type=7 +01 IST
0184: 2015-10-25T01:00:00Z unix=1445734800 wall=2015-10-25T01:00:00 fold-until(2015-10-25T02:00:00) type=6 +00 GMT dst
0185: 2016-03-27T01:00:00Z unix=1459040400 wall=2016-03-27T01:00:00 gap-until(2016-03-27T02:00:00) type=7 +01 IST
0186: 2016-10-30T01:00:00Z unix=1477789200 wall=2016-10-30T01:00:00 fold-until(2016-10-30T02:00:00) type=6 +00 GMT dst
0187: 2017-03-26T01:00:00Z unix=1490490000 wall=2017-03-26T01:00:00 gap-until(2017-03-26T02:00:00) type=7 +01 IST
0188: 2017-10-29T01:00:00Z unix=1509238800 wall=2017-10-29T01:00:00 fold-until(2017-10-29T02:00:00) type=6 +00 GMT dst
0189: 2018-03-25T01:00:00Z unix=1521939600 wall=2018-03-25T01:00:00 gap-until(2018-03-25T02:00:00) type=7 +01 IST
0190: 2018-10-28T01:00:00Z unix=1540688400 wall=2018-10-28T01:00:00 fold-until(2018-10-28T02:00:00) type=6 +00 GMT dst
0191: 2019-03-31T01:00:00Z unix=1553994000 wall=2019-03-31T01:00:00 gap-until(2019-03-31T02:00:00) type=7 +01 IST
0192: 2019-10-27T01:00:00Z unix=1572138000 wall=2019-10-27T01:00:00 fold-until(2019-10-27T02:00:00) type=6 +00 GMT dst
0193: 2020-03-29T01:00:00Z unix=1585443600 wall=2020-03-29T01:00:00 gap-until(2020-03-29T02:00:00) type=7 +01 IST
0194: 2020-10-25T01:00:00Z unix=1603587600 wall=2020-10-25T01:00:00 fold-until(2020-10-25T02:00:00) type=6 +00 GMT dst
0195: 2021-03-28T01:00:00Z unix=1616893200 wall=2021-03-28T01:00:00 gap-until(2021-03-28T02:00:00) type=7 +01 IST
0196: 2021-10-31T01:00:00Z unix=1635642000 wall=2021-10-31T01:00:00 fold-until(2021-10-31T02:00:00) type=6 +00 GMT dst
0197: 2022-03-27T01:00:00Z unix=1648342800 wall=2022-03-27T01:00:00 gap-until(2022-03-27T02:00:00) type=7 +01 IST
0198: 2022-10-30T01:00:00Z unix=1667091600 wall=2022-10-30T01:00:00 fold-until(2022-10-30T02:00:00) type=6 +00 GMT dst
0199: 2023-03-26T01:00:00Z unix=1679792400 wall=2023-03-26T01:00:00 gap-until(2023-03-26T02:00:00) type=7 +01 IST
0200: 2023-10-29T01:00:00Z unix=1698541200 wall=2023-10-29T01:00:00 fold-until(2023-10-29T02:00:00) type=6 +00 GMT dst
0201: 2024-03-31T01:00:00Z unix=1711846800 wall=2024-03-31T01:00:00 gap-until(2024-03-31T02:00:00) type=7 +01 IST
0202: 2024-10-27T01:00:00Z unix=1729990800 wall=2024-10-27T01:00:00 fold-until(2024-10-27T02:00:00) type=6 +00 GMT dst
0203: 2025-03-30T01:00:00Z unix=1743296400 wall=2025-03-30T01:00:00 gap-until(2025-03-30T02:00:00) type=7 +01 IST
0204: 2025-10-26T01:00:00Z unix=1761440400 wall=2025-10-26T01:00:00 fold-until(2025-10-26T02:00:00) type=6 +00 GMT dst
0205: 2026-03-29T01:00:00Z unix=1774746000 wall=2026-03-29T01:00:00 gap-until(2026-03-29T02:00:00) type=7 +01 IST
0206: 2026-10-25T01:00:00Z unix=1792890000 wall=2026-10-25T01:00:00 fold-until(2026-10-25T02:00:00) type=6 +00 GMT dst
0207: 2027-03-28T01:00:00Z unix=1806195600 wall=2027-03-28T01:00:00 gap-until(2027-03-28T02:00:00) type=7 +01 IST
0208: 2027-10-31T01:00:00Z unix=1824944400 wall=2027-10-31T01:00:00 fold-until(2027-10-31T02:00:00) type=6 +00 GMT dst
0209: 2028-03-26T01:00:00Z unix=1837645200 wall=2028-03-26T01:00:00 gap-until(2028-03-26T02:00:00) type=7 +01 IST
0210: 2028-10-29T01:00:00Z unix=1856394000 wall=2028-10-29T01:00:00 fold-until(2028-10-29T02:00:00) type=6 +00 GMT dst
0211: 2029-03-25T01:00:00Z unix=1869094800 wall=2029-03-25T01:00:00 gap-until(2029-03-25T02:00:00) type=7 +01 IST
0212: 2029-10-28T01:00:00Z unix=1887843600 wall=2029-10-28T01:00:00 fold-until(2029-10-28T02:00:00) type=6 +00 GMT dst
0213: 2030-03-31T01:00:00Z unix=1901149200 wall=2030-03-31T01:00:00 gap-until(2030-03-31T02:00:00) type=7 +01 IST
0214: 2030-10-27T01:00:00Z unix=1919293200 wall=2030-10-27T01:00:00 fold-until(2030-10-27T02:00:00) type=6 +00 GMT dst
0215: 2031-03-30T01:00:00Z unix=1932598800 wall=2031-03-30T01:00:00 gap-until(2031-03-30T02:00:00) type=7 +01 IST
0216: 2031-10-26T01:00:00Z unix=1950742800 wall=2031-10-26T01:00:00 fold-until(2031-10-26T02:00:00) type=6 +00 GMT dst
0217: 2032-03-28T01:00:00Z unix=1964048400 wall=2032-03-28T01:00:00 gap-until(2032-03-28T02:00:00) type=7 +01 IST
0218: 2032-10-31T01:00:00Z unix=1982797200 wall=2032-10-31T01:00:00 fold-until(2032-10-31T02:00:00) type=6 +00 GMT dst
0219: 2033-03-27T01:00:00Z unix=1995498000 wall=2033-03-27T01:00:00 gap-until(2033-03-27T02:00:00) type=7 +01 IST
0220: 2033-10-30T01:00:00Z unix=2014246800 wall=2033-10-30T01:00:00 fold-until(2033-10-30T02:00:00) type=6 +00 GMT dst
0221: 2034-03-26T01:00:00Z unix=2026947600 wall=2034-03-26T01:00:00 gap-until(2034-03-26T02:00:00) type=7 +01 IST
0222: 2034-10-29T01:00:00Z unix=2045696400 wall=2034-10-29T01:00:00 fold-until(2034-10-29T02:00:00) type=6 +00 GMT dst
0223: 2035-03-25T01:00:00Z unix=2058397200 wall=2035-03-25T01:00:00 gap-until(2035-03-25T02:00:00) type=7 +01 IST
0224: 2035-10-28T01:00:00Z unix=2077146000 wall=2035-10-28T01:00:00 fold-until(2035-10-28T02:00:00) type=6 +00 GMT dst
0225: 2036-03-30T01:00:00Z unix=2090451600 wall=2036-03-30T01:00:00 gap-until(2036-03-30T02:00:00) type=7 +01 IST
0226: 2036-10-26T01:00:00Z unix=2108595600 wall=2036-10-26T01:00:00 fold-until(2036-10-26T02:00:00) type=6 +00 GMT dst
0227: 2037-03-29T01:00:00Z unix=2121901200 wall=2037-03-29T01:00:00 gap-until(2037-03-29T02:00:00) type=7 +01 IST
0228: 2037-10-25T01:00:00Z unix=2140045200 wall=2037-10-25T01:00:00 fold-until(2037-10-25T02:00:00) type=6 +00 GMT dst

View file

@ -0,0 +1,248 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
Europe/Dublin
LOCAL TIME TYPES
000: offset=-00:25:21 designation=LMT indicator=local/wall
001: offset=-00:25:21 designation=DMT indicator=local/wall
002: offset=+00:34:39 designation=IST dst indicator=local/std
003: offset=+01 designation=BST dst indicator=local/std
004: offset=+00 designation=GMT indicator=local/std
005: offset=+01 designation=IST dst indicator=local/std
006: offset=+00 designation=GMT dst indicator=ut/std
007: offset=+01 designation=IST indicator=ut/std
008: offset=+01 designation=IST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-02T01:34:38 unambiguous type=0 -00:25:21 LMT
0001: 1880-08-02T00:25:21Z unix=-2821649679 wall=1880-08-02T00:00:00 unambiguous type=1 -00:25:21 DMT
0002: 1916-05-21T02:25:21Z unix=-1691962479 wall=1916-05-21T02:00:00 gap-until(1916-05-21T03:00:00) type=2 +00:34:39 IST dst
0003: 1916-10-01T02:25:21Z unix=-1680471279 wall=1916-10-01T02:25:21 fold-until(1916-10-01T03:00:00) type=4 +00 GMT
0004: 1917-04-08T02:00:00Z unix=-1664143200 wall=1917-04-08T02:00:00 gap-until(1917-04-08T03:00:00) type=3 +01 BST dst
0005: 1917-09-17T02:00:00Z unix=-1650146400 wall=1917-09-17T02:00:00 fold-until(1917-09-17T03:00:00) type=4 +00 GMT
0006: 1918-03-24T02:00:00Z unix=-1633903200 wall=1918-03-24T02:00:00 gap-until(1918-03-24T03:00:00) type=3 +01 BST dst
0007: 1918-09-30T02:00:00Z unix=-1617487200 wall=1918-09-30T02:00:00 fold-until(1918-09-30T03:00:00) type=4 +00 GMT
0008: 1919-03-30T02:00:00Z unix=-1601848800 wall=1919-03-30T02:00:00 gap-until(1919-03-30T03:00:00) type=3 +01 BST dst
0009: 1919-09-29T02:00:00Z unix=-1586037600 wall=1919-09-29T02:00:00 fold-until(1919-09-29T03:00:00) type=4 +00 GMT
0010: 1920-03-28T02:00:00Z unix=-1570399200 wall=1920-03-28T02:00:00 gap-until(1920-03-28T03:00:00) type=3 +01 BST dst
0011: 1920-10-25T02:00:00Z unix=-1552168800 wall=1920-10-25T02:00:00 fold-until(1920-10-25T03:00:00) type=4 +00 GMT
0012: 1921-04-03T02:00:00Z unix=-1538344800 wall=1921-04-03T02:00:00 gap-until(1921-04-03T03:00:00) type=3 +01 BST dst
0013: 1921-10-03T02:00:00Z unix=-1522533600 wall=1921-10-03T02:00:00 fold-until(1921-10-03T03:00:00) type=4 +00 GMT
0014: 1922-03-26T02:00:00Z unix=-1507500000 wall=1922-03-26T02:00:00 gap-until(1922-03-26T03:00:00) type=5 +01 IST dst
0015: 1922-10-08T02:00:00Z unix=-1490565600 wall=1922-10-08T02:00:00 fold-until(1922-10-08T03:00:00) type=4 +00 GMT
0016: 1923-04-22T02:00:00Z unix=-1473631200 wall=1923-04-22T02:00:00 gap-until(1923-04-22T03:00:00) type=5 +01 IST dst
0017: 1923-09-16T02:00:00Z unix=-1460930400 wall=1923-09-16T02:00:00 fold-until(1923-09-16T03:00:00) type=4 +00 GMT
0018: 1924-04-13T02:00:00Z unix=-1442786400 wall=1924-04-13T02:00:00 gap-until(1924-04-13T03:00:00) type=5 +01 IST dst
0019: 1924-09-21T02:00:00Z unix=-1428876000 wall=1924-09-21T02:00:00 fold-until(1924-09-21T03:00:00) type=4 +00 GMT
0020: 1925-04-19T02:00:00Z unix=-1410732000 wall=1925-04-19T02:00:00 gap-until(1925-04-19T03:00:00) type=5 +01 IST dst
0021: 1925-10-04T02:00:00Z unix=-1396216800 wall=1925-10-04T02:00:00 fold-until(1925-10-04T03:00:00) type=4 +00 GMT
0022: 1926-04-18T02:00:00Z unix=-1379282400 wall=1926-04-18T02:00:00 gap-until(1926-04-18T03:00:00) type=5 +01 IST dst
0023: 1926-10-03T02:00:00Z unix=-1364767200 wall=1926-10-03T02:00:00 fold-until(1926-10-03T03:00:00) type=4 +00 GMT
0024: 1927-04-10T02:00:00Z unix=-1348437600 wall=1927-04-10T02:00:00 gap-until(1927-04-10T03:00:00) type=5 +01 IST dst
0025: 1927-10-02T02:00:00Z unix=-1333317600 wall=1927-10-02T02:00:00 fold-until(1927-10-02T03:00:00) type=4 +00 GMT
0026: 1928-04-22T02:00:00Z unix=-1315778400 wall=1928-04-22T02:00:00 gap-until(1928-04-22T03:00:00) type=5 +01 IST dst
0027: 1928-10-07T02:00:00Z unix=-1301263200 wall=1928-10-07T02:00:00 fold-until(1928-10-07T03:00:00) type=4 +00 GMT
0028: 1929-04-21T02:00:00Z unix=-1284328800 wall=1929-04-21T02:00:00 gap-until(1929-04-21T03:00:00) type=5 +01 IST dst
0029: 1929-10-06T02:00:00Z unix=-1269813600 wall=1929-10-06T02:00:00 fold-until(1929-10-06T03:00:00) type=4 +00 GMT
0030: 1930-04-13T02:00:00Z unix=-1253484000 wall=1930-04-13T02:00:00 gap-until(1930-04-13T03:00:00) type=5 +01 IST dst
0031: 1930-10-05T02:00:00Z unix=-1238364000 wall=1930-10-05T02:00:00 fold-until(1930-10-05T03:00:00) type=4 +00 GMT
0032: 1931-04-19T02:00:00Z unix=-1221429600 wall=1931-04-19T02:00:00 gap-until(1931-04-19T03:00:00) type=5 +01 IST dst
0033: 1931-10-04T02:00:00Z unix=-1206914400 wall=1931-10-04T02:00:00 fold-until(1931-10-04T03:00:00) type=4 +00 GMT
0034: 1932-04-17T02:00:00Z unix=-1189980000 wall=1932-04-17T02:00:00 gap-until(1932-04-17T03:00:00) type=5 +01 IST dst
0035: 1932-10-02T02:00:00Z unix=-1175464800 wall=1932-10-02T02:00:00 fold-until(1932-10-02T03:00:00) type=4 +00 GMT
0036: 1933-04-09T02:00:00Z unix=-1159135200 wall=1933-04-09T02:00:00 gap-until(1933-04-09T03:00:00) type=5 +01 IST dst
0037: 1933-10-08T02:00:00Z unix=-1143410400 wall=1933-10-08T02:00:00 fold-until(1933-10-08T03:00:00) type=4 +00 GMT
0038: 1934-04-22T02:00:00Z unix=-1126476000 wall=1934-04-22T02:00:00 gap-until(1934-04-22T03:00:00) type=5 +01 IST dst
0039: 1934-10-07T02:00:00Z unix=-1111960800 wall=1934-10-07T02:00:00 fold-until(1934-10-07T03:00:00) type=4 +00 GMT
0040: 1935-04-14T02:00:00Z unix=-1095631200 wall=1935-04-14T02:00:00 gap-until(1935-04-14T03:00:00) type=5 +01 IST dst
0041: 1935-10-06T02:00:00Z unix=-1080511200 wall=1935-10-06T02:00:00 fold-until(1935-10-06T03:00:00) type=4 +00 GMT
0042: 1936-04-19T02:00:00Z unix=-1063576800 wall=1936-04-19T02:00:00 gap-until(1936-04-19T03:00:00) type=5 +01 IST dst
0043: 1936-10-04T02:00:00Z unix=-1049061600 wall=1936-10-04T02:00:00 fold-until(1936-10-04T03:00:00) type=4 +00 GMT
0044: 1937-04-18T02:00:00Z unix=-1032127200 wall=1937-04-18T02:00:00 gap-until(1937-04-18T03:00:00) type=5 +01 IST dst
0045: 1937-10-03T02:00:00Z unix=-1017612000 wall=1937-10-03T02:00:00 fold-until(1937-10-03T03:00:00) type=4 +00 GMT
0046: 1938-04-10T02:00:00Z unix=-1001282400 wall=1938-04-10T02:00:00 gap-until(1938-04-10T03:00:00) type=5 +01 IST dst
0047: 1938-10-02T02:00:00Z unix=-986162400 wall=1938-10-02T02:00:00 fold-until(1938-10-02T03:00:00) type=4 +00 GMT
0048: 1939-04-16T02:00:00Z unix=-969228000 wall=1939-04-16T02:00:00 gap-until(1939-04-16T03:00:00) type=5 +01 IST dst
0049: 1939-11-19T02:00:00Z unix=-950479200 wall=1939-11-19T02:00:00 fold-until(1939-11-19T03:00:00) type=4 +00 GMT
0050: 1940-02-25T02:00:00Z unix=-942012000 wall=1940-02-25T02:00:00 gap-until(1940-02-25T03:00:00) type=5 +01 IST dst
0051: 1946-10-06T02:00:00Z unix=-733356000 wall=1946-10-06T02:00:00 fold-until(1946-10-06T03:00:00) type=4 +00 GMT
0052: 1947-03-16T02:00:00Z unix=-719445600 wall=1947-03-16T02:00:00 gap-until(1947-03-16T03:00:00) type=5 +01 IST dst
0053: 1947-11-02T02:00:00Z unix=-699487200 wall=1947-11-02T02:00:00 fold-until(1947-11-02T03:00:00) type=4 +00 GMT
0054: 1948-04-18T02:00:00Z unix=-684972000 wall=1948-04-18T02:00:00 gap-until(1948-04-18T03:00:00) type=5 +01 IST dst
0055: 1948-10-31T02:00:00Z unix=-668037600 wall=1948-10-31T02:00:00 fold-until(1948-10-31T03:00:00) type=4 +00 GMT
0056: 1949-04-03T02:00:00Z unix=-654732000 wall=1949-04-03T02:00:00 gap-until(1949-04-03T03:00:00) type=5 +01 IST dst
0057: 1949-10-30T02:00:00Z unix=-636588000 wall=1949-10-30T02:00:00 fold-until(1949-10-30T03:00:00) type=4 +00 GMT
0058: 1950-04-16T02:00:00Z unix=-622072800 wall=1950-04-16T02:00:00 gap-until(1950-04-16T03:00:00) type=5 +01 IST dst
0059: 1950-10-22T02:00:00Z unix=-605743200 wall=1950-10-22T02:00:00 fold-until(1950-10-22T03:00:00) type=4 +00 GMT
0060: 1951-04-15T02:00:00Z unix=-590623200 wall=1951-04-15T02:00:00 gap-until(1951-04-15T03:00:00) type=5 +01 IST dst
0061: 1951-10-21T02:00:00Z unix=-574293600 wall=1951-10-21T02:00:00 fold-until(1951-10-21T03:00:00) type=4 +00 GMT
0062: 1952-04-20T02:00:00Z unix=-558568800 wall=1952-04-20T02:00:00 gap-until(1952-04-20T03:00:00) type=5 +01 IST dst
0063: 1952-10-26T02:00:00Z unix=-542239200 wall=1952-10-26T02:00:00 fold-until(1952-10-26T03:00:00) type=4 +00 GMT
0064: 1953-04-19T02:00:00Z unix=-527119200 wall=1953-04-19T02:00:00 gap-until(1953-04-19T03:00:00) type=5 +01 IST dst
0065: 1953-10-04T02:00:00Z unix=-512604000 wall=1953-10-04T02:00:00 fold-until(1953-10-04T03:00:00) type=4 +00 GMT
0066: 1954-04-11T02:00:00Z unix=-496274400 wall=1954-04-11T02:00:00 gap-until(1954-04-11T03:00:00) type=5 +01 IST dst
0067: 1954-10-03T02:00:00Z unix=-481154400 wall=1954-10-03T02:00:00 fold-until(1954-10-03T03:00:00) type=4 +00 GMT
0068: 1955-04-17T02:00:00Z unix=-464220000 wall=1955-04-17T02:00:00 gap-until(1955-04-17T03:00:00) type=5 +01 IST dst
0069: 1955-10-02T02:00:00Z unix=-449704800 wall=1955-10-02T02:00:00 fold-until(1955-10-02T03:00:00) type=4 +00 GMT
0070: 1956-04-22T02:00:00Z unix=-432165600 wall=1956-04-22T02:00:00 gap-until(1956-04-22T03:00:00) type=5 +01 IST dst
0071: 1956-10-07T02:00:00Z unix=-417650400 wall=1956-10-07T02:00:00 fold-until(1956-10-07T03:00:00) type=4 +00 GMT
0072: 1957-04-14T02:00:00Z unix=-401320800 wall=1957-04-14T02:00:00 gap-until(1957-04-14T03:00:00) type=5 +01 IST dst
0073: 1957-10-06T02:00:00Z unix=-386200800 wall=1957-10-06T02:00:00 fold-until(1957-10-06T03:00:00) type=4 +00 GMT
0074: 1958-04-20T02:00:00Z unix=-369266400 wall=1958-04-20T02:00:00 gap-until(1958-04-20T03:00:00) type=5 +01 IST dst
0075: 1958-10-05T02:00:00Z unix=-354751200 wall=1958-10-05T02:00:00 fold-until(1958-10-05T03:00:00) type=4 +00 GMT
0076: 1959-04-19T02:00:00Z unix=-337816800 wall=1959-04-19T02:00:00 gap-until(1959-04-19T03:00:00) type=5 +01 IST dst
0077: 1959-10-04T02:00:00Z unix=-323301600 wall=1959-10-04T02:00:00 fold-until(1959-10-04T03:00:00) type=4 +00 GMT
0078: 1960-04-10T02:00:00Z unix=-306972000 wall=1960-04-10T02:00:00 gap-until(1960-04-10T03:00:00) type=5 +01 IST dst
0079: 1960-10-02T02:00:00Z unix=-291852000 wall=1960-10-02T02:00:00 fold-until(1960-10-02T03:00:00) type=4 +00 GMT
0080: 1961-03-26T02:00:00Z unix=-276732000 wall=1961-03-26T02:00:00 gap-until(1961-03-26T03:00:00) type=5 +01 IST dst
0081: 1961-10-29T02:00:00Z unix=-257983200 wall=1961-10-29T02:00:00 fold-until(1961-10-29T03:00:00) type=4 +00 GMT
0082: 1962-03-25T02:00:00Z unix=-245282400 wall=1962-03-25T02:00:00 gap-until(1962-03-25T03:00:00) type=5 +01 IST dst
0083: 1962-10-28T02:00:00Z unix=-226533600 wall=1962-10-28T02:00:00 fold-until(1962-10-28T03:00:00) type=4 +00 GMT
0084: 1963-03-31T02:00:00Z unix=-213228000 wall=1963-03-31T02:00:00 gap-until(1963-03-31T03:00:00) type=5 +01 IST dst
0085: 1963-10-27T02:00:00Z unix=-195084000 wall=1963-10-27T02:00:00 fold-until(1963-10-27T03:00:00) type=4 +00 GMT
0086: 1964-03-22T02:00:00Z unix=-182383200 wall=1964-03-22T02:00:00 gap-until(1964-03-22T03:00:00) type=5 +01 IST dst
0087: 1964-10-25T02:00:00Z unix=-163634400 wall=1964-10-25T02:00:00 fold-until(1964-10-25T03:00:00) type=4 +00 GMT
0088: 1965-03-21T02:00:00Z unix=-150933600 wall=1965-03-21T02:00:00 gap-until(1965-03-21T03:00:00) type=5 +01 IST dst
0089: 1965-10-24T02:00:00Z unix=-132184800 wall=1965-10-24T02:00:00 fold-until(1965-10-24T03:00:00) type=4 +00 GMT
0090: 1966-03-20T02:00:00Z unix=-119484000 wall=1966-03-20T02:00:00 gap-until(1966-03-20T03:00:00) type=5 +01 IST dst
0091: 1966-10-23T02:00:00Z unix=-100735200 wall=1966-10-23T02:00:00 fold-until(1966-10-23T03:00:00) type=4 +00 GMT
0092: 1967-03-19T02:00:00Z unix=-88034400 wall=1967-03-19T02:00:00 gap-until(1967-03-19T03:00:00) type=5 +01 IST dst
0093: 1967-10-29T02:00:00Z unix=-68680800 wall=1967-10-29T02:00:00 fold-until(1967-10-29T03:00:00) type=4 +00 GMT
0094: 1968-02-18T02:00:00Z unix=-59004000 wall=1968-02-18T02:00:00 gap-until(1968-02-18T03:00:00) type=5 +01 IST dst
0095: 1968-10-26T23:00:00Z unix=-37242000 wall=1968-10-27T00:00:00 unambiguous type=8 +01 IST
0096: 1971-10-31T02:00:00Z unix=57722400 wall=1971-10-31T02:00:00 fold-until(1971-10-31T03:00:00) type=6 +00 GMT dst
0097: 1972-03-19T02:00:00Z unix=69818400 wall=1972-03-19T02:00:00 gap-until(1972-03-19T03:00:00) type=7 +01 IST
0098: 1972-10-29T02:00:00Z unix=89172000 wall=1972-10-29T02:00:00 fold-until(1972-10-29T03:00:00) type=6 +00 GMT dst
0099: 1973-03-18T02:00:00Z unix=101268000 wall=1973-03-18T02:00:00 gap-until(1973-03-18T03:00:00) type=7 +01 IST
0100: 1973-10-28T02:00:00Z unix=120621600 wall=1973-10-28T02:00:00 fold-until(1973-10-28T03:00:00) type=6 +00 GMT dst
0101: 1974-03-17T02:00:00Z unix=132717600 wall=1974-03-17T02:00:00 gap-until(1974-03-17T03:00:00) type=7 +01 IST
0102: 1974-10-27T02:00:00Z unix=152071200 wall=1974-10-27T02:00:00 fold-until(1974-10-27T03:00:00) type=6 +00 GMT dst
0103: 1975-03-16T02:00:00Z unix=164167200 wall=1975-03-16T02:00:00 gap-until(1975-03-16T03:00:00) type=7 +01 IST
0104: 1975-10-26T02:00:00Z unix=183520800 wall=1975-10-26T02:00:00 fold-until(1975-10-26T03:00:00) type=6 +00 GMT dst
0105: 1976-03-21T02:00:00Z unix=196221600 wall=1976-03-21T02:00:00 gap-until(1976-03-21T03:00:00) type=7 +01 IST
0106: 1976-10-24T02:00:00Z unix=214970400 wall=1976-10-24T02:00:00 fold-until(1976-10-24T03:00:00) type=6 +00 GMT dst
0107: 1977-03-20T02:00:00Z unix=227671200 wall=1977-03-20T02:00:00 gap-until(1977-03-20T03:00:00) type=7 +01 IST
0108: 1977-10-23T02:00:00Z unix=246420000 wall=1977-10-23T02:00:00 fold-until(1977-10-23T03:00:00) type=6 +00 GMT dst
0109: 1978-03-19T02:00:00Z unix=259120800 wall=1978-03-19T02:00:00 gap-until(1978-03-19T03:00:00) type=7 +01 IST
0110: 1978-10-29T02:00:00Z unix=278474400 wall=1978-10-29T02:00:00 fold-until(1978-10-29T03:00:00) type=6 +00 GMT dst
0111: 1979-03-18T02:00:00Z unix=290570400 wall=1979-03-18T02:00:00 gap-until(1979-03-18T03:00:00) type=7 +01 IST
0112: 1979-10-28T02:00:00Z unix=309924000 wall=1979-10-28T02:00:00 fold-until(1979-10-28T03:00:00) type=6 +00 GMT dst
0113: 1980-03-16T02:00:00Z unix=322020000 wall=1980-03-16T02:00:00 gap-until(1980-03-16T03:00:00) type=7 +01 IST
0114: 1980-10-26T02:00:00Z unix=341373600 wall=1980-10-26T02:00:00 fold-until(1980-10-26T03:00:00) type=6 +00 GMT dst
0115: 1981-03-29T01:00:00Z unix=354675600 wall=1981-03-29T01:00:00 gap-until(1981-03-29T02:00:00) type=7 +01 IST
0116: 1981-10-25T01:00:00Z unix=372819600 wall=1981-10-25T01:00:00 fold-until(1981-10-25T02:00:00) type=6 +00 GMT dst
0117: 1982-03-28T01:00:00Z unix=386125200 wall=1982-03-28T01:00:00 gap-until(1982-03-28T02:00:00) type=7 +01 IST
0118: 1982-10-24T01:00:00Z unix=404269200 wall=1982-10-24T01:00:00 fold-until(1982-10-24T02:00:00) type=6 +00 GMT dst
0119: 1983-03-27T01:00:00Z unix=417574800 wall=1983-03-27T01:00:00 gap-until(1983-03-27T02:00:00) type=7 +01 IST
0120: 1983-10-23T01:00:00Z unix=435718800 wall=1983-10-23T01:00:00 fold-until(1983-10-23T02:00:00) type=6 +00 GMT dst
0121: 1984-03-25T01:00:00Z unix=449024400 wall=1984-03-25T01:00:00 gap-until(1984-03-25T02:00:00) type=7 +01 IST
0122: 1984-10-28T01:00:00Z unix=467773200 wall=1984-10-28T01:00:00 fold-until(1984-10-28T02:00:00) type=6 +00 GMT dst
0123: 1985-03-31T01:00:00Z unix=481078800 wall=1985-03-31T01:00:00 gap-until(1985-03-31T02:00:00) type=7 +01 IST
0124: 1985-10-27T01:00:00Z unix=499222800 wall=1985-10-27T01:00:00 fold-until(1985-10-27T02:00:00) type=6 +00 GMT dst
0125: 1986-03-30T01:00:00Z unix=512528400 wall=1986-03-30T01:00:00 gap-until(1986-03-30T02:00:00) type=7 +01 IST
0126: 1986-10-26T01:00:00Z unix=530672400 wall=1986-10-26T01:00:00 fold-until(1986-10-26T02:00:00) type=6 +00 GMT dst
0127: 1987-03-29T01:00:00Z unix=543978000 wall=1987-03-29T01:00:00 gap-until(1987-03-29T02:00:00) type=7 +01 IST
0128: 1987-10-25T01:00:00Z unix=562122000 wall=1987-10-25T01:00:00 fold-until(1987-10-25T02:00:00) type=6 +00 GMT dst
0129: 1988-03-27T01:00:00Z unix=575427600 wall=1988-03-27T01:00:00 gap-until(1988-03-27T02:00:00) type=7 +01 IST
0130: 1988-10-23T01:00:00Z unix=593571600 wall=1988-10-23T01:00:00 fold-until(1988-10-23T02:00:00) type=6 +00 GMT dst
0131: 1989-03-26T01:00:00Z unix=606877200 wall=1989-03-26T01:00:00 gap-until(1989-03-26T02:00:00) type=7 +01 IST
0132: 1989-10-29T01:00:00Z unix=625626000 wall=1989-10-29T01:00:00 fold-until(1989-10-29T02:00:00) type=6 +00 GMT dst
0133: 1990-03-25T01:00:00Z unix=638326800 wall=1990-03-25T01:00:00 gap-until(1990-03-25T02:00:00) type=7 +01 IST
0134: 1990-10-28T01:00:00Z unix=657075600 wall=1990-10-28T01:00:00 fold-until(1990-10-28T02:00:00) type=6 +00 GMT dst
0135: 1991-03-31T01:00:00Z unix=670381200 wall=1991-03-31T01:00:00 gap-until(1991-03-31T02:00:00) type=7 +01 IST
0136: 1991-10-27T01:00:00Z unix=688525200 wall=1991-10-27T01:00:00 fold-until(1991-10-27T02:00:00) type=6 +00 GMT dst
0137: 1992-03-29T01:00:00Z unix=701830800 wall=1992-03-29T01:00:00 gap-until(1992-03-29T02:00:00) type=7 +01 IST
0138: 1992-10-25T01:00:00Z unix=719974800 wall=1992-10-25T01:00:00 fold-until(1992-10-25T02:00:00) type=6 +00 GMT dst
0139: 1993-03-28T01:00:00Z unix=733280400 wall=1993-03-28T01:00:00 gap-until(1993-03-28T02:00:00) type=7 +01 IST
0140: 1993-10-24T01:00:00Z unix=751424400 wall=1993-10-24T01:00:00 fold-until(1993-10-24T02:00:00) type=6 +00 GMT dst
0141: 1994-03-27T01:00:00Z unix=764730000 wall=1994-03-27T01:00:00 gap-until(1994-03-27T02:00:00) type=7 +01 IST
0142: 1994-10-23T01:00:00Z unix=782874000 wall=1994-10-23T01:00:00 fold-until(1994-10-23T02:00:00) type=6 +00 GMT dst
0143: 1995-03-26T01:00:00Z unix=796179600 wall=1995-03-26T01:00:00 gap-until(1995-03-26T02:00:00) type=7 +01 IST
0144: 1995-10-22T01:00:00Z unix=814323600 wall=1995-10-22T01:00:00 fold-until(1995-10-22T02:00:00) type=6 +00 GMT dst
0145: 1996-03-31T01:00:00Z unix=828234000 wall=1996-03-31T01:00:00 gap-until(1996-03-31T02:00:00) type=7 +01 IST
0146: 1996-10-27T01:00:00Z unix=846378000 wall=1996-10-27T01:00:00 fold-until(1996-10-27T02:00:00) type=6 +00 GMT dst
0147: 1997-03-30T01:00:00Z unix=859683600 wall=1997-03-30T01:00:00 gap-until(1997-03-30T02:00:00) type=7 +01 IST
0148: 1997-10-26T01:00:00Z unix=877827600 wall=1997-10-26T01:00:00 fold-until(1997-10-26T02:00:00) type=6 +00 GMT dst
0149: 1998-03-29T01:00:00Z unix=891133200 wall=1998-03-29T01:00:00 gap-until(1998-03-29T02:00:00) type=7 +01 IST
0150: 1998-10-25T01:00:00Z unix=909277200 wall=1998-10-25T01:00:00 fold-until(1998-10-25T02:00:00) type=6 +00 GMT dst
0151: 1999-03-28T01:00:00Z unix=922582800 wall=1999-03-28T01:00:00 gap-until(1999-03-28T02:00:00) type=7 +01 IST
0152: 1999-10-31T01:00:00Z unix=941331600 wall=1999-10-31T01:00:00 fold-until(1999-10-31T02:00:00) type=6 +00 GMT dst
0153: 2000-03-26T01:00:00Z unix=954032400 wall=2000-03-26T01:00:00 gap-until(2000-03-26T02:00:00) type=7 +01 IST
0154: 2000-10-29T01:00:00Z unix=972781200 wall=2000-10-29T01:00:00 fold-until(2000-10-29T02:00:00) type=6 +00 GMT dst
0155: 2001-03-25T01:00:00Z unix=985482000 wall=2001-03-25T01:00:00 gap-until(2001-03-25T02:00:00) type=7 +01 IST
0156: 2001-10-28T01:00:00Z unix=1004230800 wall=2001-10-28T01:00:00 fold-until(2001-10-28T02:00:00) type=6 +00 GMT dst
0157: 2002-03-31T01:00:00Z unix=1017536400 wall=2002-03-31T01:00:00 gap-until(2002-03-31T02:00:00) type=7 +01 IST
0158: 2002-10-27T01:00:00Z unix=1035680400 wall=2002-10-27T01:00:00 fold-until(2002-10-27T02:00:00) type=6 +00 GMT dst
0159: 2003-03-30T01:00:00Z unix=1048986000 wall=2003-03-30T01:00:00 gap-until(2003-03-30T02:00:00) type=7 +01 IST
0160: 2003-10-26T01:00:00Z unix=1067130000 wall=2003-10-26T01:00:00 fold-until(2003-10-26T02:00:00) type=6 +00 GMT dst
0161: 2004-03-28T01:00:00Z unix=1080435600 wall=2004-03-28T01:00:00 gap-until(2004-03-28T02:00:00) type=7 +01 IST
0162: 2004-10-31T01:00:00Z unix=1099184400 wall=2004-10-31T01:00:00 fold-until(2004-10-31T02:00:00) type=6 +00 GMT dst
0163: 2005-03-27T01:00:00Z unix=1111885200 wall=2005-03-27T01:00:00 gap-until(2005-03-27T02:00:00) type=7 +01 IST
0164: 2005-10-30T01:00:00Z unix=1130634000 wall=2005-10-30T01:00:00 fold-until(2005-10-30T02:00:00) type=6 +00 GMT dst
0165: 2006-03-26T01:00:00Z unix=1143334800 wall=2006-03-26T01:00:00 gap-until(2006-03-26T02:00:00) type=7 +01 IST
0166: 2006-10-29T01:00:00Z unix=1162083600 wall=2006-10-29T01:00:00 fold-until(2006-10-29T02:00:00) type=6 +00 GMT dst
0167: 2007-03-25T01:00:00Z unix=1174784400 wall=2007-03-25T01:00:00 gap-until(2007-03-25T02:00:00) type=7 +01 IST
0168: 2007-10-28T01:00:00Z unix=1193533200 wall=2007-10-28T01:00:00 fold-until(2007-10-28T02:00:00) type=6 +00 GMT dst
0169: 2008-03-30T01:00:00Z unix=1206838800 wall=2008-03-30T01:00:00 gap-until(2008-03-30T02:00:00) type=7 +01 IST
0170: 2008-10-26T01:00:00Z unix=1224982800 wall=2008-10-26T01:00:00 fold-until(2008-10-26T02:00:00) type=6 +00 GMT dst
0171: 2009-03-29T01:00:00Z unix=1238288400 wall=2009-03-29T01:00:00 gap-until(2009-03-29T02:00:00) type=7 +01 IST
0172: 2009-10-25T01:00:00Z unix=1256432400 wall=2009-10-25T01:00:00 fold-until(2009-10-25T02:00:00) type=6 +00 GMT dst
0173: 2010-03-28T01:00:00Z unix=1269738000 wall=2010-03-28T01:00:00 gap-until(2010-03-28T02:00:00) type=7 +01 IST
0174: 2010-10-31T01:00:00Z unix=1288486800 wall=2010-10-31T01:00:00 fold-until(2010-10-31T02:00:00) type=6 +00 GMT dst
0175: 2011-03-27T01:00:00Z unix=1301187600 wall=2011-03-27T01:00:00 gap-until(2011-03-27T02:00:00) type=7 +01 IST
0176: 2011-10-30T01:00:00Z unix=1319936400 wall=2011-10-30T01:00:00 fold-until(2011-10-30T02:00:00) type=6 +00 GMT dst
0177: 2012-03-25T01:00:00Z unix=1332637200 wall=2012-03-25T01:00:00 gap-until(2012-03-25T02:00:00) type=7 +01 IST
0178: 2012-10-28T01:00:00Z unix=1351386000 wall=2012-10-28T01:00:00 fold-until(2012-10-28T02:00:00) type=6 +00 GMT dst
0179: 2013-03-31T01:00:00Z unix=1364691600 wall=2013-03-31T01:00:00 gap-until(2013-03-31T02:00:00) type=7 +01 IST
0180: 2013-10-27T01:00:00Z unix=1382835600 wall=2013-10-27T01:00:00 fold-until(2013-10-27T02:00:00) type=6 +00 GMT dst
0181: 2014-03-30T01:00:00Z unix=1396141200 wall=2014-03-30T01:00:00 gap-until(2014-03-30T02:00:00) type=7 +01 IST
0182: 2014-10-26T01:00:00Z unix=1414285200 wall=2014-10-26T01:00:00 fold-until(2014-10-26T02:00:00) type=6 +00 GMT dst
0183: 2015-03-29T01:00:00Z unix=1427590800 wall=2015-03-29T01:00:00 gap-until(2015-03-29T02:00:00) type=7 +01 IST
0184: 2015-10-25T01:00:00Z unix=1445734800 wall=2015-10-25T01:00:00 fold-until(2015-10-25T02:00:00) type=6 +00 GMT dst
0185: 2016-03-27T01:00:00Z unix=1459040400 wall=2016-03-27T01:00:00 gap-until(2016-03-27T02:00:00) type=7 +01 IST
0186: 2016-10-30T01:00:00Z unix=1477789200 wall=2016-10-30T01:00:00 fold-until(2016-10-30T02:00:00) type=6 +00 GMT dst
0187: 2017-03-26T01:00:00Z unix=1490490000 wall=2017-03-26T01:00:00 gap-until(2017-03-26T02:00:00) type=7 +01 IST
0188: 2017-10-29T01:00:00Z unix=1509238800 wall=2017-10-29T01:00:00 fold-until(2017-10-29T02:00:00) type=6 +00 GMT dst
0189: 2018-03-25T01:00:00Z unix=1521939600 wall=2018-03-25T01:00:00 gap-until(2018-03-25T02:00:00) type=7 +01 IST
0190: 2018-10-28T01:00:00Z unix=1540688400 wall=2018-10-28T01:00:00 fold-until(2018-10-28T02:00:00) type=6 +00 GMT dst
0191: 2019-03-31T01:00:00Z unix=1553994000 wall=2019-03-31T01:00:00 gap-until(2019-03-31T02:00:00) type=7 +01 IST
0192: 2019-10-27T01:00:00Z unix=1572138000 wall=2019-10-27T01:00:00 fold-until(2019-10-27T02:00:00) type=6 +00 GMT dst
0193: 2020-03-29T01:00:00Z unix=1585443600 wall=2020-03-29T01:00:00 gap-until(2020-03-29T02:00:00) type=7 +01 IST
0194: 2020-10-25T01:00:00Z unix=1603587600 wall=2020-10-25T01:00:00 fold-until(2020-10-25T02:00:00) type=6 +00 GMT dst
0195: 2021-03-28T01:00:00Z unix=1616893200 wall=2021-03-28T01:00:00 gap-until(2021-03-28T02:00:00) type=7 +01 IST
0196: 2021-10-31T01:00:00Z unix=1635642000 wall=2021-10-31T01:00:00 fold-until(2021-10-31T02:00:00) type=6 +00 GMT dst
0197: 2022-03-27T01:00:00Z unix=1648342800 wall=2022-03-27T01:00:00 gap-until(2022-03-27T02:00:00) type=7 +01 IST
0198: 2022-10-30T01:00:00Z unix=1667091600 wall=2022-10-30T01:00:00 fold-until(2022-10-30T02:00:00) type=6 +00 GMT dst
0199: 2023-03-26T01:00:00Z unix=1679792400 wall=2023-03-26T01:00:00 gap-until(2023-03-26T02:00:00) type=7 +01 IST
0200: 2023-10-29T01:00:00Z unix=1698541200 wall=2023-10-29T01:00:00 fold-until(2023-10-29T02:00:00) type=6 +00 GMT dst
0201: 2024-03-31T01:00:00Z unix=1711846800 wall=2024-03-31T01:00:00 gap-until(2024-03-31T02:00:00) type=7 +01 IST
0202: 2024-10-27T01:00:00Z unix=1729990800 wall=2024-10-27T01:00:00 fold-until(2024-10-27T02:00:00) type=6 +00 GMT dst
0203: 2025-03-30T01:00:00Z unix=1743296400 wall=2025-03-30T01:00:00 gap-until(2025-03-30T02:00:00) type=7 +01 IST
0204: 2025-10-26T01:00:00Z unix=1761440400 wall=2025-10-26T01:00:00 fold-until(2025-10-26T02:00:00) type=6 +00 GMT dst
0205: 2026-03-29T01:00:00Z unix=1774746000 wall=2026-03-29T01:00:00 gap-until(2026-03-29T02:00:00) type=7 +01 IST
0206: 2026-10-25T01:00:00Z unix=1792890000 wall=2026-10-25T01:00:00 fold-until(2026-10-25T02:00:00) type=6 +00 GMT dst
0207: 2027-03-28T01:00:00Z unix=1806195600 wall=2027-03-28T01:00:00 gap-until(2027-03-28T02:00:00) type=7 +01 IST
0208: 2027-10-31T01:00:00Z unix=1824944400 wall=2027-10-31T01:00:00 fold-until(2027-10-31T02:00:00) type=6 +00 GMT dst
0209: 2028-03-26T01:00:00Z unix=1837645200 wall=2028-03-26T01:00:00 gap-until(2028-03-26T02:00:00) type=7 +01 IST
0210: 2028-10-29T01:00:00Z unix=1856394000 wall=2028-10-29T01:00:00 fold-until(2028-10-29T02:00:00) type=6 +00 GMT dst
0211: 2029-03-25T01:00:00Z unix=1869094800 wall=2029-03-25T01:00:00 gap-until(2029-03-25T02:00:00) type=7 +01 IST
0212: 2029-10-28T01:00:00Z unix=1887843600 wall=2029-10-28T01:00:00 fold-until(2029-10-28T02:00:00) type=6 +00 GMT dst
0213: 2030-03-31T01:00:00Z unix=1901149200 wall=2030-03-31T01:00:00 gap-until(2030-03-31T02:00:00) type=7 +01 IST
0214: 2030-10-27T01:00:00Z unix=1919293200 wall=2030-10-27T01:00:00 fold-until(2030-10-27T02:00:00) type=6 +00 GMT dst
0215: 2031-03-30T01:00:00Z unix=1932598800 wall=2031-03-30T01:00:00 gap-until(2031-03-30T02:00:00) type=7 +01 IST
0216: 2031-10-26T01:00:00Z unix=1950742800 wall=2031-10-26T01:00:00 fold-until(2031-10-26T02:00:00) type=6 +00 GMT dst
0217: 2032-03-28T01:00:00Z unix=1964048400 wall=2032-03-28T01:00:00 gap-until(2032-03-28T02:00:00) type=7 +01 IST
0218: 2032-10-31T01:00:00Z unix=1982797200 wall=2032-10-31T01:00:00 fold-until(2032-10-31T02:00:00) type=6 +00 GMT dst
0219: 2033-03-27T01:00:00Z unix=1995498000 wall=2033-03-27T01:00:00 gap-until(2033-03-27T02:00:00) type=7 +01 IST
0220: 2033-10-30T01:00:00Z unix=2014246800 wall=2033-10-30T01:00:00 fold-until(2033-10-30T02:00:00) type=6 +00 GMT dst
0221: 2034-03-26T01:00:00Z unix=2026947600 wall=2034-03-26T01:00:00 gap-until(2034-03-26T02:00:00) type=7 +01 IST
0222: 2034-10-29T01:00:00Z unix=2045696400 wall=2034-10-29T01:00:00 fold-until(2034-10-29T02:00:00) type=6 +00 GMT dst
0223: 2035-03-25T01:00:00Z unix=2058397200 wall=2035-03-25T01:00:00 gap-until(2035-03-25T02:00:00) type=7 +01 IST
0224: 2035-10-28T01:00:00Z unix=2077146000 wall=2035-10-28T01:00:00 fold-until(2035-10-28T02:00:00) type=6 +00 GMT dst
0225: 2036-03-30T01:00:00Z unix=2090451600 wall=2036-03-30T01:00:00 gap-until(2036-03-30T02:00:00) type=7 +01 IST
0226: 2036-10-26T01:00:00Z unix=2108595600 wall=2036-10-26T01:00:00 fold-until(2036-10-26T02:00:00) type=6 +00 GMT dst
0227: 2037-03-29T01:00:00Z unix=2121901200 wall=2037-03-29T01:00:00 gap-until(2037-03-29T02:00:00) type=7 +01 IST
0228: 2037-10-25T01:00:00Z unix=2140045200 wall=2037-10-25T01:00:00 fold-until(2037-10-25T02:00:00) type=6 +00 GMT dst
POSIX TIME ZONE STRING
IST-1GMT0,M10.5.0,M3.5.0/1

View file

@ -0,0 +1,22 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
Pacific/Honolulu
LOCAL TIME TYPES
000: offset=-10:31:26 designation=LMT indicator=local/wall
001: offset=-10:30 designation=HST indicator=local/wall
002: offset=-09:30 designation=HDT dst indicator=local/wall
003: offset=-09:30 designation=HWT dst indicator=local/wall
004: offset=-09:30 designation=HPT dst indicator=ut/std
005: offset=-10 designation=HST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T15:28:33 unambiguous type=0 -10:31:26 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-13T10:14:26 gap-until(1901-12-13T10:15:52) type=1 -10:30 HST
0002: 1933-04-30T12:30:00Z unix=-1157283000 wall=1933-04-30T02:00:00 gap-until(1933-04-30T03:00:00) type=2 -09:30 HDT dst
0003: 1933-05-21T21:30:00Z unix=-1155436200 wall=1933-05-21T11:00:00 fold-until(1933-05-21T12:00:00) type=1 -10:30 HST
0004: 1942-02-09T12:30:00Z unix=-880198200 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=3 -09:30 HWT dst
0005: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T13:30:00 unambiguous type=4 -09:30 HPT dst
0006: 1945-09-30T11:30:00Z unix=-765376200 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=1 -10:30 HST
0007: 1947-06-08T12:30:00Z unix=-712150200 wall=1947-06-08T02:00:00 gap-until(1947-06-08T02:30:00) type=5 -10 HST

View file

@ -0,0 +1,24 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
Pacific/Honolulu
LOCAL TIME TYPES
000: offset=-10:31:26 designation=LMT indicator=local/wall
001: offset=-10:30 designation=HST indicator=local/wall
002: offset=-09:30 designation=HDT dst indicator=local/wall
003: offset=-09:30 designation=HWT dst indicator=local/wall
004: offset=-09:30 designation=HPT dst indicator=ut/std
005: offset=-10 designation=HST indicator=local/wall
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T15:28:33 unambiguous type=0 -10:31:26 LMT
0001: 1896-01-13T22:31:26Z unix=-2334101314 wall=1896-01-13T12:00:00 gap-until(1896-01-13T12:01:26) type=1 -10:30 HST
0002: 1933-04-30T12:30:00Z unix=-1157283000 wall=1933-04-30T02:00:00 gap-until(1933-04-30T03:00:00) type=2 -09:30 HDT dst
0003: 1933-05-21T21:30:00Z unix=-1155436200 wall=1933-05-21T11:00:00 fold-until(1933-05-21T12:00:00) type=1 -10:30 HST
0004: 1942-02-09T12:30:00Z unix=-880198200 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=3 -09:30 HWT dst
0005: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T13:30:00 unambiguous type=4 -09:30 HPT dst
0006: 1945-09-30T11:30:00Z unix=-765376200 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=1 -10:30 HST
0007: 1947-06-08T12:30:00Z unix=-712150200 wall=1947-06-08T02:00:00 gap-until(1947-06-08T02:30:00) type=5 -10 HST
POSIX TIME ZONE STRING
HST10

View file

@ -0,0 +1,279 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse_v1())
---
TIME ZONE NAME
right/America/New_York
LOCAL TIME TYPES
000: offset=-04:56:02 designation=LMT indicator=local/wall
001: offset=-04 designation=EDT dst indicator=local/wall
002: offset=-05 designation=EST indicator=local/wall
003: offset=-05 designation=EST indicator=ut/std
004: offset=-04 designation=EWT dst indicator=local/wall
005: offset=-04 designation=EPT dst indicator=ut/std
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T21:03:57 unambiguous type=0 -04:56:02 LMT
0001: 1901-12-13T20:45:52Z unix=-2147483648 wall=1901-12-13T15:45:52 fold-until(1901-12-13T15:49:50) type=3 -05 EST
0002: 1918-03-31T07:00:00Z unix=-1633280400 wall=1918-03-31T02:00:00 gap-until(1918-03-31T03:00:00) type=1 -04 EDT dst
0003: 1918-10-27T06:00:00Z unix=-1615140000 wall=1918-10-27T01:00:00 fold-until(1918-10-27T02:00:00) type=2 -05 EST
0004: 1919-03-30T07:00:00Z unix=-1601830800 wall=1919-03-30T02:00:00 gap-until(1919-03-30T03:00:00) type=1 -04 EDT dst
0005: 1919-10-26T06:00:00Z unix=-1583690400 wall=1919-10-26T01:00:00 fold-until(1919-10-26T02:00:00) type=2 -05 EST
0006: 1920-03-28T07:00:00Z unix=-1570381200 wall=1920-03-28T02:00:00 gap-until(1920-03-28T03:00:00) type=1 -04 EDT dst
0007: 1920-10-31T06:00:00Z unix=-1551636000 wall=1920-10-31T01:00:00 fold-until(1920-10-31T02:00:00) type=2 -05 EST
0008: 1921-04-24T07:00:00Z unix=-1536512400 wall=1921-04-24T02:00:00 gap-until(1921-04-24T03:00:00) type=1 -04 EDT dst
0009: 1921-09-25T06:00:00Z unix=-1523210400 wall=1921-09-25T01:00:00 fold-until(1921-09-25T02:00:00) type=2 -05 EST
0010: 1922-04-30T07:00:00Z unix=-1504458000 wall=1922-04-30T02:00:00 gap-until(1922-04-30T03:00:00) type=1 -04 EDT dst
0011: 1922-09-24T06:00:00Z unix=-1491760800 wall=1922-09-24T01:00:00 fold-until(1922-09-24T02:00:00) type=2 -05 EST
0012: 1923-04-29T07:00:00Z unix=-1473008400 wall=1923-04-29T02:00:00 gap-until(1923-04-29T03:00:00) type=1 -04 EDT dst
0013: 1923-09-30T06:00:00Z unix=-1459706400 wall=1923-09-30T01:00:00 fold-until(1923-09-30T02:00:00) type=2 -05 EST
0014: 1924-04-27T07:00:00Z unix=-1441558800 wall=1924-04-27T02:00:00 gap-until(1924-04-27T03:00:00) type=1 -04 EDT dst
0015: 1924-09-28T06:00:00Z unix=-1428256800 wall=1924-09-28T01:00:00 fold-until(1924-09-28T02:00:00) type=2 -05 EST
0016: 1925-04-26T07:00:00Z unix=-1410109200 wall=1925-04-26T02:00:00 gap-until(1925-04-26T03:00:00) type=1 -04 EDT dst
0017: 1925-09-27T06:00:00Z unix=-1396807200 wall=1925-09-27T01:00:00 fold-until(1925-09-27T02:00:00) type=2 -05 EST
0018: 1926-04-25T07:00:00Z unix=-1378659600 wall=1926-04-25T02:00:00 gap-until(1926-04-25T03:00:00) type=1 -04 EDT dst
0019: 1926-09-26T06:00:00Z unix=-1365357600 wall=1926-09-26T01:00:00 fold-until(1926-09-26T02:00:00) type=2 -05 EST
0020: 1927-04-24T07:00:00Z unix=-1347210000 wall=1927-04-24T02:00:00 gap-until(1927-04-24T03:00:00) type=1 -04 EDT dst
0021: 1927-09-25T06:00:00Z unix=-1333908000 wall=1927-09-25T01:00:00 fold-until(1927-09-25T02:00:00) type=2 -05 EST
0022: 1928-04-29T07:00:00Z unix=-1315155600 wall=1928-04-29T02:00:00 gap-until(1928-04-29T03:00:00) type=1 -04 EDT dst
0023: 1928-09-30T06:00:00Z unix=-1301853600 wall=1928-09-30T01:00:00 fold-until(1928-09-30T02:00:00) type=2 -05 EST
0024: 1929-04-28T07:00:00Z unix=-1283706000 wall=1929-04-28T02:00:00 gap-until(1929-04-28T03:00:00) type=1 -04 EDT dst
0025: 1929-09-29T06:00:00Z unix=-1270404000 wall=1929-09-29T01:00:00 fold-until(1929-09-29T02:00:00) type=2 -05 EST
0026: 1930-04-27T07:00:00Z unix=-1252256400 wall=1930-04-27T02:00:00 gap-until(1930-04-27T03:00:00) type=1 -04 EDT dst
0027: 1930-09-28T06:00:00Z unix=-1238954400 wall=1930-09-28T01:00:00 fold-until(1930-09-28T02:00:00) type=2 -05 EST
0028: 1931-04-26T07:00:00Z unix=-1220806800 wall=1931-04-26T02:00:00 gap-until(1931-04-26T03:00:00) type=1 -04 EDT dst
0029: 1931-09-27T06:00:00Z unix=-1207504800 wall=1931-09-27T01:00:00 fold-until(1931-09-27T02:00:00) type=2 -05 EST
0030: 1932-04-24T07:00:00Z unix=-1189357200 wall=1932-04-24T02:00:00 gap-until(1932-04-24T03:00:00) type=1 -04 EDT dst
0031: 1932-09-25T06:00:00Z unix=-1176055200 wall=1932-09-25T01:00:00 fold-until(1932-09-25T02:00:00) type=2 -05 EST
0032: 1933-04-30T07:00:00Z unix=-1157302800 wall=1933-04-30T02:00:00 gap-until(1933-04-30T03:00:00) type=1 -04 EDT dst
0033: 1933-09-24T06:00:00Z unix=-1144605600 wall=1933-09-24T01:00:00 fold-until(1933-09-24T02:00:00) type=2 -05 EST
0034: 1934-04-29T07:00:00Z unix=-1125853200 wall=1934-04-29T02:00:00 gap-until(1934-04-29T03:00:00) type=1 -04 EDT dst
0035: 1934-09-30T06:00:00Z unix=-1112551200 wall=1934-09-30T01:00:00 fold-until(1934-09-30T02:00:00) type=2 -05 EST
0036: 1935-04-28T07:00:00Z unix=-1094403600 wall=1935-04-28T02:00:00 gap-until(1935-04-28T03:00:00) type=1 -04 EDT dst
0037: 1935-09-29T06:00:00Z unix=-1081101600 wall=1935-09-29T01:00:00 fold-until(1935-09-29T02:00:00) type=2 -05 EST
0038: 1936-04-26T07:00:00Z unix=-1062954000 wall=1936-04-26T02:00:00 gap-until(1936-04-26T03:00:00) type=1 -04 EDT dst
0039: 1936-09-27T06:00:00Z unix=-1049652000 wall=1936-09-27T01:00:00 fold-until(1936-09-27T02:00:00) type=2 -05 EST
0040: 1937-04-25T07:00:00Z unix=-1031504400 wall=1937-04-25T02:00:00 gap-until(1937-04-25T03:00:00) type=1 -04 EDT dst
0041: 1937-09-26T06:00:00Z unix=-1018202400 wall=1937-09-26T01:00:00 fold-until(1937-09-26T02:00:00) type=2 -05 EST
0042: 1938-04-24T07:00:00Z unix=-1000054800 wall=1938-04-24T02:00:00 gap-until(1938-04-24T03:00:00) type=1 -04 EDT dst
0043: 1938-09-25T06:00:00Z unix=-986752800 wall=1938-09-25T01:00:00 fold-until(1938-09-25T02:00:00) type=2 -05 EST
0044: 1939-04-30T07:00:00Z unix=-968000400 wall=1939-04-30T02:00:00 gap-until(1939-04-30T03:00:00) type=1 -04 EDT dst
0045: 1939-09-24T06:00:00Z unix=-955303200 wall=1939-09-24T01:00:00 fold-until(1939-09-24T02:00:00) type=2 -05 EST
0046: 1940-04-28T07:00:00Z unix=-936550800 wall=1940-04-28T02:00:00 gap-until(1940-04-28T03:00:00) type=1 -04 EDT dst
0047: 1940-09-29T06:00:00Z unix=-923248800 wall=1940-09-29T01:00:00 fold-until(1940-09-29T02:00:00) type=2 -05 EST
0048: 1941-04-27T07:00:00Z unix=-905101200 wall=1941-04-27T02:00:00 gap-until(1941-04-27T03:00:00) type=1 -04 EDT dst
0049: 1941-09-28T06:00:00Z unix=-891799200 wall=1941-09-28T01:00:00 fold-until(1941-09-28T02:00:00) type=2 -05 EST
0050: 1942-02-09T07:00:00Z unix=-880218000 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=4 -04 EWT dst
0051: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T19:00:00 unambiguous type=5 -04 EPT dst
0052: 1945-09-30T06:00:00Z unix=-765396000 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=2 -05 EST
0053: 1946-04-28T07:00:00Z unix=-747248400 wall=1946-04-28T02:00:00 gap-until(1946-04-28T03:00:00) type=1 -04 EDT dst
0054: 1946-09-29T06:00:00Z unix=-733946400 wall=1946-09-29T01:00:00 fold-until(1946-09-29T02:00:00) type=2 -05 EST
0055: 1947-04-27T07:00:00Z unix=-715798800 wall=1947-04-27T02:00:00 gap-until(1947-04-27T03:00:00) type=1 -04 EDT dst
0056: 1947-09-28T06:00:00Z unix=-702496800 wall=1947-09-28T01:00:00 fold-until(1947-09-28T02:00:00) type=2 -05 EST
0057: 1948-04-25T07:00:00Z unix=-684349200 wall=1948-04-25T02:00:00 gap-until(1948-04-25T03:00:00) type=1 -04 EDT dst
0058: 1948-09-26T06:00:00Z unix=-671047200 wall=1948-09-26T01:00:00 fold-until(1948-09-26T02:00:00) type=2 -05 EST
0059: 1949-04-24T07:00:00Z unix=-652899600 wall=1949-04-24T02:00:00 gap-until(1949-04-24T03:00:00) type=1 -04 EDT dst
0060: 1949-09-25T06:00:00Z unix=-639597600 wall=1949-09-25T01:00:00 fold-until(1949-09-25T02:00:00) type=2 -05 EST
0061: 1950-04-30T07:00:00Z unix=-620845200 wall=1950-04-30T02:00:00 gap-until(1950-04-30T03:00:00) type=1 -04 EDT dst
0062: 1950-09-24T06:00:00Z unix=-608148000 wall=1950-09-24T01:00:00 fold-until(1950-09-24T02:00:00) type=2 -05 EST
0063: 1951-04-29T07:00:00Z unix=-589395600 wall=1951-04-29T02:00:00 gap-until(1951-04-29T03:00:00) type=1 -04 EDT dst
0064: 1951-09-30T06:00:00Z unix=-576093600 wall=1951-09-30T01:00:00 fold-until(1951-09-30T02:00:00) type=2 -05 EST
0065: 1952-04-27T07:00:00Z unix=-557946000 wall=1952-04-27T02:00:00 gap-until(1952-04-27T03:00:00) type=1 -04 EDT dst
0066: 1952-09-28T06:00:00Z unix=-544644000 wall=1952-09-28T01:00:00 fold-until(1952-09-28T02:00:00) type=2 -05 EST
0067: 1953-04-26T07:00:00Z unix=-526496400 wall=1953-04-26T02:00:00 gap-until(1953-04-26T03:00:00) type=1 -04 EDT dst
0068: 1953-09-27T06:00:00Z unix=-513194400 wall=1953-09-27T01:00:00 fold-until(1953-09-27T02:00:00) type=2 -05 EST
0069: 1954-04-25T07:00:00Z unix=-495046800 wall=1954-04-25T02:00:00 gap-until(1954-04-25T03:00:00) type=1 -04 EDT dst
0070: 1954-09-26T06:00:00Z unix=-481744800 wall=1954-09-26T01:00:00 fold-until(1954-09-26T02:00:00) type=2 -05 EST
0071: 1955-04-24T07:00:00Z unix=-463597200 wall=1955-04-24T02:00:00 gap-until(1955-04-24T03:00:00) type=1 -04 EDT dst
0072: 1955-10-30T06:00:00Z unix=-447271200 wall=1955-10-30T01:00:00 fold-until(1955-10-30T02:00:00) type=2 -05 EST
0073: 1956-04-29T07:00:00Z unix=-431542800 wall=1956-04-29T02:00:00 gap-until(1956-04-29T03:00:00) type=1 -04 EDT dst
0074: 1956-10-28T06:00:00Z unix=-415821600 wall=1956-10-28T01:00:00 fold-until(1956-10-28T02:00:00) type=2 -05 EST
0075: 1957-04-28T07:00:00Z unix=-400093200 wall=1957-04-28T02:00:00 gap-until(1957-04-28T03:00:00) type=1 -04 EDT dst
0076: 1957-10-27T06:00:00Z unix=-384372000 wall=1957-10-27T01:00:00 fold-until(1957-10-27T02:00:00) type=2 -05 EST
0077: 1958-04-27T07:00:00Z unix=-368643600 wall=1958-04-27T02:00:00 gap-until(1958-04-27T03:00:00) type=1 -04 EDT dst
0078: 1958-10-26T06:00:00Z unix=-352922400 wall=1958-10-26T01:00:00 fold-until(1958-10-26T02:00:00) type=2 -05 EST
0079: 1959-04-26T07:00:00Z unix=-337194000 wall=1959-04-26T02:00:00 gap-until(1959-04-26T03:00:00) type=1 -04 EDT dst
0080: 1959-10-25T06:00:00Z unix=-321472800 wall=1959-10-25T01:00:00 fold-until(1959-10-25T02:00:00) type=2 -05 EST
0081: 1960-04-24T07:00:00Z unix=-305744400 wall=1960-04-24T02:00:00 gap-until(1960-04-24T03:00:00) type=1 -04 EDT dst
0082: 1960-10-30T06:00:00Z unix=-289418400 wall=1960-10-30T01:00:00 fold-until(1960-10-30T02:00:00) type=2 -05 EST
0083: 1961-04-30T07:00:00Z unix=-273690000 wall=1961-04-30T02:00:00 gap-until(1961-04-30T03:00:00) type=1 -04 EDT dst
0084: 1961-10-29T06:00:00Z unix=-257968800 wall=1961-10-29T01:00:00 fold-until(1961-10-29T02:00:00) type=2 -05 EST
0085: 1962-04-29T07:00:00Z unix=-242240400 wall=1962-04-29T02:00:00 gap-until(1962-04-29T03:00:00) type=1 -04 EDT dst
0086: 1962-10-28T06:00:00Z unix=-226519200 wall=1962-10-28T01:00:00 fold-until(1962-10-28T02:00:00) type=2 -05 EST
0087: 1963-04-28T07:00:00Z unix=-210790800 wall=1963-04-28T02:00:00 gap-until(1963-04-28T03:00:00) type=1 -04 EDT dst
0088: 1963-10-27T06:00:00Z unix=-195069600 wall=1963-10-27T01:00:00 fold-until(1963-10-27T02:00:00) type=2 -05 EST
0089: 1964-04-26T07:00:00Z unix=-179341200 wall=1964-04-26T02:00:00 gap-until(1964-04-26T03:00:00) type=1 -04 EDT dst
0090: 1964-10-25T06:00:00Z unix=-163620000 wall=1964-10-25T01:00:00 fold-until(1964-10-25T02:00:00) type=2 -05 EST
0091: 1965-04-25T07:00:00Z unix=-147891600 wall=1965-04-25T02:00:00 gap-until(1965-04-25T03:00:00) type=1 -04 EDT dst
0092: 1965-10-31T06:00:00Z unix=-131565600 wall=1965-10-31T01:00:00 fold-until(1965-10-31T02:00:00) type=2 -05 EST
0093: 1966-04-24T07:00:00Z unix=-116442000 wall=1966-04-24T02:00:00 gap-until(1966-04-24T03:00:00) type=1 -04 EDT dst
0094: 1966-10-30T06:00:00Z unix=-100116000 wall=1966-10-30T01:00:00 fold-until(1966-10-30T02:00:00) type=2 -05 EST
0095: 1967-04-30T07:00:00Z unix=-84387600 wall=1967-04-30T02:00:00 gap-until(1967-04-30T03:00:00) type=1 -04 EDT dst
0096: 1967-10-29T06:00:00Z unix=-68666400 wall=1967-10-29T01:00:00 fold-until(1967-10-29T02:00:00) type=2 -05 EST
0097: 1968-04-28T07:00:00Z unix=-52938000 wall=1968-04-28T02:00:00 gap-until(1968-04-28T03:00:00) type=1 -04 EDT dst
0098: 1968-10-27T06:00:00Z unix=-37216800 wall=1968-10-27T01:00:00 fold-until(1968-10-27T02:00:00) type=2 -05 EST
0099: 1969-04-27T07:00:00Z unix=-21488400 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=1 -04 EDT dst
0100: 1969-10-26T06:00:00Z unix=-5767200 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=2 -05 EST
0101: 1970-04-26T07:00:00Z unix=9961200 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=1 -04 EDT dst
0102: 1970-10-25T06:00:00Z unix=25682400 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=2 -05 EST
0103: 1971-04-25T07:00:00Z unix=41410800 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=1 -04 EDT dst
0104: 1971-10-31T06:00:00Z unix=57736800 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=2 -05 EST
0105: 1972-04-30T07:00:00Z unix=73465200 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=1 -04 EDT dst
0106: 1972-10-29T06:00:01Z unix=89186401 wall=1972-10-29T01:00:01 fold-until(1972-10-29T02:00:01) type=2 -05 EST
0107: 1973-04-29T07:00:02Z unix=104914802 wall=1973-04-29T02:00:02 gap-until(1973-04-29T03:00:02) type=1 -04 EDT dst
0108: 1973-10-28T06:00:02Z unix=120636002 wall=1973-10-28T01:00:02 fold-until(1973-10-28T02:00:02) type=2 -05 EST
0109: 1974-01-06T07:00:03Z unix=126687603 wall=1974-01-06T02:00:03 gap-until(1974-01-06T03:00:03) type=1 -04 EDT dst
0110: 1974-10-27T06:00:03Z unix=152085603 wall=1974-10-27T01:00:03 fold-until(1974-10-27T02:00:03) type=2 -05 EST
0111: 1975-02-23T07:00:04Z unix=162370804 wall=1975-02-23T02:00:04 gap-until(1975-02-23T03:00:04) type=1 -04 EDT dst
0112: 1975-10-26T06:00:04Z unix=183535204 wall=1975-10-26T01:00:04 fold-until(1975-10-26T02:00:04) type=2 -05 EST
0113: 1976-04-25T07:00:05Z unix=199263605 wall=1976-04-25T02:00:05 gap-until(1976-04-25T03:00:05) type=1 -04 EDT dst
0114: 1976-10-31T06:00:05Z unix=215589605 wall=1976-10-31T01:00:05 fold-until(1976-10-31T02:00:05) type=2 -05 EST
0115: 1977-04-24T07:00:06Z unix=230713206 wall=1977-04-24T02:00:06 gap-until(1977-04-24T03:00:06) type=1 -04 EDT dst
0116: 1977-10-30T06:00:06Z unix=247039206 wall=1977-10-30T01:00:06 fold-until(1977-10-30T02:00:06) type=2 -05 EST
0117: 1978-04-30T07:00:07Z unix=262767607 wall=1978-04-30T02:00:07 gap-until(1978-04-30T03:00:07) type=1 -04 EDT dst
0118: 1978-10-29T06:00:07Z unix=278488807 wall=1978-10-29T01:00:07 fold-until(1978-10-29T02:00:07) type=2 -05 EST
0119: 1979-04-29T07:00:08Z unix=294217208 wall=1979-04-29T02:00:08 gap-until(1979-04-29T03:00:08) type=1 -04 EDT dst
0120: 1979-10-28T06:00:08Z unix=309938408 wall=1979-10-28T01:00:08 fold-until(1979-10-28T02:00:08) type=2 -05 EST
0121: 1980-04-27T07:00:09Z unix=325666809 wall=1980-04-27T02:00:09 gap-until(1980-04-27T03:00:09) type=1 -04 EDT dst
0122: 1980-10-26T06:00:09Z unix=341388009 wall=1980-10-26T01:00:09 fold-until(1980-10-26T02:00:09) type=2 -05 EST
0123: 1981-04-26T07:00:09Z unix=357116409 wall=1981-04-26T02:00:09 gap-until(1981-04-26T03:00:09) type=1 -04 EDT dst
0124: 1981-10-25T06:00:10Z unix=372837610 wall=1981-10-25T01:00:10 fold-until(1981-10-25T02:00:10) type=2 -05 EST
0125: 1982-04-25T07:00:10Z unix=388566010 wall=1982-04-25T02:00:10 gap-until(1982-04-25T03:00:10) type=1 -04 EDT dst
0126: 1982-10-31T06:00:11Z unix=404892011 wall=1982-10-31T01:00:11 fold-until(1982-10-31T02:00:11) type=2 -05 EST
0127: 1983-04-24T07:00:11Z unix=420015611 wall=1983-04-24T02:00:11 gap-until(1983-04-24T03:00:11) type=1 -04 EDT dst
0128: 1983-10-30T06:00:12Z unix=436341612 wall=1983-10-30T01:00:12 fold-until(1983-10-30T02:00:12) type=2 -05 EST
0129: 1984-04-29T07:00:12Z unix=452070012 wall=1984-04-29T02:00:12 gap-until(1984-04-29T03:00:12) type=1 -04 EDT dst
0130: 1984-10-28T06:00:12Z unix=467791212 wall=1984-10-28T01:00:12 fold-until(1984-10-28T02:00:12) type=2 -05 EST
0131: 1985-04-28T07:00:12Z unix=483519612 wall=1985-04-28T02:00:12 gap-until(1985-04-28T03:00:12) type=1 -04 EDT dst
0132: 1985-10-27T06:00:13Z unix=499240813 wall=1985-10-27T01:00:13 fold-until(1985-10-27T02:00:13) type=2 -05 EST
0133: 1986-04-27T07:00:13Z unix=514969213 wall=1986-04-27T02:00:13 gap-until(1986-04-27T03:00:13) type=1 -04 EDT dst
0134: 1986-10-26T06:00:13Z unix=530690413 wall=1986-10-26T01:00:13 fold-until(1986-10-26T02:00:13) type=2 -05 EST
0135: 1987-04-05T07:00:13Z unix=544604413 wall=1987-04-05T02:00:13 gap-until(1987-04-05T03:00:13) type=1 -04 EDT dst
0136: 1987-10-25T06:00:13Z unix=562140013 wall=1987-10-25T01:00:13 fold-until(1987-10-25T02:00:13) type=2 -05 EST
0137: 1988-04-03T07:00:14Z unix=576054014 wall=1988-04-03T02:00:14 gap-until(1988-04-03T03:00:14) type=1 -04 EDT dst
0138: 1988-10-30T06:00:14Z unix=594194414 wall=1988-10-30T01:00:14 fold-until(1988-10-30T02:00:14) type=2 -05 EST
0139: 1989-04-02T07:00:14Z unix=607503614 wall=1989-04-02T02:00:14 gap-until(1989-04-02T03:00:14) type=1 -04 EDT dst
0140: 1989-10-29T06:00:14Z unix=625644014 wall=1989-10-29T01:00:14 fold-until(1989-10-29T02:00:14) type=2 -05 EST
0141: 1990-04-01T07:00:15Z unix=638953215 wall=1990-04-01T02:00:15 gap-until(1990-04-01T03:00:15) type=1 -04 EDT dst
0142: 1990-10-28T06:00:15Z unix=657093615 wall=1990-10-28T01:00:15 fold-until(1990-10-28T02:00:15) type=2 -05 EST
0143: 1991-04-07T07:00:16Z unix=671007616 wall=1991-04-07T02:00:16 gap-until(1991-04-07T03:00:16) type=1 -04 EDT dst
0144: 1991-10-27T06:00:16Z unix=688543216 wall=1991-10-27T01:00:16 fold-until(1991-10-27T02:00:16) type=2 -05 EST
0145: 1992-04-05T07:00:16Z unix=702457216 wall=1992-04-05T02:00:16 gap-until(1992-04-05T03:00:16) type=1 -04 EDT dst
0146: 1992-10-25T06:00:17Z unix=719992817 wall=1992-10-25T01:00:17 fold-until(1992-10-25T02:00:17) type=2 -05 EST
0147: 1993-04-04T07:00:17Z unix=733906817 wall=1993-04-04T02:00:17 gap-until(1993-04-04T03:00:17) type=1 -04 EDT dst
0148: 1993-10-31T06:00:18Z unix=752047218 wall=1993-10-31T01:00:18 fold-until(1993-10-31T02:00:18) type=2 -05 EST
0149: 1994-04-03T07:00:18Z unix=765356418 wall=1994-04-03T02:00:18 gap-until(1994-04-03T03:00:18) type=1 -04 EDT dst
0150: 1994-10-30T06:00:19Z unix=783496819 wall=1994-10-30T01:00:19 fold-until(1994-10-30T02:00:19) type=2 -05 EST
0151: 1995-04-02T07:00:19Z unix=796806019 wall=1995-04-02T02:00:19 gap-until(1995-04-02T03:00:19) type=1 -04 EDT dst
0152: 1995-10-29T06:00:19Z unix=814946419 wall=1995-10-29T01:00:19 fold-until(1995-10-29T02:00:19) type=2 -05 EST
0153: 1996-04-07T07:00:20Z unix=828860420 wall=1996-04-07T02:00:20 gap-until(1996-04-07T03:00:20) type=1 -04 EDT dst
0154: 1996-10-27T06:00:20Z unix=846396020 wall=1996-10-27T01:00:20 fold-until(1996-10-27T02:00:20) type=2 -05 EST
0155: 1997-04-06T07:00:20Z unix=860310020 wall=1997-04-06T02:00:20 gap-until(1997-04-06T03:00:20) type=1 -04 EDT dst
0156: 1997-10-26T06:00:21Z unix=877845621 wall=1997-10-26T01:00:21 fold-until(1997-10-26T02:00:21) type=2 -05 EST
0157: 1998-04-05T07:00:21Z unix=891759621 wall=1998-04-05T02:00:21 gap-until(1998-04-05T03:00:21) type=1 -04 EDT dst
0158: 1998-10-25T06:00:21Z unix=909295221 wall=1998-10-25T01:00:21 fold-until(1998-10-25T02:00:21) type=2 -05 EST
0159: 1999-04-04T07:00:22Z unix=923209222 wall=1999-04-04T02:00:22 gap-until(1999-04-04T03:00:22) type=1 -04 EDT dst
0160: 1999-10-31T06:00:22Z unix=941349622 wall=1999-10-31T01:00:22 fold-until(1999-10-31T02:00:22) type=2 -05 EST
0161: 2000-04-02T07:00:22Z unix=954658822 wall=2000-04-02T02:00:22 gap-until(2000-04-02T03:00:22) type=1 -04 EDT dst
0162: 2000-10-29T06:00:22Z unix=972799222 wall=2000-10-29T01:00:22 fold-until(2000-10-29T02:00:22) type=2 -05 EST
0163: 2001-04-01T07:00:22Z unix=986108422 wall=2001-04-01T02:00:22 gap-until(2001-04-01T03:00:22) type=1 -04 EDT dst
0164: 2001-10-28T06:00:22Z unix=1004248822 wall=2001-10-28T01:00:22 fold-until(2001-10-28T02:00:22) type=2 -05 EST
0165: 2002-04-07T07:00:22Z unix=1018162822 wall=2002-04-07T02:00:22 gap-until(2002-04-07T03:00:22) type=1 -04 EDT dst
0166: 2002-10-27T06:00:22Z unix=1035698422 wall=2002-10-27T01:00:22 fold-until(2002-10-27T02:00:22) type=2 -05 EST
0167: 2003-04-06T07:00:22Z unix=1049612422 wall=2003-04-06T02:00:22 gap-until(2003-04-06T03:00:22) type=1 -04 EDT dst
0168: 2003-10-26T06:00:22Z unix=1067148022 wall=2003-10-26T01:00:22 fold-until(2003-10-26T02:00:22) type=2 -05 EST
0169: 2004-04-04T07:00:22Z unix=1081062022 wall=2004-04-04T02:00:22 gap-until(2004-04-04T03:00:22) type=1 -04 EDT dst
0170: 2004-10-31T06:00:22Z unix=1099202422 wall=2004-10-31T01:00:22 fold-until(2004-10-31T02:00:22) type=2 -05 EST
0171: 2005-04-03T07:00:22Z unix=1112511622 wall=2005-04-03T02:00:22 gap-until(2005-04-03T03:00:22) type=1 -04 EDT dst
0172: 2005-10-30T06:00:22Z unix=1130652022 wall=2005-10-30T01:00:22 fold-until(2005-10-30T02:00:22) type=2 -05 EST
0173: 2006-04-02T07:00:23Z unix=1143961223 wall=2006-04-02T02:00:23 gap-until(2006-04-02T03:00:23) type=1 -04 EDT dst
0174: 2006-10-29T06:00:23Z unix=1162101623 wall=2006-10-29T01:00:23 fold-until(2006-10-29T02:00:23) type=2 -05 EST
0175: 2007-03-11T07:00:23Z unix=1173596423 wall=2007-03-11T02:00:23 gap-until(2007-03-11T03:00:23) type=1 -04 EDT dst
0176: 2007-11-04T06:00:23Z unix=1194156023 wall=2007-11-04T01:00:23 fold-until(2007-11-04T02:00:23) type=2 -05 EST
0177: 2008-03-09T07:00:23Z unix=1205046023 wall=2008-03-09T02:00:23 gap-until(2008-03-09T03:00:23) type=1 -04 EDT dst
0178: 2008-11-02T06:00:23Z unix=1225605623 wall=2008-11-02T01:00:23 fold-until(2008-11-02T02:00:23) type=2 -05 EST
0179: 2009-03-08T07:00:24Z unix=1236495624 wall=2009-03-08T02:00:24 gap-until(2009-03-08T03:00:24) type=1 -04 EDT dst
0180: 2009-11-01T06:00:24Z unix=1257055224 wall=2009-11-01T01:00:24 fold-until(2009-11-01T02:00:24) type=2 -05 EST
0181: 2010-03-14T07:00:24Z unix=1268550024 wall=2010-03-14T02:00:24 gap-until(2010-03-14T03:00:24) type=1 -04 EDT dst
0182: 2010-11-07T06:00:24Z unix=1289109624 wall=2010-11-07T01:00:24 fold-until(2010-11-07T02:00:24) type=2 -05 EST
0183: 2011-03-13T07:00:24Z unix=1299999624 wall=2011-03-13T02:00:24 gap-until(2011-03-13T03:00:24) type=1 -04 EDT dst
0184: 2011-11-06T06:00:24Z unix=1320559224 wall=2011-11-06T01:00:24 fold-until(2011-11-06T02:00:24) type=2 -05 EST
0185: 2012-03-11T07:00:24Z unix=1331449224 wall=2012-03-11T02:00:24 gap-until(2012-03-11T03:00:24) type=1 -04 EDT dst
0186: 2012-11-04T06:00:25Z unix=1352008825 wall=2012-11-04T01:00:25 fold-until(2012-11-04T02:00:25) type=2 -05 EST
0187: 2013-03-10T07:00:25Z unix=1362898825 wall=2013-03-10T02:00:25 gap-until(2013-03-10T03:00:25) type=1 -04 EDT dst
0188: 2013-11-03T06:00:25Z unix=1383458425 wall=2013-11-03T01:00:25 fold-until(2013-11-03T02:00:25) type=2 -05 EST
0189: 2014-03-09T07:00:25Z unix=1394348425 wall=2014-03-09T02:00:25 gap-until(2014-03-09T03:00:25) type=1 -04 EDT dst
0190: 2014-11-02T06:00:25Z unix=1414908025 wall=2014-11-02T01:00:25 fold-until(2014-11-02T02:00:25) type=2 -05 EST
0191: 2015-03-08T07:00:25Z unix=1425798025 wall=2015-03-08T02:00:25 gap-until(2015-03-08T03:00:25) type=1 -04 EDT dst
0192: 2015-11-01T06:00:26Z unix=1446357626 wall=2015-11-01T01:00:26 fold-until(2015-11-01T02:00:26) type=2 -05 EST
0193: 2016-03-13T07:00:26Z unix=1457852426 wall=2016-03-13T02:00:26 gap-until(2016-03-13T03:00:26) type=1 -04 EDT dst
0194: 2016-11-06T06:00:26Z unix=1478412026 wall=2016-11-06T01:00:26 fold-until(2016-11-06T02:00:26) type=2 -05 EST
0195: 2017-03-12T07:00:27Z unix=1489302027 wall=2017-03-12T02:00:27 gap-until(2017-03-12T03:00:27) type=1 -04 EDT dst
0196: 2017-11-05T06:00:27Z unix=1509861627 wall=2017-11-05T01:00:27 fold-until(2017-11-05T02:00:27) type=2 -05 EST
0197: 2018-03-11T07:00:27Z unix=1520751627 wall=2018-03-11T02:00:27 gap-until(2018-03-11T03:00:27) type=1 -04 EDT dst
0198: 2018-11-04T06:00:27Z unix=1541311227 wall=2018-11-04T01:00:27 fold-until(2018-11-04T02:00:27) type=2 -05 EST
0199: 2019-03-10T07:00:27Z unix=1552201227 wall=2019-03-10T02:00:27 gap-until(2019-03-10T03:00:27) type=1 -04 EDT dst
0200: 2019-11-03T06:00:27Z unix=1572760827 wall=2019-11-03T01:00:27 fold-until(2019-11-03T02:00:27) type=2 -05 EST
0201: 2020-03-08T07:00:27Z unix=1583650827 wall=2020-03-08T02:00:27 gap-until(2020-03-08T03:00:27) type=1 -04 EDT dst
0202: 2020-11-01T06:00:27Z unix=1604210427 wall=2020-11-01T01:00:27 fold-until(2020-11-01T02:00:27) type=2 -05 EST
0203: 2021-03-14T07:00:27Z unix=1615705227 wall=2021-03-14T02:00:27 gap-until(2021-03-14T03:00:27) type=1 -04 EDT dst
0204: 2021-11-07T06:00:27Z unix=1636264827 wall=2021-11-07T01:00:27 fold-until(2021-11-07T02:00:27) type=2 -05 EST
0205: 2022-03-13T07:00:27Z unix=1647154827 wall=2022-03-13T02:00:27 gap-until(2022-03-13T03:00:27) type=1 -04 EDT dst
0206: 2022-11-06T06:00:27Z unix=1667714427 wall=2022-11-06T01:00:27 fold-until(2022-11-06T02:00:27) type=2 -05 EST
0207: 2023-03-12T07:00:27Z unix=1678604427 wall=2023-03-12T02:00:27 gap-until(2023-03-12T03:00:27) type=1 -04 EDT dst
0208: 2023-11-05T06:00:27Z unix=1699164027 wall=2023-11-05T01:00:27 fold-until(2023-11-05T02:00:27) type=2 -05 EST
0209: 2024-03-10T07:00:27Z unix=1710054027 wall=2024-03-10T02:00:27 gap-until(2024-03-10T03:00:27) type=1 -04 EDT dst
0210: 2024-11-03T06:00:27Z unix=1730613627 wall=2024-11-03T01:00:27 fold-until(2024-11-03T02:00:27) type=2 -05 EST
0211: 2025-03-09T07:00:27Z unix=1741503627 wall=2025-03-09T02:00:27 gap-until(2025-03-09T03:00:27) type=1 -04 EDT dst
0212: 2025-11-02T06:00:27Z unix=1762063227 wall=2025-11-02T01:00:27 fold-until(2025-11-02T02:00:27) type=2 -05 EST
0213: 2026-03-08T07:00:27Z unix=1772953227 wall=2026-03-08T02:00:27 gap-until(2026-03-08T03:00:27) type=1 -04 EDT dst
0214: 2026-11-01T06:00:27Z unix=1793512827 wall=2026-11-01T01:00:27 fold-until(2026-11-01T02:00:27) type=2 -05 EST
0215: 2027-03-14T07:00:27Z unix=1805007627 wall=2027-03-14T02:00:27 gap-until(2027-03-14T03:00:27) type=1 -04 EDT dst
0216: 2027-11-07T06:00:27Z unix=1825567227 wall=2027-11-07T01:00:27 fold-until(2027-11-07T02:00:27) type=2 -05 EST
0217: 2028-03-12T07:00:27Z unix=1836457227 wall=2028-03-12T02:00:27 gap-until(2028-03-12T03:00:27) type=1 -04 EDT dst
0218: 2028-11-05T06:00:27Z unix=1857016827 wall=2028-11-05T01:00:27 fold-until(2028-11-05T02:00:27) type=2 -05 EST
0219: 2029-03-11T07:00:27Z unix=1867906827 wall=2029-03-11T02:00:27 gap-until(2029-03-11T03:00:27) type=1 -04 EDT dst
0220: 2029-11-04T06:00:27Z unix=1888466427 wall=2029-11-04T01:00:27 fold-until(2029-11-04T02:00:27) type=2 -05 EST
0221: 2030-03-10T07:00:27Z unix=1899356427 wall=2030-03-10T02:00:27 gap-until(2030-03-10T03:00:27) type=1 -04 EDT dst
0222: 2030-11-03T06:00:27Z unix=1919916027 wall=2030-11-03T01:00:27 fold-until(2030-11-03T02:00:27) type=2 -05 EST
0223: 2031-03-09T07:00:27Z unix=1930806027 wall=2031-03-09T02:00:27 gap-until(2031-03-09T03:00:27) type=1 -04 EDT dst
0224: 2031-11-02T06:00:27Z unix=1951365627 wall=2031-11-02T01:00:27 fold-until(2031-11-02T02:00:27) type=2 -05 EST
0225: 2032-03-14T07:00:27Z unix=1962860427 wall=2032-03-14T02:00:27 gap-until(2032-03-14T03:00:27) type=1 -04 EDT dst
0226: 2032-11-07T06:00:27Z unix=1983420027 wall=2032-11-07T01:00:27 fold-until(2032-11-07T02:00:27) type=2 -05 EST
0227: 2033-03-13T07:00:27Z unix=1994310027 wall=2033-03-13T02:00:27 gap-until(2033-03-13T03:00:27) type=1 -04 EDT dst
0228: 2033-11-06T06:00:27Z unix=2014869627 wall=2033-11-06T01:00:27 fold-until(2033-11-06T02:00:27) type=2 -05 EST
0229: 2034-03-12T07:00:27Z unix=2025759627 wall=2034-03-12T02:00:27 gap-until(2034-03-12T03:00:27) type=1 -04 EDT dst
0230: 2034-11-05T06:00:27Z unix=2046319227 wall=2034-11-05T01:00:27 fold-until(2034-11-05T02:00:27) type=2 -05 EST
0231: 2035-03-11T07:00:27Z unix=2057209227 wall=2035-03-11T02:00:27 gap-until(2035-03-11T03:00:27) type=1 -04 EDT dst
0232: 2035-11-04T06:00:27Z unix=2077768827 wall=2035-11-04T01:00:27 fold-until(2035-11-04T02:00:27) type=2 -05 EST
0233: 2036-03-09T07:00:27Z unix=2088658827 wall=2036-03-09T02:00:27 gap-until(2036-03-09T03:00:27) type=1 -04 EDT dst
0234: 2036-11-02T06:00:27Z unix=2109218427 wall=2036-11-02T01:00:27 fold-until(2036-11-02T02:00:27) type=2 -05 EST
0235: 2037-03-08T07:00:27Z unix=2120108427 wall=2037-03-08T02:00:27 gap-until(2037-03-08T03:00:27) type=1 -04 EDT dst
0236: 2037-11-01T06:00:27Z unix=2140668027 wall=2037-11-01T01:00:27 fold-until(2037-11-01T02:00:27) type=2 -05 EST
LEAP SECONDS
1972-07-01T00:00:00 correction=1
1973-01-01T00:00:01 correction=2
1974-01-01T00:00:02 correction=3
1975-01-01T00:00:03 correction=4
1976-01-01T00:00:04 correction=5
1977-01-01T00:00:05 correction=6
1978-01-01T00:00:06 correction=7
1979-01-01T00:00:07 correction=8
1980-01-01T00:00:08 correction=9
1981-07-01T00:00:09 correction=10
1982-07-01T00:00:10 correction=11
1983-07-01T00:00:11 correction=12
1985-07-01T00:00:12 correction=13
1988-01-01T00:00:13 correction=14
1990-01-01T00:00:14 correction=15
1991-01-01T00:00:15 correction=16
1992-07-01T00:00:16 correction=17
1993-07-01T00:00:17 correction=18
1994-07-01T00:00:18 correction=19
1996-01-01T00:00:19 correction=20
1997-07-01T00:00:20 correction=21
1999-01-01T00:00:21 correction=22
2006-01-01T00:00:22 correction=23
2009-01-01T00:00:23 correction=24
2012-07-01T00:00:24 correction=25
2015-07-01T00:00:25 correction=26
2017-01-01T00:00:26 correction=27

View file

@ -0,0 +1,281 @@
---
source: src/tz/tzif.rs
expression: tzif_to_human_readable(&tzif_test.parse())
---
TIME ZONE NAME
right/America/New_York
LOCAL TIME TYPES
000: offset=-04:56:02 designation=LMT indicator=local/wall
001: offset=-04 designation=EDT dst indicator=local/wall
002: offset=-05 designation=EST indicator=local/wall
003: offset=-05 designation=EST indicator=ut/std
004: offset=-04 designation=EWT dst indicator=local/wall
005: offset=-04 designation=EPT dst indicator=ut/std
TRANSITIONS
0000: -9999-01-02T01:59:59Z unix=-377705023201 wall=-9999-01-01T21:03:57 unambiguous type=0 -04:56:02 LMT
0001: 1883-11-18T17:00:00Z unix=-2717650800 wall=1883-11-18T12:00:00 fold-until(1883-11-18T12:03:58) type=3 -05 EST
0002: 1918-03-31T07:00:00Z unix=-1633280400 wall=1918-03-31T02:00:00 gap-until(1918-03-31T03:00:00) type=1 -04 EDT dst
0003: 1918-10-27T06:00:00Z unix=-1615140000 wall=1918-10-27T01:00:00 fold-until(1918-10-27T02:00:00) type=2 -05 EST
0004: 1919-03-30T07:00:00Z unix=-1601830800 wall=1919-03-30T02:00:00 gap-until(1919-03-30T03:00:00) type=1 -04 EDT dst
0005: 1919-10-26T06:00:00Z unix=-1583690400 wall=1919-10-26T01:00:00 fold-until(1919-10-26T02:00:00) type=2 -05 EST
0006: 1920-03-28T07:00:00Z unix=-1570381200 wall=1920-03-28T02:00:00 gap-until(1920-03-28T03:00:00) type=1 -04 EDT dst
0007: 1920-10-31T06:00:00Z unix=-1551636000 wall=1920-10-31T01:00:00 fold-until(1920-10-31T02:00:00) type=2 -05 EST
0008: 1921-04-24T07:00:00Z unix=-1536512400 wall=1921-04-24T02:00:00 gap-until(1921-04-24T03:00:00) type=1 -04 EDT dst
0009: 1921-09-25T06:00:00Z unix=-1523210400 wall=1921-09-25T01:00:00 fold-until(1921-09-25T02:00:00) type=2 -05 EST
0010: 1922-04-30T07:00:00Z unix=-1504458000 wall=1922-04-30T02:00:00 gap-until(1922-04-30T03:00:00) type=1 -04 EDT dst
0011: 1922-09-24T06:00:00Z unix=-1491760800 wall=1922-09-24T01:00:00 fold-until(1922-09-24T02:00:00) type=2 -05 EST
0012: 1923-04-29T07:00:00Z unix=-1473008400 wall=1923-04-29T02:00:00 gap-until(1923-04-29T03:00:00) type=1 -04 EDT dst
0013: 1923-09-30T06:00:00Z unix=-1459706400 wall=1923-09-30T01:00:00 fold-until(1923-09-30T02:00:00) type=2 -05 EST
0014: 1924-04-27T07:00:00Z unix=-1441558800 wall=1924-04-27T02:00:00 gap-until(1924-04-27T03:00:00) type=1 -04 EDT dst
0015: 1924-09-28T06:00:00Z unix=-1428256800 wall=1924-09-28T01:00:00 fold-until(1924-09-28T02:00:00) type=2 -05 EST
0016: 1925-04-26T07:00:00Z unix=-1410109200 wall=1925-04-26T02:00:00 gap-until(1925-04-26T03:00:00) type=1 -04 EDT dst
0017: 1925-09-27T06:00:00Z unix=-1396807200 wall=1925-09-27T01:00:00 fold-until(1925-09-27T02:00:00) type=2 -05 EST
0018: 1926-04-25T07:00:00Z unix=-1378659600 wall=1926-04-25T02:00:00 gap-until(1926-04-25T03:00:00) type=1 -04 EDT dst
0019: 1926-09-26T06:00:00Z unix=-1365357600 wall=1926-09-26T01:00:00 fold-until(1926-09-26T02:00:00) type=2 -05 EST
0020: 1927-04-24T07:00:00Z unix=-1347210000 wall=1927-04-24T02:00:00 gap-until(1927-04-24T03:00:00) type=1 -04 EDT dst
0021: 1927-09-25T06:00:00Z unix=-1333908000 wall=1927-09-25T01:00:00 fold-until(1927-09-25T02:00:00) type=2 -05 EST
0022: 1928-04-29T07:00:00Z unix=-1315155600 wall=1928-04-29T02:00:00 gap-until(1928-04-29T03:00:00) type=1 -04 EDT dst
0023: 1928-09-30T06:00:00Z unix=-1301853600 wall=1928-09-30T01:00:00 fold-until(1928-09-30T02:00:00) type=2 -05 EST
0024: 1929-04-28T07:00:00Z unix=-1283706000 wall=1929-04-28T02:00:00 gap-until(1929-04-28T03:00:00) type=1 -04 EDT dst
0025: 1929-09-29T06:00:00Z unix=-1270404000 wall=1929-09-29T01:00:00 fold-until(1929-09-29T02:00:00) type=2 -05 EST
0026: 1930-04-27T07:00:00Z unix=-1252256400 wall=1930-04-27T02:00:00 gap-until(1930-04-27T03:00:00) type=1 -04 EDT dst
0027: 1930-09-28T06:00:00Z unix=-1238954400 wall=1930-09-28T01:00:00 fold-until(1930-09-28T02:00:00) type=2 -05 EST
0028: 1931-04-26T07:00:00Z unix=-1220806800 wall=1931-04-26T02:00:00 gap-until(1931-04-26T03:00:00) type=1 -04 EDT dst
0029: 1931-09-27T06:00:00Z unix=-1207504800 wall=1931-09-27T01:00:00 fold-until(1931-09-27T02:00:00) type=2 -05 EST
0030: 1932-04-24T07:00:00Z unix=-1189357200 wall=1932-04-24T02:00:00 gap-until(1932-04-24T03:00:00) type=1 -04 EDT dst
0031: 1932-09-25T06:00:00Z unix=-1176055200 wall=1932-09-25T01:00:00 fold-until(1932-09-25T02:00:00) type=2 -05 EST
0032: 1933-04-30T07:00:00Z unix=-1157302800 wall=1933-04-30T02:00:00 gap-until(1933-04-30T03:00:00) type=1 -04 EDT dst
0033: 1933-09-24T06:00:00Z unix=-1144605600 wall=1933-09-24T01:00:00 fold-until(1933-09-24T02:00:00) type=2 -05 EST
0034: 1934-04-29T07:00:00Z unix=-1125853200 wall=1934-04-29T02:00:00 gap-until(1934-04-29T03:00:00) type=1 -04 EDT dst
0035: 1934-09-30T06:00:00Z unix=-1112551200 wall=1934-09-30T01:00:00 fold-until(1934-09-30T02:00:00) type=2 -05 EST
0036: 1935-04-28T07:00:00Z unix=-1094403600 wall=1935-04-28T02:00:00 gap-until(1935-04-28T03:00:00) type=1 -04 EDT dst
0037: 1935-09-29T06:00:00Z unix=-1081101600 wall=1935-09-29T01:00:00 fold-until(1935-09-29T02:00:00) type=2 -05 EST
0038: 1936-04-26T07:00:00Z unix=-1062954000 wall=1936-04-26T02:00:00 gap-until(1936-04-26T03:00:00) type=1 -04 EDT dst
0039: 1936-09-27T06:00:00Z unix=-1049652000 wall=1936-09-27T01:00:00 fold-until(1936-09-27T02:00:00) type=2 -05 EST
0040: 1937-04-25T07:00:00Z unix=-1031504400 wall=1937-04-25T02:00:00 gap-until(1937-04-25T03:00:00) type=1 -04 EDT dst
0041: 1937-09-26T06:00:00Z unix=-1018202400 wall=1937-09-26T01:00:00 fold-until(1937-09-26T02:00:00) type=2 -05 EST
0042: 1938-04-24T07:00:00Z unix=-1000054800 wall=1938-04-24T02:00:00 gap-until(1938-04-24T03:00:00) type=1 -04 EDT dst
0043: 1938-09-25T06:00:00Z unix=-986752800 wall=1938-09-25T01:00:00 fold-until(1938-09-25T02:00:00) type=2 -05 EST
0044: 1939-04-30T07:00:00Z unix=-968000400 wall=1939-04-30T02:00:00 gap-until(1939-04-30T03:00:00) type=1 -04 EDT dst
0045: 1939-09-24T06:00:00Z unix=-955303200 wall=1939-09-24T01:00:00 fold-until(1939-09-24T02:00:00) type=2 -05 EST
0046: 1940-04-28T07:00:00Z unix=-936550800 wall=1940-04-28T02:00:00 gap-until(1940-04-28T03:00:00) type=1 -04 EDT dst
0047: 1940-09-29T06:00:00Z unix=-923248800 wall=1940-09-29T01:00:00 fold-until(1940-09-29T02:00:00) type=2 -05 EST
0048: 1941-04-27T07:00:00Z unix=-905101200 wall=1941-04-27T02:00:00 gap-until(1941-04-27T03:00:00) type=1 -04 EDT dst
0049: 1941-09-28T06:00:00Z unix=-891799200 wall=1941-09-28T01:00:00 fold-until(1941-09-28T02:00:00) type=2 -05 EST
0050: 1942-02-09T07:00:00Z unix=-880218000 wall=1942-02-09T02:00:00 gap-until(1942-02-09T03:00:00) type=4 -04 EWT dst
0051: 1945-08-14T23:00:00Z unix=-769395600 wall=1945-08-14T19:00:00 unambiguous type=5 -04 EPT dst
0052: 1945-09-30T06:00:00Z unix=-765396000 wall=1945-09-30T01:00:00 fold-until(1945-09-30T02:00:00) type=2 -05 EST
0053: 1946-04-28T07:00:00Z unix=-747248400 wall=1946-04-28T02:00:00 gap-until(1946-04-28T03:00:00) type=1 -04 EDT dst
0054: 1946-09-29T06:00:00Z unix=-733946400 wall=1946-09-29T01:00:00 fold-until(1946-09-29T02:00:00) type=2 -05 EST
0055: 1947-04-27T07:00:00Z unix=-715798800 wall=1947-04-27T02:00:00 gap-until(1947-04-27T03:00:00) type=1 -04 EDT dst
0056: 1947-09-28T06:00:00Z unix=-702496800 wall=1947-09-28T01:00:00 fold-until(1947-09-28T02:00:00) type=2 -05 EST
0057: 1948-04-25T07:00:00Z unix=-684349200 wall=1948-04-25T02:00:00 gap-until(1948-04-25T03:00:00) type=1 -04 EDT dst
0058: 1948-09-26T06:00:00Z unix=-671047200 wall=1948-09-26T01:00:00 fold-until(1948-09-26T02:00:00) type=2 -05 EST
0059: 1949-04-24T07:00:00Z unix=-652899600 wall=1949-04-24T02:00:00 gap-until(1949-04-24T03:00:00) type=1 -04 EDT dst
0060: 1949-09-25T06:00:00Z unix=-639597600 wall=1949-09-25T01:00:00 fold-until(1949-09-25T02:00:00) type=2 -05 EST
0061: 1950-04-30T07:00:00Z unix=-620845200 wall=1950-04-30T02:00:00 gap-until(1950-04-30T03:00:00) type=1 -04 EDT dst
0062: 1950-09-24T06:00:00Z unix=-608148000 wall=1950-09-24T01:00:00 fold-until(1950-09-24T02:00:00) type=2 -05 EST
0063: 1951-04-29T07:00:00Z unix=-589395600 wall=1951-04-29T02:00:00 gap-until(1951-04-29T03:00:00) type=1 -04 EDT dst
0064: 1951-09-30T06:00:00Z unix=-576093600 wall=1951-09-30T01:00:00 fold-until(1951-09-30T02:00:00) type=2 -05 EST
0065: 1952-04-27T07:00:00Z unix=-557946000 wall=1952-04-27T02:00:00 gap-until(1952-04-27T03:00:00) type=1 -04 EDT dst
0066: 1952-09-28T06:00:00Z unix=-544644000 wall=1952-09-28T01:00:00 fold-until(1952-09-28T02:00:00) type=2 -05 EST
0067: 1953-04-26T07:00:00Z unix=-526496400 wall=1953-04-26T02:00:00 gap-until(1953-04-26T03:00:00) type=1 -04 EDT dst
0068: 1953-09-27T06:00:00Z unix=-513194400 wall=1953-09-27T01:00:00 fold-until(1953-09-27T02:00:00) type=2 -05 EST
0069: 1954-04-25T07:00:00Z unix=-495046800 wall=1954-04-25T02:00:00 gap-until(1954-04-25T03:00:00) type=1 -04 EDT dst
0070: 1954-09-26T06:00:00Z unix=-481744800 wall=1954-09-26T01:00:00 fold-until(1954-09-26T02:00:00) type=2 -05 EST
0071: 1955-04-24T07:00:00Z unix=-463597200 wall=1955-04-24T02:00:00 gap-until(1955-04-24T03:00:00) type=1 -04 EDT dst
0072: 1955-10-30T06:00:00Z unix=-447271200 wall=1955-10-30T01:00:00 fold-until(1955-10-30T02:00:00) type=2 -05 EST
0073: 1956-04-29T07:00:00Z unix=-431542800 wall=1956-04-29T02:00:00 gap-until(1956-04-29T03:00:00) type=1 -04 EDT dst
0074: 1956-10-28T06:00:00Z unix=-415821600 wall=1956-10-28T01:00:00 fold-until(1956-10-28T02:00:00) type=2 -05 EST
0075: 1957-04-28T07:00:00Z unix=-400093200 wall=1957-04-28T02:00:00 gap-until(1957-04-28T03:00:00) type=1 -04 EDT dst
0076: 1957-10-27T06:00:00Z unix=-384372000 wall=1957-10-27T01:00:00 fold-until(1957-10-27T02:00:00) type=2 -05 EST
0077: 1958-04-27T07:00:00Z unix=-368643600 wall=1958-04-27T02:00:00 gap-until(1958-04-27T03:00:00) type=1 -04 EDT dst
0078: 1958-10-26T06:00:00Z unix=-352922400 wall=1958-10-26T01:00:00 fold-until(1958-10-26T02:00:00) type=2 -05 EST
0079: 1959-04-26T07:00:00Z unix=-337194000 wall=1959-04-26T02:00:00 gap-until(1959-04-26T03:00:00) type=1 -04 EDT dst
0080: 1959-10-25T06:00:00Z unix=-321472800 wall=1959-10-25T01:00:00 fold-until(1959-10-25T02:00:00) type=2 -05 EST
0081: 1960-04-24T07:00:00Z unix=-305744400 wall=1960-04-24T02:00:00 gap-until(1960-04-24T03:00:00) type=1 -04 EDT dst
0082: 1960-10-30T06:00:00Z unix=-289418400 wall=1960-10-30T01:00:00 fold-until(1960-10-30T02:00:00) type=2 -05 EST
0083: 1961-04-30T07:00:00Z unix=-273690000 wall=1961-04-30T02:00:00 gap-until(1961-04-30T03:00:00) type=1 -04 EDT dst
0084: 1961-10-29T06:00:00Z unix=-257968800 wall=1961-10-29T01:00:00 fold-until(1961-10-29T02:00:00) type=2 -05 EST
0085: 1962-04-29T07:00:00Z unix=-242240400 wall=1962-04-29T02:00:00 gap-until(1962-04-29T03:00:00) type=1 -04 EDT dst
0086: 1962-10-28T06:00:00Z unix=-226519200 wall=1962-10-28T01:00:00 fold-until(1962-10-28T02:00:00) type=2 -05 EST
0087: 1963-04-28T07:00:00Z unix=-210790800 wall=1963-04-28T02:00:00 gap-until(1963-04-28T03:00:00) type=1 -04 EDT dst
0088: 1963-10-27T06:00:00Z unix=-195069600 wall=1963-10-27T01:00:00 fold-until(1963-10-27T02:00:00) type=2 -05 EST
0089: 1964-04-26T07:00:00Z unix=-179341200 wall=1964-04-26T02:00:00 gap-until(1964-04-26T03:00:00) type=1 -04 EDT dst
0090: 1964-10-25T06:00:00Z unix=-163620000 wall=1964-10-25T01:00:00 fold-until(1964-10-25T02:00:00) type=2 -05 EST
0091: 1965-04-25T07:00:00Z unix=-147891600 wall=1965-04-25T02:00:00 gap-until(1965-04-25T03:00:00) type=1 -04 EDT dst
0092: 1965-10-31T06:00:00Z unix=-131565600 wall=1965-10-31T01:00:00 fold-until(1965-10-31T02:00:00) type=2 -05 EST
0093: 1966-04-24T07:00:00Z unix=-116442000 wall=1966-04-24T02:00:00 gap-until(1966-04-24T03:00:00) type=1 -04 EDT dst
0094: 1966-10-30T06:00:00Z unix=-100116000 wall=1966-10-30T01:00:00 fold-until(1966-10-30T02:00:00) type=2 -05 EST
0095: 1967-04-30T07:00:00Z unix=-84387600 wall=1967-04-30T02:00:00 gap-until(1967-04-30T03:00:00) type=1 -04 EDT dst
0096: 1967-10-29T06:00:00Z unix=-68666400 wall=1967-10-29T01:00:00 fold-until(1967-10-29T02:00:00) type=2 -05 EST
0097: 1968-04-28T07:00:00Z unix=-52938000 wall=1968-04-28T02:00:00 gap-until(1968-04-28T03:00:00) type=1 -04 EDT dst
0098: 1968-10-27T06:00:00Z unix=-37216800 wall=1968-10-27T01:00:00 fold-until(1968-10-27T02:00:00) type=2 -05 EST
0099: 1969-04-27T07:00:00Z unix=-21488400 wall=1969-04-27T02:00:00 gap-until(1969-04-27T03:00:00) type=1 -04 EDT dst
0100: 1969-10-26T06:00:00Z unix=-5767200 wall=1969-10-26T01:00:00 fold-until(1969-10-26T02:00:00) type=2 -05 EST
0101: 1970-04-26T07:00:00Z unix=9961200 wall=1970-04-26T02:00:00 gap-until(1970-04-26T03:00:00) type=1 -04 EDT dst
0102: 1970-10-25T06:00:00Z unix=25682400 wall=1970-10-25T01:00:00 fold-until(1970-10-25T02:00:00) type=2 -05 EST
0103: 1971-04-25T07:00:00Z unix=41410800 wall=1971-04-25T02:00:00 gap-until(1971-04-25T03:00:00) type=1 -04 EDT dst
0104: 1971-10-31T06:00:00Z unix=57736800 wall=1971-10-31T01:00:00 fold-until(1971-10-31T02:00:00) type=2 -05 EST
0105: 1972-04-30T07:00:00Z unix=73465200 wall=1972-04-30T02:00:00 gap-until(1972-04-30T03:00:00) type=1 -04 EDT dst
0106: 1972-10-29T06:00:01Z unix=89186401 wall=1972-10-29T01:00:01 fold-until(1972-10-29T02:00:01) type=2 -05 EST
0107: 1973-04-29T07:00:02Z unix=104914802 wall=1973-04-29T02:00:02 gap-until(1973-04-29T03:00:02) type=1 -04 EDT dst
0108: 1973-10-28T06:00:02Z unix=120636002 wall=1973-10-28T01:00:02 fold-until(1973-10-28T02:00:02) type=2 -05 EST
0109: 1974-01-06T07:00:03Z unix=126687603 wall=1974-01-06T02:00:03 gap-until(1974-01-06T03:00:03) type=1 -04 EDT dst
0110: 1974-10-27T06:00:03Z unix=152085603 wall=1974-10-27T01:00:03 fold-until(1974-10-27T02:00:03) type=2 -05 EST
0111: 1975-02-23T07:00:04Z unix=162370804 wall=1975-02-23T02:00:04 gap-until(1975-02-23T03:00:04) type=1 -04 EDT dst
0112: 1975-10-26T06:00:04Z unix=183535204 wall=1975-10-26T01:00:04 fold-until(1975-10-26T02:00:04) type=2 -05 EST
0113: 1976-04-25T07:00:05Z unix=199263605 wall=1976-04-25T02:00:05 gap-until(1976-04-25T03:00:05) type=1 -04 EDT dst
0114: 1976-10-31T06:00:05Z unix=215589605 wall=1976-10-31T01:00:05 fold-until(1976-10-31T02:00:05) type=2 -05 EST
0115: 1977-04-24T07:00:06Z unix=230713206 wall=1977-04-24T02:00:06 gap-until(1977-04-24T03:00:06) type=1 -04 EDT dst
0116: 1977-10-30T06:00:06Z unix=247039206 wall=1977-10-30T01:00:06 fold-until(1977-10-30T02:00:06) type=2 -05 EST
0117: 1978-04-30T07:00:07Z unix=262767607 wall=1978-04-30T02:00:07 gap-until(1978-04-30T03:00:07) type=1 -04 EDT dst
0118: 1978-10-29T06:00:07Z unix=278488807 wall=1978-10-29T01:00:07 fold-until(1978-10-29T02:00:07) type=2 -05 EST
0119: 1979-04-29T07:00:08Z unix=294217208 wall=1979-04-29T02:00:08 gap-until(1979-04-29T03:00:08) type=1 -04 EDT dst
0120: 1979-10-28T06:00:08Z unix=309938408 wall=1979-10-28T01:00:08 fold-until(1979-10-28T02:00:08) type=2 -05 EST
0121: 1980-04-27T07:00:09Z unix=325666809 wall=1980-04-27T02:00:09 gap-until(1980-04-27T03:00:09) type=1 -04 EDT dst
0122: 1980-10-26T06:00:09Z unix=341388009 wall=1980-10-26T01:00:09 fold-until(1980-10-26T02:00:09) type=2 -05 EST
0123: 1981-04-26T07:00:09Z unix=357116409 wall=1981-04-26T02:00:09 gap-until(1981-04-26T03:00:09) type=1 -04 EDT dst
0124: 1981-10-25T06:00:10Z unix=372837610 wall=1981-10-25T01:00:10 fold-until(1981-10-25T02:00:10) type=2 -05 EST
0125: 1982-04-25T07:00:10Z unix=388566010 wall=1982-04-25T02:00:10 gap-until(1982-04-25T03:00:10) type=1 -04 EDT dst
0126: 1982-10-31T06:00:11Z unix=404892011 wall=1982-10-31T01:00:11 fold-until(1982-10-31T02:00:11) type=2 -05 EST
0127: 1983-04-24T07:00:11Z unix=420015611 wall=1983-04-24T02:00:11 gap-until(1983-04-24T03:00:11) type=1 -04 EDT dst
0128: 1983-10-30T06:00:12Z unix=436341612 wall=1983-10-30T01:00:12 fold-until(1983-10-30T02:00:12) type=2 -05 EST
0129: 1984-04-29T07:00:12Z unix=452070012 wall=1984-04-29T02:00:12 gap-until(1984-04-29T03:00:12) type=1 -04 EDT dst
0130: 1984-10-28T06:00:12Z unix=467791212 wall=1984-10-28T01:00:12 fold-until(1984-10-28T02:00:12) type=2 -05 EST
0131: 1985-04-28T07:00:12Z unix=483519612 wall=1985-04-28T02:00:12 gap-until(1985-04-28T03:00:12) type=1 -04 EDT dst
0132: 1985-10-27T06:00:13Z unix=499240813 wall=1985-10-27T01:00:13 fold-until(1985-10-27T02:00:13) type=2 -05 EST
0133: 1986-04-27T07:00:13Z unix=514969213 wall=1986-04-27T02:00:13 gap-until(1986-04-27T03:00:13) type=1 -04 EDT dst
0134: 1986-10-26T06:00:13Z unix=530690413 wall=1986-10-26T01:00:13 fold-until(1986-10-26T02:00:13) type=2 -05 EST
0135: 1987-04-05T07:00:13Z unix=544604413 wall=1987-04-05T02:00:13 gap-until(1987-04-05T03:00:13) type=1 -04 EDT dst
0136: 1987-10-25T06:00:13Z unix=562140013 wall=1987-10-25T01:00:13 fold-until(1987-10-25T02:00:13) type=2 -05 EST
0137: 1988-04-03T07:00:14Z unix=576054014 wall=1988-04-03T02:00:14 gap-until(1988-04-03T03:00:14) type=1 -04 EDT dst
0138: 1988-10-30T06:00:14Z unix=594194414 wall=1988-10-30T01:00:14 fold-until(1988-10-30T02:00:14) type=2 -05 EST
0139: 1989-04-02T07:00:14Z unix=607503614 wall=1989-04-02T02:00:14 gap-until(1989-04-02T03:00:14) type=1 -04 EDT dst
0140: 1989-10-29T06:00:14Z unix=625644014 wall=1989-10-29T01:00:14 fold-until(1989-10-29T02:00:14) type=2 -05 EST
0141: 1990-04-01T07:00:15Z unix=638953215 wall=1990-04-01T02:00:15 gap-until(1990-04-01T03:00:15) type=1 -04 EDT dst
0142: 1990-10-28T06:00:15Z unix=657093615 wall=1990-10-28T01:00:15 fold-until(1990-10-28T02:00:15) type=2 -05 EST
0143: 1991-04-07T07:00:16Z unix=671007616 wall=1991-04-07T02:00:16 gap-until(1991-04-07T03:00:16) type=1 -04 EDT dst
0144: 1991-10-27T06:00:16Z unix=688543216 wall=1991-10-27T01:00:16 fold-until(1991-10-27T02:00:16) type=2 -05 EST
0145: 1992-04-05T07:00:16Z unix=702457216 wall=1992-04-05T02:00:16 gap-until(1992-04-05T03:00:16) type=1 -04 EDT dst
0146: 1992-10-25T06:00:17Z unix=719992817 wall=1992-10-25T01:00:17 fold-until(1992-10-25T02:00:17) type=2 -05 EST
0147: 1993-04-04T07:00:17Z unix=733906817 wall=1993-04-04T02:00:17 gap-until(1993-04-04T03:00:17) type=1 -04 EDT dst
0148: 1993-10-31T06:00:18Z unix=752047218 wall=1993-10-31T01:00:18 fold-until(1993-10-31T02:00:18) type=2 -05 EST
0149: 1994-04-03T07:00:18Z unix=765356418 wall=1994-04-03T02:00:18 gap-until(1994-04-03T03:00:18) type=1 -04 EDT dst
0150: 1994-10-30T06:00:19Z unix=783496819 wall=1994-10-30T01:00:19 fold-until(1994-10-30T02:00:19) type=2 -05 EST
0151: 1995-04-02T07:00:19Z unix=796806019 wall=1995-04-02T02:00:19 gap-until(1995-04-02T03:00:19) type=1 -04 EDT dst
0152: 1995-10-29T06:00:19Z unix=814946419 wall=1995-10-29T01:00:19 fold-until(1995-10-29T02:00:19) type=2 -05 EST
0153: 1996-04-07T07:00:20Z unix=828860420 wall=1996-04-07T02:00:20 gap-until(1996-04-07T03:00:20) type=1 -04 EDT dst
0154: 1996-10-27T06:00:20Z unix=846396020 wall=1996-10-27T01:00:20 fold-until(1996-10-27T02:00:20) type=2 -05 EST
0155: 1997-04-06T07:00:20Z unix=860310020 wall=1997-04-06T02:00:20 gap-until(1997-04-06T03:00:20) type=1 -04 EDT dst
0156: 1997-10-26T06:00:21Z unix=877845621 wall=1997-10-26T01:00:21 fold-until(1997-10-26T02:00:21) type=2 -05 EST
0157: 1998-04-05T07:00:21Z unix=891759621 wall=1998-04-05T02:00:21 gap-until(1998-04-05T03:00:21) type=1 -04 EDT dst
0158: 1998-10-25T06:00:21Z unix=909295221 wall=1998-10-25T01:00:21 fold-until(1998-10-25T02:00:21) type=2 -05 EST
0159: 1999-04-04T07:00:22Z unix=923209222 wall=1999-04-04T02:00:22 gap-until(1999-04-04T03:00:22) type=1 -04 EDT dst
0160: 1999-10-31T06:00:22Z unix=941349622 wall=1999-10-31T01:00:22 fold-until(1999-10-31T02:00:22) type=2 -05 EST
0161: 2000-04-02T07:00:22Z unix=954658822 wall=2000-04-02T02:00:22 gap-until(2000-04-02T03:00:22) type=1 -04 EDT dst
0162: 2000-10-29T06:00:22Z unix=972799222 wall=2000-10-29T01:00:22 fold-until(2000-10-29T02:00:22) type=2 -05 EST
0163: 2001-04-01T07:00:22Z unix=986108422 wall=2001-04-01T02:00:22 gap-until(2001-04-01T03:00:22) type=1 -04 EDT dst
0164: 2001-10-28T06:00:22Z unix=1004248822 wall=2001-10-28T01:00:22 fold-until(2001-10-28T02:00:22) type=2 -05 EST
0165: 2002-04-07T07:00:22Z unix=1018162822 wall=2002-04-07T02:00:22 gap-until(2002-04-07T03:00:22) type=1 -04 EDT dst
0166: 2002-10-27T06:00:22Z unix=1035698422 wall=2002-10-27T01:00:22 fold-until(2002-10-27T02:00:22) type=2 -05 EST
0167: 2003-04-06T07:00:22Z unix=1049612422 wall=2003-04-06T02:00:22 gap-until(2003-04-06T03:00:22) type=1 -04 EDT dst
0168: 2003-10-26T06:00:22Z unix=1067148022 wall=2003-10-26T01:00:22 fold-until(2003-10-26T02:00:22) type=2 -05 EST
0169: 2004-04-04T07:00:22Z unix=1081062022 wall=2004-04-04T02:00:22 gap-until(2004-04-04T03:00:22) type=1 -04 EDT dst
0170: 2004-10-31T06:00:22Z unix=1099202422 wall=2004-10-31T01:00:22 fold-until(2004-10-31T02:00:22) type=2 -05 EST
0171: 2005-04-03T07:00:22Z unix=1112511622 wall=2005-04-03T02:00:22 gap-until(2005-04-03T03:00:22) type=1 -04 EDT dst
0172: 2005-10-30T06:00:22Z unix=1130652022 wall=2005-10-30T01:00:22 fold-until(2005-10-30T02:00:22) type=2 -05 EST
0173: 2006-04-02T07:00:23Z unix=1143961223 wall=2006-04-02T02:00:23 gap-until(2006-04-02T03:00:23) type=1 -04 EDT dst
0174: 2006-10-29T06:00:23Z unix=1162101623 wall=2006-10-29T01:00:23 fold-until(2006-10-29T02:00:23) type=2 -05 EST
0175: 2007-03-11T07:00:23Z unix=1173596423 wall=2007-03-11T02:00:23 gap-until(2007-03-11T03:00:23) type=1 -04 EDT dst
0176: 2007-11-04T06:00:23Z unix=1194156023 wall=2007-11-04T01:00:23 fold-until(2007-11-04T02:00:23) type=2 -05 EST
0177: 2008-03-09T07:00:23Z unix=1205046023 wall=2008-03-09T02:00:23 gap-until(2008-03-09T03:00:23) type=1 -04 EDT dst
0178: 2008-11-02T06:00:23Z unix=1225605623 wall=2008-11-02T01:00:23 fold-until(2008-11-02T02:00:23) type=2 -05 EST
0179: 2009-03-08T07:00:24Z unix=1236495624 wall=2009-03-08T02:00:24 gap-until(2009-03-08T03:00:24) type=1 -04 EDT dst
0180: 2009-11-01T06:00:24Z unix=1257055224 wall=2009-11-01T01:00:24 fold-until(2009-11-01T02:00:24) type=2 -05 EST
0181: 2010-03-14T07:00:24Z unix=1268550024 wall=2010-03-14T02:00:24 gap-until(2010-03-14T03:00:24) type=1 -04 EDT dst
0182: 2010-11-07T06:00:24Z unix=1289109624 wall=2010-11-07T01:00:24 fold-until(2010-11-07T02:00:24) type=2 -05 EST
0183: 2011-03-13T07:00:24Z unix=1299999624 wall=2011-03-13T02:00:24 gap-until(2011-03-13T03:00:24) type=1 -04 EDT dst
0184: 2011-11-06T06:00:24Z unix=1320559224 wall=2011-11-06T01:00:24 fold-until(2011-11-06T02:00:24) type=2 -05 EST
0185: 2012-03-11T07:00:24Z unix=1331449224 wall=2012-03-11T02:00:24 gap-until(2012-03-11T03:00:24) type=1 -04 EDT dst
0186: 2012-11-04T06:00:25Z unix=1352008825 wall=2012-11-04T01:00:25 fold-until(2012-11-04T02:00:25) type=2 -05 EST
0187: 2013-03-10T07:00:25Z unix=1362898825 wall=2013-03-10T02:00:25 gap-until(2013-03-10T03:00:25) type=1 -04 EDT dst
0188: 2013-11-03T06:00:25Z unix=1383458425 wall=2013-11-03T01:00:25 fold-until(2013-11-03T02:00:25) type=2 -05 EST
0189: 2014-03-09T07:00:25Z unix=1394348425 wall=2014-03-09T02:00:25 gap-until(2014-03-09T03:00:25) type=1 -04 EDT dst
0190: 2014-11-02T06:00:25Z unix=1414908025 wall=2014-11-02T01:00:25 fold-until(2014-11-02T02:00:25) type=2 -05 EST
0191: 2015-03-08T07:00:25Z unix=1425798025 wall=2015-03-08T02:00:25 gap-until(2015-03-08T03:00:25) type=1 -04 EDT dst
0192: 2015-11-01T06:00:26Z unix=1446357626 wall=2015-11-01T01:00:26 fold-until(2015-11-01T02:00:26) type=2 -05 EST
0193: 2016-03-13T07:00:26Z unix=1457852426 wall=2016-03-13T02:00:26 gap-until(2016-03-13T03:00:26) type=1 -04 EDT dst
0194: 2016-11-06T06:00:26Z unix=1478412026 wall=2016-11-06T01:00:26 fold-until(2016-11-06T02:00:26) type=2 -05 EST
0195: 2017-03-12T07:00:27Z unix=1489302027 wall=2017-03-12T02:00:27 gap-until(2017-03-12T03:00:27) type=1 -04 EDT dst
0196: 2017-11-05T06:00:27Z unix=1509861627 wall=2017-11-05T01:00:27 fold-until(2017-11-05T02:00:27) type=2 -05 EST
0197: 2018-03-11T07:00:27Z unix=1520751627 wall=2018-03-11T02:00:27 gap-until(2018-03-11T03:00:27) type=1 -04 EDT dst
0198: 2018-11-04T06:00:27Z unix=1541311227 wall=2018-11-04T01:00:27 fold-until(2018-11-04T02:00:27) type=2 -05 EST
0199: 2019-03-10T07:00:27Z unix=1552201227 wall=2019-03-10T02:00:27 gap-until(2019-03-10T03:00:27) type=1 -04 EDT dst
0200: 2019-11-03T06:00:27Z unix=1572760827 wall=2019-11-03T01:00:27 fold-until(2019-11-03T02:00:27) type=2 -05 EST
0201: 2020-03-08T07:00:27Z unix=1583650827 wall=2020-03-08T02:00:27 gap-until(2020-03-08T03:00:27) type=1 -04 EDT dst
0202: 2020-11-01T06:00:27Z unix=1604210427 wall=2020-11-01T01:00:27 fold-until(2020-11-01T02:00:27) type=2 -05 EST
0203: 2021-03-14T07:00:27Z unix=1615705227 wall=2021-03-14T02:00:27 gap-until(2021-03-14T03:00:27) type=1 -04 EDT dst
0204: 2021-11-07T06:00:27Z unix=1636264827 wall=2021-11-07T01:00:27 fold-until(2021-11-07T02:00:27) type=2 -05 EST
0205: 2022-03-13T07:00:27Z unix=1647154827 wall=2022-03-13T02:00:27 gap-until(2022-03-13T03:00:27) type=1 -04 EDT dst
0206: 2022-11-06T06:00:27Z unix=1667714427 wall=2022-11-06T01:00:27 fold-until(2022-11-06T02:00:27) type=2 -05 EST
0207: 2023-03-12T07:00:27Z unix=1678604427 wall=2023-03-12T02:00:27 gap-until(2023-03-12T03:00:27) type=1 -04 EDT dst
0208: 2023-11-05T06:00:27Z unix=1699164027 wall=2023-11-05T01:00:27 fold-until(2023-11-05T02:00:27) type=2 -05 EST
0209: 2024-03-10T07:00:27Z unix=1710054027 wall=2024-03-10T02:00:27 gap-until(2024-03-10T03:00:27) type=1 -04 EDT dst
0210: 2024-11-03T06:00:27Z unix=1730613627 wall=2024-11-03T01:00:27 fold-until(2024-11-03T02:00:27) type=2 -05 EST
0211: 2025-03-09T07:00:27Z unix=1741503627 wall=2025-03-09T02:00:27 gap-until(2025-03-09T03:00:27) type=1 -04 EDT dst
0212: 2025-11-02T06:00:27Z unix=1762063227 wall=2025-11-02T01:00:27 fold-until(2025-11-02T02:00:27) type=2 -05 EST
0213: 2026-03-08T07:00:27Z unix=1772953227 wall=2026-03-08T02:00:27 gap-until(2026-03-08T03:00:27) type=1 -04 EDT dst
0214: 2026-11-01T06:00:27Z unix=1793512827 wall=2026-11-01T01:00:27 fold-until(2026-11-01T02:00:27) type=2 -05 EST
0215: 2027-03-14T07:00:27Z unix=1805007627 wall=2027-03-14T02:00:27 gap-until(2027-03-14T03:00:27) type=1 -04 EDT dst
0216: 2027-11-07T06:00:27Z unix=1825567227 wall=2027-11-07T01:00:27 fold-until(2027-11-07T02:00:27) type=2 -05 EST
0217: 2028-03-12T07:00:27Z unix=1836457227 wall=2028-03-12T02:00:27 gap-until(2028-03-12T03:00:27) type=1 -04 EDT dst
0218: 2028-11-05T06:00:27Z unix=1857016827 wall=2028-11-05T01:00:27 fold-until(2028-11-05T02:00:27) type=2 -05 EST
0219: 2029-03-11T07:00:27Z unix=1867906827 wall=2029-03-11T02:00:27 gap-until(2029-03-11T03:00:27) type=1 -04 EDT dst
0220: 2029-11-04T06:00:27Z unix=1888466427 wall=2029-11-04T01:00:27 fold-until(2029-11-04T02:00:27) type=2 -05 EST
0221: 2030-03-10T07:00:27Z unix=1899356427 wall=2030-03-10T02:00:27 gap-until(2030-03-10T03:00:27) type=1 -04 EDT dst
0222: 2030-11-03T06:00:27Z unix=1919916027 wall=2030-11-03T01:00:27 fold-until(2030-11-03T02:00:27) type=2 -05 EST
0223: 2031-03-09T07:00:27Z unix=1930806027 wall=2031-03-09T02:00:27 gap-until(2031-03-09T03:00:27) type=1 -04 EDT dst
0224: 2031-11-02T06:00:27Z unix=1951365627 wall=2031-11-02T01:00:27 fold-until(2031-11-02T02:00:27) type=2 -05 EST
0225: 2032-03-14T07:00:27Z unix=1962860427 wall=2032-03-14T02:00:27 gap-until(2032-03-14T03:00:27) type=1 -04 EDT dst
0226: 2032-11-07T06:00:27Z unix=1983420027 wall=2032-11-07T01:00:27 fold-until(2032-11-07T02:00:27) type=2 -05 EST
0227: 2033-03-13T07:00:27Z unix=1994310027 wall=2033-03-13T02:00:27 gap-until(2033-03-13T03:00:27) type=1 -04 EDT dst
0228: 2033-11-06T06:00:27Z unix=2014869627 wall=2033-11-06T01:00:27 fold-until(2033-11-06T02:00:27) type=2 -05 EST
0229: 2034-03-12T07:00:27Z unix=2025759627 wall=2034-03-12T02:00:27 gap-until(2034-03-12T03:00:27) type=1 -04 EDT dst
0230: 2034-11-05T06:00:27Z unix=2046319227 wall=2034-11-05T01:00:27 fold-until(2034-11-05T02:00:27) type=2 -05 EST
0231: 2035-03-11T07:00:27Z unix=2057209227 wall=2035-03-11T02:00:27 gap-until(2035-03-11T03:00:27) type=1 -04 EDT dst
0232: 2035-11-04T06:00:27Z unix=2077768827 wall=2035-11-04T01:00:27 fold-until(2035-11-04T02:00:27) type=2 -05 EST
0233: 2036-03-09T07:00:27Z unix=2088658827 wall=2036-03-09T02:00:27 gap-until(2036-03-09T03:00:27) type=1 -04 EDT dst
0234: 2036-11-02T06:00:27Z unix=2109218427 wall=2036-11-02T01:00:27 fold-until(2036-11-02T02:00:27) type=2 -05 EST
0235: 2037-03-08T07:00:27Z unix=2120108427 wall=2037-03-08T02:00:27 gap-until(2037-03-08T03:00:27) type=1 -04 EDT dst
0236: 2037-11-01T06:00:27Z unix=2140668027 wall=2037-11-01T01:00:27 fold-until(2037-11-01T02:00:27) type=2 -05 EST
LEAP SECONDS
1972-07-01T00:00:00 correction=1
1973-01-01T00:00:01 correction=2
1974-01-01T00:00:02 correction=3
1975-01-01T00:00:03 correction=4
1976-01-01T00:00:04 correction=5
1977-01-01T00:00:05 correction=6
1978-01-01T00:00:06 correction=7
1979-01-01T00:00:07 correction=8
1980-01-01T00:00:08 correction=9
1981-07-01T00:00:09 correction=10
1982-07-01T00:00:10 correction=11
1983-07-01T00:00:11 correction=12
1985-07-01T00:00:12 correction=13
1988-01-01T00:00:13 correction=14
1990-01-01T00:00:14 correction=15
1991-01-01T00:00:15 correction=16
1992-07-01T00:00:16 correction=17
1993-07-01T00:00:17 correction=18
1994-07-01T00:00:18 correction=19
1996-01-01T00:00:19 correction=20
1997-07-01T00:00:20 correction=21
1999-01-01T00:00:21 correction=22
2006-01-01T00:00:22 correction=23
2009-01-01T00:00:23 correction=24
2012-07-01T00:00:24 correction=25
2015-07-01T00:00:25 correction=26
2017-01-01T00:00:26 correction=27
POSIX TIME ZONE STRING
EST5EDT,M3.2.0,M11.1.0

89
src/tz/testdata.rs Normal file
View file

@ -0,0 +1,89 @@
use crate::tz::Tzif;
/// A list of all TZif files in our testdata directory.
///
/// Feel free to add more if there are other "interesting" cases. Note that
/// some tests iterate over this slice to check each entry. So adding a new
/// test file here might require updating some tests.
///
/// # Revisions
///
/// * `2024-03-27`: Initial set pulled from my local copy of `tzdata 2024a`.
pub(crate) const TZIF_TEST_FILES: &[TzifTestFile] = &[
TzifTestFile {
name: "America/New_York",
data: include_bytes!("testdata/america-new-york.tzif"),
},
TzifTestFile {
name: "America/Sitka",
data: include_bytes!("testdata/america-sitka.tzif"),
},
TzifTestFile {
name: "America/St_Johns",
data: include_bytes!("testdata/america-st-johns.tzif"),
},
TzifTestFile {
name: "right/America/New_York",
data: include_bytes!("testdata/right-america-new-york.tzif"),
},
TzifTestFile {
name: "Antarctica/Troll",
data: include_bytes!("testdata/antarctica-troll.tzif"),
},
TzifTestFile {
name: "Australia/Tasmania",
data: include_bytes!("testdata/australia-tasmania.tzif"),
},
TzifTestFile {
name: "Europe/Dublin",
data: include_bytes!("testdata/europe-dublin.tzif"),
},
TzifTestFile {
name: "Pacific/Honolulu",
data: include_bytes!("testdata/pacific-honolulu.tzif"),
},
];
/// A single TZif datum.
///
/// It contains the name of the time zone and the raw bytes of the
/// corresponding TZif file.
#[derive(Clone, Copy)]
pub(crate) struct TzifTestFile {
pub(crate) name: &'static str,
pub(crate) data: &'static [u8],
}
impl TzifTestFile {
/// Look up the TZif test file for the given time zone name.
///
/// If one doesn't exist, then this panics and fails the current
/// test.
pub(crate) fn get(name: &str) -> TzifTestFile {
for &tzif_file in TZIF_TEST_FILES {
if tzif_file.name == name {
return tzif_file;
}
}
panic!("could not find TZif test file for {name:?}")
}
/// Parse this test TZif data into a structured representation.
pub(crate) fn parse(self) -> Tzif {
Tzif::parse(self.name, self.data).unwrap_or_else(|err| {
panic!("failed to parse TZif test file for {:?}: {err}", self.name)
})
}
/// Parse this test TZif data as if it were V1.
pub(crate) fn parse_v1(self) -> Tzif {
let mut data = self.data.to_vec();
data[4] = 0;
Tzif::parse(self.name, &data).unwrap_or_else(|err| {
panic!(
"failed to parse V1 TZif test file for {:?}: {err}",
self.name
)
})
}
}

BIN
src/tz/testdata/america-new-york.tzif vendored Normal file

Binary file not shown.

BIN
src/tz/testdata/america-sitka.tzif vendored Normal file

Binary file not shown.

BIN
src/tz/testdata/america-st-johns.tzif vendored Normal file

Binary file not shown.

BIN
src/tz/testdata/antarctica-troll.tzif vendored Normal file

Binary file not shown.

BIN
src/tz/testdata/australia-tasmania.tzif vendored Normal file

Binary file not shown.

BIN
src/tz/testdata/europe-dublin.tzif vendored Normal file

Binary file not shown.

BIN
src/tz/testdata/pacific-honolulu.tzif vendored Normal file

Binary file not shown.

Binary file not shown.

1424
src/tz/tzif.rs Normal file

File diff suppressed because it is too large Load diff

2770
src/tz/zic.rs Normal file

File diff suppressed because it is too large Load diff

57
src/util/alloc.rs Normal file
View file

@ -0,0 +1,57 @@
// The `MaybeBox` type is kind of suspect. It's mostly used to shrink error
// types whenever we have `alloc` available. But it might actually make more
// sense to reduce the amount of error information available when `alloc`
// isn't enabled, since presumably the size of types is *more* of an issue in
// environments without `alloc`. If you find yourself in this conundrum, please
// file an issue.
/// A conditional `Box` for some type.
///
/// This uses a `Box` internally when the `alloc` crate feature is enabled,
/// and inlines the `T` into this type otherwise. This is useful for reducing
/// the size of types when dynamic memory allocation is available.
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct MaybeBox<T>(
#[cfg(feature = "alloc")] alloc::boxed::Box<T>,
#[cfg(not(feature = "alloc"))] T,
);
impl<T> MaybeBox<T> {
/// Create a new conditional box for `T`.
pub(crate) fn new(t: T) -> MaybeBox<T> {
#[cfg(feature = "alloc")]
{
MaybeBox(alloc::boxed::Box::new(t))
}
#[cfg(not(feature = "alloc"))]
{
MaybeBox(t)
}
}
/// Move out of this box.
pub(crate) fn into_inner(this: MaybeBox<T>) -> T {
#[cfg(feature = "alloc")]
{
*this.0
}
#[cfg(not(feature = "alloc"))]
{
this.0
}
}
}
impl<T> core::ops::Deref for MaybeBox<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> core::ops::DerefMut for MaybeBox<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}

175
src/util/common.rs Normal file
View file

@ -0,0 +1,175 @@
/// A collection of datetime related utility functions.
///
/// # Constant functions
///
/// This module also contains some utilities for making some API items `const`.
///
/// One of the main reasons why more of this crate isn't `const` is because of
/// our ranged integer types. We very specifically use those types wherever we
/// do arithmetic as a mitigation for the absolute insance boundary conditions
/// that one must deal with in a datetime library. Unfortunately, these range
/// integer types are very difficult to make use of in `const` context. One of
/// the main blockers is the fact that Rust doesn't support `const` methods in
/// traits. That means we'd be unable to do `x + y` where `x` and `y` are range
/// integers. (Normal `x + y` arithmetic only works in `const` today because of
/// special support for Rust's standard integer types.)
///
/// With that said, it is sometimes useful to provide APIs that are const. For
/// example, `Date::new`. But we still need to do some checking on the inputs
/// to ensure they are valid. Since most of our arithmetic code is defined
/// using range integers, we have to re-write some it here using standard
/// primitive types.
///
/// In general, this code should not be used unless it is specifically required
/// to make something `const` AND if it is worth making it `const` in the first
/// place. Remember, much of this library _could_ be `const` if we were willing
/// to write `const`-compatible code, but I deemed it far too annoying to do
/// so.
use crate::util::{
rangeint::{self, ri16, ri32, ri64, ri8},
t::{Day, Month, Year, C},
};
/// Returns true if and only if the given year is a leap year.
///
/// A leap year is a year with 366 days. Typical years have 365 days.
pub(crate) fn is_leap_year(year: Year) -> bool {
year % C(4) == 0 && (year % C(100) != 0 || year % C(400) == 0)
}
/// Returns true if and only if the given year is a leap year.
///
/// A leap year is a year with 366 days. Typical years have 365 days.
///
/// This doesn't used range integers and is thus `const`.
pub(crate) const fn is_leap_year_unranged(year: i16) -> bool {
year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
/// Saturates the given day in the month.
///
/// That is, if the day exceeds the maximum number of days in the given year
/// and month, then this returns the maximum. Otherwise, it returns the day
/// given.
pub(crate) fn saturate_day_in_month(
year: Year,
month: Month,
day: Day,
) -> Day {
day.min(days_in_month(year, month))
}
/// Returns the number of days in the given year and month.
///
/// This correctly returns `29` when the year is a leap year and the month is
/// February.
pub(crate) fn days_in_month(year: Year, month: Month) -> Day {
if month == 2 && is_leap_year(year) {
Day::N::<29>()
} else {
days_in_month_common_year(month)
}
}
/// Return the number of days in the given month.
///
/// When the given month is invalid, this returns `0`.
pub(crate) const fn days_in_month_unranged(year: i16, month: i8) -> i8 {
match month {
1 => 31,
2 if is_leap_year_unranged(year) => 29,
2 => 28,
3 => 31,
4 => 30,
5 => 31,
6 => 30,
7 => 31,
8 => 31,
9 => 30,
10 => 31,
11 => 30,
12 => 31,
_ => 0,
}
}
/// Returns the number of days in the given month for a non-leap year.
pub(crate) fn days_in_month_common_year(month: Month) -> Day {
const BY_MONTH: [Day; 13] = [
Day::N::<0>(),
Day::N::<31>(),
Day::N::<28>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
];
// FIXME: How do we want to handle indexing with range integers more
// generally? At the very least add `as_usize()`...
BY_MONTH[usize::from(month.get() as u8)]
}
/// Returns the number of days in the given month for a leap year.
pub(crate) fn days_in_month_leap_year(month: Month) -> Day {
const BY_MONTH: [Day; 13] = [
Day::N::<0>(),
Day::N::<31>(),
Day::N::<29>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
Day::N::<30>(),
Day::N::<31>(),
];
// FIXME: How do we want to handle indexing with range integers more
// generally? At the very least add `as_usize()`...
BY_MONTH[usize::from(month.get() as u8)]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_is_leap_year() {
let is_leap_year = |y: i16| is_leap_year(Year::new(y).unwrap());
assert!(is_leap_year(2024));
assert!(!is_leap_year(2023));
assert!(!is_leap_year(2025));
assert!(is_leap_year(2000));
assert!(!is_leap_year(1900));
assert!(!is_leap_year(1800));
assert!(!is_leap_year(1700));
assert!(is_leap_year(1600));
assert!(is_leap_year(0));
assert!(!is_leap_year(-1));
assert!(!is_leap_year(-2));
assert!(!is_leap_year(-3));
assert!(is_leap_year(-4));
assert!(!is_leap_year(-100));
assert!(!is_leap_year(-200));
assert!(!is_leap_year(-300));
assert!(is_leap_year(400));
assert!(!is_leap_year(9999));
assert!(!is_leap_year(-9999));
}
#[test]
fn t_days_in_month() {
let days_in_month = |year: i16, month| {
days_in_month(Year::new(year).unwrap(), Month::new(month).unwrap())
};
assert_eq!(28, days_in_month(-9999, 2).get());
}
}

140
src/util/escape.rs Normal file
View file

@ -0,0 +1,140 @@
/*!
Provides convenience routines for escaping raw bytes.
This was copied from `regex-automata` with a few light edits.
*/
/// Provides a convenient `Debug` implementation for a `u8`.
///
/// The `Debug` impl treats the byte as an ASCII, and emits a human readable
/// representation of it. If the byte isn't ASCII, then it's emitted as a hex
/// escape sequence.
#[derive(Clone, Copy)]
pub struct Byte(pub u8);
impl core::fmt::Display for Byte {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
core::fmt::Debug::fmt(self, f)
}
}
impl core::fmt::Debug for Byte {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
// Special case ASCII space. It's too hard to read otherwise, so
// put quotes around it. I sometimes wonder whether just '\x20' would
// be better...
if self.0 == b' ' {
return write!(f, "' '");
}
// 10 bytes is enough to cover any output from ascii::escape_default.
let mut bytes = [0u8; 10];
let mut len = 0;
for (i, mut b) in core::ascii::escape_default(self.0).enumerate() {
// capitalize \xab to \xAB
if i >= 2 && b'a' <= b && b <= b'f' {
b -= 32;
}
bytes[len] = b;
len += 1;
}
write!(f, "{}", core::str::from_utf8(&bytes[..len]).unwrap())
}
}
/// Provides a convenient `Debug` implementation for `&[u8]`.
///
/// This generally works best when the bytes are presumed to be mostly UTF-8,
/// but will work for anything. For any bytes that aren't UTF-8, they are
/// emitted as hex escape sequences.
pub struct Bytes<'a>(pub &'a [u8]);
impl<'a> core::fmt::Display for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
// This is a sad re-implementation of a similar impl found in bstr.
let mut bytes = self.0;
while let Some(result) = utf8_decode(bytes) {
let ch = match result {
Ok(ch) => ch,
Err(byte) => {
write!(f, r"\x{:02x}", byte)?;
bytes = &bytes[1..];
continue;
}
};
bytes = &bytes[ch.len_utf8()..];
match ch {
'\0' => write!(f, "\\0")?,
// ASCII control characters except \0, \n, \r, \t
'\x01'..='\x08'
| '\x0b'
| '\x0c'
| '\x0e'..='\x19'
| '\x7f' => {
write!(f, "\\x{:02x}", u32::from(ch))?;
}
'\n' | '\r' | '\t' | _ => {
write!(f, "{}", ch.escape_debug())?;
}
}
}
Ok(())
}
}
impl<'a> core::fmt::Debug for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "\"")?;
core::fmt::Display::fmt(self, f)?;
write!(f, "\"")?;
Ok(())
}
}
/// Decodes the next UTF-8 encoded codepoint from the given byte slice.
///
/// If no valid encoding of a codepoint exists at the beginning of the given
/// byte slice, then the first byte is returned instead.
///
/// This returns `None` if and only if `bytes` is empty.
///
/// This never panics.
///
/// *WARNING*: This is not designed for performance. If you're looking for a
/// fast UTF-8 decoder, this is not it. If you feel like you need one in this
/// crate, then please file an issue and discuss your use case.
fn utf8_decode(bytes: &[u8]) -> Option<Result<char, u8>> {
if bytes.is_empty() {
return None;
}
let len = match utf8_len(bytes[0]) {
None => return Some(Err(bytes[0])),
Some(len) if len > bytes.len() => return Some(Err(bytes[0])),
Some(1) => return Some(Ok(char::from(bytes[0]))),
Some(len) => len,
};
match core::str::from_utf8(&bytes[..len]) {
Ok(s) => Some(Ok(s.chars().next().unwrap())),
Err(_) => Some(Err(bytes[0])),
}
}
/// Given a UTF-8 leading byte, this returns the total number of code units
/// in the following encoded codepoint.
///
/// If the given byte is not a valid UTF-8 leading byte, then this returns
/// `None`.
fn utf8_len(byte: u8) -> Option<usize> {
if byte <= 0x7F {
return Some(1);
} else if byte & 0b1100_0000 == 0b1000_0000 {
return None;
} else if byte <= 0b1101_1111 {
Some(2)
} else if byte <= 0b1110_1111 {
Some(3)
} else if byte <= 0b1111_0111 {
Some(4)
} else {
None
}
}

6
src/util/mod.rs Normal file
View file

@ -0,0 +1,6 @@
pub(crate) mod alloc;
pub(crate) mod common;
pub(crate) mod escape;
pub(crate) mod parse;
pub(crate) mod rangeint;
pub(crate) mod t;

218
src/util/parse.rs Normal file
View file

@ -0,0 +1,218 @@
use alloc::{
boxed::Box,
string::{String, ToString},
};
use crate::{
error::{err, Error},
util::escape::{Byte, Bytes},
};
/// Parses an `i64` number from the beginning to the end of the given slice of
/// ASCII digit characters.
///
/// If any byte in the given slice is not `[0-9]`, then this returns an error.
/// Similarly, if the number parsed does not fit into a `i64`, then this
/// returns an error. Notably, this routine does not permit parsing a negative
/// integer. (We use `i64` because everything in this crate uses signed
/// integers, and because a higher level routine might want to parse the sign
/// and then apply it to the result of this routine.)
pub(crate) fn i64(bytes: &[u8]) -> Result<i64, Error> {
if bytes.is_empty() {
return Err(err!("invalid number, no digits found"));
}
let mut n: i64 = 0;
for &byte in bytes {
let digit = match byte.checked_sub(b'0') {
None => {
return Err(err!(
"invalid digit, expected 0-9 but got {}",
Byte(byte),
));
}
Some(digit) if digit > 9 => {
return Err(err!(
"invalid digit, expected 0-9 but got {}",
Byte(byte),
))
}
Some(digit) => {
debug_assert!((0..=9).contains(&digit));
i64::from(digit)
}
};
n = n.checked_mul(10).and_then(|n| n.checked_add(digit)).ok_or_else(
|| {
err!(
"number '{}' too big to parse into 64-bit integer",
Bytes(bytes),
)
},
)?;
}
Ok(n)
}
/// Parses an `i64` fractional number from the beginning to the end of the
/// given slice of ASCII digit characters.
///
/// The fraction's maximum precision must be provided. The returned integer
/// will always be in units of `10^{max_precision}`. For example, to parse a
/// fractional amount of seconds with a maximum precision of nanoseconds, then
/// use `max_precision=9`.
///
/// If any byte in the given slice is not `[0-9]`, then this returns an error.
/// Similarly, if the fraction parsed does not fit into a `i64`, then this
/// returns an error. Notably, this routine does not permit parsing a negative
/// integer. (We use `i64` because everything in this crate uses signed
/// integers, and because a higher level routine might want to parse the sign
/// and then apply it to the result of this routine.)
pub(crate) fn fraction(
bytes: &[u8],
max_precision: usize,
) -> Result<i64, Error> {
if bytes.is_empty() {
return Err(err!("invalid fraction, no digits found"));
} else if bytes.len() > max_precision {
return Err(err!(
"invalid fraction, too many digits \
(at most {max_precision} are allowed"
));
}
let mut n: i64 = 0;
for &byte in bytes {
let digit = match byte.checked_sub(b'0') {
None => {
return Err(err!(
"invalid fractional digit, expected 0-9 but got {}",
Byte(byte),
));
}
Some(digit) if digit > 9 => {
return Err(err!(
"invalid fractional digit, expected 0-9 but got {}",
Byte(byte),
))
}
Some(digit) => {
debug_assert!((0..=9).contains(&digit));
i64::from(digit)
}
};
n = n.checked_mul(10).and_then(|n| n.checked_add(digit)).ok_or_else(
|| {
err!(
"fractional '{}' too big to parse into 64-bit integer",
Bytes(bytes),
)
},
)?;
}
for _ in bytes.len()..max_precision {
n = n.checked_mul(10).ok_or_else(|| {
err!(
"fractional '{}' too big to parse into 64-bit integer \
(too much precision supported)",
Bytes(bytes)
)
})?;
}
Ok(n)
}
/// Parses an `OsStr` into a `&str` when `&[u8]` isn't easily available.
///
/// This is effectively `OsStr::to_str`, but with a slightly better error
/// message.
pub(crate) fn os_str_utf8<'o, O>(os_str: &'o O) -> Result<&'o str, Error>
where
O: ?Sized + AsRef<std::ffi::OsStr>,
{
let os_str = os_str.as_ref();
os_str
.to_str()
.ok_or_else(|| err!("environment value {os_str:?} is not valid UTF-8"))
}
/// Parses an `OsStr` into a `&str` when `&[u8]` isn't easily available.
///
/// The main difference between this and `OsStr::to_str` is that this will
/// be a zero-cost conversion on Unix platforms to `&[u8]`. On Windows, this
/// will do UTF-8 validation and return an error if it's invalid UTF-8.
pub(crate) fn os_str_bytes<'o, O>(os_str: &'o O) -> Result<&'o [u8], Error>
where
O: ?Sized + AsRef<std::ffi::OsStr>,
{
let os_str = os_str.as_ref();
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
Ok(os_str.as_bytes())
}
#[cfg(not(unix))]
{
let string = os_str.to_str().ok_or_else(|| {
err!("environment value {os_str:?} is not valid UTF-8")
})?;
// It is suspect that we're doing UTF-8 validation and then throwing
// away the fact that we did UTF-8 validation. So this could lead
// to an extra UTF-8 check if the caller ultimately needs UTF-8. If
// that's important, we can add a new API that returns a `&str`. But it
// probably won't matter because an `OsStr` in this crate is usually
// just an environment variable.
Ok(string.as_bytes())
}
}
/// Splits the given input into two slices at the given position.
///
/// If the position is greater than the length of the slice given, then this
/// returns `None`.
pub(crate) fn split(input: &[u8], at: usize) -> Option<(&[u8], &[u8])> {
if at > input.len() {
None
} else {
Some(input.split_at(at))
}
}
/// Returns a function that converts two slices to an offset.
///
/// It takes the starting point as input and returns a function that, when
/// given an ending point (greater than or equal to the starting point), then
/// the corresponding pointers are subtracted and an offset relative to the
/// starting point is returned.
///
/// This is useful as a helper function in parsing routines that use slices
/// but want to report offsets.
///
/// # Panics
///
/// This may panic if the ending point is not a suffix slice of `start`.
pub(crate) fn offseter<'a>(
start: &'a [u8],
) -> impl Fn(&'a [u8]) -> usize + 'a {
move |end| (end.as_ptr() as usize) - (start.as_ptr() as usize)
}
/// Returns a function that converts two slices to the slice between them.
///
/// This takes a starting point as input and returns a function that, when
/// given an ending point (greater than or equal to the starting point), it
/// returns a slice beginning at the starting point and ending just at the
/// ending point.
///
/// This is useful as a helper function in parsing routines.
///
/// # Panics
///
/// This may panic if the ending point is not a suffix slice of `start`.
pub(crate) fn slicer<'a>(
start: &'a [u8],
) -> impl Fn(&'a [u8]) -> &'a [u8] + 'a {
let mkoffset = offseter(start);
move |end| {
let offset = mkoffset(end);
&start[..offset]
}
}

2420
src/util/rangeint.rs Normal file

File diff suppressed because it is too large Load diff

635
src/util/t.rs Normal file
View file

@ -0,0 +1,635 @@
use crate::util::rangeint::{self, ri128, ri16, ri32, ri64, ri8, RInto};
/// A type alias for the sign of a number.
///
/// It can be -1 for a negative sign, 1 for a positive sign or 0 for no sign.
pub(crate) type Sign = ri8<-1, 1>;
/// A type alias for a ranged integer with no units.
///
/// In particular, the range of this type is just the range of an `i64`. This
/// is useful when too many things with different units need to be combined at
/// once, and it's just too painful to keep them straight. In cases like that,
/// it's useful to just convert everything to `NoUnits`, do the necessary math,
/// and then convert back to the appropriate ranged types.
///
/// Note that we don't actually lose much by doing this, since the computed
/// min/max values are retained even when converting *to and from* this type.
/// In general, this type is just about making some math easier by making
/// everything uniform.
pub(crate) type NoUnits = ri64<{ i64::MIN as i128 }, { i64::MAX as i128 }>;
/// A type alias for a ranged 128-bit integer with no units.
///
/// This is like `NoUnits`, but useful in contexts where one wants to limit
/// values to what can be represented by an `i128`.
pub(crate) type NoUnits128 = ri128<{ i128::MIN }, { i128::MAX }>;
/// A type alias for a ranged 32-bit integer with no units.
///
/// This is like `NoUnits`, but useful in contexts where one wants to limit
/// values to what can be represented by an `i32`.
pub(crate) type NoUnits32 = ri32<{ i32::MIN as i128 }, { i32::MAX as i128 }>;
/// A type alias for a ranged 16-bit integer with no units.
///
/// This is like `NoUnits`, but useful in contexts where one wants to limit
/// values to what can be represented by an `i16`.
pub(crate) type NoUnits16 = ri16<{ i16::MIN as i128 }, { i16::MAX as i128 }>;
/// A type alias for a ranged 8-bit integer with no units.
///
/// This is like `NoUnits`, but useful in contexts where one wants to limit
/// values to what can be represented by an `i8`.
pub(crate) type NoUnits8 = ri8<{ i8::MIN as i128 }, { i8::MAX as i128 }>;
/// The range of years supported by jiff.
///
/// This is ultimately where some of the other ranges (like `UnixSeconds`)
/// were determined from. That is, the range of years is the primary point at
/// which the space of supported time instants is derived from. If one wanted
/// to expand this range, you'd need to change it here and then compute the
/// corresponding min/max values for `UnixSeconds`.
pub(crate) type Year = ri16<-9999, 9999>;
/// The range of BCE years supported by jiff.
pub(crate) type YearBCE = ri16<1, { Year::MAX + 1 }>;
/// The range of Unix seconds supported by Jiff.
///
/// This range should correspond to the first second of `Year::MIN` up through
/// (and including) the last second of `Year::MAX`. Actually computing that is
/// non-trivial, however, it can be computed easily enough using Unix programs
/// like `date`:
///
/// ```text
/// $ TZ=0 date -d 'Mon Jan 1 12:00:00 AM -9999' +'%s'
/// date: invalid date Mon Jan 1 12:00:00 AM -9999
/// $ TZ=0 date -d 'Fri Dec 31 23:59:59 9999' +'%s'
/// 253402300799
/// ```
///
/// Well, almost easily enough. `date` apparently doesn't support negative
/// years. But it does support negative timestamps:
///
/// ```text
/// $ TZ=0 date -d '@-377705116800'
/// Mon Jan 1 12:00:00 AM -9999
/// $ TZ=0 date -d '@253402300799'
/// Fri Dec 31 11:59:59 PM 9999
/// ```
///
/// With that said, we actually end up restricting the range a bit more than
/// what's above. Namely, what's above is what we support for civil datetimes.
/// Because of time zones, we need to choose whether all `Instant` values
/// can be infallibly converted to `civil::DateTime` values, or whether all
/// `civil::DateTime` values can be infallibly converted to `Instant` values.
/// I chose the former because getting a civil datetime is important for
/// formatting. If I didn't choose the former, there would be some instants
/// that could not be formatted. Thus, we make room by shrinking the range of
/// allowed instants by precisely the maximum supported time zone offset.
pub(crate) type UnixSeconds = ri64<
{ -377705116800 - SpanZoneOffset::MIN },
{ 253402300799 - SpanZoneOffset::MAX },
>;
/// Like UnixSeconds, but expressed in units of nanoseconds.
pub(crate) type UnixNanoseconds = ri128<
{ UnixSeconds::MIN * NANOS_PER_SECOND.bound() },
{
UnixSeconds::MAX * NANOS_PER_SECOND.bound() + FractionalNanosecond::MAX
},
>;
/// A timestamp representing time since 1970-01-01T00:00:00Z, but in TAI.
///
/// TAI stands for "International Atomic Time." It doesn't bother with things
/// like leap seconds because there is no requirement that it line up with
/// mean solar time. The advantage of TAI over Unix time is that it can be
/// unambiguously converted to and from UTC. The disadvantage is that you need
/// a table of leap second offsets to work with it effectively.
///
/// Since leap second offsets can be added to Unix time to get TAI (or
/// subtracted), we "make room" by shrinking the range of TAI time by 1 day on
/// both ends when compared to Unix time. This makes it possible to convert all
/// TAI timestamps to Unix timestamps. But, the reverse is fallible. (This is
/// similar to the time zone offset dance. We need to choose one direction to
/// be fallible and the other to be infallible. Unix timestamps tend to be used
/// in more places, and it's also the default, so I deemed it more important to
/// have infallible conversions *to* Unix time.)
///
/// We use 1 day here because it keeps things nice and even and because it
/// will be a long time before leap second offsets (or whatever replaces it)
/// tally up to more than 1 day.
pub(crate) type TaiSeconds = ri64<
{ UnixSeconds::MIN - LeapSecondOffset::MIN },
{ UnixSeconds::MAX - LeapSecondOffset::MAX },
>;
/// Like TaiSeconds, but expressed in units of nanoseconds.
pub(crate) type TaiNanoseconds = ri128<
{ TaiSeconds::MIN * NANOS_PER_SECOND.bound() },
{ TaiSeconds::MAX * NANOS_PER_SECOND.bound() + FractionalNanosecond::MAX },
>;
/// The maximum leap second offset that Jiff supports.
///
/// This could be bigger, but this seems big enough to last a long time.
pub(crate) type LeapSecondOffset = ri64<
{ -SECONDS_PER_CIVIL_DAY.bound() },
{ SECONDS_PER_CIVIL_DAY.bound() },
>;
/// The range of possible month values.
pub(crate) type Month = ri8<1, 12>;
/// The range of a weekday, offset from zero.
pub(crate) type WeekdayZero = ri8<0, 6>;
/// The range of a weekday, offset from one.
pub(crate) type WeekdayOne = ri8<1, 7>;
/// The range of possible day values.
///
/// Obviously this range is not valid for every month. Therefore, code working
/// with days needs to be careful to check that it is valid for whatever month
/// is being used.
pub(crate) type Day = ri8<1, 31>;
pub(crate) type ISOYear = ri16<-9999, 9999>;
pub(crate) type ISOWeek = ri8<1, 53>;
/// The range of possible hour values.
pub(crate) type Hour = ri8<0, 23>;
/// The range of possible minute values.
pub(crate) type Minute = ri8<0, 59>;
/// The range of possible second values for UTC.
pub(crate) type UtcSecond = ri8<0, 60>;
/// The range of possible second values not accounting for leap seconds.
pub(crate) type Second = ri8<0, 59>;
/// The range of possible millisecond values.
pub(crate) type Millisecond = ri16<0, 999>;
/// The range of possible microsecond values.
pub(crate) type Microsecond = ri16<0, 999>;
/// The range of possible nanosecond values.
pub(crate) type Nanosecond = ri16<0, 999>;
/// The range of possible nanosecond values.
pub(crate) type SubsecNanosecond = ri32<0, { NANOS_PER_SECOND.bound() - 1 }>;
/// A range representing each possible nanosecond in a single civil day.
pub(crate) type CivilDayNanosecond =
ri64<0, { NANOS_PER_CIVIL_DAY.bound() - 1 }>;
/// The number of seconds permitted in a single day.
///
/// This is mostly just a "sensible" cap on what is possible. We allow one day
/// to span up to 7 civil days.
///
/// It must also be at least 1 second long.
pub(crate) type ZonedDaySeconds =
ri64<{ 1 }, { 7 * SECONDS_PER_CIVIL_DAY.bound() }>;
/// The number of nanoseconds permitted in a single day.
///
/// This is mostly just a "sensible" cap on what is possible. We allow one day
/// to span up to 7 civil days.
///
/// It must also be at least 1 second long.
pub(crate) type ZonedDayNanoseconds = ri64<
{ ZonedDaySeconds::MIN * NANOS_PER_SECOND.bound() },
{ ZonedDaySeconds::MAX * NANOS_PER_SECOND.bound() },
>;
/// The number of days from the Unix epoch for the Gregorian calendar.
///
/// The range supported is based on the range of Unix timestamps that we
/// support.
///
/// While I had originally used the "rate die" concept from Calendrical
/// Calculations, I found [Howard Hinnant's formulation][date-algorithms]
/// much more straight-forward. And while I didn't benchmark them, it also
/// appears faster.
///
/// [date-algorithms]: http://howardhinnant.github.io/date_algorithms.html
pub(crate) type UnixEpochDays = ri32<
{
(UnixSeconds::MIN + SpanZoneOffset::MIN)
.div_euclid(SECONDS_PER_CIVIL_DAY.bound())
},
{
(UnixSeconds::MAX + SpanZoneOffset::MAX)
.div_euclid(SECONDS_PER_CIVIL_DAY.bound())
},
>;
/// A precise min/max of the allowed range of a duration in years.
pub(crate) type SpanYears = ri16<{ -(Year::LEN - 1) }, { Year::LEN - 1 }>;
/// A precise min/max of the allowed range of a duration in months.
pub(crate) type SpanMonths = ri32<
{ SpanYears::MIN * MONTHS_PER_YEAR.bound() },
{ SpanYears::MAX * MONTHS_PER_YEAR.bound() },
>;
/// A range of the allowed number of weeks.
///
/// This is an upper bound and not actually a precise maximum. I believe a
/// precise max could be fractional and not an integer.
pub(crate) type SpanWeeks = ri32<{ SpanDays::MIN * 7 }, { SpanDays::MAX * 7 }>;
/// A range of the allowed number of days.
pub(crate) type SpanDays =
ri32<{ SpanHours::MIN / 24 }, { SpanHours::MAX / 24 }>;
/// A range of the allowed number of hours.
///
/// Like days, this is an upper bound because some days (because of DST) have
/// 25 hours.
pub(crate) type SpanHours =
ri32<{ SpanMinutes::MIN / 60 }, { SpanMinutes::MAX / 60 }>;
/// A range of the allowed number of minutes.
pub(crate) type SpanMinutes =
ri64<{ SpanSeconds::MIN / 60 }, { SpanSeconds::MAX / 60 }>;
/// The maximum number of seconds that can be expressed with a span.
///
/// All of our span types (except for years and months, since they have
/// variable length even in civil datetimes) are defined in terms of this
/// constant. The way it's defined is a little odd, so let's break it down.
///
/// Firstly, a span of seconds should be able to represent at least
/// the complete span supported by `Instant`. Thus, it's based off of
/// `UnixSeconds::LEN`. That is, a span should be able to represent the value
/// `UnixSeconds::MAX - UnixSeconds::MIN`.
///
/// Secondly, a span should also be able to account for any amount of possible
/// time that a time zone offset might add or subtract to an `Instant`. This
/// also means it can account for any difference between two `civil::DateTime`
/// values.
///
/// Thirdly, the span should also be able to represent differences between
/// times that take leap seconds into account. At present, there are 37 of them
/// when converting between TAI and UTC. For this, we add our standard maximum
/// allotted range of leap second offset (at time of writing, it's 1 day).
///
/// Fourthly, we would like our span to be divisible by
/// `SECONDS_PER_CIVIL_DAY`. This isn't strictly required, but it makes
/// defining boundaries a little smoother. If it weren't divisible, then the
/// lower bounds on some types would need to be adjusted by one.
///
/// Note that neither the existence of this constant nor defining our spans
/// based on it impacts the correctness of doing arithmetic on zoned instants.
/// Artihemetic on zoned instants still uses "civil" spans, but the length
/// of time for some units (like a day) might vary. The arithmetic for zoned
/// instants accounts for this explicitly. But it still must obey the limits
/// set here.
const SPAN_CIVIL_SECONDS: i128 = next_multiple_of(
UnixSeconds::LEN + SpanZoneOffset::MAX + LeapSecondOffset::MAX,
SECONDS_PER_CIVIL_DAY.bound(),
);
/// A range of the allowed number of seconds.
///
/// Note that we specifically add a ~day's worth of seconds to either end
/// of the range to account for the fact that some time scales might exceed
/// the range of UnixSeconds because of leap seconds. More specifically,
/// we'd like to reuse the span logic in this module to do arithmetic on
/// jiff::Instant values, and so want to represent the full span of possible
/// seconds between any two points in time. If all we cared about was unix
/// time, then `(-(UnixSeconds::LEN - 1))..=(UnixSeconds::LEN - 1)` would
/// be sufficient.
pub(crate) type SpanSeconds =
ri64<{ -SPAN_CIVIL_SECONDS }, SPAN_CIVIL_SECONDS>;
/// A range of the allowed number of milliseconds.
pub(crate) type SpanMilliseconds =
ri64<{ SpanSeconds::MIN * 1_000 }, { SpanSeconds::MAX * 1_000 }>;
/// A range of the allowed number of microseconds.
pub(crate) type SpanMicroseconds =
ri64<{ SpanMilliseconds::MIN * 1_000 }, { SpanMilliseconds::MAX * 1_000 }>;
/// A range of the allowed number of nanoseconds.
///
/// For this, we cannot cover the full span of supported time instants since
/// `UnixSeconds::MAX * NANOSECONDS_PER_SECOND` cannot fit into 64-bits. We
/// could use a `i128`, but it doesn't seem worth it.
///
/// Also note that our min is equal to -max, so that the total number of values
/// in this range is one less than the number of distinct `i64` values. We do
/// that so that the absolute value is always defined.
pub(crate) type SpanNanoseconds =
ri64<{ (i64::MIN + 1) as i128 }, { i64::MAX as i128 }>;
/// The range of allowable fractional nanoseconds.
///
/// That is, this corresponds to the range of nanoseconds allowable within a
/// single second. It can be either positive or negative.
pub(crate) type FractionalNanosecond = ri32<
{ -(NANOS_PER_SECOND.bound() - 1) },
{ NANOS_PER_SECOND.bound() - 1 },
>;
/// The span of seconds permitted for expressing the offset of a time zone.
pub(crate) type SpanZoneOffset =
ri32<{ -SPAN_ZONE_OFFSET_TOTAL_SECONDS }, SPAN_ZONE_OFFSET_TOTAL_SECONDS>;
/// The max number of seconds that can be expressed in a time zone offset.
///
/// This is computed here based on the span offset types below for convenience
/// use in the `SpanZoneOffset` definition above.
const SPAN_ZONE_OFFSET_TOTAL_SECONDS: i128 =
(SpanZoneOffsetHours::MAX * 60 * 60)
+ (SpanZoneOffsetMinutes::MAX * 60)
+ SpanZoneOffsetSeconds::MAX;
/// The number of hours allowed in a time zone offset.
///
/// This number was somewhat arbitrarily chosen. In part because it's
/// bigger than any current offset by a wide margin, and in part because
/// POSIX `TZ` strings require the ability to store offsets in the range
/// `-24:59:59..=25:59:59`. Note though that we make the range a little bigger
/// with `-25:59:59..=25:59:59` so that negating an offset always produces a
/// valid offset.
///
/// Note that RFC 8536 actually allows offsets to be much bigger, namely, in
/// the range `(-2^31, 2^31)`, where both ends are _exclusive_ (`-2^31` is
/// explicitly disallowed, and `2^31` overflows a signed 32-bit integer). But
/// RFC 8536 does say that it *should* be in the range `[-89999, 93599]`, which
/// matches POSIX. In order to keep our offset small, we stick roughly to what
/// POSIX requires.
pub(crate) type SpanZoneOffsetHours = ri8<-25, 25>;
/// The number of minutes allowed in a time zone offset.
pub(crate) type SpanZoneOffsetMinutes = ri8<-59, 59>;
/// The number of seconds allowed in a time zone offset.
pub(crate) type SpanZoneOffsetSeconds = ri8<-59, 59>;
/// The number of months in a year.
pub(crate) const MONTHS_PER_YEAR: Constant = Constant(12);
/// The number of whole hours in one day.
pub(crate) const HOURS_PER_CIVIL_DAY: Constant = Constant(24);
/// The number of minutes in a civil day.
pub(crate) const MINUTES_PER_CIVIL_DAY: Constant =
Constant(HOURS_PER_CIVIL_DAY.value() * MINUTES_PER_HOUR.value());
/// The number of minutes in an hour.
pub(crate) const MINUTES_PER_HOUR: Constant = Constant(60);
/// The number of seconds in a civil day.
///
/// Some days will have more or less seconds because of DST transitions (or
/// even leap seconds). But such things are ignored when dealing with civil
/// time, and so this constant is still useful.
pub(crate) const SECONDS_PER_CIVIL_DAY: Constant =
Constant(HOURS_PER_CIVIL_DAY.value() * SECONDS_PER_HOUR.value());
/// The number of seconds in a typical week.
///
/// Some weeks will have more or less seconds because of DST transitions (or
/// even leap seconds). But such things are ignored when dealing with civil
/// time, and so this constant is still useful.
pub(crate) const SECONDS_PER_CIVIL_WEEK: Constant =
Constant(7 * SECONDS_PER_CIVIL_DAY.value());
/// The number of seconds in a single hour.
pub(crate) const SECONDS_PER_HOUR: Constant =
Constant(SECONDS_PER_MINUTE.value() * 60);
/// The number of seconds in a single minute.
pub(crate) const SECONDS_PER_MINUTE: Constant = Constant(60);
/// The number of microseconds in a civil day.
pub(crate) const MILLIS_PER_CIVIL_DAY: Constant =
Constant(SECONDS_PER_CIVIL_DAY.value() * MILLIS_PER_SECOND.value());
/// The number of milliseconds in a single second.
pub(crate) const MILLIS_PER_SECOND: Constant = Constant(1_000);
/// The number of microseconds in a civil day.
pub(crate) const MICROS_PER_CIVIL_DAY: Constant =
Constant(SECONDS_PER_CIVIL_DAY.value() * MICROS_PER_SECOND.value());
/// The number of microseconds in a single second.
pub(crate) const MICROS_PER_SECOND: Constant = Constant(1_000_000);
/// The number of microseconds in a single millisecond.
pub(crate) const MICROS_PER_MILLI: Constant = Constant(1_000);
/// The number of nanoseconds in a civil day.
///
/// Some days will have more or less seconds because of DST transitions (or
/// even leap seconds). But such things are ignored when dealing with civil
/// time, and so this constant is still useful.
pub(crate) const NANOS_PER_CIVIL_DAY: Constant =
Constant(SECONDS_PER_CIVIL_DAY.value() * NANOS_PER_SECOND.value());
/// The number of nanoseconds in a single hour.
pub(crate) const NANOS_PER_HOUR: Constant =
Constant(SECONDS_PER_HOUR.value() * NANOS_PER_SECOND.value());
/// The number of nanoseconds in a single minute.
pub(crate) const NANOS_PER_MINUTE: Constant =
Constant(SECONDS_PER_MINUTE.value() * NANOS_PER_SECOND.value());
/// The number of nanoseconds in a single second.
pub(crate) const NANOS_PER_SECOND: Constant = Constant(1_000_000_000);
/// The number of nanoseconds in a single millisecond.
pub(crate) const NANOS_PER_MILLI: Constant = Constant(1_000_000);
/// The number of nanoseconds in a single microsecond.
pub(crate) const NANOS_PER_MICRO: Constant = Constant(1_000);
pub(crate) fn sign<T: Ord>(t1: T, t2: T) -> Sign {
use core::cmp::Ordering::*;
match t1.cmp(&t2) {
Less => Sign::N::<-1>(),
Equal => Sign::N::<0>(),
Greater => Sign::N::<1>(),
}
}
/// A constant value for use in arithmetic in this crate.
///
/// This type is basically a bunch of shenanigans to make constants work in
/// a sensible way with our range integers. Essentially, we really want
/// constants to satisfy the following criteria:
///
/// 1. Defined in one place.
/// 2. Composable in that we can define constants in terms of other constants.
/// 3. Easy to use with any kind of range integer type.
/// 4. Specially constructed when used with ranged integers. That is, a ranged
/// integer value build from a constant should have computed min/max bounds
/// equivalent to the constant itself. (Normally, a `rN::new` will set the
/// computed min/max bounds to the MIN/MAX bounds overall, since it is assumed
/// that `rN::new` accepts a value that can vary to any legal value in the
/// range. But a constant needs tight bounds because, well, it can literally
/// never vary.)
///
/// # Trait implementations
///
/// It'd be nice to impl `Add/Sub/Mul/Div` for `Constant` itself, but they
/// can't be used in a const context... which is where it would be most useful.
/// Otherwise, we just define `Add/Sub/Mul/Div` impls for all of the ranged
/// integer types so that constants can be used on the left-hand side of
/// arithmetic expressions. (The ranged integer types have impls that are
/// generic enough to support arithmetic with constants on the right hand
/// side.)
///
/// We do a similar thing for the `Partial{Eq,Ord}` traits. The ranged integers
/// already have impls for `Constant` on the right-hand side. Below are the
/// impls for `Constant` on the left-hand side.
///
/// All of the trait impls that deal with constants and ranged integers are
/// implemented with the ranged integer types.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub(crate) struct Constant(pub(crate) i64);
/// A short-hand creating a generic `Constant` value as a ranged integer.
///
/// Callers do need to ensure that the `MIN` and `MAX` bounds are specified (or
/// more likely inferred), but otherwise, the `ri64` returned will be usable
/// in most contexts even with other ranged integers (like `ri8`).
#[allow(non_snake_case)]
pub(crate) fn C(
constant: i64,
) -> ri64<{ i64::MIN as i128 }, { i64::MAX as i128 }> {
Constant(constant).rinto()
}
#[allow(non_snake_case)]
pub(crate) fn C128(constant: i64) -> ri128<{ i128::MIN }, { i128::MAX }> {
Constant(constant).rinto()
}
impl Constant {
/// Return the primitive value of this constant.
pub(crate) const fn value(self) -> i64 {
self.0
}
/// Return this constant as a bound intended to be used in const generics.
pub(crate) const fn bound(self) -> i128 {
self.value() as i128
}
}
impl core::ops::Neg for Constant {
type Output = Constant;
fn neg(self) -> Constant {
Constant(-self.0)
}
}
impl From<Constant> for i8 {
fn from(c: Constant) -> i8 {
#[cfg(not(debug_assertions))]
{
c.value() as i8
}
#[cfg(debug_assertions)]
{
i8::try_from(c.value()).unwrap_or_else(|_| {
panic!("{c:?} is out of range {:?}..={:?}", i8::MIN, i8::MAX);
})
}
}
}
impl From<Constant> for i16 {
fn from(c: Constant) -> i16 {
#[cfg(not(debug_assertions))]
{
c.value() as i16
}
#[cfg(debug_assertions)]
{
i16::try_from(c.value()).unwrap_or_else(|_| {
panic!(
"{c:?} is out of range {:?}..={:?}",
i16::MIN,
i16::MAX
);
})
}
}
}
impl From<Constant> for i32 {
fn from(c: Constant) -> i32 {
#[cfg(not(debug_assertions))]
{
c.value() as i32
}
#[cfg(debug_assertions)]
{
i32::try_from(c.value()).unwrap_or_else(|_| {
panic!(
"{c:?} is out of range {:?}..={:?}",
i32::MIN,
i32::MAX
);
})
}
}
}
impl From<Constant> for i64 {
fn from(c: Constant) -> i64 {
c.value()
}
}
impl From<Constant> for i128 {
fn from(c: Constant) -> i128 {
i128::from(c.value())
}
}
/// Computes the next multiple of `rhs` that is greater than or equal to `lhs`.
///
/// Taken from:
/// https://github.com/rust-lang/rust/blob/eff958c59e8c07ba0515e164b825c9001b242294/library/core/src/num/int_macros.rs
const fn next_multiple_of(lhs: i128, rhs: i128) -> i128 {
// This would otherwise fail when calculating `r` when self == T::MIN.
if rhs == -1 {
return lhs;
}
let r = lhs % rhs;
let m = if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) { r + rhs } else { r };
if m == 0 {
lhs
} else {
lhs + (rhs - m)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn divisible() {
// We requires that our span of seconds is divisible by an even number
// of days. When it's not divisible, some of the boundary conditions
// get a little trickier, but I do not believe it's necessary for
// correctness. Without this assertion, some of the minimum values for
// our range types above need to be one less. (I believe.)
assert_eq!(0, SpanSeconds::MAX_REPR % 86_400);
}
}

574
src/zoned.rs Normal file
View file

@ -0,0 +1,574 @@
use crate::{
civil::{DateTime, Time},
error::{err, Error, ErrorContext},
instant::{Instant, TimeScale, Unix},
round::{Round, Unit},
span::Span,
tz::{Offset, TimeZone},
util::{
rangeint::{RFrom, RInto, TryRFrom},
t::{self, NoUnits, NoUnits128, ZonedDayNanoseconds, C},
},
};
// BREADCRUMBS: Filling out the rest of the methods on Zoned doesn't look like
// it's too tricky. The only interesting one beyond what we've already built
// is `Zoned::round`. And in particular, we need `Instant::round` too. I wonder
// if that's just going to be the same as `DateTime::round`?
//
// The real trickiness comes with rounding `Span` values relative to a `Zoned`
// instant.
//
// Do Instant::round next.
#[derive(Clone)]
pub struct Zoned<S: TimeScale = Unix> {
instant: Instant<S>,
tz: TimeZone,
}
impl Zoned {
pub const UNIX_EPOCH: Zoned =
Zoned::new(Instant::UNIX_EPOCH, TimeZone::UTC);
#[cfg(feature = "std")]
#[inline]
pub fn now() -> Zoned {
Zoned::now_with_scale().expect("system time reports valid value")
}
}
impl<S: TimeScale> Zoned<S> {
#[inline]
pub const fn new(instant: Instant<S>, tz: TimeZone) -> Zoned<S> {
Zoned { instant, tz }
}
#[inline]
pub fn time_zone(&self) -> &TimeZone {
&self.tz
}
#[inline]
pub fn to_instant(&self) -> Instant<S> {
self.instant
}
#[inline]
pub fn to_datetime(&self) -> DateTime {
self.time_zone().to_datetime(self.to_instant())
}
#[inline]
pub fn with_instant(&self, instant: Instant<S>) -> Zoned<S> {
Zoned::new(instant, self.time_zone().clone())
}
#[inline]
pub fn with_time_zone(&self, time_zone: TimeZone) -> Zoned<S> {
Zoned::new(self.to_instant(), time_zone)
}
#[inline]
pub fn since(&self, other: &Zoned<S>) -> Span {
self.until(other).negate()
}
#[inline]
pub(crate) fn since_with_largest_unit(
&self,
largest: Unit,
mut other: &Zoned<S>,
) -> Result<Span, Error> {
Ok(-self.until_with_largest_unit(largest, other)?)
}
#[inline]
pub fn until(&self, other: &Zoned<S>) -> Span {
self.until_with_largest_unit(Unit::Hour, other).expect("always okay")
// self.to_instant().until(other.to_instant())
}
#[inline]
pub(crate) fn until_with_largest_unit(
&self,
largest: Unit,
mut other: &Zoned<S>,
) -> Result<Span, Error> {
use crate::instant::private::Nanoseconds;
let (zdt1, zdt2) = (self, other);
// FIXME: Turn this into an error. And iron out time zone equality
// story.
assert_eq!(zdt1.time_zone(), zdt2.time_zone());
let sign = t::sign(zdt2, zdt1);
if sign == 0 {
return Ok(Span::new());
}
if largest < Unit::Day {
return zdt1
.to_instant()
.until_with_largest_unit(largest, zdt2.to_instant());
}
let (dt1, mut dt2) = (zdt1.to_datetime(), zdt2.to_datetime());
let mut day_correct: t::SpanDays = C(0).rinto();
if -sign == dt1.time().until_nanoseconds(dt2.time()).signum() {
day_correct += C(1);
}
let mut mid = dt2
.date()
.checked_add(Span::new().days_ranged(day_correct * -sign))
.with_context(|| {
err!(
"failed to add {days} days to date in {dt2}",
days = day_correct * -sign,
)
})?
.to_datetime(dt1.time());
let mut zmid: Zoned<S> = mid
.to_zoned_with(self.time_zone().clone())
.with_context(|| {
err!(
"failed to convert intermediate datetime {mid} \
to zoned instant in time zone {tz}",
tz = self.time_zone().name(),
)
})?;
if t::sign(zdt2, &zmid) == -sign {
if sign == -1 {
panic!("this should be an error");
}
day_correct += C(1);
mid = dt2
.date()
.checked_add(Span::new().days_ranged(day_correct * -sign))
.with_context(|| {
err!(
"failed to add {days} days to date in {dt2}",
days = day_correct * -sign,
)
})?
.to_datetime(dt1.time());
zmid = mid.to_zoned_with(self.time_zone().clone()).with_context(
|| {
err!(
"failed to convert intermediate datetime {mid} \
to zoned instant in time zone {tz}",
tz = self.time_zone().name(),
)
},
)?;
if t::sign(zdt2, &zmid) == -sign {
panic!("this should be an error too");
}
}
let remainder_nano =
zdt2.to_instant().as_nanosecond_ranged().to_no_units()
- zmid.to_instant().as_nanosecond_ranged().to_no_units();
dt2 = mid;
let date_span =
dt1.date().until_with_largest_unit(largest, dt2.date());
Ok(Span::from_invariant_nanoseconds(Unit::Hour, remainder_nano)
.expect("difference between time always fits in span")
.years_ranged(date_span.get_years_ranged())
.months_ranged(date_span.get_months_ranged())
.weeks_ranged(date_span.get_weeks_ranged())
.days_ranged(date_span.get_days_ranged()))
}
/// # Example
///
/// ```
/// use jiff::{civil::DateTime, ToSpan, Zoned};
///
/// let dt = DateTime::constant(2024, 3, 10, 0, 0, 0, 0);
/// let inst: Zoned = dt.to_zoned("America/New_York")?;
///
/// let later_day = inst.checked_add(1.day())?;
/// assert_eq!(
/// later_day.to_datetime(),
/// DateTime::constant(2024, 3, 11, 0, 0, 0, 0),
/// );
///
/// let later_hours = inst.checked_add(24.hours())?;
/// assert_eq!(
/// later_hours.to_datetime(),
/// DateTime::constant(2024, 3, 11, 1, 0, 0, 0),
/// );
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn checked_add(&self, span: Span) -> Result<Zoned<S>, Error> {
let span_calendar = span.without_lower(Unit::Day);
// If our duration only consists of "time" (hours, minutes, etc), then
// we can short-circuit and do timestamp math. This also avoids dealing
// with ambiguity and time zone bullshit.
if span_calendar.is_zero() {
return Ok(self.with_instant(
self.to_instant().checked_add(span).with_context(|| {
err!(
"failed to add span {span} to instant {instant} \
from zoned instant {zoned}",
instant = self.to_instant(),
zoned = self,
)
})?,
));
}
let span_time = span.only_lower(Unit::Day);
let dt = self.to_datetime().checked_add(span_calendar).with_context(
|| {
err!(
"failed to add span {span_calendar} to datetime {dt} \
from zoned instant {zoned}",
dt = self.to_datetime(),
zoned = self,
)
},
)?;
let instant =
self.time_zone().to_instant_with_scale(dt).with_context(|| {
err!(
"failed to convert civil datetime {dt} to instant \
with time zone {tz}",
tz = self.time_zone().name(),
)
})?;
let instant = instant.checked_add(span_time).with_context(|| {
err!("failed to add span {span_time} to instant {instant}")
})?;
Ok(self.with_instant(instant))
}
#[inline]
pub fn checked_sub(&self, span: Span) -> Result<Zoned<S>, Error> {
self.checked_add(span.negate())
}
/// # Example
///
/// ```
/// use jiff::{civil::DateTime, Zoned};
///
/// let dt = DateTime::constant(2015, 10, 18, 12, 0, 0, 0);
/// let inst: Zoned = dt.to_zoned("America/Sao_Paulo")?;
/// assert_eq!(
/// inst.start_of_day()?.to_datetime(),
/// DateTime::constant(2015, 10, 18, 1, 0, 0, 0),
/// );
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// ```
/// use jiff::{civil::DateTime, tz::{TimeZone, Offset}, Zoned};
///
/// // While -9999-01-03T04:00:00+25:59:59 is representable as a Zoned
/// // value, the start of the corresponding day is not!
/// let dt = DateTime::constant(-9999, 1, 3, 4, 0, 0, 0);
/// let inst: Zoned = dt.to_zoned_with(TimeZone::fixed(Offset::MAX))?;
/// assert!(inst.start_of_day().is_err());
/// // The next day works fine since -9999-01-04T00:00:00+25:59:59 is
/// // representable.
/// let dt = DateTime::constant(-9999, 1, 4, 15, 0, 0, 0);
/// let inst: Zoned = dt.to_zoned_with(TimeZone::fixed(Offset::MAX))?;
/// assert_eq!(
/// inst.start_of_day()?.to_datetime(),
/// DateTime::constant(-9999, 1, 4, 0, 0, 0, 0),
/// );
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn start_of_day(&self) -> Result<Zoned<S>, Error> {
let civil_start = self.to_datetime().map_time(|_| Time::midnight());
let instant = self.time_zone().to_instant_with_scale(civil_start)?;
Ok(self.with_instant(instant))
}
/// # Example
///
/// ```
/// use jiff::{civil::DateTime, round::{RoundMode, Unit}, Zoned};
///
/// let dt = DateTime::constant(1995, 12, 7, 3, 24, 30, 3500);
/// let inst: Zoned = dt.to_zoned("America/Los_Angeles")?;
///
/// let rounded = inst.round(Unit::Hour);
/// assert_eq!(
/// rounded.to_datetime(),
/// DateTime::constant(1995, 12, 7, 3, 0, 0, 0),
/// );
///
/// let rounded = inst.round(Unit::Minute.increment(30));
/// assert_eq!(
/// rounded.to_datetime(),
/// DateTime::constant(1995, 12, 7, 3, 30, 0, 0),
/// );
///
/// let rounded = inst.round(
/// Unit::Minute.increment(30).mode(RoundMode::Floor),
/// );
/// assert_eq!(
/// rounded.to_datetime(),
/// DateTime::constant(1995, 12, 7, 3, 0, 0, 0),
/// );
///
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
#[inline]
pub fn round(&self, options: impl Into<Round>) -> Zoned<S> {
self.try_round(options).expect("invalid round options")
}
#[inline]
pub fn try_round(
&self,
options: impl Into<Round>,
) -> Result<Zoned<S>, Error> {
let options = options.into();
let day_length = self.day_length_ranged()?;
let start = self.to_datetime();
let end = options.round_datetime(Some(day_length), start)?;
end.to_zoned_with(self.time_zone().clone())
}
#[inline]
pub(crate) fn day_length_ranged(
&self,
) -> Result<ZonedDayNanoseconds, Error> {
let start = self.start_of_day()?;
let end = start.checked_add(Span::new().days_ranged(C(1)))?;
let len = start.to_instant().until(end.to_instant());
let seconds = t::ZonedDaySeconds::try_rfrom(
"seconds-per-zoned-day",
len.get_seconds_ranged(),
)
.with_context(|| {
err!(
"failed to convert span between {start} until {end} \
to seconds",
)
})?;
let nanos = seconds * t::NANOS_PER_SECOND;
ZonedDayNanoseconds::try_rfrom("nanoseconds-per-zoned-day", nanos)
.with_context(|| {
err!(
"failed to convert span between {start} until {end} \
to nanoseconds",
)
})
}
#[inline]
pub fn to_unix_scale(&self) -> Zoned {
let instant = self.to_instant().to_unix_scale();
Zoned::new(instant, self.time_zone().clone())
}
}
impl<S: TimeScale> Zoned<S> {
#[cfg(feature = "std")]
#[inline]
pub fn now_with_scale() -> Result<Zoned<S>, Error> {
// TODO: We should use the local time zone.
Ok(Zoned::new(Instant::now_with_scale()?, TimeZone::UTC))
}
#[inline]
pub(crate) fn from_offset_timezone(
dt: DateTime,
offset: Option<Offset>,
time_zone: Option<TimeZone>,
) -> Result<Zoned<S>, Error> {
todo!()
}
}
impl<S: TimeScale> Default for Zoned<S> {
#[inline]
fn default() -> Zoned<S> {
Zoned::new(Instant::default(), TimeZone::UTC)
}
}
impl<S> core::fmt::Debug for Zoned<S>
where
S: TimeScale + core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Zoned")
.field("instant", &self.to_instant())
.field("time_zone", &self.time_zone().name())
.finish()
}
}
impl<S: TimeScale> core::fmt::Display for Zoned<S> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use crate::format::{temporal::DateTimePrinter, FmtWrite};
static P: DateTimePrinter = DateTimePrinter::new();
// Printing to `f` should never fail.
Ok(P.print_zoned(self, FmtWrite(f)).unwrap())
}
}
impl<S: TimeScale> Eq for Zoned<S> {}
impl<S: TimeScale> PartialEq for Zoned<S> {
#[inline]
fn eq(&self, rhs: &Zoned<S>) -> bool {
self.to_instant().eq(&rhs.to_instant())
}
}
impl<S: TimeScale> Ord for Zoned<S> {
#[inline]
fn cmp(&self, rhs: &Zoned<S>) -> core::cmp::Ordering {
self.to_instant().cmp(&rhs.to_instant())
}
}
impl<S: TimeScale> PartialOrd for Zoned<S> {
#[inline]
fn partial_cmp(&self, rhs: &Zoned<S>) -> Option<core::cmp::Ordering> {
Some(self.cmp(rhs))
}
}
#[cfg(feature = "std")]
impl<S: TimeScale> TryFrom<std::time::SystemTime> for Zoned<S> {
type Error = Error;
#[inline]
fn try_from(
system_time: std::time::SystemTime,
) -> Result<Zoned<S>, Error> {
let instant = Instant::try_from(system_time)?;
Ok(Zoned::new(instant, TimeZone::UTC))
}
}
#[cfg(feature = "std")]
impl<S: TimeScale> From<Zoned<S>> for std::time::SystemTime {
#[inline]
fn from(time: Zoned<S>) -> std::time::SystemTime {
time.to_instant().into()
}
}
#[cfg(test)]
impl<S: TimeScale + 'static> quickcheck::Arbitrary for Zoned<S> {
fn arbitrary(g: &mut quickcheck::Gen) -> Zoned<S> {
use quickcheck::Arbitrary;
let instant = Instant::arbitrary(g);
let tz = TimeZone::UTC; // TODO: do something better here?
Zoned::new(instant, tz)
}
fn shrink(&self) -> alloc::boxed::Box<dyn Iterator<Item = Self>> {
let instant = self.to_instant();
alloc::boxed::Box::new(
instant.shrink().map(|instant| Zoned::new(instant, TimeZone::UTC)),
)
}
}
#[cfg(test)]
mod tests {
use crate::ToSpan;
use super::*;
#[test]
fn until_with_largest_unit() {
let zdt1: Zoned = DateTime::constant(1995, 12, 7, 3, 24, 30, 3500)
.to_zoned("Asia/Kolkata")
.unwrap();
let zdt2: Zoned = DateTime::constant(2019, 1, 31, 15, 30, 0, 0)
.to_zoned("Asia/Kolkata")
.unwrap();
let span = zdt1.until(&zdt2);
assert_eq!(
span,
202956
.hours()
.minutes(5)
.seconds(29)
.milliseconds(999)
.microseconds(996)
.nanoseconds(500)
);
let span = zdt1.until_with_largest_unit(Unit::Year, &zdt2).unwrap();
assert_eq!(
span,
23.years()
.months(1)
.days(24)
.hours(12)
.minutes(5)
.seconds(29)
.milliseconds(999)
.microseconds(996)
.nanoseconds(500)
);
let span = zdt2.until_with_largest_unit(Unit::Year, &zdt1).unwrap();
assert_eq!(
span,
-23.years()
.months(1)
.days(24)
.hours(12)
.minutes(5)
.seconds(29)
.milliseconds(999)
.microseconds(996)
.nanoseconds(500)
);
let span =
zdt1.until_with_largest_unit(Unit::Nanosecond, &zdt2).unwrap();
assert_eq!(span, 730641929999996500i64.nanoseconds(),);
let zdt1: Zoned = DateTime::constant(2020, 1, 1, 0, 0, 0, 0)
.to_zoned("America/New_York")
.unwrap();
let zdt2: Zoned = DateTime::constant(2020, 4, 24, 21, 0, 0, 0)
.to_zoned("America/New_York")
.unwrap();
let span = zdt1.until(&zdt2);
assert_eq!(span, 2756.hours());
let span = zdt1.until_with_largest_unit(Unit::Year, &zdt2).unwrap();
assert_eq!(span, 3.months().days(23).hours(21));
let zdt1: Zoned = DateTime::constant(2000, 10, 29, 0, 0, 0, 0)
.to_zoned("America/Vancouver")
.unwrap();
let zdt2: Zoned = DateTime::constant(2000, 10, 29, 23, 0, 0, 5)
.to_zoned("America/Vancouver")
.unwrap();
let span = zdt1.until_with_largest_unit(Unit::Day, &zdt2).unwrap();
assert_eq!(span, 24.hours().nanoseconds(5));
}
#[test]
fn zoned_size() {
#[cfg(debug_assertions)]
{
assert_eq!(48, core::mem::size_of::<Zoned>());
}
#[cfg(not(debug_assertions))]
{
assert_eq!(24, core::mem::size_of::<Zoned>());
}
}
}

38
tests/lib.rs Normal file
View file

@ -0,0 +1,38 @@
#![allow(warnings)]
use jiff::{
civil::{Date, DateTime, Time as CivilTime},
Instant, Span,
};
mod tc39_262;
#[test]
fn scratch() {
// let t = Time::now();
// dbg!(t);
// let dt = t.to_datetime();
// dbg!(dt);
//
// let t = Time::from_unix(-1, 0).unwrap();
// let dt = t.to_datetime();
// dbg!(dt);
//
// let t = Time::from_unix(0, -1).unwrap();
// let dt = t.to_datetime();
// dbg!(dt);
// let t = Instant::from_unix(-377705116799, -999_999_999).unwrap();
// let dt = t.to_datetime();
// dbg!(dt);
// let start = Date::new(2024, 2, 28).unwrap();
// let span = Span::new().years(1).months(1);
// let span = Span::new()
// .hours(23)
// .minutes(59)
// .seconds(59)
// .nanoseconds(1_000_000_000);
// let end = start.add(span);
// eprintln!("{end:?}");
}

20
tests/tc39_262/README.md Normal file
View file

@ -0,0 +1,20 @@
These tests come from [TC39's Test262] for the [Temporal proposal]. The tests
here try to mimic what's in Test262 as much as possible, but there are many
tests that are omitted because they are caught by Rust's type system. On
occasion, we diverge in behavior from Temporal. These differences are, to the
best of my ability, marked with `DIFFERENCE`.
These tests were ported by hand. There is, at time of writing, no mechanism for
ensuring they remain in sync with Test262.
[TC39's Test262]: https://github.com/tc39/test262/tree/9e03c403e73341658d8d485a673798ae61f6f94a/test/built-ins/Temporal
[Temporal proposal]: https://github.com/tc39/proposal-temporal
# TODO
These are some test files that I came across while porting things that I
couldn't quite write yet. For example, tests that require parsing ISO 8601
duration strings, since the parsing code doesn't exist yet (at time of writing,
2024-03-06).
I tried to list what I could as `TODO` comments in the test files.

View file

@ -0,0 +1,2 @@
mod round;
mod since;

View file

@ -0,0 +1,43 @@
use jiff::{
civil::DateTime,
round::{RoundMode, Unit},
};
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainDateTime/prototype/round/balance.js
#[test]
fn balance() {
let dt = DateTime::constant(1976, 11, 18, 23, 59, 59, 999_999_999);
let expected = DateTime::constant(1976, 11, 19, 0, 0, 0, 0);
assert_eq!(dt.round(Unit::Day), expected);
assert_eq!(dt.round(Unit::Hour), expected);
assert_eq!(dt.round(Unit::Minute), expected);
assert_eq!(dt.round(Unit::Second), expected);
assert_eq!(dt.round(Unit::Millisecond), expected);
assert_eq!(dt.round(Unit::Microsecond), expected);
assert_eq!(dt.round(Unit::Nanosecond), dt);
}
/// DIFFERENCE: Temporal and Jiff have different maximums, so we use our own
/// values here.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainDateTime/prototype/round/limits.js
#[test]
fn limits() {
let dt = DateTime::constant(9999, 12, 31, 23, 59, 59, 999_999_999);
assert!(dt.try_round(Unit::Day).is_err());
assert!(dt.try_round(Unit::Microsecond).is_err());
let dt = DateTime::constant(9999, 12, 31, 23, 59, 59, 999_999_499);
assert!(dt.try_round(Unit::Day).is_err());
assert_eq!(
dt.round(Unit::Microsecond),
DateTime::constant(9999, 12, 31, 23, 59, 59, 999_999_000)
);
let dt = DateTime::constant(-9999, 1, 1, 0, 0, 0, 000_000_001);
assert_eq!(
dt.round(Unit::Microsecond.mode(RoundMode::Floor)),
DateTime::constant(-9999, 1, 1, 0, 0, 0, 0)
);
}

View file

@ -0,0 +1 @@
mod wrapping_at_end_of_month;

View file

@ -0,0 +1,27 @@
use jiff::{civil::DateTime, round::Unit, ToSpan};
/// Test that 1-day backoff to maintain date/time sign compatibility backs-off
/// from correct end while moving *forwards* in time and does not interfere
/// with month boundaries.
///
/// Ref: https://github.com/tc39/proposal-temporal/issues/2820
#[test]
fn one_day_backoff1() {
let dt1 = DateTime::constant(2023, 2, 28, 3, 0, 0, 0);
let dt2 = DateTime::constant(2023, 4, 1, 2, 0, 0, 0);
let span = dt1.since_with_largest_unit(Unit::Year, dt2).unwrap();
assert_eq!(span, -1.month().days(3).hours(23));
}
/// Test that 1-day backoff to maintain date/time sign compatibility backs-off
/// from correct end while moving *forwards* in time and does not interfere
/// with month boundaries.
///
/// Ref: https://github.com/tc39/proposal-temporal/issues/2820
#[test]
fn one_day_backoff2() {
let dt1 = DateTime::constant(2023, 3, 1, 2, 0, 0, 0);
let dt2 = DateTime::constant(2023, 1, 1, 3, 0, 0, 0);
let span = dt1.since_with_largest_unit(Unit::Year, dt2).unwrap();
assert_eq!(span, 1.month().days(30).hours(23));
}

View file

@ -0,0 +1,2 @@
mod datetime;
mod time;

View file

@ -0,0 +1,327 @@
use jiff::{civil::Time, span::Span};
/// TODO
///
/// All of these require parsing of some kind. Durations I believe.
///
/// * https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-duration-out-of-range.js
/// * https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-fractional-units-rounding-mode.js
/// * https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-negative-fractional-units.js
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-duration.js
#[test]
fn argument_duration() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().hours(16);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, Time::constant(7, 23, 30, 123_456_789));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js
#[test]
fn argument_duration_max() {
let t1 = Time::constant(0, 0, 0, 0);
let expected = Time::constant(7, 36, 31, 999_999_999);
let span = Span::new()
.years(19_998)
.days(7_304_482)
.nanoseconds(27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new()
.months(239_976)
.days(7_304_482)
.nanoseconds(27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new()
.weeks(51_131_374)
.days(7_304_482)
.nanoseconds(27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new().days(7_304_482).nanoseconds(27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new().hours(175_307_591).nanoseconds(65498124754943i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span =
Span::new().minutes(10_518_455_460i64).nanoseconds(65498124754943i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span =
Span::new().seconds(631_107_327_600i64).nanoseconds(65498124754943i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-duration-max.js
#[test]
fn argument_duration_min() {
let t1 = Time::constant(0, 0, 0, 0);
let expected = Time::constant(16, 23, 28, 000_000_001);
let span = Span::new()
.years(-19_998)
.days(-7_304_482)
.nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new()
.months(-239_976)
.days(-7_304_482)
.nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new()
.weeks(-51_131_374)
.days(-7_304_482)
.nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new().days(-7_304_482).nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new().hours(-175_307_591).nanoseconds(-65498124754943i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new()
.minutes(-10_518_455_460i64)
.nanoseconds(-65498124754943i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
let span = Span::new()
.seconds(-631_107_327_600i64)
.nanoseconds(-65498124754943i64);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, expected);
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-higher-units.js
#[test]
fn argument_higher_units() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().days(1);
assert_eq!(t1, t1.wrapping_add(span));
let span = Span::new().weeks(1);
assert_eq!(t1, t1.wrapping_add(span));
let span = Span::new().months(1);
assert_eq!(t1, t1.wrapping_add(span));
let span = Span::new().years(1);
assert_eq!(t1, t1.wrapping_add(span));
}
/// DIFFERENCE: We "allow" mixed signs in spans, but they normalize. That is,
/// if *any* component of a span is negative, then the whole span is negative.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-mixed-sign.js
#[test]
fn argument_mixed_sign() {
let t1 = Time::constant(15, 30, 45, 987_654_321);
let span = Span::new().hours(1).minutes(-30);
let t2 = t1.wrapping_add(span);
assert_eq!(t2, Time::constant(14, 0, 45, 987_654_321));
}
/// In Test262, this is seemingly just testing that "plain" objects can
/// be passed to the `PlainTime.add` API, as opposed to proper `Duration`
/// objects. That doesn't really apply to jiff, because Rust, but we still
/// capture the tests here.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-object.js
#[test]
fn argument_object() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().hours(16);
assert_eq!(t1.wrapping_add(span), Time::constant(7, 23, 30, 123_456_789));
let span = Span::new().minutes(45);
assert_eq!(t1.wrapping_add(span), Time::constant(16, 8, 30, 123_456_789));
let span = Span::new().seconds(800);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 36, 50, 123_456_789));
let span = Span::new().milliseconds(800);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 923_456_789));
let span = Span::new().microseconds(800);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 124_256_789));
let span = Span::new().nanoseconds(300);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_457_089));
let t1 = Time::constant(7, 23, 30, 123_456_789);
let span = Span::new().hours(-16);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(16, 8, 30, 123_456_789);
let span = Span::new().minutes(-45);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 36, 50, 123_456_789);
let span = Span::new().seconds(-800);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 23, 30, 923_456_789);
let span = Span::new().milliseconds(-800);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 23, 30, 124_256_789);
let span = Span::new().microseconds(-800);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 23, 30, 123_457_089);
let span = Span::new().nanoseconds(-300);
assert_eq!(t1.wrapping_add(span), Time::constant(15, 23, 30, 123_456_789));
}
/// Temporal doesn't have checked arithmetic, so this test just copied
/// `argument_object`, but with checked arithmetic.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-object.js
#[test]
fn argument_object_checked() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().hours(16);
assert_eq!(t1.checked_add(span).ok(), None);
// Added our own test to avoid wrapping.
let span = Span::new().hours(2);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(17, 23, 30, 123_456_789)
);
let span = Span::new().minutes(45);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(16, 8, 30, 123_456_789)
);
let span = Span::new().seconds(800);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 36, 50, 123_456_789)
);
let span = Span::new().milliseconds(800);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 923_456_789)
);
let span = Span::new().microseconds(800);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 124_256_789)
);
let span = Span::new().nanoseconds(300);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 123_457_089)
);
let t1 = Time::constant(7, 23, 30, 123_456_789);
let span = Span::new().hours(-16);
assert_eq!(t1.checked_add(span).ok(), None);
// Added our own test to avoid wrapping.
let t1 = Time::constant(7, 23, 30, 123_456_789);
let span = Span::new().hours(-2);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(5, 23, 30, 123_456_789)
);
let t1 = Time::constant(16, 8, 30, 123_456_789);
let span = Span::new().minutes(-45);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 36, 50, 123_456_789);
let span = Span::new().seconds(-800);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 23, 30, 923_456_789);
let span = Span::new().milliseconds(-800);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 23, 30, 124_256_789);
let span = Span::new().microseconds(-800);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 23, 30, 123_457_089);
let span = Span::new().nanoseconds(-300);
assert_eq!(
t1.checked_add(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
}
/// DIFFERENCE: Wrapping arithmetic on `Time` always wraps, even when the span
/// represents an interval of time bigger than what is supported.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/argument-string-duration-too-large.js
#[test]
fn argument_string_duration_too_large() {
let t1 = Time::constant(0, 0, 0, 0);
let span = Span::new().years(19_998).months(239_976);
assert_eq!(t1.wrapping_add(span), t1);
assert_eq!(t1.checked_add(span).ok(), None);
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/add/balance-negative-time-units.js
#[test]
fn balance_negative_time_units() {
let t1 = Time::constant(1, 1, 1, 001_001_001);
let span = Span::new().nanoseconds(-2);
assert_eq!(t1.wrapping_add(span), Time::constant(1, 1, 1, 001_000_999));
let span = Span::new().microseconds(-2);
assert_eq!(t1.wrapping_add(span), Time::constant(1, 1, 1, 000_999_001));
let span = Span::new().milliseconds(-2);
assert_eq!(t1.wrapping_add(span), Time::constant(1, 1, 0, 999_001_001));
let span = Span::new().seconds(-2);
assert_eq!(t1.wrapping_add(span), Time::constant(1, 0, 59, 001_001_001));
let span = Span::new().minutes(-2);
assert_eq!(t1.wrapping_add(span), Time::constant(0, 59, 1, 001_001_001));
let span = Span::new().hours(-2);
assert_eq!(t1.wrapping_add(span), Time::constant(23, 1, 1, 001_001_001));
}

View file

@ -0,0 +1,16 @@
use jiff::{civil::Time, Instant};
/// TODO: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/equals
///
/// Most tests seem to require parsing. Some also deal with timezones. Many
/// are irrelevant because of types.
/// TODO: Switch to a zoned time, or at least add it.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/equals/argument-zoneddatetime-negative-epochnanoseconds.js
#[test]
fn argument_zoneddatetime_negative_epochnanoseconds() {
let instant = Instant::from_unix(-13849764, -999_999_999).unwrap();
let time = instant.to_datetime().time();
assert_eq!(time, Time::constant(16, 50, 35, 000_000_001));
}

View file

@ -0,0 +1,5 @@
mod add;
mod equals;
mod round;
mod sub;
mod until;

View file

@ -0,0 +1,551 @@
use jiff::{
civil::Time,
round::{Round, RoundMode, Unit},
};
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/rounding-cross-midnight.js
#[test]
fn rounding_cross_midnight() {
let t1 = Time::constant(23, 59, 59, 999_999_999);
let t2 = t1.round(Unit::Nanosecond);
assert_eq!(t2, t1);
let t2 = t1.round(Unit::Millisecond);
assert_eq!(t2, Time::constant(0, 0, 0, 0));
let t2 = t1.round(Unit::Microsecond);
assert_eq!(t2, Time::constant(0, 0, 0, 0));
let t2 = t1.round(Unit::Millisecond);
assert_eq!(t2, Time::constant(0, 0, 0, 0));
let t2 = t1.round(Unit::Second);
assert_eq!(t2, Time::constant(0, 0, 0, 0));
let t2 = t1.round(Unit::Minute);
assert_eq!(t2, Time::constant(0, 0, 0, 0));
let t2 = t1.round(Unit::Hour);
assert_eq!(t2, Time::constant(0, 0, 0, 0));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-hours.js
#[test]
fn rounding_increment_hours() {
let t1 = Time::constant(3, 34, 56, 987_654_321);
let t2 = t1.round(Unit::Hour.increment(1));
assert_eq!(t2, Time::constant(4, 0, 0, 0));
let t2 = t1.round(Unit::Hour.increment(2));
assert_eq!(t2, Time::constant(4, 0, 0, 0));
let t2 = t1.round(Unit::Hour.increment(3));
assert_eq!(t2, Time::constant(3, 0, 0, 0));
let t2 = t1.round(Unit::Hour.increment(4));
assert_eq!(t2, Time::constant(4, 0, 0, 0));
let t2 = t1.round(Unit::Hour.increment(6));
assert_eq!(t2, Time::constant(6, 0, 0, 0));
let t2 = t1.round(Unit::Hour.increment(8));
assert_eq!(t2, Time::constant(0, 0, 0, 0));
let t2 = t1.round(Unit::Hour.increment(12));
assert_eq!(t2, Time::constant(0, 0, 0, 0));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-minutes.js
#[test]
fn rounding_increment_minutes() {
let t1 = Time::constant(3, 34, 56, 987_654_321);
let t2 = t1.round(Unit::Minute.increment(1));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
let t2 = t1.round(Unit::Minute.increment(2));
assert_eq!(t2, Time::constant(3, 34, 0, 0));
let t2 = t1.round(Unit::Minute.increment(3));
assert_eq!(t2, Time::constant(3, 36, 0, 0));
let t2 = t1.round(Unit::Minute.increment(4));
assert_eq!(t2, Time::constant(3, 36, 0, 0));
let t2 = t1.round(Unit::Minute.increment(5));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
let t2 = t1.round(Unit::Minute.increment(6));
assert_eq!(t2, Time::constant(3, 36, 0, 0));
let t2 = t1.round(Unit::Minute.increment(10));
assert_eq!(t2, Time::constant(3, 30, 0, 0));
let t2 = t1.round(Unit::Minute.increment(12));
assert_eq!(t2, Time::constant(3, 36, 0, 0));
let t2 = t1.round(Unit::Minute.increment(15));
assert_eq!(t2, Time::constant(3, 30, 0, 0));
let t2 = t1.round(Unit::Minute.increment(20));
assert_eq!(t2, Time::constant(3, 40, 0, 0));
let t2 = t1.round(Unit::Minute.increment(30));
assert_eq!(t2, Time::constant(3, 30, 0, 0));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-seconds.js
#[test]
fn rounding_increment_seconds() {
let t1 = Time::constant(3, 34, 56, 987_654_321);
let t2 = t1.round(Unit::Second.increment(1));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Second.increment(2));
assert_eq!(t2, Time::constant(3, 34, 56, 0));
let t2 = t1.round(Unit::Second.increment(3));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Second.increment(4));
assert_eq!(t2, Time::constant(3, 34, 56, 0));
let t2 = t1.round(Unit::Second.increment(5));
assert_eq!(t2, Time::constant(3, 34, 55, 0));
let t2 = t1.round(Unit::Second.increment(6));
assert_eq!(t2, Time::constant(3, 34, 54, 0));
let t2 = t1.round(Unit::Second.increment(10));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
let t2 = t1.round(Unit::Second.increment(12));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
let t2 = t1.round(Unit::Second.increment(15));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
let t2 = t1.round(Unit::Second.increment(20));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
let t2 = t1.round(Unit::Second.increment(30));
assert_eq!(t2, Time::constant(3, 35, 0, 0));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-milliseconds.js
#[test]
fn rounding_increment_milliseconds() {
let t1 = Time::constant(3, 34, 56, 987_654_321);
let t2 = t1.round(Unit::Millisecond.increment(1));
assert_eq!(t2, Time::constant(3, 34, 56, 988_000_000));
let t2 = t1.round(Unit::Millisecond.increment(2));
assert_eq!(t2, Time::constant(3, 34, 56, 988_000_000));
let t2 = t1.round(Unit::Millisecond.increment(4));
assert_eq!(t2, Time::constant(3, 34, 56, 988_000_000));
let t2 = t1.round(Unit::Millisecond.increment(5));
assert_eq!(t2, Time::constant(3, 34, 56, 990_000_000));
let t2 = t1.round(Unit::Millisecond.increment(8));
assert_eq!(t2, Time::constant(3, 34, 56, 984_000_000));
let t2 = t1.round(Unit::Millisecond.increment(10));
assert_eq!(t2, Time::constant(3, 34, 56, 990_000_000));
let t2 = t1.round(Unit::Millisecond.increment(20));
assert_eq!(t2, Time::constant(3, 34, 56, 980_000_000));
let t2 = t1.round(Unit::Millisecond.increment(25));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(40));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(50));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(100));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(125));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(200));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(250));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
let t2 = t1.round(Unit::Millisecond.increment(500));
assert_eq!(t2, Time::constant(3, 34, 57, 0));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-microseconds.js
#[test]
fn rounding_increment_microseconds() {
let t1 = Time::constant(3, 34, 56, 987_654_321);
let t2 = t1.round(Unit::Microsecond.increment(1));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_000));
let t2 = t1.round(Unit::Microsecond.increment(2));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_000));
let t2 = t1.round(Unit::Microsecond.increment(4));
assert_eq!(t2, Time::constant(3, 34, 56, 987_656_000));
let t2 = t1.round(Unit::Microsecond.increment(5));
assert_eq!(t2, Time::constant(3, 34, 56, 987_655_000));
let t2 = t1.round(Unit::Microsecond.increment(8));
assert_eq!(t2, Time::constant(3, 34, 56, 987_656_000));
let t2 = t1.round(Unit::Microsecond.increment(10));
assert_eq!(t2, Time::constant(3, 34, 56, 987_650_000));
let t2 = t1.round(Unit::Microsecond.increment(20));
assert_eq!(t2, Time::constant(3, 34, 56, 987_660_000));
let t2 = t1.round(Unit::Microsecond.increment(25));
assert_eq!(t2, Time::constant(3, 34, 56, 987_650_000));
let t2 = t1.round(Unit::Microsecond.increment(40));
assert_eq!(t2, Time::constant(3, 34, 56, 987_640_000));
let t2 = t1.round(Unit::Microsecond.increment(50));
assert_eq!(t2, Time::constant(3, 34, 56, 987_650_000));
let t2 = t1.round(Unit::Microsecond.increment(100));
assert_eq!(t2, Time::constant(3, 34, 56, 987_700_000));
let t2 = t1.round(Unit::Microsecond.increment(125));
assert_eq!(t2, Time::constant(3, 34, 56, 987_625_000));
let t2 = t1.round(Unit::Microsecond.increment(200));
assert_eq!(t2, Time::constant(3, 34, 56, 987_600_000));
let t2 = t1.round(Unit::Microsecond.increment(250));
assert_eq!(t2, Time::constant(3, 34, 56, 987_750_000));
let t2 = t1.round(Unit::Microsecond.increment(500));
assert_eq!(t2, Time::constant(3, 34, 56, 987_500_000));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-nanoseconds.js
#[test]
fn rounding_increment_nanoseconds() {
let t1 = Time::constant(3, 34, 56, 987_654_321);
let t2 = t1.round(Unit::Nanosecond.increment(1));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_321));
let t2 = t1.round(Unit::Nanosecond.increment(2));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_322));
let t2 = t1.round(Unit::Nanosecond.increment(4));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_320));
let t2 = t1.round(Unit::Nanosecond.increment(5));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_320));
let t2 = t1.round(Unit::Nanosecond.increment(8));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_320));
let t2 = t1.round(Unit::Nanosecond.increment(10));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_320));
let t2 = t1.round(Unit::Nanosecond.increment(20));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_320));
let t2 = t1.round(Unit::Nanosecond.increment(25));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_325));
let t2 = t1.round(Unit::Nanosecond.increment(40));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_320));
let t2 = t1.round(Unit::Nanosecond.increment(50));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_300));
let t2 = t1.round(Unit::Nanosecond.increment(100));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_300));
let t2 = t1.round(Unit::Nanosecond.increment(125));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_375));
let t2 = t1.round(Unit::Nanosecond.increment(200));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_400));
let t2 = t1.round(Unit::Nanosecond.increment(250));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_250));
let t2 = t1.round(Unit::Nanosecond.increment(500));
assert_eq!(t2, Time::constant(3, 34, 56, 987_654_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-invalid.js
#[test]
fn rounding_increment_invalid() {
let t1 = Time::constant(8, 22, 36, 123456789);
assert!(t1.try_round(Unit::Hour.increment(11)).is_err());
assert!(t1.try_round(Unit::Minute.increment(29)).is_err());
assert!(t1.try_round(Unit::Second.increment(29)).is_err());
assert!(t1.try_round(Unit::Millisecond.increment(29)).is_err());
assert!(t1.try_round(Unit::Microsecond.increment(29)).is_err());
assert!(t1.try_round(Unit::Nanosecond.increment(29)).is_err());
assert!(t1.try_round(Unit::Hour.increment(24)).is_err());
assert!(t1.try_round(Unit::Minute.increment(60)).is_err());
assert!(t1.try_round(Unit::Second.increment(60)).is_err());
assert!(t1.try_round(Unit::Millisecond.increment(1_000)).is_err());
assert!(t1.try_round(Unit::Microsecond.increment(1_000)).is_err());
assert!(t1.try_round(Unit::Nanosecond.increment(1_000)).is_err());
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingincrement-out-of-range.js
#[test]
fn rounding_increment_out_of_range() {
let t1 = Time::constant(12, 34, 56, 000_000_005);
assert!(t1.try_round(Unit::Nanosecond.increment(-1)).is_err());
assert!(t1.try_round(Unit::Nanosecond.increment(0)).is_err());
assert!(t1.try_round(Unit::Nanosecond.increment(1_000)).is_err());
assert!(t1.try_round(Unit::Nanosecond.increment(1_000_000_001)).is_err());
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-ceil.js
#[test]
fn rounding_mode_ceil() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::Ceil));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::Ceil));
assert_eq!(t2, Time::constant(13, 47, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::Ceil));
assert_eq!(t2, Time::constant(13, 46, 24, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::Ceil));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::Ceil));
assert_eq!(t2, Time::constant(13, 46, 23, 123_988_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::Ceil));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-expand.js
#[test]
fn rounding_mode_expand() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::Expand));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::Expand));
assert_eq!(t2, Time::constant(13, 47, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::Expand));
assert_eq!(t2, Time::constant(13, 46, 24, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::Expand));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::Expand));
assert_eq!(t2, Time::constant(13, 46, 23, 123_988_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::Expand));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-floor.js
#[test]
fn rounding_mode_floor() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::Floor));
assert_eq!(t2, Time::constant(13, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::Floor));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::Floor));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::Floor));
assert_eq!(t2, Time::constant(13, 46, 23, 123_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::Floor));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::Floor));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfCeil.js
#[test]
fn rounding_mode_half_ceil() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::HalfCeil));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::HalfCeil));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::HalfCeil));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::HalfCeil));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::HalfCeil));
assert_eq!(t2, Time::constant(13, 46, 23, 123_988_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::HalfCeil));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfEven.js
#[test]
fn rounding_mode_half_even() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::HalfEven));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::HalfEven));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::HalfEven));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::HalfEven));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::HalfEven));
assert_eq!(t2, Time::constant(13, 46, 23, 123_988_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::HalfEven));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfExpand.js
#[test]
fn rounding_mode_half_expand() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::HalfExpand));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::HalfExpand));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::HalfExpand));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::HalfExpand));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::HalfExpand));
assert_eq!(t2, Time::constant(13, 46, 23, 123_988_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::HalfExpand));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfFloor.js
#[test]
fn rounding_mode_half_floor() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::HalfFloor));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::HalfFloor));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::HalfFloor));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::HalfFloor));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::HalfFloor));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::HalfFloor));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-halfTrunc.js
#[test]
fn rounding_mode_half_trunc() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::HalfTrunc));
assert_eq!(t2, Time::constant(14, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::HalfTrunc));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::HalfTrunc));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::HalfTrunc));
assert_eq!(t2, Time::constant(13, 46, 23, 124_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::HalfTrunc));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::HalfTrunc));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/roundingmode-trunc.js
#[test]
fn rounding_mode_trunc() {
let t1 = Time::constant(13, 46, 23, 123_987_500);
let t2 = t1.round(Unit::Hour.mode(RoundMode::Trunc));
assert_eq!(t2, Time::constant(13, 0, 0, 0));
let t2 = t1.round(Unit::Minute.mode(RoundMode::Trunc));
assert_eq!(t2, Time::constant(13, 46, 0, 0));
let t2 = t1.round(Unit::Second.mode(RoundMode::Trunc));
assert_eq!(t2, Time::constant(13, 46, 23, 0));
let t2 = t1.round(Unit::Millisecond.mode(RoundMode::Trunc));
assert_eq!(t2, Time::constant(13, 46, 23, 123_000_000));
let t2 = t1.round(Unit::Microsecond.mode(RoundMode::Trunc));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_000));
let t2 = t1.round(Unit::Nanosecond.mode(RoundMode::Trunc));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}
/// DIFFERENCE: Unlike Temporal, we permit the smallest unit to be missing. It
/// defaults to `Nanosecond`.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/round/smallestunit-missing.js
#[test]
fn smallest_unit_missing() {
let t1 = Time::constant(13, 46, 23, 123_987_499);
let t2 = t1.round(Round::new());
assert_eq!(t2, t1);
let t2 = t1.round(Round::new().increment(500));
assert_eq!(t2, Time::constant(13, 46, 23, 123_987_500));
}

View file

@ -0,0 +1,327 @@
use jiff::{civil::Time, span::Span};
/// TODO
///
/// All of these require parsing of some kind. Durations I believe.
///
/// * https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-out-of-range.js
/// * https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-fractional-units-rounding-mode.js
/// * https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-negative-fractional-units.js
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration.js
#[test]
fn argument_duration() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().hours(16);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, Time::constant(23, 23, 30, 123_456_789));
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js
#[test]
fn argument_duration_max() {
let t1 = Time::midnight();
let expected = Time::constant(16, 23, 28, 000_000_001);
let span = Span::new()
.years(19_998)
.days(7_304_482)
.nanoseconds(27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new()
.months(239_976)
.days(7_304_482)
.nanoseconds(27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new()
.weeks(51_131_374)
.days(7_304_482)
.nanoseconds(27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new().days(7_304_482).nanoseconds(27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new().hours(175_307_591).nanoseconds(65498124754943i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span =
Span::new().minutes(10_518_455_460i64).nanoseconds(65498124754943i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span =
Span::new().seconds(631_107_327_600i64).nanoseconds(65498124754943i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-duration-max.js
#[test]
fn argument_duration_min() {
let t1 = Time::constant(0, 0, 0, 0);
let expected = Time::constant(7, 36, 31, 999_999_999);
let span = Span::new()
.years(-19_998)
.days(-7_304_482)
.nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new()
.months(-239_976)
.days(-7_304_482)
.nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new()
.weeks(-51_131_374)
.days(-7_304_482)
.nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new().days(-7_304_482).nanoseconds(-27391999999999i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new().hours(-175_307_591).nanoseconds(-65498124754943i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new()
.minutes(-10_518_455_460i64)
.nanoseconds(-65498124754943i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
let span = Span::new()
.seconds(-631_107_327_600i64)
.nanoseconds(-65498124754943i64);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, expected);
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-higher-units.js
#[test]
fn argument_higher_units() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().days(1);
assert_eq!(t1, t1.wrapping_sub(span));
let span = Span::new().weeks(1);
assert_eq!(t1, t1.wrapping_sub(span));
let span = Span::new().months(1);
assert_eq!(t1, t1.wrapping_sub(span));
let span = Span::new().years(1);
assert_eq!(t1, t1.wrapping_sub(span));
}
/// DIFFERENCE: We "allow" mixed signs in spans, but they normalize. That is,
/// if *any* component of a span is negative, then the whole span is negative.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-mixed-sign.js
#[test]
fn argument_mixed_sign() {
let t1 = Time::constant(15, 30, 45, 987_654_321);
let span = Span::new().hours(1).minutes(-30);
let t2 = t1.wrapping_sub(span);
assert_eq!(t2, Time::constant(17, 0, 45, 987_654_321));
}
/// In Test262, this is seemingly just testing that "plain" objects can
/// be passed to the `PlainTime.add` API, as opposed to proper `Duration`
/// objects. That doesn't really apply to jiff, because Rust, but we still
/// capture the tests here.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js
#[test]
fn argument_object() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().hours(16);
assert_eq!(t1.wrapping_sub(span), Time::constant(23, 23, 30, 123_456_789));
let span = Span::new().minutes(45);
assert_eq!(t1.wrapping_sub(span), Time::constant(14, 38, 30, 123_456_789));
let span = Span::new().seconds(45);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 22, 45, 123_456_789));
let span = Span::new().milliseconds(800);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 29, 323_456_789));
let span = Span::new().microseconds(800);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 122_656_789));
let span = Span::new().nanoseconds(800);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_455_989));
let t1 = Time::constant(23, 23, 30, 123_456_789);
let span = Span::new().hours(-16);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(14, 38, 30, 123_456_789);
let span = Span::new().minutes(-45);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 22, 45, 123_456_789);
let span = Span::new().seconds(-45);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 23, 29, 323_456_789);
let span = Span::new().milliseconds(-800);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 23, 30, 122_656_789);
let span = Span::new().microseconds(-800);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_456_789));
let t1 = Time::constant(15, 23, 30, 123_455_989);
let span = Span::new().nanoseconds(-800);
assert_eq!(t1.wrapping_sub(span), Time::constant(15, 23, 30, 123_456_789));
}
/// Temporal doesn't have checked arithmetic, so this test just copied
/// `argument_object`, but with checked arithmetic.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-object.js
#[test]
fn argument_object_checked() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let span = Span::new().hours(16);
assert_eq!(t1.checked_sub(span).ok(), None);
// Added our own test to avoid wrapping.
let span = Span::new().hours(2);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(13, 23, 30, 123_456_789)
);
let span = Span::new().minutes(45);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(14, 38, 30, 123_456_789)
);
let span = Span::new().seconds(45);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 22, 45, 123_456_789)
);
let span = Span::new().milliseconds(800);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 29, 323_456_789)
);
let span = Span::new().microseconds(800);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 122_656_789)
);
let span = Span::new().nanoseconds(800);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 123_455_989)
);
let t1 = Time::constant(23, 23, 30, 123_456_789);
let span = Span::new().hours(-16);
assert_eq!(t1.checked_sub(span).ok(), None);
// Added our own test to avoid wrapping.
let t1 = Time::constant(23, 23, 30, 123_456_789);
let span = Span::new().hours(2);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(21, 23, 30, 123_456_789)
);
let t1 = Time::constant(14, 38, 30, 123_456_789);
let span = Span::new().minutes(-45);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 22, 45, 123_456_789);
let span = Span::new().seconds(-45);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 23, 29, 323_456_789);
let span = Span::new().milliseconds(-800);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 23, 30, 122_656_789);
let span = Span::new().microseconds(-800);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
let t1 = Time::constant(15, 23, 30, 123_455_989);
let span = Span::new().nanoseconds(-800);
assert_eq!(
t1.checked_sub(span).unwrap(),
Time::constant(15, 23, 30, 123_456_789)
);
}
/// DIFFERENCE: Wrapping arithmetic on `Time` always wraps, even when the span
/// represents an interval of time bigger than what is supported.
///
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/argument-string-duration-too-large.js
#[test]
fn argument_string_duration_too_large() {
let t1 = Time::constant(0, 0, 0, 0);
let span = Span::new().years(19_998).months(239_976);
assert_eq!(t1.wrapping_sub(span), t1);
assert_eq!(t1.checked_sub(span).ok(), None);
}
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/subtract/balance-negative-time-units.js
#[test]
fn balance_negative_time_units() {
let t1 = Time::constant(1, 1, 1, 001_001_001);
let span = Span::new().nanoseconds(2);
assert_eq!(t1.wrapping_sub(span), Time::constant(1, 1, 1, 001_000_999));
let span = Span::new().microseconds(2);
assert_eq!(t1.wrapping_sub(span), Time::constant(1, 1, 1, 000_999_001));
let span = Span::new().milliseconds(2);
assert_eq!(t1.wrapping_sub(span), Time::constant(1, 1, 0, 999_001_001));
let span = Span::new().seconds(2);
assert_eq!(t1.wrapping_sub(span), Time::constant(1, 0, 59, 001_001_001));
let span = Span::new().minutes(2);
assert_eq!(t1.wrapping_sub(span), Time::constant(0, 59, 1, 001_001_001));
let span = Span::new().hours(2);
assert_eq!(t1.wrapping_sub(span), Time::constant(23, 1, 1, 001_001_001));
}

View file

@ -0,0 +1,16 @@
use jiff::{civil::Time, ToSpan};
/// Source: https://github.com/tc39/test262/blob/62626e083bd506124aac6c799464d76c2c42851b/test/built-ins/Temporal/PlainTime/prototype/until/argument-cast.js
#[test]
fn argument_cast() {
let t1 = Time::constant(15, 23, 30, 123_456_789);
let t2 = Time::constant(16, 34, 0, 0);
let span = 1
.hours()
.minutes(10)
.seconds(29)
.milliseconds(876)
.microseconds(543)
.nanoseconds(211);
assert_eq!(t1.until(t2), span);
}

1
tests/tc39_262/mod.rs Normal file
View file

@ -0,0 +1 @@
mod civil;