Is it possible to specify an ISO Date with a timezone (i.e. GMT offset) but without a time? - swift

I'm trying to see if you can specify a date with a timezone in ISO but without also specifying a time.
This may seem odd to ask about having a timezone without actually having a time, but technically a date represents a range between two times... the 24-hour period spanning from midnight to midnight, and that 'midnight' has to be in a timezone.
In our case, we have an API that wants to say 'Filter things on-or-before date X and on-or-after date Y' and we want the user to specify 'April 9th' (in their time zone) for both to get all things that happen on that day.
Of course we solve this by adding a day to the first date, then changing it to a pure 'before', but the front-end is required to do that math. We can't do it on the backend because having to send a date with a time means we would be sending April 9th at midnight, then on the backend adding a day to that, but what if someone passed in 4pm?
We could fail the date if it has a non-midnight time, but then we're back to why pass it in the first place.
So again, can you have a date with a timezone but not have a time component?

To decode ISO8601 dates only with year-month-day and time zone set the appropriate formatOptions of the ISO8601DateFormatter
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withFullDate, .withTimeZone]

If by time zone you mean a UTC offset (as used with ISO 8601 dates with times), this is no problem. If by time zone you mean a true time zone with historic, present and known future offsets from UTC, including for example summer time/DST, like America/New_York or North American Eastern Time, then ISO 8601 does not support that, neither for dates with nor without time of day.
2020-04-25-04:00
This is perfectly valid ISO 8601 for April 25 this year at offset -04:00. So you may use it for representing the interval from 2020-04-25T00:00-04:00 (inclusive) to 2020-04-26T00:00-04:00 (exclusive). Which would then be equivalent to 2020-04-25T04:00Z to 2020-04-26T04:00Z (Z meaning UTC).
Java example code
I don’t know any Swift, so cannot tell you how to format or parse such a string in Swift. In Java formatting it is not bad. Example:
LocalDate date = LocalDate.of(2020, Month.APRIL, 25);
String isoOffsetDateString = date
.atStartOfDay(ZoneId.of("America/New_York"))
.format(DateTimeFormatter.ISO_OFFSET_DATE);
System.out.println(isoOffsetDateString);
Output:
2020-04-25-04:00
I am using Java’s built-in ISO_OFFSET_DATE formatter. The documentation informs us that this formnatter is:
The ISO date formatter that formats or parses a date with an offset,
such as '2011-12-03+01:00'.
Parsing the string and producing the start and end of the day takes a little more:
TemporalAccessor parsed
= DateTimeFormatter.ISO_OFFSET_DATE.parse(isoOffsetDateString);
Instant start = LocalDate.from(parsed)
.atStartOfDay(ZoneOffset.from(parsed))
.toInstant();
Instant end = start.plus(1, ChronoUnit.DAYS);
System.out.println("From " + start + " inclusive to " + end + " exclusive");
From 2020-04-25T04:00:00Z inclusive to 2020-04-26T04:00:00Z exclusive
I have opted to convert to Instant, the class for a moment in time independent of offset or time zone. Instants print in UTC, as the trailing Z on each says. In your Java code you may prefer not to do this conversion or to do a different conversion, all depending on circumstances.
Link
Documentation of DateTimeFormatter.ISO_OFFSET_DATE

Related

PostgreSQL TIMESTAMPTZ is not working with SpringBoot Java Query

