Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DateTime::fix_offset #1030

Merged
merged 1 commit into from
May 15, 2023
Merged

Add DateTime::fix_offset #1030

merged 1 commit into from
May 15, 2023

Conversation

pitdicker
Copy link
Collaborator

As an anecdote, I was stumped for days how to let a hobby project handle timestamps with timezones. Entries could come from various sources, with different associated timezones. So I had generic DateTime<Tz: Timezone> objects. How to collect those in a data structure?

Finally it 'clicked' that the full timezone didn't matter in my case, just the offset from UTC of an entry should be preserved.

Converting a generic DateTime to DateTime<FixedOffset> is trivial when you know the innards of chrono. But if there was a Datetime::fix_offset method it probably would have set me on the right track. (Then this took me quite a while because the fix() method is somewhat hidden in the Offset trait, and I saw a similar story in one of the issues.)

Note that a generic From implementation is not possible:

impl<Tz: TimeZone> From<DateTime<Tz>> for DateTime<FixedOffset> {
    fn from(src: DateTime<Tz>) -> Self {
        src.with_timezone(&src.offset().fix())
    }
}

That fails with conflicting implementation in crate 'core': impl<T> From<T> for T.

I also remember one of my naive tries was with_timezone(&FixedOffset), comparable to with_timezone(&Utc). But that doesn't just work, the FixedOffset must be derived from the DateTime first.

What a fix_offset method would also be an alternative to is the unfortunate implementation of
From<DateTime<Local>> for DateTime<FixedOffset>, that says: "Note that the converted value returned by this will be created with a fixed timezone offset of 0."

I am curious about your opinions. Can converting a generic DateTime to DateTime<FixedOffset> be considered a fundamental enough thing to do to add this method?

@pitdicker pitdicker changed the title Add DateTime::Fix_offset Add DateTime::fix_offset Apr 19, 2023
@pitdicker
Copy link
Collaborator Author

Reading through other issues/PRs, I see a method like this was suggested before by @esheppa in #829 (comment).

@pitdicker
Copy link
Collaborator Author

Also suggested by @epipheus in #624 (comment).

@pitdicker
Copy link
Collaborator Author

The closed PR #305 had a similar motivation.

Copy link
Contributor

@jtmoon79 jtmoon79 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jtmoon79
Copy link
Contributor

Reading through other issues/PRs, I see a method like this was suggested before by @esheppa in #829 (comment).

Also suggested by @epipheus in #624 (comment).

The closed PR #305 had a similar motivation.

This is getting at the biggest difficulty in this entire crate: converting between datetime types.
When 0.5 is released, I plan to put together a "chrono cheat sheet". A big XY table of conversions and their recipes.

@pitdicker
Copy link
Collaborator Author

When 0.5 is released, I plan to put together a "chrono cheat sheet". A big XY table of conversions and their recipes.

I may have made just that for the DateTime type: some documentation on how to create it, which actions are available, and how to convert it to other types. Will have a PR probably later today 😄. It is a first step to expand the documentation, which was my biggest trouble with chrono.

@jtmoon79
Copy link
Contributor

jtmoon79 commented Apr 26, 2023

When 0.5 is released, I plan to put together a "chrono cheat sheet". A big XY table of conversions and their recipes.

I may have made just that for the DateTime type: some documentation on how to create it, which actions are available, and how to convert it to other types. Will have a PR probably later today 😄. It is a first step to expand the documentation, which was my biggest trouble with chrono.

I was imagining it might be fun to use a markdown table where the XY meeting point has a link to the code recipe.

