If I use AMAZON.DATE as a slot type, the user is able to input all kind of dates. Stating from the documentation:
"2015-12", "2017-WI" (for winter of 2017) or "2015-W48-WE" (for weekend of week 48 of 2015). I want to parse these dates into Java dates/durations and I wonder how to achieve this.
Is there a Java library that takes a date like "2015-12" and returns two dates or a date and a duration?
I can see a lot of potential problems like time zone handling or ambiguous dates, but I hope this is a common problem.
I could write a parser to save it as two Dates, one for the beginning of the month and one for the end of the month. Or a date for the beginning and a duration, but this seems like a lot of overhead to comply with ISO 8601.
Amazon's Calendar Reader skill sample contains a JavaScript AMAZON.DATE parser that should be fairly easy to convert to other languages. It takes the slot value and returns a JavaScript Date object. According to the comments, it handles all of the idiomatic date formats.
// Utterances that map to the weekend for a specific week (such as 'this weekend') convert to a date indicating the week number and weekend: 2015-W49-WE.
// Utterances that map to a month, but not a specific day (such as 'next month', or 'December') convert to a date with just the year and month: 2015-12.
// Utterances that map to a year (such as 'next year') convert to a date containing just the year: 2016.
// Utterances that map to a decade convert to a date indicating the decade: 201X.
// Utterances that map to a season (such as 'next winter') convert to a date with the year and a season indicator: winter: WI, spring: SP, summer: SU, fall: FA)
You can use The Amazon date parser in Nodejs for converting it.
The amazon-date-parser is an useful npm package (more info here). It converts the AMAZON.DATE slot value to a JS object composed by a startDate and an endDate i.e.:
var AmazonDateParser = require('amazon-date-parser');
var date = new AmazonDateParser('2017-W48');
console.log(date);
Related
So I have an object ('Task') that has an attribute 'Start Date' which is basically a Timestamp object. So this date is in this format 'YYYY/MM/dd hh:mm:ss:ms'. But for a test case I am authoring, I need this date to be in this format 'YYYY/MM/dd hh:mm'. Also it needs to be a timestamp object as I have to set this value back to the 'Task' object.
I have tried several approaches including the snippet shown below:
SimpleDateFormat formatter = new SimpleDateFormat("YYYY-MM-dd hh:mm");
if (task.getStartDate() != null) {
String newDate = formatter.format(task.getStartDate());
Date date = formatter.parse(newDate);
task.setStartDate(new Timestamp(date.getTime()));
}
I expected the value of the timestamp to be in the format '2018-12-30 09:54' but it resulted in '2018-12-30 09:54:00.0'. So the questions that I have in mind is:
Is there a way to not consider the seconds and millis in the Timestamp object?
If no, then, is the snippet provided an efficient way to update the Timestamp object?
TL;DR
Avoid the Timestamp class if you can. It’s poorly designed and long outdated.
To answer your questions, no, a Timestamp hasn’t got, as in cannot have a format (the same holds true for its modern replacement, Instant (or LocalDateTime)).
Under all circumstances avoid SimpleDateFormat and Date. The former in particular is notoriously troublesome, and both are long outdated too.
Don’t put a format into your model class
You should not want an Instant nor a Timestamp with a specific format. Good practice in all but the simplest throw-away programs is to keep your user interface apart from your model and your business logic. The value of the Instant object belongs in your model, so keep your Instant or Timestamp there and never let the user see it directly. I hope that it’s clear to you that 2018-12-30 09:54 and 2018-12-30 09:54:00.0 represent the same value, the same Timestamp. Just like 17, 0017 and 0x11 represent the same integer value. When you adhere to what I said, it will never matter which format the Instant has got.
Whenever the user should see the date and time, this happens in the UI, not in the model. Format it into a String and show the string to the user. Similarly if you need a specific format for persistence or exchange with another system, format the Instant into a string for that purpose.
java.time and JDBC 4.2
Also for exchange with your database over JDBC, provided that you’ve got a JDBC 4.2 compliant driver, prefer to use a type from java.time over Timestamp. If the datatype on the database side is timestamp with time zone, very clearly recommended for a timestamp, pass an OffsetDateTime like
OffsetDateTime dateTime = yourInstant.atOffset(ZoneOffset.UTC);
yourPreparedStatement.setObject(4, dateTime);
Use setObject, not setTimestamp. Some drivers accept the Instant directly, without conversion to OffsetDateTime. If on the database side you need a mere timestamp (without time zone), use LocalDateTime in Java instead and pass one to setObject in the same way as above.
PS There are errors in your format pattern string
In a format pattern string, uppercase YYYY is for week based year and only useful with a week number. For year use either uuuu or lowercase yyyy. Similarly lowercase hh is for hour within AM or PM from 01 through 12 and only useful with an AM or PM marker. For hour of day from 00 through 23 you need uppercase HH. These errors will give you incorrect dates and times in most cases. Using the wrong case of format pattern letters is a very common mistake. SimpleDateFormat generally doesn’t mind, it just gives incorrect results. The modern DateTimeFormatter does a somewhat better job of notifying you of such errors.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related questions
Formatting timestamp in Java about getting rid of the .0 decimal on the second of a Timestamp.
timestamp formatting in scala [duplicate] about getting a Timestamp with only date and hour (no minute, second or fraction of second).
java parsing string to date about uppercase Y for year in a format pattern string.
Comparing two times in android about lowercase h for hour of day in a format pattern string.
See SO 15728744 and SO 51058875.
How can the following be correct:
LocalDate gregorianDate = LocalDate.parse(gregorianString, dateFormatter);
HijrahDate islamicDate = HijrahDate.from(gregorianDate);
I thought that the Hijri day ends at sunset, not at midnight, so a gregorianDateTime should have been required.
You are right that the Hijri calendar starts days at sunset on previous western day. You are also right to say that standard Java does not support it.
Why?
I have searched in old Threeten-archives but found nothing. However, the main developer and architect of java.time-API has once stated in the documentation of the ancestor Joda-Time following sentence:
This implementation defines a day as midnight to midnight exactly as
per the ISO chronology. This correct start of day is at sunset on the
previous day, however this cannot readily be modelled and has been
ignored.
My speculation is: The same motivation was also responsible why java.time does not take into account the time of day in conversions to or from islamic calendar. It is difficult to implement mainly because it requires astronomical calculations.
How to handle this deficiency?
In the scope of standard Java, users are advised to ignore the time of day as much as possible. So please keep in mind that such conversions are time-free abstractions. Of course, if you want to determine the CURRENT day (which indirectly involves the time of day and civil time zone) then such conversions are more or less faulty!
If you don't want to ignore sunset as start of day...
... then you can use my library Time4J (and I don't know any other lib which can handle sunset as start of day). Watch out the documentation of class HijriCalendar. Example for a generic conversion from a moment/instant to a Hijri calendar date:
// the geographic location
SolarTime meccaTime = SolarTime.ofLocation(21.4225, 39.826111);
// or even simple: meccaTime = SolarTime.ofMecca()
// the moment to be converted
Moment now = SystemClock.currentTime();
// alternative using an explicit gregorian date:
// now = PlainDate.of(2019, 5, 26).atTime(18, 45).inStdTimezone();
// alternative using modern Java:
// now = Moment.from(Instant.now());
// alternative using outdated old API:
// java.util.Date instant = new java.util.Date();
// now = TemporalType.JAVA_UTIL_DATE.translate(instant);
// the conversion
HijriCalendar hcal = now.toGeneralTimestamp(
HijriCalendar.family(),
HijriCalendar.VARIANT_UMALQURA, // attention: there is no single islamic calendar
Timezone.ofSystem().getID(), // or use something like: ()-> "Europe/London"
StartOfDay.definedBy(meccaTime.sunset()));
Time4J also contains a format engine which is capable of parsing either gregorian dates or Hijri calendar date-times in many ways. The start of day at sunset can be taken into account also during formatting and parsing based on ChronoFormatter.
The reverse conversion from a Hijri calendar date to a moment is also supported. Example:
HijriCalendar hcal = ...;
Moment m =
hcal.atTime(18, 45).in(Timezone.ofSystem(), StartOfDay.definedBy(meccaTime.sunset()));
It is mentioned in Java docs that LocalDate class does not consider time.
Refer : https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
This class does not store or represent a time or
time-zone. Instead, it is a description of the date, as used for
birthdays.
Consider using LocalDateTime for your requirement.
I am trying to parse a date into an appropriate format, but I keep getting the error
Unparseable date
Can anyone tell me what the mistake is?
try {
System.out.println(new SimpleDateFormat("d-MMM-Y").parse("05-03-2018").toString());
} catch (ParseException e) {
e.printStackTrace();
}
I want the date to have this format:
05-Mar-18
Since you want to change the format, first read and parse the date (from String) of your own format in a Date type object. Then use that date object by formatting it into a new (desired) format using a SimpleDateFormat.
The error in your code is with the MMM and Y. MMM is the month in string while your input is a numeric value. Plus the Y in your SimpleDateFormat is an invalid year. yy is what needs to be added.
So here is a code that would fix your problem.
SimpleDateFormat dateFormat = new SimpleDateFormat("d-MM-yyyy");
Date date = dateFormat.parse("05-03-2018");
dateFormat = new SimpleDateFormat("dd-MMM-yy");
System.out.println(dateFormat.format(date));
I hope this is what you're looking for.
There are some concepts about dates you should be aware of.
There's a difference between a date and a text that represents a date.
Example: today's date is March 9th 2018. That date is just a concept, an idea of "a specific point in our calendar system".
The same date, though, can be represented in many formats. It can be "graphical", in the form of a circle around a number in a piece of paper with lots of other numbers in some specific order, or it can be in plain text, such as:
09/03/2018 (day/month/year)
03/09/2018 (monty/day/year)
2018-03-09 (ISO8601 format)
March, 9th 2018
9 de março de 2018 (in Portuguese)
2018年3月5日 (in Japanese)
and so on...
Note that the text representations are different, but all of them represent the same date (the same value).
With that in mind, let's see how Java works with these concepts.
a text is represented by a String. This class contains a sequence of characters, nothing more. These characters can represent anything; in this case, it's a date
a date was initially represented by java.util.Date, and then by java.util.Calendar, but those classes are full of problems and you should avoid them if possible. Today we have a better API for that.
With the java.time API (or the respective backport for versions lower than 8), you have easier and more reliable tools to deal with dates.
In your case, you have a String (a text representing a date) and you want to convert it to another format. You must do it in 2 steps:
convert the String to some date-type (transform the text to numerical day/month/year values) - that's called parsing
convert this date-type value to some format (transform the numerical values to text in a specific format) - that's called formatting
For step 1, you can use a LocalDate, a type that represents a date (day, month and year, without hours and without timezone), because that's what your input is:
String input = "05-03-2018";
DateTimeFormatter inputParser = DateTimeFormatter.ofPattern("dd-MM-yyyy");
// parse the input
LocalDate date = LocalDate.parse(input, inputParser);
That's more reliable than SimpleDateFormat because it solves lots of strange bugs and problems of the old API.
Now that we have our LocalDate object, we can do step 2:
// convert to another format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MMM-yy", Locale.ENGLISH);
String output = date.format(formatter);
Note that I used a java.util.Locale. That's because the output you want has a month name in English, and if you don't specify a locale, it'll use the JVM's default (and who guarantees it'll always be English? it's better to tell the API which language you're using instead of relying on the default configs, because those can be changed anytime, even by other applications running in the same JVM).
And how do I know which letters must be used in DateTimeFormatter? Well, I've just read the javadoc. Many developers ignore the documentation, but we must create the habit to check it, specially the javadoc, that tells you things like the difference between uppercase Y and lowercase y in SimpleDateFormat.
I store dates on server side in UTC timezone.
When client (browser) wants to pass some date to server it sends date like
"Tue Jan 03 2012 16:50:32 GMT+0400 (Russian Standard Time)"
Is this format standard across all browsers?
If 1. is false, how can I redefine the Date format function? Is this a good practice?
How can I convert JS Dates to a UTC java.util.Date with java.text.SimpleDateFormat?
UPDATE
I thought that passing date as formatted string (with timezone part) will cause less headache since I shouldn't bother to convert dates to UTC on client side. So I avoid any date conversions in JS code.
You should send the number of milliseconds since epoch (1 Jan 1970 UTC) that is available via the + prefix operator as in +new Date(2012, 0, 1).
Sending anything with a timezone requires both machines to have the same timezone definitions which means you will likely end up with subtle bugs where two dates occurred in one order on one machine but in a different order on another. You can eliminate that whole class of bugs by using the millis since epoch representation.
To answer your questions:
Is this format standard across all browsers?
Date.prototype.toString and toUTCString are both implementation dependent but toISOString is reliable.
http://es5.github.com/#x15.9.5.43
15.9.5.43 Date.prototype.toISOString ( ) # Ⓣ Ⓡ
This function returns a String value represent the instance in time represented by this Date object. The format of the String is the Date Time string format defined in 15.9.1.15. All fields are present in the String. The time zone is always UTC, denoted by the suffix Z. If the time value of this object is not a finite Number a RangeError exception is thrown.
15.9.1.15 Date Time String Format # Ⓣ Ⓔ Ⓑ
ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ
Whereas http://es5.github.com/#x15.9.5.2 says
15.9.5.2 Date.prototype.toString ( ) # Ⓣ Ⓡ
This function returns a String value. The contents of the String are implementation-dependent, but are intended to represent the Date in the current time zone in a convenient, human-readable form.
http://es5.github.com/#x15.9.1.15
15.9.5.42 Date.prototype.toUTCString ( ) # Ⓣ Ⓡ
This function returns a String value. The contents of the String are implementation-dependent, but are intended to represent the Date in a convenient, human-readable form in UTC.
NOTE The intent is to produce a String representation of a date that is more readable than the format specified in 15.9.1.15. It is not essential that the chosen format be unambiguous or easily machine parsable. If an implementation does not have a preferred human-readable format it is recommended to use the format defined in 15.9.1.15 but with a space rather than a “T” used to separate the date and time elements.
I don't know whether this format is standardized for all browsers.
But you could use the getTime() function in JavaScript which returns the milliseconds since 1 January 1970 00:00:00 UTC and initializes the Java Date object with this value.
You could send the data in UNIX time format, maybe?
So me and my partner have been working on this project for a while now. We work with dates A LOT in this project, and we recently noticed an issue, and we are rather deep in at this point.
We store our times in SQLlite (Android project) as a formatted string, since a lot of the time they are directly bound to listviews and such.
The problem we noticed, which i found kind of odd, is that that SimpleDateTimeFormat object, when used to format to 24h time (its a medical based project, so 24h time is the convention here) 12:00am-12:59am are formatted to 24:00-24:59, instead of 00:00-00:59...
This isn't too much of an issue until we query the database and order the results by the dates, any data that is between 12:00am and 12:59am will show up at the end of the list, but it should show up at the beginning...
Anyone else encountered this problem? or know a way around it? The best thing possible would be a way to store the data as 00:00 not 24:00.
Cheers
I strongly suspect you're using the wrong pattern. We've got to guess as you haven't posted any code (hint, hint), but I suspect you're using a pattern such as
kk:mm:ss
instead of
HH:mm:ss
Sample code:
import java.util.*;
import java.text.*;
public class Test {
public static void main(String[] args) throws Exception {
SimpleDateFormat broken = new SimpleDateFormat("kk:mm:ss");
broken.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
SimpleDateFormat working = new SimpleDateFormat("HH:mm:ss");
working.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
Date epoch = new Date(0);
System.out.println(broken.format(epoch));
System.out.println(working.format(epoch));
}
}
Additionally, as others have pointed out, you shouldn't be storing your values in string format to start with... avoid string conversions wherever you can, as each conversion is a potential pain point.
Please read this and this about how SQLite stores dates (or doesn't store dates). SQLite doesn't have a "Date" type, so it is stored as a string. You should store your date as an integer (milliseconds), and then you can use date and time functions to pull them out (from the first link).
From the documentation
1.2 Date and Time Datatype
SQLite does not have a storage class set aside for storing dates
and/or times. Instead, the built-in Date And Time Functions of SQLite
are capable of storing dates and times as TEXT, REAL, or INTEGER
values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS"). REAL as Julian
day numbers, the number of days since noon in Greenwich on November
24, 4714 B.C. according to the proleptic Gregorian calendar. INTEGER
as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
Applications can chose to store dates and times in any of these
formats and freely convert between formats using the built-in date and
time functions.
I prefer INTEGER / Unix time storage, then use the built in date and time functions to format when pulling from DB.
EDIT: Also, this will take care of sorting. I'm guessing your current "sorting" of the dates in SQLite is string based, which is bad mmmmkay.
What is the format string you are passing to your SimpleDateFormat? According to the docs, using 'H' for the hours should get you 0-23, using 'k' should get you 1-24.