I need a timezone display values as follows :
(UTC + 05:30) Chennai, Kolkata, Mumbai, New Delhi
But by using following method I am getting bit different output. How should I get the timezone display name as above ? (if required, I can use JODA).
public class TimeZoneUtil {
private static final String TIMEZONE_ID_PREFIXES =
"^(Africa|America|Asia|Atlantic|Australia|Europe|Indian|Pacific)/.*";
private static List<TimeZone> timeZones;
public static List<TimeZone> getTimeZones() {
if (timeZones == null) {
timeZones = new ArrayList<TimeZone>();
final String[] timeZoneIds = TimeZone.getAvailableIDs();
for (final String id : timeZoneIds) {
if (id.matches(TIMEZONE_ID_PREFIXES)) {
timeZones.add(TimeZone.getTimeZone(id));
}
}
Collections.sort(timeZones, new Comparator<TimeZone>() {
public int compare(final TimeZone t1, final TimeZone t2) {
return t1.getID().compareTo(t2.getID());
}
});
}
return timeZones;
}
public static String getName(TimeZone timeZone) {
return timeZone.getID().replaceAll("_", " ") + " - " + timeZone.getDisplayName();
}
public static void main(String[] args) {
timeZones = getTimeZones();
for (TimeZone timeZone : timeZones) {
System.out.println(getName(timeZone));
}
}
}
This code may do the trick for you:
public static void main(String[] args) {
for (String timeZoneId: TimeZone.getAvailableIDs()) {
TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
// Filter out timezone IDs such as "GMT+3"; more thorough filtering is required though
if (!timeZoneId.matches(".*/.*")) {
continue;
}
String region = timeZoneId.replaceAll(".*/", "").replaceAll("_", " ");
int hours = Math.abs(timeZone.getRawOffset()) / 3600000;
int minutes = Math.abs(timeZone.getRawOffset() / 60000) % 60;
String sign = timeZone.getRawOffset() >= 0 ? "+" : "-";
String timeZonePretty = String.format("(UTC %s %02d:%02d) %s", sign, hours, minutes, region);
System.out.println(timeZonePretty);
}
}
The output looks like this:
(UTC + 09:00) Tokyo
There are, however, a few caveats:
I only filter out timezones whose ID matches the format "continent/region" (e.g. "America/New_York"). You would have to do a more thorough filtering process to get rid of outputs such as (UTC - 08:00) GMT+8 though.
You should read the documentation for TimeZone.getRawOffSet() and understand what it's doing. For example, it doesn't DST effects into consideration.
On the whole, you should know that this is a messy approach, primarily because the timezone ID can be of so many different formats. Maybe you could restrict yourself down to the timezones that matter for your application, and just have a key value mapping of timezone IDs to display names?
Related
There are two methods of same class .I want to compare two variables value by using assertion .
here are the two methods
//Method 1
public void Edit_window() throws InterruptedException {
collection_title.clear();
// driver.wait(2000);
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
Date date = new Date();
collection_title.sendKeys(dateFormat.format(date));
}
//Method 2
public void name_collection()
{
String updated_title=col_title.getText() ;
System.out.println("the title eof the collectio is " + updated_title) ;
System.out.println("the value is" + date) ;
}
So if you can see there is one variable "date " and it contains the current date. I want to that variable value and compare with the variable value "updated_title" which is defined in the method 2 . Any input please !
You can basically change the return type of the methods to String type and then add assertion. Sample below:
public String Edit_window() throws InterruptedException {
collection_title.clear();
// driver.wait(2000);
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
Date date = new Date();
collection_title.sendKeys(dateFormat.format(date));
return dateFormat.format(date);
}
public String name_collection()
{
String updated_title=col_title.getText() ;
System.out.println("the title eof the collectio is " + updated_title) ;
System.out.println("the value is" + date) ;
return date;
}
//And then for assertion you can basically: Assert.asserEquals(classObject.Edit_window(), classObject.name_collection);
Hope the solution provided by the Mrunal is works for you. I will suggest you to create a public class and store the Reusable values such as id, email, autogenerated numbers etc. So that you should be able to access anywhere from your framework.
we can't say that always we will only do the comparison in the same class. So please try to create the class as below and use it to store the values.
public class Repovalues {
private string id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
I created method for parsing a view different date formats during data import (400 K records). My method catches ParseException and trying to parse date with next format when it's different.
Question: Is better way(and faster) to set correct date format during data import?
private static final String DMY_DASH_FORMAT = "dd-MM-yyyy";
private static final String DMY_DOT_FORMAT = "dd.MM.yyyy";
private static final String YMD_DASH_FORMAT = "yyyy-MM-dd";
private static final String YMD_DOT_FORMAT = "yyyy.MM.dd";
private static final String SIMPLE_YEAR_FORMAT = "yyyy";
private final List<String> dateFormats = Arrays.asList(YMD_DASH_FORMAT, DMY_DASH_FORMAT,
DMY_DOT_FORMAT, YMD_DOT_FORMAT);
private Date parseDateFromString(String date) throws ParseException {
if (date.equals("0")) {
return null;
}
if (date.length() == 4) {
SimpleDateFormat simpleDF = new SimpleDateFormat(SIMPLE_YEAR_FORMAT);
simpleDF.setLenient(false);
return new Date(simpleDF.parse(date).getTime());
}
for (String format : dateFormats) {
SimpleDateFormat simpleDF = new SimpleDateFormat(format);
try {
return new Date(simpleDF.parse(date).getTime());
} catch (ParseException exception) {
}
}
throw new ParseException("Unknown date format", 0);
}
If you're running single threaded, an obvious improvement is to create the SimpleDateFormat objects only once. In a multithreaded situation using ThreadLocal<SimpleDateFormat> would be required.
Also fix your exception handling. It looks like it's written by someone who shouldn't be trusted to import any data.
For a similar problem statememt , i had used time4j library in the past. Here is an example. This uses the following dependencies given below as well
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import net.time4j.PlainDate;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.MultiFormatParser;
import net.time4j.format.expert.ParseLog;
import net.time4j.format.expert.PatternType;
public class MultiDateParser {
static final MultiFormatParser<PlainDate> MULTI_FORMAT_PARSER;
static {
ChronoFormatter<PlainDate> style1 = ChronoFormatter.ofDatePattern("dd-MM-yyyy", PatternType.CLDR,
Locale.GERMAN);
ChronoFormatter<PlainDate> style2 = ChronoFormatter.ofDatePattern("dd.MM.yyyy", PatternType.CLDR, Locale.US);
ChronoFormatter<PlainDate> style3 = ChronoFormatter.ofDatePattern("yyyy-MM-dd", PatternType.CLDR, Locale.US);
ChronoFormatter<PlainDate> style4 = ChronoFormatter.ofDatePattern("yyyy.MM.dd", PatternType.CLDR, Locale.US);
//this is not supported
//ChronoFormatter<PlainDate> style5 = ChronoFormatter.ofDatePattern("yyyy", PatternType.CLDR, Locale.US);
MULTI_FORMAT_PARSER = MultiFormatParser.of(style1, style2, style3, style4);
}
public List<PlainDate> parse() throws ParseException {
String[] input = { "11-09-2001", "09.11.2001", "2011-11-01", "2011.11.01", "2012" };
List<PlainDate> dates = new ArrayList<>();
ParseLog plog = new ParseLog();
for (String s : input) {
plog.reset(); // initialization
PlainDate date = MULTI_FORMAT_PARSER.parse(s, plog);
if (date == null || plog.isError()) {
System.out.println("Wrong entry found: " + s + " at position " + dates.size() + ", error-message="
+ plog.getErrorMessage());
} else {
dates.add(date);
}
}
System.out.println(dates);
return dates;
}
public static void main(String[] args) throws ParseException {
MultiDateParser mdp = new MultiDateParser();
mdp.parse();
}
}
<dependency>
<groupId>net.time4j</groupId>
<artifactId>time4j-core</artifactId>
<version>4.19</version>
</dependency>
<dependency>
<groupId>net.time4j</groupId>
<artifactId>time4j-misc</artifactId>
<version>4.19</version>
</dependency>
The case yyyy will have to be handled differently as it is not a date. May be similar logic that you have used (length ==4) is a choice.
The above code returns , you can check a quick perf run to see if this scales for the 400k records you have.
Wrong entry found: 2012 at position 4, error-message=Not matched by any format: 2012
[2001-09-11, 2001-11-09, 2011-11-01, 2011-11-01]
Talking about 400K records, it might be reasonable to do some "bare hands" optimization here.
For example: if your incoming string has a "-" on position 5, then you know that the only (potentially) matching format would be "yyyy-MM-dd". If it is "."; you know that it is the other format that starts yyyy.
So, if you really want to optimize, you could fetch that character and see what it is. Could save 3 attempts of parsing with the wrong format!
Beyond that: I am not sure if sure if "dd" means that your other dates start with "01" ... or if "1.1.2016" would be possible, too. If all your dates always use two digits for dd/mm; then you can repeat that game - as you would fetch on position 3 - to choose between "dd...." and "dd-....".
Of course; there is one disadvantage - if you follow that idea, you are very much "hard-coding" the expected formats into your code; so adding other formats will become harder. On the other hand; you would save a lot.
Finally: the other thing that might greatly speed up things would be to use stream operations for reading/parsing that information; because then you could look into parallel streams, and simply exploit the ability of modern hardware to process 4, 8, 16 dates in parallel.
I am trying to understand the need of using ThreadLocal. Lot of people mention ThreadLocal should be used to provide a Per-Thread SimpleDateFormat, but they do not mention how will a mangled SimpleDateFormat will be seen if ThreadLocal is not used. I try the following code, seems it is just fine, I don't see a mangled SimpleDateFormat.
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadLocalTest {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
private static final Date TODAY = new Date();
private static final String expected = "07/09/2016";
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
#Override
public void run() {
for (int j = 0; j < 1000; j++) {
String real = dateFormat.format(TODAY);
if (!real.equals(expected)) {
throw new RuntimeException("Mangled SimpleDateFormat");
}
}
}
}).start();
}
}
}
How can I produce a exception like NumberFormatException because I don't use a ThreadLocal ?
The crucial point is: the SimpleDateFormat implementation is not thread safe.
That doesn't mean that it will throw an exception.
It is worse: maybe, occasionally the shared formatter will simply give you wrong output!
You know, if "multi-threading issues" would nicely throw exceptions at you ... people would be much less afraid of them. Because we would have a direct hint that something went wrong.
Instead, things go wrong - and unnoticed.
Suggestion: enhance your test to
always format the same Date object
check that the result of formatting that Date is as expected (for example by comparing it against the result of an initial, first formatting operation)
And of course: only print mismatches, so that notice when they happen. Or better: throw your own exception on mismatch!
EDIT: turns out that the "better" way to enforce inconsistencies is to not use formatting but parsing!
Finally, to address another comment: of course, inconsistencies can only occur for objects that are shared between multiple threads. When each thread has its own format object, than there is no sharing; thus no problem.
Just run these code, you will get "java.lang.NumberFormatException". If not occur, run a few more times
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class ThreadLocalDemo1 implements Runnable {
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
ThreadLocalDemo1 td = new ThreadLocalDemo1();
Thread t1 = new Thread(td, "Thread-1");
Thread t2 = new Thread(td, "Thread-2");
t1.start();
t2.start();
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread run execution started for " + Thread.currentThread().getName());
System.out.println("Date formatter pattern is " + simpleDateFormat.toPattern());
try {
System.out.println("Formatted date is " + simpleDateFormat.parse("2013-05-24 06:02:20"));
} catch (ParseException pe) {
pe.printStackTrace();
}
System.out.println("=========================================================");
}
}
}
Date formats are not thread safe.
I think if you format the same day you can't reproduce, you should use 2 different dates or format also second and dates that have different seconds etc. Date format uses a Calendar under the hood on which it sets the date. If the first thread sets a date and start formatting the string and another thread with a different date comes and sets it on the same calendar, you will get wrong output.
Following code produces an exception/error:
final Date today = new Date();
String expectedToday = dateFormat.format(today);
Date yesterday = new Date(today.getTime() - TimeUnit.DAYS.toMillis(1));
String expectedYesterday = dateFormat.format(yesterday);
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
String format = dateFormat.format(today);
if (!expectedToday.equals(format)) {
System.out.println("error: " + format + " " + expectedToday);//Throw exception if you want
}
format = dateFormat.format(yesterday);
if (!expectedYesterday.equals(format)) {
System.out.println("error: " + format + " " + expectedYesterday);//Throw exception if you want
}
}
}
}).start();
}
One of the ways that SimpleDateFormat is not thread safe is that it has an internal calendar field, which holds a Calendar object. Pretty much the first thing that SimpleDateFormat does before actually formatting the date is call this.calendar.setTime(theDateYouPassedIn), no synchronization or locks. I'm not sure if this is the only way, but it should be fairly straightforward to inspect the code.
So, one way to get SimpleDateFormat to fail is to use dates that will produce differing output in different threads. Here is an example:
public class NotThreadSafe
{
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
public static void main(String[] args) {
Date dref = new Date();
// Dates for yesterday and tomorrow
Date[] ds = new Date[] {
new Date(dref.getTime() - (24L * 60L * 60L * 1000L)),
new Date(dref.getTime() + (24L * 60L * 60L * 1000L))
};
String[] refs = new String[ds.length];
for (int i = 0; i < ds.length; ++i) {
// How the corresponding ds[i] should be formatted
refs[i] = dateFormat.format(ds[i]);
}
for (int i = 0; i < 100; i++) {
// Every even numbered thread uses ds[0] and refs[0],
// odd numbered threads use ds[1] and refs[1].
int index = (i % 2);
final Date d = ds[index];
final String ref = refs[index];
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
String s = dateFormat.format(d);
if (!ref.equals(s)) {
throw new IllegalStateException("Expected: " + ref + ", got: " + s);
}
}
}
}).start();
}
}
}
As the comments show, every even numbered thread will format yesterday's date, and odd numbered threads will use tomorrows date.
If you run this, threads will pretty much immediately start committing suicide by throwing exceptions until such time as you have only a handful left, likely all formatting the same date.
So I am trying to create a class that represents a fictional Licence Number for a given person. The Licence Number is constructed from the following:
The initials of the person's forename and surname
The year the licence was issued
A random arbitrary serial number
So for example, Maggie Smith whose licence was issued in 1990, might have a Licence Number of MS-1990-11, with 11 being the serial number. However, Mark Sanders might have had his licence issued the same year meaning the beginning of his licence may be MS-1990 also.
So this is where the problem occurs. I need to make sure that the serial number for this person is not the same as Maggie's. So I'd have to check through any records that have the same initials and issue year, then generate a new unique serial number. Here's my code so far:
public class LicenceNumber {
private final Name driverName;
private final Date issueDate;
private static final Map<String, LicenceNumber> LICENCENOS = new HashMap<String, LicenceNumber>();
public LicenceNumber(Name driverName, Date issueDate){
this.driverName = driverName;
this.issueDate = issueDate;
}
public static LicenceNumber getInstance(Name driverName, Date issueDate){
Calendar tempCal = Calendar.getInstance();
tempCal.setTime(issueDate);
String issueYear = String.valueOf(tempCal.get(Calendar.YEAR));
int serialNo = 1;
String k = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0, 1) + "-" + issueYear + "-" + serialNo;
if(!LICENCENOS.containsKey(k)){
LICENCENOS.put(k, new LicenceNumber(driverName,issueDate));
}
return LICENCENOS.get(k);
}
public boolean isUnique(){
return true;
}
public Name getDriverName() {
return driverName;
}
public Date getIssueDate() {
return issueDate;
}
}
And a snippet of how it may be instantiated:
public final class DrivingLicence {
private final Name driverName;
private final Date driverDOB;
private final Date issueDate;
private final LicenceNumber licenceNo;
private final boolean isFull;
public DrivingLicence(Name driverName, Date driverDOB, Date issueDate, boolean isFull){
//TO-DO validate inputs
this.driverName = driverName;
this.driverDOB = driverDOB;
this.issueDate = issueDate;
this.licenceNo = LicenceNumber.getInstance(driverName, issueDate);
//this.licenceNo = new LicenceNumber(driverName, issueDate);//instantiate a licence number using the driverName and dateOfIssue
this.isFull = isFull;
}
}
I've based it on some lecture notes that discuss how you can use factories for uniqueness. I'm also unsure if I should create a LicenceNumber using getInstance or by creating a new Object. Does anyone know a way I could check if a serial number for a given string, e.g. XX-XXXX already exists?
Here is an approach that will create incremental numbers for the duration of the program. Since there is no backing database, each run of the program will reset.
It works by using an AtomicInteger to ensure uniqueness. I have used a ConcurrentMap to take advantage of thread safety as well as the .putIfAbsent method. However, it can be easily converted to use a standard Map. I also just used a String, but a better approach would be to use a real domain object. It is sufficient for handling the OP's question and for illustrative purposes.
// a Map for holding the sequencing
private ConcurrentMap<String, AtomicInteger> _sequence =
new ConcurrentHashMap<>();
/**
* Returns a unique, incrementing sequence, formatted to
* 0 prefixed, 3 places, based upon the User's initials
* and the registration year
*/
public String getSequence(String initials, String year)
{
String key = makePrefix(initials, year);
AtomicInteger chk = new AtomicInteger(0);
AtomicInteger ai = _sequence.putIfAbsent(key, chk);
if (ai == null) {
ai = chk;
}
int val = ai.incrementAndGet();
String fmt = String.format("%03d", val);
return fmt;
}
/**
* A helper method to make the prefix, which is the
* concatintion of the initials, a "-", and a year.
*/
private String makePrefix (String initials, String year)
{
return initials + "-" + year;
}
Test Example:
public static void main(String[] args)
{
LicensePlate_37169055 lp = new LicensePlate_37169055();
System.out.println("ko, 1999: " + lp.getSequence("ko", "1999"));
System.out.println("ac, 1999: " + lp.getSequence("ac", "1999"));
System.out.println("ko, 1999: " + lp.getSequence("ko", "1999"));
System.out.println("ac, 1999: " + lp.getSequence("ac", "1999"));
System.out.println("ms, 1999: " + lp.getSequence("ms", "1999"));
System.out.println("ko, 2001: " + lp.getSequence("ko", "2001"));
}
Example Output:
ko, 1999: 001
ac, 1999: 001
ko, 1999: 002
ac, 1999: 002
ms, 1999: 001
ko, 2001: 001
To integrate into the OP's code, the following modifications are suggestive:
public static LicenceNumber getInstance(Name driverName, Date issueDate){
Calendar tempCal = Calendar.getInstance();
tempCal.setTime(issueDate);
String issueYear = String.valueOf(tempCal.get(Calendar.YEAR));
// ** get the initials; I would actually move this functionality to be
// a method on the Name class
String initials = driverName.getForename().substring(0, 1) + driverName.getSurname().substring(0, 1);
// get the unique serial number
String serial = getSequence(initials, issueYear);
// make the full licenseplate String
String k = makePrefix(initials, issueYear) + "-" + serial;
if(!LICENCENOS.containsKey(k)){
LICENCENOS.put(k, new LicenceNumber(driverName,issueDate));
}
return LICENCENOS.get(k);
}
I have below method in which different date patterns have been handled
below is the method in which different date formats have been handled now
now for the particulat format YYYY-MM-dd i don't want it to go for the check where we are prefixing 20 before in code please advise how can i skip that part lets say if the date pattern is YYYY-MM-dd then avoid the logic of prefixing 20 in front of year
below is my code
public java.util.Date extractDate(String dateStr, String dateType) {
String[] datePatternsOfUk = { "d-M-yy", "d-M-yyyy", "d/M/yy", "d/M/yyyy", "yyyy-MM-dd","dd-MM-yy", "dd-MMM-yy","dd-MMM-yyyy","dd-MM-yyyy",
"dd/MM/yy","dd/MMM/yy","dd/MMM/yyyy"};
String[] datePatternsOfUs = { "M-d-yy","MM-dd-yy","M/d/yy","MM/dd/yy", "MM/dd/yy", "MMM-dd-yy",
"MMM/dd/yy", "MMM-dd-yyyy", "MM-dd-yyyy", "MMM/dd/yyyy",
"MM/dd/yyyy" };
java.util.Date date = null;
String[] datePatterns = datePatternsOfUk;
if (dateType.equals("US")) {
datePatterns = datePatternsOfUs;
} else if (dateType.equals("UK")) {
datePatterns = datePatternsOfUk;
}
///******code should not go in this check where date pattern is YYYY-MM-dd
int p = dateStr.lastIndexOf("/");
if (p == -1) {
p = dateStr.lastIndexOf("-");
}
String firstSubstring = dateStr.substring(0, p + 1);
String secondSubstring = dateStr.substring(p + 1);
if (p != -1 && secondSubstring.length() <= 2) {
secondSubstring = Integer.toString(2000 + Integer.parseInt(secondSubstring));
dateStr = firstSubstring + secondSubstring;
}
///****************************************//
try {
date = DateUtils.parseDate(dateStr, datePatterns);
} catch (ParseException ex) {
logger.error("##$$$$$### Error in invoice inside extractDate method : ##$$$$$$#### "
+ ErrorUtility.getStackTraceForException(ex));
}
return date;
}
You could avoid trying any inappropriate pattern by checking if the string "looks like" the pattern before parsing with the pattern.
The general way to do this is:
String datePattern = "yyyy-MM-dd"; // for example
String input;
if (input.matches(datePattern.replaceAll("\\w", "\\d"))) {
// the input looks like the pattern
// in this example "dddd-dd-dd" where "d" is any digit
// so go ahead and try the parse
}
You can enhance this logic to add:
if (input.matches("\\d\\d\\D.*")) {
// then it only has a two digit year, so add "20" to the front
}
if (!dateStr.equals("YYYY-MM-dd")) {
// code
}