The SpringBoot Query returns null while using TIMESTAMPTZ as the Datatype, but the Query works for other Datatypes like TIMESTAMP etc. My Date formats are like, "2022-07-24 10:11:29.452+00".
The DB screenshot is added below.
Also the date type is defined as follows
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "datem")
private Date datem;
The API calls the below code
Date start = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse("2022-07-24 10:11:29.452+00");
Date end = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse("2022-07-24 10:11:49.452+00");
List<MqttMessageParsed> sensor_data = messageParsedRepository.findByCreatedAtBetween(start, end);
The Query function is as follows
#Query("SELECT t FROM MqttMessageParsed t WHERE t.datem BETWEEN :startDate AND :endDate") List<MqttMessageParsed> findByCreatedAtBetween(#Param("startDate")Date start, #Param("endDate")Date end);
The API shoud return the data between the above start and end dates, but it is returning null now. Am i missing something?
Thanks
Avoid legacy classes
You are using terrible date-time classes that were years ago supplanted by the modern java.time classes. Avoid Date, SimpleDateFormat, and Timestamp.
java.time
For a column of a type akin to the SQL standard type TIMESTAMP WITH TIME ZONE, use the class OffsetDateTime in JDBC 4.2 and later.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Writing:
myPreparedStatement.setObject( … , odt);
Hibernate was years ago updated to support java.time. Ditto for Jakarta Persistence, formerly Java Persistence API (JPA).
ISO 8601
I suggest you educate the publisher of your inputs about the value of strict compliance with the ISO 8601 standard for date-time formats.
Replace SPACE in the middle with a T.
Use full offset with both hours and minutes, separated by a COLON character.
So this:
"2022-07-24 10:11:29.452+00"
… should be:
"2022-07-24T10:11:29.452+00:00"
… or alternatively use a Z as the suffix to indicate an offset of zero:
"2022-07-24T10:11:29.452Z"
If you cannot effect that change, then define a custom formatting pattern to parse that non-standard format. Use DateTimeFormatter class, as has been covered many times already on Stack Overflow.
By the way, know that the other data type, TIMESTAMP WITHOUT TIME ZONE cannot be used to record a moment, a specific point on the timeline. This type stores only a date and a time-of-day without the context of a time zone or offset-from-UTC. So, for example, given the date of last January 23rd at 12:00, we cannot know if that meant noon in Tokyo Japan, noon in Toulouse France, or noon in Toledo Ohio US — three different moments several hours apart.
If you need to track when something happened, always use TIMESTAMP WITH TIME ZONE. In the case of Postgres, any time zone or offset info supplied with an input is used to adjust to UTC (an offset of zero) and then discarded. The moment is always stored in UTC, in Postgres for this type. If you care about the original time zone, store that in a second column.

How to add TIMEZONE to VS Code User Defined Snippets?

I see the time/date variables available to VS Code User Defined Snippets are:
CURRENT_YEAR The current year
CURRENT_YEAR_SHORT The current year's last two digits
CURRENT_MONTH The month as two digits (example '02')
CURRENT_MONTH_NAME The full name of the month (example 'July')
CURRENT_MONTH_NAME_SHORT The short name of the month (example 'Jul')
CURRENT_DATE The day of the month as two digits (example '08')
CURRENT_DAY_NAME The name of day (example 'Monday')
CURRENT_DAY_NAME_SHORT The short name of the day (example 'Mon')
CURRENT_HOUR The current hour in 24-hour clock format
CURRENT_MINUTE The current minute as two digits
CURRENT_SECOND The current second as two digits
CURRENT_SECONDS_UNIX The number of seconds since the Unix epoch
Unfortunately, without TIMEZONE or TIMEZONE OFFSET from UTC, you can't automatically create an accurate, time-aware timestamp using these variables.
Example:
${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}T${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}
Evaluates to: 2022-06-03T07:32:09, but thats not accurate enough. Without a TIMEZONE identifier, that "timestamp" is +/- 23:59:59 reflection of "actual time".
I need:
2022-06-03T07:32:09-07:00 and I want the timezone offset to adjust for daylight savings time to 2022-11-04T07:32:09-08:00 as appropriate.
Must be human readable. Not going to use Unix time.
How can I do this without spinning up a whole Extension?
I came here looking for the same thing. Its not a snippet, but I use the Insert Date String extension to handle this. You can define the default format as iso, and it comes with a key binding of ctrl-shift-alt-I. Works well enough, but I too would prefer a snippet.

What is the standard for encoding a date as a timestamp?

