Skip to content

Asta reader is locale-dependent: exponent values parsed with a default-locale DecimalFormat #935

Description

@0xbobski

Reading the same Asta .pp file under different default locales produces different task data. Reproduced on 16.7.0 using only the public API:

locale=en-US   tasks=10344   actualFinish=1593   critical=1057
locale=en-AU   tasks=10344   actualFinish=460    critical=2190
locale=de-DE   tasks=10344   actualFinish=460    critical=2190

Same bytes, same library, three answers. Scheduled dates are unaffected — across all 10,344 tasks start, finish, early_start and actual_start are identical under every locale. What changes is actual_finish, which the affected locales leave null where the correct parse sets it; early_finish, late_finish and critical follow from that.

Cause

org.mpxj.asta.DatatypeConverter.parseDouble routes any value containing an exponent through a ThreadLocal<DecimalFormat> built as:

new DecimalFormat("#.#E0")

No Locale argument, so it takes the JVM default's DecimalFormatSymbols. Where those do not match the file's ASCII 1.0E9 notation, DecimalFormat.parse stops at the first character it cannot read and returns the mantissa without throwing:

Locale.setDefault(Locale.forLanguageTag("en-AU"));
new DecimalFormat("#.#E0").parse("1.0E9");   // returns 1
Double.valueOf("1.0E9");                     // 1.0E9

en-AU and en-CA use a lowercase e as the exponent separator; fr/de/es use a comma decimal separator; ar-EG uses اس. Measured against Double.valueOf as ground truth:

locale exponent symbol 1.0E9 parses as correct
en-US, en-GB, en-NZ, en-IN E 1000000000 yes
en-AU, en-CA e 1 no
fr-FR, de-DE, es-ES E 1 no (comma decimal)
ar-EG اس 1 no

The non-exponent path already uses locale-independent Double.valueOf, so the two branches disagree.

Suggested fix

new DecimalFormat("#.#E0", DecimalFormatSymbols.getInstance(Locale.ROOT))

Notes

Found while investigating why the same schedule produced different critical-path counts on a developer machine and a server. Our workaround is to pin the JVM locale before reading.

I have not identified which Asta fields carry exponent notation — only that the path is exercised and the outputs diverge. Happy to supply a sample file privately if that would help; the one used here is client-confidential so I would rather not attach it publicly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions