Giter Site home page Giter Site logo

timeshape's Introduction

Timeshape

Maven Central Build status Gitter

Timeshape is a Java library that can be used to determine to which time zone a given geo coordinate belongs. It's based on data published at https://github.com/evansiroky/timezone-boundary-builder/releases, which itself is inherited from the OpenStreetMap data.

But what if knowing just time zone for a geo coordinate is not enough? Wouldn't it be nice to know more, like administrative area or city neighborhood? Check out GeoBundle, now there's a Java library for that, too!

Quote

“Time is an illusion.”

Albert Einstein

Getting started

Timeshape is published on Maven Central. The coordinates are the following:

<dependency>
    <groupId>net.iakovlev</groupId>
    <artifactId>timeshape</artifactId>
    <version>2023b.21</version>
</dependency>

Starting from release 2023b.20, Timeshape re-introduces support for Java 8. It was inadevrtently dropped in one of the previous releases, but now it's brought back.

Android

Android developers should add Timeshape to the app level gradle.build as follows:

implementation('net.iakovlev:timeshape:2023b.21') {
  // Exclude standard compression library
  exclude group: 'com.github.luben', module: 'zstd-jni'
}
// Import aar for native component compilation
implementation 'com.github.luben:zstd-jni:1.5.5-11@aar'

Adopters

Are you using Timeshape? Please consider opening a pull request to list your organization here:

  • Name and website of your organization
  • Hopper

Using the library

The user API of library is in net.iakovlev.timeshape.TimeZoneEngine class. To use it, follow these steps:

Initialization

Initialize the class with the data for the whole world:

import net.iakovlev.timeshape.TimeZoneEngine;
TimeZoneEngine engine = TimeZoneEngine.initialize();

Or, alternatively, initialize it with some bounding box only, to reduce memory usage:

TimeZoneEngine engine = TimeZoneEngine.initialize(47.0599, 4.8237, 55.3300, 15.2486);

It is important to mention that for the time zone to be loaded by the second method, it must be covered by the bounding box completely, not just intersect with it.

During initialization, the data is read from resource and the index is built. Initialization takes a significant amount of time (approximately 1 second), so do it only once in program lifetime.

Data can also be read from an external file, see overloads of TimeZoneEngine.initialize that accept TarArchiveInputStream. The file format should be same as produced by running builder sbt project (see here). It is responsibility of caller to close input stream passed to TimeZoneEngine.initialize.

Query for java.time.ZoneId:

Once initialization is completed, you can query the ZoneId based on latitude and longitude:

import java.util.Optional;
import java.time.ZoneId;
Optional<ZoneId> maybeZoneId = engine.query(52.52, 13.40);

Multiple time zones for single geo point

Starting from release 2019b.7, the data source from which Timeshape is built contains overlapping geometries. In other words, some geo points can simultaneously belong to multiple time zones. To accommodate this, Timeshape has made the following change. It's now possible to query all the time zones, to which given geo point belongs:

List<ZoneId> allZones = engine.queryAll(52.52, 13.40);

The Optional<ZoneId> query(double latitude, double longitude) method is still there, and it still returns maximum one ZoneId. If given geo point belongs to multiple time zones, only single one will be returned. Which of multiple zones will be returned is entirely arbitrary. Because of this, method Optional<ZoneId> query(double latitude, double longitude) is not suitable for use cases where such choice must be deliberate. In such cases use List<ZoneId> queryAll(double latitude, double longitude) method and apply further business logic to its output to choose the right time zone. Consult file https://raw.githubusercontent.com/evansiroky/timezone-boundary-builder/master/expectedZoneOverlaps.json for information about areas of the world where multiple time zones are to be expected.

Querying polyline

Timeshape supports querying of multiple sequential geo points (a polyline, e.g. a GPS trace) in an optimized way using method List<SameZoneSpan> queryPolyline(double[] points). Performance tests (see net.iakovlev.timeshape.PolylineQueryBenchmark) show significant speedup of using this method for querying a polyline, comparing to separately querying each point from polyline using Optional<ZoneId> query(double latitude, double longitude) method:

Benchmark                                  Mode  Cnt  Score   Error  Units
PolylineQueryBenchmark.testQueryPoints    thrpt    5  2,188 ▒ 0,044  ops/s
PolylineQueryBenchmark.testQueryPolyline  thrpt    5  4,073 ▒ 0,017  ops/s

Architecture

See dedicated document for description of Timeshape internals.

Versioning

Version of Timeshape consist of data version and software version, divided by a '.' symbol. Data version is as specified at https://github.com/evansiroky/timezone-boundary-builder/releases. Software version is an integer, starting from 1 and incrementing for each published artifact.

Licenses

The code of the library is licensed under the MIT License.

The time zone data contained in library is licensed under the Open Data Commons Open Database License (ODbL).

timeshape's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

timeshape's Issues

Fully automate core artifact creation

Currently it's required to manually run the builder app to produce the resource with time zones data for the core library. It would be better to make this process automatic, to simplify the development and publishing process.

Improve configurability of TimeZoneEngine class

Currently the net.iakovlev.timeshape.TimeZoneEngine class is initialized using a bunch of overrides of initialize methods. This was fine in the beginning, but now there are already 5 overrides and more is needed (including but not limiting the #58). It's clearly time to introduce a different approach.

My current preference is to use some sort of TimeZoneEngineConfig class for this. This class will have all the configuration parameters exposed as getters. To configure an instance of this class, the Builder pattern will be used, since this approach seems to be sort of standard in the industry (see e.g. AWS and Google Cloud Java SDKs, or the Protobuf, it's really popular). The user of Timeshape will create and configure an instance of TimeZoneEngineConfig and pass it to the TimeZoneEngine.initialize method.

This will require another override of initialize method, but it will be the final one. I'll keep all the current initialize methods for backward compatibility.

If you have any suggestions or comments on this, please comment on this issue.

Support querying the polylines more efficiently

The use case to query a single geographical position seems to work rather well, but when it comes to querying multiple positions, there is a room for improvement. Particularly, there is a use case to query the contiguous sequence of points (a polyline, e.g. a GPS trace). In that situation, it might make sense to skip repeated quad tree queries and try to first test the same geometry as the one which contained the previous point in the polyline.

java.lang.NoSuchMethodError on AWS EMR with commons-compress

Exception in thread "main" java.lang.NoSuchMethodError: org.apache.commons.compress.utils.IOUtils.toByteArray(Ljava/io/InputStream;)[B
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:107)
at com.ceabs.fleetperformance.base.TimeZoneCommon.initTimeZone(TimeZoneCommon.java:61)

Maybe should upgrade:
// https://mvnrepository.com/artifact/org.apache.commons/commons-compress
compile group: 'org.apache.commons', name: 'commons-compress', version: '1.18'

2020a

Hi, I just wanted to make you aware of the fact that version 2020a of the data has been released :-)

source code lack some class;

souce code lack the class:net.iakovlev.timeshape.proto.Geojson

and this source code can not be find in google...

please help to fix it

thanks;

Split geojson-proto into a separate build/repository

Right now publishing from Travis fails because of different versioning scheme of core Timeshape artifact and geojson-proto artifact. Particularly, if a release of core artifact is required, while geojson-proto hasn't been updated, there'll be an error in publishing, as can be seen here: https://travis-ci.com/RomanIakovlev/timeshape/builds/90908423. One solution to this is to put the these projects into 2 sbt builds, with consistent versioning scheme each, or even in 2 different git repositories (this would probably simplify Travis setup).

Travis CI builds don't run

Travis CI is still used to run the release builds, and it doesn't run for some reason. I suspect it might be related to billing, since Timeshape uses free credits from Travis. There seems to still be sufficient amount of credits to run the build, so I suspect it's some glitch in their billing system. However this blocks the releases, e.g. #110.

Asia/Qostanay time zone is missing

Asia/Qostanay time zone was added in 2018h, but not in the list of timezones received by this function

this.engine.getKnownZoneIds();

Also when queried with coordinates of Qostanay the following function returns an empty list

private final TimeZoneEngine engine;
    
private TimeZoneService() {
  this.engine = TimeZoneEngine.initialize();
}
  
public List<ZoneId> findZoneIds(53.205415658681055, 63.646202087402344) {
  return engine.queryAll(lat, lng);
}

Android studio error:Error:Error converting bytecode to dex: Cause: com.android.dex.DexException: Multiple dex files define Lnet/iakovlev/timeshape/TimeZoneEngine

i tried to compile it with android studio

But it always notice me :
Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lnet/iakovlev/timeshape/TimeZoneEngine$$Lambda$0;
Error:com.android.dex.DexException: Multiple dex files define Lnet/iakovlev/timeshape/TimeZoneEngine$$Lambda$0;
Error: at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:661)
Error: at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:616)
Error: at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:598)
Error: at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:171)
Error: at com.android.dx.merge.DexMerger.merge(DexMerger.java:198)
Error: at com.android.builder.dexing.DexArchiveMergerCallable.call(DexArchiveMergerCallable.java:61)
Error: at com.android.builder.dexing.DexArchiveMergerCallable.call(DexArchiveMergerCallable.java:36)
Error: at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1424)
Error: at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
Error: at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
Error: at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
Error: at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Error:Execution failed for task ':app:transformDexArchiveWithDexMergerForDebug'.

com.android.build.api.transform.TransformException: com.android.dex.DexException: Multiple dex files define Lnet/iakovlev/timeshape/TimeZoneEngine$$Lambda$0;

i don't know how to solve it ...

UnsatisfiedLinkError in Android

I'm trying to use this library in Android. To import it, I use this in build.gradle:

dependencies{
    implementation group: 'net.iakovlev', name: 'timeshape', version: '2020d.12'
}

Then I use it in my Java code like this:

import net.iakovlev.timeshape.TimeZoneEngine;

TimeZoneEngine engine = TimeZoneEngine.initialize();

This compiles just fine, but it crashes at runtime with the following error:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.my_app, PID: 4629
    java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.my_app-CnG68M7pV_GWaAOEveKg9Q==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.my_app-CnG68M7pV_GWaAOEveKg9Q==/lib/arm, /system/lib, /product/lib]]] couldn't find "libzstd-jni-1.5.0-4.so"
    Unsupported OS/arch, cannot find /linux/armv7l/libzstd-jni-1.5.0-4.so or load zstd-jni-1.5.0-4 from system libraries. Please try building from source the jar or providing libzstd-jni-1.5.0-4 in your system.
        at java.lang.Runtime.loadLibrary0(Runtime.java:1067)
        at java.lang.Runtime.loadLibrary0(Runtime.java:1007)
        at java.lang.System.loadLibrary(System.java:1667)
        at com.github.luben.zstd.util.Native.load(Native.java:87)
        at com.github.luben.zstd.util.Native.load(Native.java:55)
        at com.github.luben.zstd.ZstdInputStreamNoFinalizer.<clinit>(ZstdInputStreamNoFinalizer.java:23)
        at com.github.luben.zstd.ZstdInputStream.<init>(ZstdInputStream.java:24)
        at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:235)
        at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:153)

Fail to initialize due to unknown Zone Id

Fails to initialize all world data

private static final TimeZoneEngine engine = TimeZoneEngine.initialize();

With Java 8:

Caused by: java.time.zone.ZoneRulesException: Unknown time-zone ID: America/Fort_Nelson
at java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:272)
at java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:227)
at java.time.ZoneRegion.ofId(ZoneRegion.java:120)
at java.time.ZoneId.of(ZoneId.java:411)
at java.time.ZoneId.of(ZoneId.java:359)
at net.iakovlev.timeshape.Index.lambda$build$5(Index.java:81)
at net.iakovlev.timeshape.Index$$Lambda$713/437255067.accept(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at net.iakovlev.timeshape.Index.build(Index.java:68)
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:88)
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:59)

Consider adding lazy initialization to TimeZoneEngine

For certain use cases, like querying a single or little amount of points, it might make sense to skip the full initialization of the TimeZoneEngine. It might not be possible to limit the initialization (e.g. via bounding box or any other means developed in the future), because the points might be from unpredictable locations. For example, for code running in some FaaS (Function as a Service) environment, where cold start is a problem, users should not pay the price of full initialization to just query a single location.

Doing this would probably require to change the way how the time zone data is stored. Now it's a single archive, but it might make sense to split it into some sort of geo tiles and store each such file individually. When querying, first the name of the tile file will be determined, and only that file will be loaded to perform the actual query. This is only a rough idea for now, need to think about this more. Feedback and ideas are welcome!

See also #28.

Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: org/tukaani/xz/FilterOptions

Here is the issue I am getting when I upgraded from 2018d.4 to 2018d.6
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: org/tukaani/xz/FilterOptions
at org.apache.commons.compress.archivers.sevenz.Coders.(Coders.java:47)
at org.apache.commons.compress.archivers.sevenz.SevenZFile.buildDecoderStack(SevenZFile.java:1032)
at org.apache.commons.compress.archivers.sevenz.SevenZFile.buildDecodingStream(SevenZFile.java:985)
at org.apache.commons.compress.archivers.sevenz.SevenZFile.getNextEntry(SevenZFile.java:297)
at net.iakovlev.timeshape.TimeZoneEngine.lambda$initialize$0(TimeZoneEngine.java:74)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at net.iakovlev.timeshape.Index.build(Index.java:68)
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:88)
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:59)

More precise memory usage is required

Now the memory usage is estimated by the minimally sufficient amount of -Xmx parameter's value passed to the forked JVM when running the tests in the core project. It's not very precise, because it includes more things than just the library. A better solution is needed.

Timeshape with oceans

The 2018d release of timezone-boundary-builder has a version with oceans (timezones-with-oceans.geojson.zip)

Any plans to publish a version of Timeshape that includes oceans?

Allow load of external geo data

Scenario

As of now the geo data is shipped with timeshape and that makes the library very desirable since it has no setup needed neither extra dependencies.

Problem statement

What is a pro for some is a cons for others. Having to update a dependency in a project might not be the easiest way to adapt to timezone changes. The same file that is provided inside the library might be loaded externally (with a dedicated TimeZoneEngine api) to avoid re-deploy of jars upon a timezone change.

remove [DEBUG] messages when starting up timeshape

Hi all,

I am using timeshape v2019b.8 in my spring-boot application and would like to remove the [DEBUG] messages it spews to the console to signal initialization of the library - which works great, by the way.
I have tried to set a different logging level in application.yml to no success with the following entry:

logging:
  level:
    net.iakovlev.timeshape.*: ERROR

How can I do so?

The known vulnerability in the shared library zstd which timeshape-builder depends on.Can you help upgrade to patch versions?

Hi, @RomanIakovlev , @juarezr , I'd like to report a vulnerability issue in net.iakovlev:timeshape-builder:1.0.

Issue Description

I noticed that net.iakovlev:timeshape-builder:1.0 directly depends on com.github.luben:zstd-jni:v1.4.5-5 in the pom. However, as shown in the following dependency graph. com.github.luben:zstd-jni:v1.4.5-5 sufferes from the vulnerability which the C library zstd(version:1.4.5) exposed: CVE-2021-24032.

Dependency Graph between Java and Shared Libraries

image (12)

Suggested Vulnerability Patch Versions

com.github.luben:zstd-jni:v1.4.9-1 (>=v1.4.9-1) has upgraded this vulnerable C library zstd to the patch version 1.4.9.

Java build tools cannot report vulnerable C libraries, which may induce potential security issues to many downstream Java projects. Could you please upgrade this vulnerable dependency?

Thanks for your help~
Best regards,
Helen Parr

No timezone in Kiev after 2022f.15 upgrade

Hi,

I recently tried upgrading the timeshape library in one of my projects from 2021c.13 to 2022f.15, but it broke a unit test I had.
Getting the timezone from coordinates 50.4501, 30.5234 used to return [Europe/Kiev] and it now returns Optional.Empty.

It does not seem normal to me.

Thanks for your feedback!

No timezone for some places in Mexico for 2022g.16

Hi,

with the latest release there is no timezone returned for e.g. 31.752,-106.457 or 31.038,-107.045. Both places are in Chihuahua and I haven't seen an issue (yet?) for other places.
It works fine for 2022f.15.

For completeness, the code looks similar to this:
TimeZoneEngine.initialize().queryAll(lat, lon)

Thanks, Michael

timeshape release 2019b.7 freezes on initialize on AWS EMR

After upgrading to release 2019d.7 the initialization of timeshape started to freeze:

  • Version 2018d.6 worked fine
  • The only change was the the timeshape upgrade
  • On local machine it works, but when deployed to AWS EMR it freezes.

Tentative 1:

timeZoneEngine = TimeZoneEngine.initialize(-56, -83, 13, -32, false);

With results in:

19/10/21 21:18:44 INFO : SETUP: Loading timezone information...
19/10/21 21:18:44 INFO TimeZoneEngine: Initializing with bounding box: -56.0, -83.0, 13.0, -32.0

Tentative 1:

timeZoneEngine = TimeZoneEngine.initialize();

With results in:

19/10/21 22:07:29 INFO : SETUP: Loading timezone information...
19/10/21 22:07:29 INFO TimeZoneEngine: Initializing with bounding box: -90.0, -180.0, 90.0, 180.0

Any supposition for finding the problem?

timeshape not usable with Java module system

If you add

<!-- https://mvnrepository.com/artifact/net.iakovlev/timeshape -->
<dependency>
    <groupId>net.iakovlev</groupId>
    <artifactId>timeshape</artifactId>
    <version>2018d.6</version>
</dependency> 

to a project where the library ends up on the java module path (in contrast to the classpath) you will get the following error:

Error occurred during initialization of boot layer
java.lang.module.FindException: Unable to derive module descriptor for target/libs/timeshape-2018d.6.jar
Caused by: java.lang.IllegalArgumentException: timeshape.2018d.6: Invalid module name: '2018d' is not a Java identifier 

In order to fix this problem you would have to remove the 'd' from the version number because this is not allowed. Otherwise your library is not usable with the Java module system.

Initialize with TarArchiveInputStream

Hi @RomanIakovlev,
I really like your project! Thanks for the great work!
Since a couple of hours I try to initialize your TimeZoneEngine with data generated by your builder module. The builder module works just fine. At least the output is a ~20MB file (input was timezones-with-oceans.geojson.zip 2019b). But when I try to initialize TimeZoneEngine with TarArchiveInputStream(FileInputStream("file.zip")) I always receive a error:

Error detected parsing the header
Invalid byte 110 at offset 0 in 'n���F�g&' len=8

Any idea or recommendation? I already tried it with Java 1.8 and 11. Also on Win and Mac. :-/

Mention zstd-jni dependency

It wasn't obvious to me what was causing this failure at first,

java.lang.NoClassDefFoundError: com/github/luben/zstd/ZstdInputStream
    at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:129)
    at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:116)

I eventually realized it was due to zstd-jni not resolving to a platform-specific dependency. Gradle will not resolve this classifier for you, so I added the following to my build to fix the issue:

"com.github.luben:zstd-jni:${versions.zstd}-3:${env.os.equals('macosx') ? 'darwin_x86_64' : (env.os + '_amd64')}",

where env.os was already defined as

env = [
  os: System.getProperty('os.name').replaceAll(' ', '').toLowerCase(),
]

java.lang.ClassNotFoundException when initializing TimeZoneEngine in version 2018d.2

Hi, when initializing TimeZoneEngine from version 2018d.2 it throws ClassNotFoundException:

Exception in thread "main" java.lang.BootstrapMethodError: java.lang.NoClassDefFoundError: net/iakovlev/timeshape/proto/Geojson$Feature
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:72)
at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:59)

It seems that protobuf code is not included in the distributable jar from Maven repo, but TimeZoneEngine depends on it.

best way to using timeshape in spring boot

Hi, we are using the library in the spring boot environment by configuring beans to share it on multiple controllers.

SpringConfig.java
@Bean @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON) public TimeZoneEngine timeZoneEngine() { System.out.println("****************** START timeZoneEngine"); if(this.timeZoneEngine == null) this.timeZoneEngine = TimeZoneEngine.initialize(); System.out.println("****************** END START timeZoneEngine"); return timeZoneEngine; }

Controller:
@Autowired private TimeZoneEngine engine;

During the start-up phase I see the logs pass twice as if the library is instantiated several times.

11:10:17.618 [main] INFO n.iakovlev.timeshape.TimeZoneEngine - Initializing with bounding box: -90.0, -180.0, 90.0, 180.0 11:10:18.634 [main] ERROR net.iakovlev.timeshape.Index - Some of the zone ids were not recognized by the Java runtime and will be ignored. The most probable reason for this is outdated Java runtime version. The following zones were not recognized: America/Nuuk 11:10:18.635 [main] INFO net.iakovlev.timeshape.Index - Initialized index with 1412 time zones ****************** START timeZoneEngine 11:10:19.142 [main] INFO n.iakovlev.timeshape.TimeZoneEngine - Initializing with bounding box: -90.0, -180.0, 90.0, 180.0 11:10:20.106 [main] ERROR net.iakovlev.timeshape.Index - Some of the zone ids were not recognized by the Java runtime and will be ignored. The most probable reason for this is outdated Java runtime version. The following zones were not recognized: America/Nuuk 11:10:20.106 [main] INFO net.iakovlev.timeshape.Index - Initialized index with 1412 time zones ****************** END START timeZoneEngine

What is the best way to manage in a spring boot environment? thank you.

Please upgrade protobuf

Version 3.4.0 of protobuf used here gives the following warnings in java 9 and greater...

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.protobuf.UnsafeUtil (file:/home/ken/.m2/repository/com/google/protobuf/protobuf-java/3.4.0/protobuf-java-3.4.0.jar) to field java.nio.Buffer.address
WARNING: Please consider reporting this to the maintainers of com.google.protobuf.UnsafeUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

which I believe is addressed in later versions of protobuf. Cheers.

Initialize library filtering by Continent/Country

In timeshape today, there are two methods for initializing the library

  • with the data for the whole world
  • with some bounding box only, to reduce memory usage

It would be interesting to have a third option for initializing with the data of a continent or a country because the main scopes of use are political following the patterns:

  • whole world (international usage)
  • my country (USA, Brazil, etc...)
  • my region (European Union, South America, Asia, etc...)

This division could be made according to IANA existing divisions.

Index.queryPolyline does not return last empty SameZoneSpan

Index.queryPolyline() does not account for the case, when one or more points in the end of given polyline are not part of any time zone in the index.
Example of this behavior: TimeZoneEngine.initialize().queryPolyline(new double[]{54.89, 23.91, 80, 180}) returns a list with only one element:
new SameZoneSpan(new HashSet<>(Collections.singletonList(ZoneId.of("Europe/Vilnius"))), 1).
I would expect (following TimeZoneEnginePolylineTest.testNonMatchingPoints() logic) the result to contain one more element:
new SameZoneSpan(new HashSet<>(), 3).
I've added a unit test for such case and a fix: #62 .

zstd-jni-1.5.0-4 issue

I tried upgrading to latest version and still getting this issue on CentOS.
It is working fine on Windows. Any idea please?

Exception in thread "main" java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) at org.springframework.boot.loader.Launcher.launch(Launcher.java:87) at org.springframework.boot.loader.Launcher.launch(Launcher.java:51) at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:52) Caused by: java.lang.UnsatisfiedLinkError: /tmp/libzstd-jni-1.5.0-410397134098366552495.so: /tmp/libzstd-jni-1.5.0-410397134098366552495.so: failed to map segment from shared object: Operation not permitted no zstd-jni-1.5.0-4 in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib] Unsupported OS/arch, cannot find /linux/amd64/libzstd-jni-1.5.0-4.so or load zstd-jni-1.5.0-4 from system libraries. Please try building from source the jar or providing libzstd-jni-1.5.0-4 in your system. at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2660) at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:827) at java.base/java.lang.System.loadLibrary(System.java:1871) at com.github.luben.zstd.util.Native.load(Native.java:124) at com.github.luben.zstd.util.Native.load(Native.java:55) at com.github.luben.zstd.ZstdInputStreamNoFinalizer.<clinit>(ZstdInputStreamNoFinalizer.java:23) at com.github.luben.zstd.ZstdInputStream.<init>(ZstdInputStream.java:24) at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:235) at net.iakovlev.timeshape.TimeZoneEngine.initialize(TimeZoneEngine.java:153)

