Need timezone based on the ISO country code and city in Java - timezone

My requirement
::: To find the timezone based on the city and country code both. Please share Is there any way to find it? I don't want to go with city only since one city can exist in two or more countries.
And even on city bases - I can find time zone but not for all cities if i am entering Leeds (UK city) , its giving nothing.
Set<String> availableTimeZones = ZoneId.getAvailableZoneIds();
String cityName = Normalizer.normalize(city, Normalizer.Form.NFKD).replaceAll("[^\\p{ASCII}-_ ]", "")
.replace(' ', '_');
List<String> possibleTimeZones = availableTimeZones.stream().filter(zid -> zid.endsWith("/" + cityName))
.collect(Collectors.toList());

Rough way I found to achieve is -
private static String getSourceLocalTimeZone(String countryCode, String city, String sourceLocalTimeZone) {
String[] timeZones = com.ibm.icu.util.TimeZone.getAvailableIDs(countryCode);
for (String timeZone : timeZones) {
String cityFromTimeZone = null;
String[] value = timeZone.split("/");
if (value != null && value.length > 0) {
cityFromTimeZone = value[value.length - 1].replace("_", " ");
}
if (city!=null && city.matches("(.*)" + cityFromTimeZone + "(.*)")) {
sourceLocalTimeZone = timeZone;
break;
}
}
if (sourceLocalTimeZone == null || (sourceLocalTimeZone.isEmpty())) {
if(timeZones.length>0)
sourceLocalTimeZone = timeZones[0];
}
return sourceLocalTimeZone;
}
Dependency -
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>4.6</version>
</dependency>

Related

How to localize an international address especially thoroughfare and subthoroughfare

I'm working on an app that heavily uses addresses in different countries. We have a bunch of ways we input them from getting addresses imported, to dropping pins on map, to reverse geocode our current location.
My current project is to correctly format international address:
In the USA:
18 Street Name
In Norway:
Street Name 18
I've figured out a bunch of ways to instantiate CNMutablePostalAddress with CLPlacemark to get some pretty good results, the problem I'm having is this.
I want just the street name and number returned as a one line string:
So:
street1: placemarker.thoroughfare, (street name)
street2: placemarker.subThoroughfare (street number),
but CNPostalAddress only has one street property, so to use it you need to do something like this:
cNPostalAddress.street = placemarker.subThoroughfare + " " + placemarker.thoroughfare
This will not work for countries like Norway where they are revered.
You can hack it and use the take the first line from the formatted address:
CNPostalAddressFormatter.string(from: placemarker.mailingAddress , style: .mailingAddress)
but that's super hacky and I'm sure it will break with countries that order their mailing address differently like japan.
At the moment I can't even find any resources that tell me which countries reverse subThoroughfare and thoroughfare, because if I had a list like that I could just reverse it manually.
Here is some sample code of what I've managed so far:
static func mulitLineAddress(from placemarker: CLPlacemark, detail: AddressDetail) -> String {
let address = MailingAddress(
street1: placemarker.thoroughfare,
street2: placemarker.subThoroughfare,
city: placemarker.locality,
state: placemarker.administrativeArea,
postalCode: placemarker.postalCode,
countryCode: placemarker.country)
return self.mulitLineAddress(from: address, detail: detail)
}
static func mulitLineAddress(from mailingAddress: MailingAddress, detail: AddressDetail) -> String {
let address = CNMutablePostalAddress()
let street1 = mailingAddress.street1 ?? ""
let street2 = mailingAddress.street2 ?? ""
let streetSpacing = street1.isEmpty && street2.isEmpty ? "" : " "
let streetFull = street1 + streetSpacing + street2
switch detail {
case .street1:
address.street = street1
case .street2:
address.street = street2
case .streetFull:
address.street = streetFull
case .full:
address.country = mailingAddress.countryCode ?? ""
fallthrough
case .withoutCountry:
address.street = streetFull
address.city = mailingAddress.city ?? ""
address.state = mailingAddress.state ?? ""
address.postalCode = mailingAddress.postalCode ?? ""
}
return CNPostalAddressFormatter.string(from: address, style: .mailingAddress)
}
Any ideas? Even resources like list of countries that reverse street1 and street2 would be useful.
I ended up taking a hybrid approach.
For the CLPlacemark to the CNMutablePostalAddress() it's pretty stright forward:
cnPostalAddress.street = placemarker.postalAddress?.street
However this doesn't work any any other input method and can't be modified to a different format from the CNMutablePostalAddress()
When bringing in address info from other sources I needed to do it manually, here is a bit of an example that works with a few countries:
static private func generateLocalizedStreetAddress(from adderss: MailingAddress) -> String {
guard adderss.localizedStreet.isEmpty else { return adderss.localizedStreet ?? "" }
let country = CountryCode.country(for: adderss.countryCode)
let thoroughfare = adderss.thoroughfare ?? ""
let subThoroughfare = adderss.subThoroughfare ?? ""
let delimiter = self.generateDelimiter(from: thoroughfare, and: subThoroughfare, with: country)
switch country {
case .belgium, .czechRepublic, .denmark, .finland, .germany, .latvia, .netherlands, .norway, .poland, .portugal, .sweden:
return thoroughfare + delimiter + subThoroughfare
default:
return subThoroughfare + delimiter + thoroughfare
}
}
static private func generateDelimiter(from thoroughfare: String, and subThoroughfare: String, with country: Country) -> String {
guard !thoroughfare.isEmpty && !subThoroughfare.isEmpty else { return "" }
switch country {
case .spain:
return ", "
default:
return " "
}
}
CNPostalAddressFormatter has an interesting API to get an NSAttributedString where each address component is identified in it. You could use this to pull out just the information you want, like the street which includes the properly localized sub-thoroughfare and thoroughfare no matter where it may exist in the postal address.
let addressFormatter = CNPostalAddressFormatter()
let attributedAddress = addressFormatter.attributedString(from: postalAddress, withDefaultAttributes: [:])
let nsString = attributedAddress.string as NSString
let range = NSRange(location: 0, length: nsString.length)
var street: String?
attributedAddress.enumerateAttributes(in: range, options: []) { result, range, stop in
if let component = result[NSAttributedString.Key(CNPostalAddressPropertyAttribute)] as? String {
if component == CNPostalAddressStreetKey {
street = nsString.substring(with: range)
stop.pointee = true
}
}
}
I've also filed feedback with Apple to add a more powerful and flexible API: FB8648023 Template API desired to format addresses with specified components similar to DateFormatter.setLocalizedDateFormatFromTemplate

Set format of cells produced by LoadFromCollection (epplus) using data annotation

I am using eeplus to create an excel spreadsheet, like this
using (var pck = new ExcelPackage())
{
var ws = pck.Workbook.Worksheets.Add("Customers");
ws.Cells["A1"].LoadFromCollection(customers, PrintHeaders: true);
var ms = new System.IO.MemoryStream();
pck.SaveAs(ms);
ms.WriteTo(Response.OutputStream);
}
The customer class has properties like
[DisplayName("Customer creation date")]
public DateTime Created { get; set; }
DisplayName seems to get honored, so the topmost line will read Customer creation date but the cell contents show up as 43257,41667.
What I would really like to have is cells that has the format 2018-04-05.
Can I do that will data annotations? I tried both
[DisplayName("Customer creation date")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime Created { get; set; }
and
[DisplayName("Customer creation date")]
[DataType(DataType.Date)]
public DateTime Created { get; set; }
but the cell contents remains the same.
No, EPPlus doesnot format your data according to data annotations.
It formats date as integers, so you should specify the column you wish to format as
ws.Column(colPosition+1).Style.Number.Format="yyyy-mm-dd";
You can find details here:
https://github.com/JanKallman/EPPlus/wiki/Formatting-and-styling
https://codereview.stackexchange.com/questions/139569/ensuring-specific-columns-in-an-excelworksheet-format-as-shortdate
EPPlus always changed column name while updating into excel based upon DisplayName Attribute else if there is no DisplayName Attribute is set, then it will Find "_" (underscore) character & replace it with " " (Space) Character in the column name, Due to which we cannot easily find PropertyInfo with help of column name to format the column as per our need.
Here is the simple & quickest solution to format column based upon indexing the PropertyInfo
PropertyInfo[] props = typeof(T).GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < props.Length; i++)
{
Type t = props[i].PropertyType;
if (t == typeof(DateTime) || t == typeof(DateTime?))
ws.Column(i + 1).Style.Numberformat.Format = "dd-MMM-yyyy HH:mm:ss";
else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
ws.Column(i + 1).Style.Numberformat.Format = "HH:mm:ss";
}
I have another solution if you need to format columns based upon column names.
void ApplyDateTimeFormatting<T>(ExcelWorksheet ws, IEnumerable<T> data)
{
if (data.Count() == 0)
return;
Type type = data.First().GetType();
for (int c = 1; c <= toColumns; c++)
{
string column = ws.Cells[1, c].Text;
var t = type.GetPropertyWithDisplayName<T>(column).PropertyType;
if (t == typeof(DateTime) || t == typeof(DateTime?))
ws.Column(c).Style.Numberformat.Format = "dd-MMM-yyyy HH:mm:ss";
else if (t == typeof(TimeSpan) || t == typeof(TimeSpan?))
ws.Column(c).Style.Numberformat.Format = "HH:mm:ss";
}
}
PropertyInfo GetPropertyFromDisplayName(Type type, string DisplayName)
{
MemberInfo[] members = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var member in members)
{
DisplayNameAttribute displayNameAttribute = member
.GetCustomAttributes(typeof(DisplayNameAttribute), inherit: false)
.FirstOrDefault() as DisplayNameAttribute;
string text = ((displayNameAttribute == null) ? member.Name.Replace('_', ' ') :
displayNameAttribute.DisplayName);
if (text == DisplayName)
return type.GetProperty(member.Name);
}
return null;
}
I solved it as follows, so I just load the model and change as per my model if it is int or datetime
var li = typeof(Model).GetProperties().ToArray();
using (var package = new ExcelPackage(stream))
{
var workSheet = package.Workbook.Worksheets.Add("Sheet1");
var i = 0;
foreach (var c in li)
{
i++;
if(c.PropertyType.Name == typeof(DateTime).Name || c.PropertyType.Name == typeof(DateTime?).Name)
workSheet.Column(i).Style.Numberformat.Format = DateTimeFormatInfo.CurrentInfo.ShortDatePattern; ;
if (c.PropertyType.Name == typeof(int).Name || c.PropertyType.Name == typeof(int?).Name)
workSheet.Column(i).Style.Numberformat.Format = "0";
}
}