| ↓ From \ To →           | `DateTime<FixedOffset>`               | `Local`                            | `Utc`                            | `FixedOffset`                            | `Timezone`                            |
|-------------------------|---------------------------------------|------------------------------------|----------------------------------|------------------------------------------|---------------------------------------|
| `DateTime<FixedOffset>` |                                       | [#](#datetimefixedoffset_to_local) | [#](#utc_to_datetimefixedoffset) | [#](#fixedoffset_to_datetimefixedoffset) | [#](#timezone_to_datetimefixedoffset) |
| `Local`                 | [#](#local_to_datetimefixedoffset)    |                                    | [#](#utc_to_local)               | [#](#fixedoffset_to_local)               | [#](#timezone_to_local)               |
| `Utc`                   | [#](#utc_to_datetimefixedoffset)      | [#](#utc_to_local)                 |                                  | [#](#fixedoffset_to_utc)                 | [#](#timezone_to_utc)                 |
| `FixedOffset`           | [#](#fixed_to_datetimefixedoffset)    | [#](#fixedoffset_to_local)         | [#](#utc_to_fixedoffset)         |                                          | [#](#timezone_to_fixedoffset)         |
| `Timezone`              | [#](#timezone_to-datetimefixedoffset) | [#](#timezone_to_local)            | [#](#utc_to_timezone)            | [#](#fixedoffset_to_timezone)            |                                       |

Would look something like this (incomplete) example


↓ From \ To → DateTime<FixedOffset> Local Utc FixedOffset Timezone
DateTime<FixedOffset> # # # #
Local # # # #
Utc # # # #
FixedOffset # # # #
Timezone # # # #

Local to DateTime<FixedOffset>

use chrono::{DateTime, Local, FixedOffset};

let local = Local::now();
let dt: DateTime<FixedOffset> = local.into();

Utc to DateTime<FixedOffset>

// code recipe

FixedOffset to DateTime<FixedOffset>

// code recipe

... (more code recipes linked in the table above) ...

@pitdicker pitdicker force-pushed the fix_offset branch 2 times, most recently from 94b5228 to 4d57be3 Compare May 12, 2023 06:09
@djc
Copy link
Member

djc commented May 12, 2023

Can you add some basic tests for this?

@pitdicker
Copy link
Collaborator Author

Can you add some basic tests for this?

Intended to do so this morning, I have mostly learned the lesson by now 😆.

I added a line to the test test_datetime_local_from_preserves_offset to test from Local, and added a separate test for the conversion from Utc and FixedOffset (which does nothing).

/// This it useful for converting a generic `DateTime<Tz: Timezone>` to `DateTime<FixedOffset>`.
#[inline]
#[must_use]
pub fn fix_offset(&self) -> DateTime<FixedOffset> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to nitpick, but should this be with_fixed_offset to match the naming conventions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind. I picked fix_offset because it is similar to the current Offset::fix. It 'fixes' the offset to its current value.

I would expect that with a method with the name with_fixed_offset you have to supply an argument, like you have to with with_hour, with_year, etc.

Copy link
Member

@djc djc May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @pitdicker that the with_ convention doesn't fit here, as that is usually used to augment the existing value with new data which is not the case here.

On the other hand, after this comment I did wonder if fixed_offset() would be a better name, on the notion that this doesn't mutate (which fix_ suggests to me) but instead returns the new value.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found myself typing it that way (fixed_offset()) by mistake a few times.
But the method name should have a verb in it.

To continue bikeshedding: what about to_fixed_offset? It doesn't return a FixedOffset, but returning a DateTime<FixedOffset> does not seem too surprising to me.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed_offset() still does have a verb in it, just in past tense (I'm not convinced by this argument).

The fact that you mistyped it a few times seems more evidence that fixed_offset() is the correct spelling.

I think the ecosystem convention is that a to_fixed_offset() function should yield a FixedOffset, which doesn't fit here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nobody can say we didn't think about naming 😆. Changed it to fixed_offset.

Copy link
Collaborator

@esheppa esheppa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@djc djc merged commit eb92784 into chronotope:0.4.x May 15, 2023
@pitdicker pitdicker deleted the fix_offset branch May 15, 2023 14:17
crapStone pushed a commit to Calciumdibromid/CaBr2 that referenced this pull request Jun 12, 2023
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [chrono](https://github.com/chronotope/chrono) | dependencies | patch | `0.4.24` -> `0.4.26` |

---

### Release Notes

<details>
<summary>chronotope/chrono</summary>

### [`v0.4.26`](https://github.com/chronotope/chrono/releases/tag/v0.4.26): 0.4.26

[Compare Source](chronotope/chrono@v0.4.25...v0.4.26)

The changes from [#&#8203;807](chronotope/chrono#807) we merged for 0.4.25 unfortunately restricted parsing in a way that was incompatible with earlier 0.4.x releases. We reverted this in [#&#8203;1113](chronotope/chrono#1113). A small amount of other changes were merged since.

-   Update README ([#&#8203;1111](chronotope/chrono#1111), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Revert backport of [#&#8203;807](chronotope/chrono#807) ([#&#8203;1113](chronotope/chrono#1113), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Update to 2021 edition ([#&#8203;1109](chronotope/chrono#1109), thanks to [@&#8203;tottoto](https://github.com/tottoto))
-   Fix `DurationRound` panics from issue [#&#8203;1010](chronotope/chrono#1010) ([#&#8203;1093](chronotope/chrono#1093), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   tests: date path consolidate (branch 0.4.x) ([#&#8203;1090](chronotope/chrono#1090), thanks to [@&#8203;jtmoon79](https://github.com/jtmoon79))
-   Parse tests nanosecond bare dot (branch 0.4.x) ([#&#8203;1098](chronotope/chrono#1098), thanks to [@&#8203;jtmoon79](https://github.com/jtmoon79))
-   yamllint cleanup lint.yml test.yml ([#&#8203;1102](chronotope/chrono#1102), thanks to [@&#8203;jtmoon79](https://github.com/jtmoon79))
-   Remove num-iter dependency ([#&#8203;1107](chronotope/chrono#1107), thanks to [@&#8203;tottoto](https://github.com/tottoto))

Thanks on behalf of the chrono team ([@&#8203;djc](https://github.com/djc) and [@&#8203;esheppa](https://github.com/esheppa)) to all contributors!

### [`v0.4.25`](https://github.com/chronotope/chrono/releases/tag/v0.4.25): 0.4.25

[Compare Source](chronotope/chrono@v0.4.24...v0.4.25)

Time for another maintenance release. This release bumps the MSRV to 1.56; given MSRV bumps in chrono's dependencies (notably for syn 2), we felt that it no longer made sense to support any older versions. Feedback welcome in our issue tracker!

##### Additions

-   Bump the MSRV to 1.56 ([#&#8203;1053](chronotope/chrono#1053))
-   Apply comments from MSRV bump ([#&#8203;1026](chronotope/chrono#1026), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Remove num-integer dependency ([#&#8203;1037](chronotope/chrono#1037), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Add `NaiveDateTime::and_utc()` method ([#&#8203;952](chronotope/chrono#952), thanks to [@&#8203;klnusbaum](https://github.com/klnusbaum))
-   derive `Hash` for most pub types that also derive `PartialEq` ([#&#8203;938](chronotope/chrono#938), thanks to [@&#8203;bruceg](https://github.com/bruceg))
-   Add `parse_and_remainder()` methods ([#&#8203;1011](chronotope/chrono#1011), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Add `DateTime::fix_offset()` ([#&#8203;1030](chronotope/chrono#1030), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Add `#[track_caller]` to `LocalResult::unwrap` ([#&#8203;1046](chronotope/chrono#1046), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Add `#[must_use]` to some methods ([#&#8203;1007](chronotope/chrono#1007), thanks to [@&#8203;aceArt-GmbH](https://github.com/aceArt-GmbH))
-   Implement `PartialOrd` for `Month` ([#&#8203;999](chronotope/chrono#999), thanks to [@&#8203;Munksgaard](https://github.com/Munksgaard))
-   Add `impl From<NaiveDateTime> for NaiveDate` ([#&#8203;1012](chronotope/chrono#1012), thanks to [@&#8203;pezcore](https://github.com/pezcore))
-   Extract timezone info from tzdata file on Android ([#&#8203;978](chronotope/chrono#978), thanks to [@&#8203;RumovZ](https://github.com/RumovZ))

##### Fixes

-   Prevent string slicing inside char boundaries ([#&#8203;1024](chronotope/chrono#1024), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   fix IsoWeek so that its flags are always correct ([#&#8203;991](chronotope/chrono#991), thanks to [@&#8203;moshevds](https://github.com/moshevds))
-   Fix out-of-range panic in `NaiveWeek::last_day` ([#&#8203;1070](chronotope/chrono#1070), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Use correct offset in conversion from `Local` to `FixedOffset` ([#&#8203;1041](chronotope/chrono#1041), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Fix military timezones in RFC 2822 parsing ([#&#8203;1013](chronotope/chrono#1013), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Guard against overflow in NaiveDate::with_\*0 methods ([#&#8203;1023](chronotope/chrono#1023), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Fix panic in from_num_days_from_ce_opt ([#&#8203;1052](chronotope/chrono#1052), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))

##### Refactoring

-   Rely on std for getting local time offset ([#&#8203;1072](chronotope/chrono#1072), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Make functions in internals const ([#&#8203;1043](chronotope/chrono#1043), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Refactor windows module in `Local` ([#&#8203;992](chronotope/chrono#992), thanks to [@&#8203;nekevss](https://github.com/nekevss))
-   Simplify from_timestamp_millis, from_timestamp_micros ([#&#8203;1032](chronotope/chrono#1032), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Backport [#&#8203;983](chronotope/chrono#983) and [#&#8203;1000](chronotope/chrono#1000) ([#&#8203;1063](chronotope/chrono#1063), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))

##### Documentation

-   Backport documentation improvements ([#&#8203;1066](chronotope/chrono#1066), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Add documentation for %Z quirk ([#&#8203;1051](chronotope/chrono#1051), thanks to [@&#8203;campbellcole](https://github.com/campbellcole))
-   Add an example to Weekday ([#&#8203;1019](chronotope/chrono#1019), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))

##### Internal improvements

-   Gate test on `clock` feature ([#&#8203;1061](chronotope/chrono#1061), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   CI: Also run tests with `--no-default-features` ([#&#8203;1059](chronotope/chrono#1059), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Prevent `bench_year_flags_from_year` from being optimized out ([#&#8203;1034](chronotope/chrono#1034), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Fix test_leap_second during DST transition ([#&#8203;1064](chronotope/chrono#1064), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Fix warnings when running tests on Windows ([#&#8203;1038](chronotope/chrono#1038), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Fix tests on AIX ([#&#8203;1028](chronotope/chrono#1028), thanks to [@&#8203;ecnelises](https://github.com/ecnelises))
-   Fix doctest warnings, remove mention of deprecated methods from main doc ([#&#8203;1081](chronotope/chrono#1081), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Reformat `test_datetime_parse_from_str` ([#&#8203;1078](chronotope/chrono#1078), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   GitHub yml shell `set -eux`, use bash ([#&#8203;1103](chronotope/chrono#1103), thanks to [@&#8203;jtmoon79](https://github.com/jtmoon79))
-   test: explicitly set `LANG` to `c` in gnu `date` ([#&#8203;1089](chronotope/chrono#1089), thanks to [@&#8203;scarf005](https://github.com/scarf005))
-   Switch test to `TryFrom` ([#&#8203;1086](chronotope/chrono#1086), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   Add test for issue 551 ([#&#8203;1020](chronotope/chrono#1020), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   RFC 2822 single-letter obsolete tests ([#&#8203;1014](chronotope/chrono#1014), thanks to [@&#8203;jtmoon79](https://github.com/jtmoon79))
-   \[CI] Lint Windows target and documentation links ([#&#8203;1062](chronotope/chrono#1062), thanks to [@&#8203;pitdicker](https://github.com/pitdicker))
-   add test_issue\_866 ([#&#8203;1077](chronotope/chrono#1077), thanks to [@&#8203;jtmoon79](https://github.com/jtmoon79))
-   Remove AUTHORS metadata ([#&#8203;1074](chronotope/chrono#1074))

On behalf of [@&#8203;djc](https://github.com/djc) and [@&#8203;esheppa](https://github.com/esheppa), thanks to all contributors!

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xMDUuMSIsInVwZGF0ZWRJblZlciI6IjM1LjExNS4yIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcCJ9-->

Co-authored-by: cabr2-bot <cabr2.help@gmail.com>
Reviewed-on: https://codeberg.org/Calciumdibromid/CaBr2/pulls/1909
Reviewed-by: crapStone <crapstone@noreply.codeberg.org>
Co-authored-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
Co-committed-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants