Klariskao
Klariskao

Reputation: 153

Displaying time from timestamp in Unix, UTC in Android Kotlin

I am working on a simple weather app and am trying to display time in the format "K:mm a" (eg. 6:30 AM). I am fetching a timestamp in Unix, UTC for the specified place a user searches for such as NYC. The timestamp looks something like 1624836905 and the time zone offset such as -14400. I have a function which adds the two up, converts it to milliseconds and should return the time in the format specified. The function is as follows:

fun dateTime(time: Int, zone: Int, format: String = "EEE, MMMM d K:mm a"): String {
        return try {
            val sdf = SimpleDateFormat(format)
            val netDate = Date((time.plus(zone)).toLong() * 1000)
            sdf.timeZone = TimeZone.getTimeZone("UTC")
            sdf.format(netDate)
        } catch (e: Exception) {
            e.toString()
        }
    }

And I call it such as:

sunriseTextView.text = dateTime(result2.lookup<Int>("daily.sunrise")[0], timeZone, "K:mm a")
sunsetTextView.text = dateTime(result2.lookup<Int>("current.sunset")[0], timeZone, "K:mm a")

The expected output is the sunrise/sunset time such as 6:01 AM and 9:05 PM. I am also rendering the current time at the specified place also obtained from the API. As follows:

dateView.text = dateTime(result2.lookup<Int>("current.dt")[0], timeZone)

Which outputs the current date and time at the place in the format "EEE, MMMM d K:mm a" (eg. Mon June 28 8:23 AM).

The current time is always correct, however, there is a problem with the sunrise and sunset times. If I input NYC, for example, the sunrise is 7:35 PM and sunset 10:39 AM. The sunrise and sunset for Tokyo, on the other hand, appears correct at 4:27 AM and 7:00 PM.

Clearly I am missing something as I know the API data is correct. I am looking for any suggestions, however, I would appreciate one which does not have API restrictions such as kotlinx-datetime which requires API 26.

Upvotes: 5

Views: 2917

Answers (2)

Klariskao
Klariskao

Reputation: 153

I have a similar function in Python like this:

    def time(self, unix_time, time_zone):
        date = datetime.utcfromtimestamp(unix_time + time_zone)
        return (datetime.strftime(date, '%I:%M %p'))

Which I call as follows:

WeatherApp.time_zone = self.weather_results['timezone_offset']
print(self.time_date(self.weather_results['current']['dt'], self.time_zone))
print('Sunrise: ' + self.time(self.weather_results['current']['sunrise'], self.time_zone))
print('Sunset: ' + self.time(self.weather_results['current']['sunset'], self.time_zone))

I am trying to get the same result in Kotlin. However comparing the outputs is as follows:

Location: Tokyo, Japan 
Current time and date: Tue, June 29 06:53 PM (same in Python and Kotlin from Asia/Tokyo, 1624960598 1624961970
32400)
Sunrise: 04:27 AM (as outputted in Python from 32400, 1624908462)
Sunset: 07:00 PM (as outputted in Python from 32400, 1624960847)
Sunrise: 4:27 AM (as outputted in Kotlin from Asia/Tokyo, 1624908467)
Sunset: 7:00 PM (as outputted in Kotlin from Asia/Tokyo, 1624960846)
---
Location: New York, United States
Current time and date: Tue, June 29 6:02 AM  (same in Python and Kotlin from America/New_York, 1624960973)
Sunrise: 05:27 AM (as outputted in Python from -14400, 1624958860)
Sunset: 08:31 PM (as outputted in Python from -14400, 1625013070)
Sunrise: 7:35 PM (as outputted in Kotlin from America/New_York, 1624923330)
Sunset: 10:39 AM (as outputted in Kotlin from America/New_York, 1624977548)

Comparing the timestamps shows that the data obtained from the API differs for some reason. I currently solved it by using a different API. I was using the One Call API by OpenWeather. I can't think of a reason why this is happening, however, by getting the timestamp from a different API the issue no longer persists.

Upvotes: 0

deHaar
deHaar

Reputation: 18568

Since there's API Desugaring, you can use java.time with API versions below 26.

That means you don't have to rely on those outdated datetime classes, like java.util.Date and java.text.SimpleDateFormat.

Your fun can be rewritten like this:

fun dateTime(time: Int, zone: String, format: String = "EEE, MMMM d K:mm a"): String {
    // parse the time zone
    val zoneId = ZoneId.of(zone)
    // create a moment in time from the given timestamp (in seconds!)
    val instant = Instant.ofEpochSecond(time.toLong())
    // define a formatter using the given pattern and a Locale
    val formatter = DateTimeFormatter.ofPattern(format, Locale.ENGLISH)
    // then make the moment in time consider the zone and return the formatted String
    return instant.atZone(zoneId).format(formatter)
}

Here's some example use in a simple fun main():

fun main() {
    val timestamp: Int = 1624836905 // your example epoch seconds
    // try two different zones
    val newYorkTime = dateTime(timestamp, "America/New_York")
    val tokyoTime = dateTime(timestamp, "Asia/Tokyo")
    // and print the results
    println(newYorkTime)
    println(tokyoTime)
}

Output of this example:

Sun, June 27 7:35 PM
Mon, June 28 8:35 AM

Please note that you could as well use an offset: Int instead of a zone: String if you simply want to provide an offset of hours from UTC. You would need to adjust two lines of this fun then:

fun dateTime(time: Int, offset: Int, format: String = "EEE, MMMM d K:mm a"): String {
    // parse the time zone
    val zoneOffset = ZoneOffset.ofHours(offset)
    // create a moment in time from the given timestamp (in seconds!)
    val instant = Instant.ofEpochSecond(time.toLong())
    // define a formatter using the given pattern and a Locale
    val formatter = DateTimeFormatter.ofPattern(format, Locale.ENGLISH)
    // then make the moment in time consider the zone and return the formatted String
    return instant.atOffset(zoneOffset).format(formatter)
}

Using that in a main like this

fun main() {
    val timestamp: Int = 1624836905
    
    val newYorkTime = dateTime(timestamp, -4)
    val tokyoTime = dateTime(timestamp, 9)
    println(newYorkTime)
    println(tokyoTime)
}

will produce the very same output.

In addition, the Locale used in the DateTimeFormatter could as well be a function argument in case you want to support different languages (this affects the names of months and days of week).

Upvotes: 4

Related Questions