How can I get originalvalues from EF savechanges for complex primary key

i've implemented audit log for all my db objects but i'm running into an issue because of a special case.
i have code similar to the following.
private IEnumerable<CDMA_CHANGE_LOG> GetAuditRecords(DbEntityEntry ent, string userId, string primaryKeyId, bool updateFlag, object originalEntity)
{
List<CDMA_CHANGE_LOG> result = new List<CDMA_CHANGE_LOG>();
string changeId = Guid.NewGuid().ToString();
TableAttribute tableAttr = ent.Entity.GetType().GetCustomAttributes(typeof(TableAttribute), false).SingleOrDefault() as TableAttribute;
// Get table name (if it has a Table attribute, use that, otherwise get the pluralized name)
string entityName = tableAttr != null ? tableAttr.Name : ObjectContext.GetObjectType(ent.Entity.GetType()).Name;
var changeTime = DateTime.UtcNow;
if (ent.State == EntityState.Modified)
{
foreach (var prop in ent.OriginalValues.PropertyNames)
{
//we cant use this because getdatabasevalues will error out when there are 2 rows in the db for this primarykey (U,A)
//var originalValue = ent.GetDatabaseValues().GetValue<object>(prop) == null ? "" : ent.GetDatabaseValues().GetValue<object>(prop).ToString();
var currentValue = ent.CurrentValues[prop] == null ? "" : ent.CurrentValues[prop].ToString();
// For updates, we only want to capture the columns that actually changed
var primaryKey = GetPrimaryKeyValue(ent);
if (originalValue != currentValue)
{
result.Add(new CDMA_CHANGE_LOG()
{
USERID = userId,
DATECHANGED = changeTime,
EVENTTYPE = "M", // Modified
ENTITYNAME = entityName,
PRIMARYKEYVALUE = primaryKey.ToString(),
PROPERTYNAME = prop,
OLDVALUE = originalValue,
NEWVALUE = currentValue,
CHANGEID = changeId
});
}
}
}
return result;
}
now i cant use originalValue = ent.GetDatabaseValues().GetValue(prop) because the records have two fields as primary key so i thought i could query the original value manually from the controller and send as
object originalEntity
so i'm wondering how do i cast originalEntity to the current entity
var originalValues = Convert.ChangeType(originalEntity, ent.GetType());
does not work.
i'm finding it difficult to get. thanks for any help.

