shunsock / timezone_translator

simple command-line utility that converts a given time from one timezone to another.
MIT License
23 stars 1 forks source link

tackle corner cases #25

Closed shunsock closed 3 months ago

shunsock commented 3 months ago

Report

this issue is reported on reddit. https://www.reddit.com/r/rust/comments/1dr9x58/tzt_timezone_translator/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button

Case

$ timezone_translator --time '2024-03-10 02:30:00' --from 'America/New_York' --to 'America/Los_Angeles'
Translation Error: 2024-03-10 02:30:00 from America/New_York to America/Los_Angeles
$ timezone_translator --time '2024-11-03 01:30:00' --from 'America/New_York' --to 'UTC'
Translation Error: 2024-11-03 01:30:00 from America/New_York to UTC

The problem here is that the combination of a datetime and a time zone can be ambiguous during time zone transitions. Or may not exist at all. Reporting an error is reasonable behavior, although I suppose the message could be improved. You might consider other options as well, such as a disambiguation strategy such as choosing the earlier or later times.

shunsock commented 3 months ago

create branch for fixing https://github.com/shunsock/timezone_translator/tree/fix/corner_case

shunsock commented 3 months ago

Checked these cases, and found same error reported.

shunsuke.tsuchiya in ~/Hobby/timezone_translator on fix/corner_case λ timezone_translator --time '2024-03-10 02:30:00' --from 'America/New_York' --to 'America/Los_Angeles'
Translation Error: 2024-03-10 02:30:00 from America/New_York to America/Los_Angeles
shunsuke.tsuchiya in ~/Hobby/timezone_translator on fix/corner_case λ timezone_translator --time '2024-11-03 01:30:00' --from 'America/New_York' --to 'UTC'
Translation Error: 2024-11-03 01:30:00 from America/New_York to UTC
shunsuke.tsuchiya in ~/Hobby/timezone_translator on fix/corner_case λ fastfetch
                     ..'          shunsuke.tsuchiya@shunsukhiyasMBP
                 ,xNMM.           ---------------------------------
               .OMMMMo            OS: macOS Sonoma 14.5 arm64
               lMM"               Host: MacBook Pro (14-inch, 2021)
     .;loddo:.  .olloddol;.       Kernel: 23.5.0
   cKMMMMMMMMMMNWMMMMMMMMMM0:     Uptime: 25 days, 23 hours, 33 mins
 .KMMMMMMMMMMMMMMMMMMMMMMMWd.     Packages: 117 (brew), 12 (brew-cask)
 XMMMMMMMMMMMMMMMMMMMMMMMX.       Shell: zsh 5.9
;MMMMMMMMMMMMMMMMMMMMMMMM:        Display (JAPANNEXT.MNT): 1920x1200 @ 75Hz [External] *
:MMMMMMMMMMMMMMMMMMMMMMMM:        Display (PHL 221S6Q): 1920x1080 @ 60Hz [External]
.MMMMMMMMMMMMMMMMMMMMMMMMX.       DE: Aqua
 kMMMMMMMMMMMMMMMMMMMMMMMMWd.     WM: Quartz Compositor
 'XMMMMMMMMMMMMMMMMMMMMMMMMMMk    WM Theme: Multicolor (Dark)
  'XMMMMMMMMMMMMMMMMMMMMMMMMK.    Font: .AppleSystemUIFont [System], Helvetica [User]
    kMMMMMMMMMMMMMMMMMMMMMMd      Cursor: Fill - #373838FF, Outline - #2EEB2DFF (49px)
     ;KMMMMMMMWXXWMMMMMMMk.       Terminal: WezTerm 20230712-072601-f4abf8fd
       "cooc*"    "*coo'"         Terminal Font: Moralerspace Argon HWNF
                                  CPU: Apple M1 Pro (8) @ 3.23 GHz
                                  GPU: Apple M1 Pro (14) [Integrated]
                                  Memory: 12.64 GiB / 16.00 GiB (79%)
                                  Swap: 3.74 GiB / 5.00 GiB (75%)
                                  Disk (/): 180.13 GiB / 460.43 GiB (39%) - apfs [Read-only]
                                  Local IP (en0): 192.168.128.185/24 *
                                  Battery: 80% [AC connected]
                                  Power Adapter: 67W USB-C Power Adapter
                                  Locale: en_US.UTF-8

                                  ████████████████████████
                                  ████████████████████████
shunsock commented 3 months ago

Why does the error happen

LocalResult which is type of return value of .from_local_datetime() has three options: Single(time), Ambiguous(earliest, latest) and None. Our source code route only output when the status is Single(time). Therefore, we need to implement match syntax like following.

        match mapped {
            LocalResult::Single(time) => Ok(time.with_timezone(&self.to_tz)),
            LocalResult::Ambiguous(time, _) => Ok(time.with_timezone(&self.to_tz)),
            LocalResult::None => {
                let error = TranslationError::TranslationError {
                    time: self.time,
                    from_tz: self.from_tz,
                    to_tz: self.to_tz,
                };
                Err(error)
            }
        }

Method

we need option to check strategy: earliest or latest. thus we will implement optional boolean input ambiguous-strategy.

ambiguous-strategy is enum having two state

enum AmbiguousStrategy {
  Earliest,
  Latest
}

And also define helper function.

fn handle_ambiguous_strategy(
    strategy: AmbiguousStrategy,
    time1: Datetime<Tz>,
    time2: Datetime<Tz>,
    timezone: Tz
) -> DateTime<Tz> {
    return match strategy {
        AmbiguousStrategy::Earliest => (time1.with_timezone(timeezone),
        AmbiguousStrategy::Latest => (time1.with_timezone(timezone)
    }
}

And call it.

        match mapped {
            LocalResult::Single(time) => Ok(time.with_timezone(&self.to_tz)),
            LocalResult::Ambiguous(time1, time2) => Ok(handle_ambiguous_strategy(strategy, time1, time2, &self.to_tz.clone()),
            LocalResult::None => {
                let error = TranslationError::TranslationError {
                    time: self.time,
                    from_tz: self.from_tz,
                    to_tz: self.to_tz,
                };
                Err(error)
            }
        }

Referemce

chrono::offset::LocalResult https://docs.rs/chrono/latest/chrono/offset/enum.LocalResult.html#variant.Ambiguous

shunsock commented 3 months ago

merged!! #26