Is there a standard for encoding a date as a timestamp? My thoughts:
This should be 12:00pm UTC in local time, eg 9:00am at T-3, therefore anyone consuming the timestamp, regardless of their -12/+12 offset, will recognize the same date, regardless of whether they parse at the UTC timezone
It could be 12:00pm at UTC
It could be the start of the day (12:00am) at UTC
It could be start of the day (12:00am UTC) in local time eg 9:00pm at T-3
Is there an official spec or standard to adhere to?
It would be easy to point to this document and say 'this is the standard' as opposed to being unaware and having to change our logic down the line.
There isn't a standard for this, because a date and a timestamp are logically two very different concepts.
A date covers the entire range of time on that day, not a specific point in time.
It may be a different date for a person in another time zone at any given point in time, but dates themselves do not have any association with time zones. Visualize a date as just a square on a calendar, not a point on a timeline.
Many APIs will use midnight (00:00) as the default time when a date-only value is assigned to a date+time value. However:
Whether it is UTC based or local-time based is very dependent on that particular API. There is no standard for this, nor is one answer necessarily better than the other.
Assigning a local-time midnight can be problematic for time zones with transitions near midnight. For example, in Santiago, Chile on 2019-09-08, the day started at 01:00 due to the start of DST. There was no 00:00 on that day.
Also, you tagged your question with momentjs. Since a Moment object is basically a timestamp (not a date), then Moment.js will generally assign the start of the day if provided a date-only value. The time zone involved is key to deciding which moment that actually is, which illustrates my prior points.
For example:
// Parsing as UTC
moment.utc('2019-09-08').format() //=> "2019-09-08T00:00:00Z"
// Parsing as Local Time (my local time zone is US Pacific Time)
moment('2019-09-08').format() //=> "2019-09-08T00:00:00-07:00"
// Parsing in a specific time zone (on a day without midnight)
moment.tz('2019-09-08', 'America/Santiago').format() //=> "2019-09-08T01:00:00-03:00"
Also keep in mind that sometimes APIs can be misnamed. The JavaScript Date object is not a date-only value, but actually a timestamp, just like a moment.

NSDate format explanation

2015-03-04T5:06:07.000+0000
I have access to date formats as above, and intend to compare and sort them in ascending order. This is OK when I use a simpler date format in the Playground, but the actual json data bemuses me as I cannot find out what 'T5' refers to, even after looking through the relevant Unicode page.
I had assumed it was a timezone reference, but this would be Z, and I don't think it would be after the day anyway.
"T" is just a separator between the date and time in ISO-8601. It means the strings following it is a Time.
The timezone part is "+0000" i.e. UTC.
So your date format just mean "2015 March 4th, 05:06:07am, at timezone UTC±0".
The letter "Z" means UTC (i.e. Zulu time) and only appears at the end of the string i.e. 2015-03-04T05:06:07.000Z

Handling time zones in Cocoa

I just want to clarify if I am understanding how dates & time zones work.
Basically, I have a date string #"2008-07-06 12:08:49" that I want to convert to an NSDate. I want this date and time to be in whatever the current user's time zone is set in. So if they are in GMT or HST, it's still 12:08:49.
If I have date in unix form 1215382129 (UTC) and my time zone is set to London (GMT), the outputted date from NSLog() is:
2008-07-06 12:08:49 +0100
If I then change my time zone to Hawaii (HST) and output the same date, I get:
2008-07-06 12:08:49 -1000
This seems to work fine, but I was under the impression to get the time in Hawaiian, I'd have to physically add the time difference (-10hrs) to the unix time stamp. Is this not required then?
Does that mean, whatever date and time a unix time is pointing to, it always points to the same date and time in whatever time zone a user is in?
Hope this makes sense!
Edit
I've just realised (thanks to Kevin Conner!) that in fact NSDateFormatter is creating different unix timestamps for that date string depending on the current timezone! So I was totally wrong!! :-)
Disclaimer, I'm mostly a Java guy. But Cocoa seems to work like the Java library in this regard: Dates are zoneless timestamps. Time zones are in the domain of formatting dates for display. In other words, the internal format doesn't consider time zones, it's all in UTC. Time zones are relatively a convenience for humans, so they are in the display/parsing side.
I noticed there is a setTimeZone: method on NSDateFormatter. Try calling that on your formatter before performing the format.