Using delimeter to seperate Scanner input into different dataTypes.(String, int,

how can i use delimiter " : " to receive a string from (system.in ) E.g Ben : 50. Jessica : 30 and so on , and then print as Ben, 50 using my own System.out.print (name + "," + score); I was able to print out the string as string but its not printing out the integer as integer. I need to calculate the average of all the score and that is why i need integer to remain integer so the average method can work Here is my code . Here is my code so far. Please help !!!
-------------------------------------------------------------------------
import java.util.Scanner;
public class StudentScore{
public static void main(String [] args){
Scanner input = new Scanner(System.in);
input.useDelimiter(":");
System.out.print("Please enter your name and score in format: Ben : 50" );
String name = input.next();
int score = input.nextInt();
while(input.hasNext() || input.hasNextInt());{
System.out.println(name + ", " + score);
}
input.close();
}
}
this keeps creeating a new scanner and no print out.
After researching and asking several people , i found out the String class actually has a function that takes in string and then splits it into several primitive values" long", " int" and so on. You need to convert your input into an array, make sure it is spitted by a delimiter of choice .e.g "," or ":" in my own case. Then the array will be your parameter for the .split method from the String class. I posted my complete program below. The programs takes in three names and score and outputs the grade letter and then tells the best score among the three students.
import java.util.Scanner;
public class ThreeNamesAndScore {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Please enter your name and score in this format, Name : Score");
String input1 = input.nextLine();
String[] userData1 = input1.split(":");
String firstStudentName = userData1[0];
String firstStudentStrScore = userData1[1];
int firstStudentScore = Integer.parseInt(firstStudentStrScore);
System.out.print("Please enter your name and score in this format, Name : Score");
String input2 = input.nextLine();
String[] userData2 = input2.split(":");
String secondStudentName = userData2[0];
String secondStudentStrScore = userData2[1];
int secondStudentScore = Integer.parseInt(secondStudentStrScore);
System.out.print("Please enter your name and score in this format, Name : Score");
String input3 = input.nextLine();
String[] userData3 = input3.split(":");
String thirdStudentName = userData3[0];
String thirdStudentStrScore = userData3[1];
int thirdStudentScore = Integer.parseInt(thirdStudentStrScore);
System.out.println("The following is the fianl result ");
System.out.println(firstStudentName + ", " + getGradeLetter(firstStudentScore));
System.out.println(secondStudentName + ", " + getGradeLetter(secondStudentScore));
System.out.println(thirdStudentName + ", " + getGradeLetter(thirdStudentScore));
System.out.println("The best score is from " + bestScore(input1, input2, input3));
input.close();
}
public static String getGradeLetter(int score) {
if (score >= 90) {
return "A";
} else if (score >= 80) {
return "B";
} else if (score >= 70) {
return "C";
} else if (score >= 60) {
return "D";
} else
return "F";
}
public static String bestScore(String a, String b, String c) {
String bestStudent;
String[] userInputs = { a, b, c };
String[] user1 = a.split(":");
String aStrScore = user1[1];
int aScore = Integer.parseInt(aStrScore);
String[] user2 = b.split(":");
String bStrScore = user2[1];
int bScore = Integer.parseInt(bStrScore);
String[] user3 = c.split(":");
String cStrScore = user3[1];
int cScore = Integer.parseInt(cStrScore);
if ((aScore > bScore) && (aScore > cScore))
bestStudent = a;
else if ((bScore > aScore) && (bScore > cScore))
bestStudent = b;
else
bestStudent = c;
return bestStudent;
}
}

How to do If statement in Linq Query

I currently have a list that contains the following
CountryCode (string)
CountryStr (string)
RegionStr (string)
RegionID (int)
AreaStr (string)
AreaID (int)
This is a flattened set of linked data (so basically the results of a joined search that ive stored)
The MVC route will only pass one string which I then need to match up to the data at the right level in the heirachy.
So I'm trying to query the CountryStr then if it doesn't produce results the region then the area; but I need to do that bit of the query and for instance...
var datURL = (from xs in myList
//query 1
where xs.RegionStr == rarREF
select new
{
regionID = xs.RegionId,
CountryID = xs.CountryCd
}
//IF theres no results
where xs.AreaStr == rarREF
select new
{
AreaID = xs.AreaID
regionID = xs.RegionId,
CountryID = xs.CountryCd
}
).ToList();
The only way I see of doing this at the moment is running each query separately then checking which returned values and using that one. I'm hoping there's a cleverer, cleaner method.
It won't be very easy to read, but you could do this in a single pass using something like this:
var datURL = (from xs in myList
where xs.RegionStr == rarREF || xs.AreaStr == rarREF
select new
{
AreaID = (xs.AreaStr == rarRef ? xs.AreaID : default(int)),
RegionID = xs.RegionId,
CountryID = xs.CountryId
}
).ToList();
It might also be easier to read the query if it's rewritten slightly:
var datURL = (from xs in myList
let isArea = xs.AreaStr == rarREF
let isRegion = xs.RegionStr == rarREF
where isRegion || isArea
select new
{
AreaID = (isArea ? (int?)xs.AreaID : null),
RegionID = xs.RegionId,
CountryID = xs.CountryId
}
).ToList();
If we save the comparison result, we can reuse it later. I also added a cast to int? to show how you could use a nullable value instead of using 0 as your "no Area" value.
Aren't you looking for or operator? Does this not generate the results you want?
var datURL = (from xs in myList
where xs.RegionStr == rarREF || xs.AreaStr == rarREF
select new
{
AreaID = xs.AreaStr == rarREF ? xs.AreaID : default(int)
regionID = xs.RegionId,
CountryID = xs.CountryCd
}).ToList();

Resources