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.
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).
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.
Related
In my application I one string such as 2023-2-14 and I want convert this to 2023-02-14.
I write below codes:
val format = SimpleDateFormat("yyyy-MM-dd")
val date: Date = format.parse(startDateStr)
Log.e("dateLog",""+date)
But in logcat show me this : Wed Feb 15 00:00:00 GMT+03:30 2023
Why? I used this format : yyyy-MM-dd.
Why not used this format?
you are just parsing date, without a time, thus date object have set 00 for hour, day etc. now use format method for converting old String to new one
val formatAs = "yyyy-MM-dd"
var format = SimpleDateFormat(formatAs)
val date: Date = format.parse(startDateStr)
Log.e("dateLog","date:"+date)
String dateAsStringFormatted = format.format(date);
Log.e("dateLog","dateAsStringFormatted:"+dateAsStringFormatted)
some other answers in HERE
Is there a way to add time to a Timestamp formatted time without having to convert it multiple times?
val seconds: Long = Timestamp.now().seconds
val utcDT = LocalDateTime.ofEpochSecond(seconds, 0, ZoneOffset.UTC)
val withTwo: LocalDateTime = utcDT.plusHours(2)
val date: Date = Date.from(withTwo.toInstant(ZoneOffset.UTC))
val new = Timestamp(date)
Log.d("sirEgghead", "Timestamp: ${Timestamp.now().toDate()}")
Log.d("sirEgghead", "utcDT: $utcDT")
Log.d("sirEgghead", "addedTwo: $withTwo")
Log.d("sirEgghead", "date: ${date.time}")
Log.d("sirEgghead", "new: ${new.toDate()}")
This is the mess that I went through just to add two hours and return it to Timestamp format two hours in the future.
Timestamp: Wed May 18 21:53:12 EDT 2022
utcDT: 2022-05-19T01:53:12
addedTwo: 2022-05-19T03:53:12
date: 1652932392000
new: Wed May 18 23:53:12 EDT 2022
I do not want to use the local system time in case the time on the device is incorrect. The data is stored in Google Firestore.
Instead of creating a LocalDateTime instance, create an Instant instance and add 2 hours to it, using its plus method which takes TemporalUnits for addition.
val seconds: Long = Timestamp.now().seconds
val addedSeconds = Instant.ofEpochSecond(seconds).plus(2, ChronoUnit.HOURS).epochSecond
val newTimeStamp = Timestamp(addedSeconds, 0)
I get a time from server and I want to change it to Local zone How can I do that with Kotlin ?
Time coming from the server is like "2020-09-01T13:16:33.114Z"
Here's my code:
val dateStr = order.creationDate
val df = SimpleDateFormat("dd-MM-yyyy HH:mm aa", Locale.getDefault())
df.timeZone = TimeZone.getDefault()
val date = df.parse(dateStr)
val formattedDate = df.format(date)
textViewDateOrderDetail.text = formattedDate
order.creationDate : Time from server
tl;dr
This will convert the example String to the system default time zone:
import java.time.ZonedDateTime
import java.time.ZoneId
fun main() {
// example String
val orderCreationDate = "2020-09-01T13:16:33.114Z"
// parse it to a ZonedDateTime and adjust the zone to the system default
val localZonedDateTime = ZonedDateTime.parse(orderCreationDate)
.withZoneSameInstant(ZoneId.systemDefault())
// print the adjusted values
println(localZonedDateTime)
}
The output depends on the system default time zone, in the Kotlin Playground, it produces the following line:
2020-09-01T13:16:33.114Z[UTC]
which obviously means the Kotlin Playground is playing in UTC.
A little more...
It's strongly recommended to use java.time nowadays and to stop using the outdated libraries for datetime operations (java.util.Date, java.util.Calendar along with java.text.SimpleDateFormat).
If you do that, you can parse this example String without specifying an input format because it is formatted in an ISO standard.
You can create an offset-aware (java.time.OffsetDateTime) object or a zone-aware one
(java.time.ZonedDateTime), that's up to you. The following example(s) show(s) how to parse your String, how to adjust a zone or an offset and how to print in a different format:
import java.time.OffsetDateTime
import java.time.ZonedDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
fun main() {
// example String
val orderCreationDate = "2020-09-01T13:16:33.114Z"
// parse it to an OffsetDateTime (Z == UTC == +00:00 offset)
val offsetDateTime = OffsetDateTime.parse(orderCreationDate)
// or parse it to a ZonedDateTime
val zonedDateTime = ZonedDateTime.parse(orderCreationDate)
// print the default output format
println(offsetDateTime)
println(zonedDateTime)
// adjust both to a different offset or zone
val localZonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("Brazil/DeNoronha"))
val localOffsetDateTime = offsetDateTime.withOffsetSameInstant(ZoneOffset.ofHours(-2))
// print the adjusted values
println(localOffsetDateTime)
println(localZonedDateTime)
// and print your desired output format (which doesn't show a zone or offset)
println(localOffsetDateTime.format(
DateTimeFormatter.ofPattern("dd-MM-uuuu hh:mm a")
)
)
println(localZonedDateTime.format(
DateTimeFormatter.ofPattern("dd-MM-uuuu hh:mm a")
)
)
}
The output is
2020-09-01T13:16:33.114Z
2020-09-01T13:16:33.114Z
2020-09-01T11:16:33.114-02:00
2020-09-01T11:16:33.114-02:00[Brazil/DeNoronha]
01-09-2020 11:16 AM
01-09-2020 11:16 AM
For a conversion to the system zone or offset, use ZoneId.systemDefault() or ZoneOffset.systemDefault() instead of hard coded ones. Pay attention to the ZoneOffset since it does not necessarily give the correct one because only a ZoneId considers daylight saving time.
For more information, see this question and its answer
For further and more accurate information about formats to be defined for parsing or formatting output, you should read the JavaDocs of DateTimeFormatter.
I use ThreeTen module and when I print ZonedDateTime.now(), I get.
2019-07-11T22:43:36.564-07:00[America/Los_Angeles]
What's the format of this?
I tried uuuu-MM-dd'T'HH:mm:ss.SSS'Z' and It says,
org.threeten.bp.format.DateTimeParseException: Text '2019-07-11T22:43:36.564-07:00[America/Los_Angeles]' could not be parsed at index 23
So, after SSS, the 'Z' part is incorrect.
What's the proper way to implement it?
This is my code:
val pstTime = ZonedDateTime.now(ZoneId.of("America/Los_Angeles")).toString()
val timeFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS'Z'")
val mTime = LocalDateTime.parse(pstTime, timeFormatter).toString()
tv_pstTime.text = mTime
I want to parse it to the format like Tuesday, July 2 5:15:01 P.M.
How can I do that?
You can use DateTimeFormatter.ofPattern("...."). Inside .ofPattern("....") you can have any pattern you want.
Like this:
val result = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"))
.format(DateTimeFormatter.ofPattern("EEEE, MMMM d HH:mm:ss a"))
Output: Thursday, July 11 23:51:21 PM
I'm trying to show dutch months. But the month is printed out in English. This needs to work from Android API 19 and higher.
compile 'joda-time:joda-time:2.9.9'
val test = DateTime()
val l = Locale("nl_NL") // Dutch language, Netherlands country.
val f = DateTimeFormat.forPattern("dd MMM yyyy").withLocale(l)
val text = f.print(test)
Prints out:
26 Oct 2017
Should be:
26 Okt 2017
You must use the Locale's 2-arg constructor, that receives the language and country code in separate parameters:
val l = Locale("nl", "NL")
With this, the output is:
26 okt 2017
In my tests, the output is not in uppercase Okt as you wanted, but that's built-in in the API and we have no control over it. If you want Okt as output, you'll have to manipulate the string by yourself.
Correct answer:
val l = Locale("nl", "NL")
val f = DateTimeFormat.forPattern("dd MMM yyyy").withLocale(l)
val dateStr = f.print(dateTime).substring(3, 4).toUpperCase()
val capitalizedDate = StringBuilder(f.print(dateTime))
capitalizedDate.setCharAt(3, dateStr[0])
return capitalizedDate.toString().replace(".", "")
I already answered to a similar question here. You can get a localized month name using Calendar object.
private String getMonthName(final int index, final Locale locale, final boolean shortName)
{
String format = "%tB";
if (shortName)
format = "%tb";
Calendar calendar = Calendar.getInstance(locale);
calendar.set(Calendar.MONTH, index);
calendar.set(Calendar.DAY_OF_MONTH, 1);
return String.format(locale, format, calendar);
}
Example for full month name:
System.out.println(getMonthName(0, new Locale("NL"), false));
Result: januari
Example for short month name:
System.out.println(getMonthName(2, new Locale("NL"), true));
Result: jan.