Empty ZoneId

Can there be a case where the ZoneId is returned empty by engine.query() for a valid latitude and longitude?

Some Questions to ask

This project is very powerful for me;
but i can not very easy to understand it.
So many question in my mind;

i try to run the main.java in directory:builder\src\main\java\net\iakovlev\timeshape
i alwasy meet some problem when process the data ;

i have downloaded the timezones.geojson.zip in local directory;
1.why we need build proto or get featureCollection from the file;
2.The msg coming from geojson used to download the map data from open street map? or just changeed the data format?
2.it the data have been done from timezoues.geojson.zip
why not output the file in a good way .. it must filled by customers;

static void writeSevenZProto(String version, String outputPath)

3.if i failed to buildProto so i can not run the second steps;

     TimeZoneEngine engine = TimeZoneEngine.initialize(47.0599, 4.8237, 55.3300, 15.2486);
    System.out.println(engine.query(52.52, 13.40));
  1. public static TimeZoneEngine initialize(double minlat, double minlon, double maxlat, double maxlon) {
    try (InputStream resourceAsStream = TimeZoneEngine.class.getResourceAsStream("/output.pb.7z");

There is a file output.pb.7z shuld be done by first build step

but i failed to run first step... is there a file that have been processed and be used to query zone directly?

Looking forward your response.

Thanks

sbt -> maven migration

As much as I like sbt for its flexibility and rich feature set, it still doesn't enjoy the first class support in the Java world. Particularly, in the context of #59 , I'd like to use automatic generators of Builder-like classes, such as Auto-matter or Immutables. I've tried setting them up with sbt+Intellij, and couldn't make it work.

At the same time, I've used a lot of Maven recently in different context, and I think it's possible to get most (but probably not all) of functionality the build of Timeshape requires by using Maven. Particularly, the hardest part of the build, configuring and triggering the download of the source data and resources for the main artifact, looks doable.

Also Maven is sort of standard for Java builds, and using standard technology is usually preferable from maintenance perspective. All in all, I'm convinced it's best for Timeshape to switch to Maven.

It might not be possible to migrate some of the non-essential features of the build to Maven, at least in the first run, but I'm sure there are workarounds. E.g. some logic from build.sbt like checking the latest source data version or deciding whether to re-download the source data might be moved into the Java code of the builder Maven submodule (née sbt project).

I wonder if anyone has comments or ideas on how to best implement this? I'm open to feedback.

java.lang.OutOfMemoryError: Java heap space

TimeZoneEngine.initialize().query(52.52, 13.40);
result:

java.lang.OutOfMemoryError: Java heap space

at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2041)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at com.fasterxml.jackson.core.io.NumberInput.parseDouble(NumberInput.java:285)
at com.fasterxml.jackson.core.util.TextBuffer.contentsAsDouble(TextBuffer.java:399)
at com.fasterxml.jackson.core.base.ParserBase._parseSlowFloat(ParserBase.java:886)
at com.fasterxml.jackson.core.base.ParserBase._parseNumericValue(ParserBase.java:834)
at com.fasterxml.jackson.core.base.ParserBase.getDoubleValue(ParserBase.java:751)
at org.geojson.jackson.LngLatAltDeserializer.extractDouble(LngLatAltDeserializer.java:68)
at org.geojson.jackson.LngLatAltDeserializer.deserializeArray(LngLatAltDeserializer.java:30)
at org.geojson.jackson.LngLatAltDeserializer.deserialize(LngLatAltDeserializer.java:22)
at org.geojson.jackson.LngLatAltDeserializer.deserialize(LngLatAltDeserializer.java:16)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:287)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:259)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:287)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:259)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:178)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:129)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer.deserializeTypedFromObject(AsPropertyTypeDeserializer.java:97)
at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserializeWithType(AbstractDeserializer.java:142)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:502)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:178)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
at com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer._deserializeTypedForId(AsPropertyTypeDeserializer.java:129)

*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create name string at JPLISAgent.c line: 807

Which algorithm used to find Time zone id?

@RomanIakovlev
Can you help to explain which algorithm used in the project ?

i find that it should use huge memory to initial all map from lat (90 to -90) Long(-180 to 180);

But i want to reduce it's memory cost and speed the look up time spend;

Thanks;

Incorrect ZoneId

I think I have a case where an incorrect ZoneId is returned, but I am not sure.

As a small example, I have two points just east of the Florida coast. One returns America/New_York and the other returns Etc/GMT+5. I am expecting that everything in that general area to return as GMT-4.

Here are examples of points and what they return for me:

27.406189, -80.065492: America/New_York
27.405308, -80.016944: Etc/GMT+5

Here is my test code:

package org.example;

import net.iakovlev.timeshape.TimeZoneEngine;

import java.net.URI;
import java.time.ZoneId;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

public class App {

    private static final TimeZoneEngine tze = TimeZoneEngine.initialize();

    private static Optional<ZoneId> zoneId(final double latitude, final double longitude) {
        return tze.query(latitude, longitude);
    }

    private static Optional<ZoneId> zoneId(final URI geoURI) {
        if (!geoURI.getScheme().equals("geo")) {
            throw new IllegalArgumentException(
                    String.format("unexpected scheme '%s' in '%s'", geoURI.getScheme(), geoURI));
        }

        final String location = geoURI.getSchemeSpecificPart().split(";")[0];
        final String[] coordinates = location.split(",");
        final double latitude = Double.parseDouble(coordinates[0]);
        final double longitude = Double.parseDouble(coordinates[1]);
        return zoneId(latitude, longitude);
    }

    public static void main(final String[] args) {
        final Collection<URI> locations =
                List.of(URI.create("geo:27.405308,-80.016944"), URI.create("geo:37.786971,-122.399677;u=35"),
                        URI.create("geo:27.406189,-80.065492"));
        locations.forEach(l -> System.out.printf("%s: %s\n", l, zoneId(l)));
    }

}

I am thinking that maybe the problem is that the point that returns Etc/GMT+5 is just outside of the U.S. boundary and in international waters, but I would still expect it to be in the same daylight savings time regime. I am wondering if I need to handle this as a special case in my code for ocean regions not within a country.

DateTimeException when including version 2020d.11 in a Spring application

Was doing some test with your library in an existing Spring application (Java 8) and suddenly the application didn't start anymore and threw this error:

Exception in thread "main" java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 0
        at java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
        at java.time.temporal.ChronoField.checkValidValue(ChronoField.java:703)
        at java.time.LocalDate.of(LocalDate.java:267)
        at java.time.LocalDateTime.of(LocalDateTime.java:336)
        at org.springframework.boot.loader.jar.CentralDirectoryFileHeader.decodeMsDosFormatDateTime(CentralDirectoryFileHeader.java:130)
        at org.springframework.boot.loader.jar.CentralDirectoryFileHeader.getTime(CentralDirectoryFileHeader.java:119)
        at org.springframework.boot.loader.jar.JarEntry.<init>(JarEntry.java:58)
        at org.springframework.boot.loader.jar.JarFileEntries.getEntry(JarFileEntries.java:327)
        at org.springframework.boot.loader.jar.JarFileEntries.access$400(JarFileEntries.java:48)
        at org.springframework.boot.loader.jar.JarFileEntries$EntryIterator.next(JarFileEntries.java:378)
        at org.springframework.boot.loader.jar.JarFileEntries$EntryIterator.next(JarFileEntries.java:362)
        at org.springframework.boot.loader.jar.JarFile$2.nextElement(JarFile.java:198)
        at org.springframework.boot.loader.jar.JarFile$2.nextElement(JarFile.java:189)
        at org.springframework.boot.loader.archive.JarFileArchive$EntryIterator.next(JarFileArchive.java:186)
        at org.springframework.boot.loader.archive.JarFileArchive$EntryIterator.next(JarFileArchive.java:171)
        at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:84)
        at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:70)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:49)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)

I started commenting my code to check if the problem was caused by my implementation, but only after removing the dependency, the problem was fixed.

Turns out, having the dependency in the project, is enough to cause this startup error, even without any code using it.

When going back to version 2019b.9, the application starts OK.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.